From 29bd972ae8ab688b732fc5f237a48fe066c67ece Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Tue, 20 Oct 2026 20:40:26 +1300 Subject: [PATCH 1/4] chore: update Python SDK to 26.0.0 --- CHANGELOG.md | 14 + appwrite/client.py | 4 +- appwrite/models/attribute_bigint.py | 5 + appwrite/models/attribute_boolean.py | 5 + appwrite/models/attribute_datetime.py | 5 + appwrite/models/attribute_email.py | 5 + appwrite/models/attribute_enum.py | 5 + appwrite/models/attribute_float.py | 5 + appwrite/models/attribute_integer.py | 5 + appwrite/models/attribute_ip.py | 5 + appwrite/models/attribute_line.py | 5 + appwrite/models/attribute_longtext.py | 5 + appwrite/models/attribute_mediumtext.py | 5 + appwrite/models/attribute_object.py | 5 + appwrite/models/attribute_point.py | 5 + appwrite/models/attribute_polygon.py | 5 + appwrite/models/attribute_relationship.py | 5 + appwrite/models/attribute_string.py | 5 + appwrite/models/attribute_text.py | 5 + appwrite/models/attribute_url.py | 5 + appwrite/models/attribute_varchar.py | 5 + appwrite/models/attribute_vector.py | 5 + appwrite/models/billing_plan.py | 5 + appwrite/models/bucket.py | 5 + appwrite/models/collection.py | 5 + appwrite/models/column_bigint.py | 5 + appwrite/models/column_boolean.py | 5 + appwrite/models/column_datetime.py | 5 + appwrite/models/column_email.py | 5 + appwrite/models/column_enum.py | 5 + appwrite/models/column_float.py | 5 + appwrite/models/column_index.py | 5 + appwrite/models/column_integer.py | 5 + appwrite/models/column_ip.py | 5 + appwrite/models/column_line.py | 5 + appwrite/models/column_longtext.py | 5 + appwrite/models/column_mediumtext.py | 5 + appwrite/models/column_point.py | 5 + appwrite/models/column_polygon.py | 5 + appwrite/models/column_relationship.py | 5 + appwrite/models/column_string.py | 5 + appwrite/models/column_text.py | 5 + appwrite/models/column_url.py | 5 + appwrite/models/column_varchar.py | 5 + appwrite/models/database.py | 5 + appwrite/models/ephemeral_key.py | 5 + appwrite/models/function.py | 5 + appwrite/models/index.py | 5 + appwrite/models/key.py | 5 + appwrite/models/policy_password_dictionary.py | 5 + .../models/policy_password_personal_data.py | 5 + appwrite/models/site.py | 5 + appwrite/models/table.py | 5 + appwrite/models/topic.py | 5 + appwrite/models/variable.py | 5 + appwrite/models/vectorsdb_collection.py | 5 + appwrite/models/webhook.py | 5 + appwrite/services/account.py | 49 +++ appwrite/services/activities.py | 5 + appwrite/services/backups.py | 15 + appwrite/services/databases.py | 283 +++++++++++++++++- appwrite/services/documents_db.py | 96 ++++++ appwrite/services/domains.py | 10 + appwrite/services/functions.py | 20 ++ appwrite/services/messaging.py | 20 ++ appwrite/services/mongo.py | 15 + appwrite/services/mysql.py | 15 + appwrite/services/organization.py | 5 + appwrite/services/postgresql.py | 15 + appwrite/services/project.py | 30 ++ appwrite/services/sites.py | 20 ++ appwrite/services/storage.py | 10 + appwrite/services/tables_db.py | 280 ++++++++++++++++- appwrite/services/vectors_db.py | 25 ++ appwrite/services/webhooks.py | 10 + docs/examples/account/list-memberships.md | 19 ++ docs/examples/activities/list-events.md | 3 +- docs/examples/backups/list-archives.md | 3 +- docs/examples/backups/list-policies.md | 3 +- docs/examples/backups/list-restorations.md | 3 +- .../databases/create-big-int-attribute.md | 3 +- .../databases/create-boolean-attribute.md | 3 +- docs/examples/databases/create-collection.md | 3 +- .../databases/create-datetime-attribute.md | 3 +- .../databases/create-email-attribute.md | 3 +- .../databases/create-enum-attribute.md | 3 +- .../databases/create-float-attribute.md | 3 +- docs/examples/databases/create-index.md | 3 +- .../databases/create-integer-attribute.md | 3 +- .../examples/databases/create-ip-attribute.md | 3 +- .../databases/create-line-attribute.md | 3 +- .../databases/create-longtext-attribute.md | 3 +- .../databases/create-mediumtext-attribute.md | 3 +- .../databases/create-point-attribute.md | 3 +- .../databases/create-polygon-attribute.md | 3 +- docs/examples/databases/create-query.md | 23 ++ .../create-relationship-attribute.md | 3 +- .../databases/create-string-attribute.md | 3 +- .../databases/create-text-attribute.md | 3 +- .../databases/create-url-attribute.md | 3 +- .../databases/create-varchar-attribute.md | 3 +- docs/examples/databases/create.md | 3 +- .../databases/update-big-int-attribute.md | 3 +- .../databases/update-boolean-attribute.md | 3 +- docs/examples/databases/update-collection.md | 3 +- .../databases/update-datetime-attribute.md | 3 +- .../databases/update-email-attribute.md | 3 +- .../databases/update-enum-attribute.md | 3 +- .../databases/update-float-attribute.md | 3 +- .../databases/update-integer-attribute.md | 3 +- .../examples/databases/update-ip-attribute.md | 3 +- .../databases/update-line-attribute.md | 3 +- .../databases/update-longtext-attribute.md | 3 +- .../databases/update-mediumtext-attribute.md | 3 +- .../databases/update-point-attribute.md | 3 +- .../databases/update-polygon-attribute.md | 3 +- .../update-relationship-attribute.md | 3 +- .../databases/update-string-attribute.md | 3 +- .../databases/update-text-attribute.md | 3 +- .../databases/update-url-attribute.md | 3 +- .../databases/update-varchar-attribute.md | 3 +- docs/examples/databases/update.md | 3 +- .../examples/documentsdb/create-collection.md | 3 +- docs/examples/documentsdb/create-index.md | 3 +- docs/examples/documentsdb/create-query.md | 23 ++ docs/examples/documentsdb/create.md | 1 + .../examples/documentsdb/update-collection.md | 3 +- docs/examples/documentsdb/update.md | 1 + docs/examples/domains/list-records.md | 3 +- docs/examples/domains/list.md | 3 +- docs/examples/functions/create-variable.md | 3 +- docs/examples/functions/create.md | 3 +- docs/examples/functions/update-variable.md | 3 +- docs/examples/functions/update.md | 1 + docs/examples/messaging/create-email.md | 3 +- docs/examples/messaging/create-topic.md | 3 +- docs/examples/messaging/update-email.md | 3 +- docs/examples/messaging/update-topic.md | 3 +- docs/examples/mongo/list-backup-policies.md | 3 +- docs/examples/mongo/list-backups.md | 3 +- docs/examples/mongo/list.md | 3 +- docs/examples/mysql/list-backup-policies.md | 3 +- docs/examples/mysql/list-backups.md | 3 +- docs/examples/mysql/list.md | 3 +- .../organization/update-project-key.md | 3 +- .../postgresql/list-backup-policies.md | 3 +- docs/examples/postgresql/list-backups.md | 3 +- docs/examples/postgresql/list.md | 3 +- docs/examples/project/create-variable.md | 3 +- docs/examples/project/update-key.md | 3 +- .../update-password-dictionary-policy.md | 3 +- .../update-password-personal-data-policy.md | 3 +- docs/examples/project/update-variable.md | 3 +- docs/examples/sites/create-variable.md | 3 +- docs/examples/sites/create.md | 3 +- docs/examples/sites/update-variable.md | 3 +- docs/examples/sites/update.md | 3 +- docs/examples/storage/create-bucket.md | 3 +- docs/examples/storage/update-bucket.md | 3 +- .../tablesdb/create-big-int-column.md | 3 +- .../tablesdb/create-boolean-column.md | 3 +- .../tablesdb/create-datetime-column.md | 3 +- docs/examples/tablesdb/create-email-column.md | 3 +- docs/examples/tablesdb/create-enum-column.md | 3 +- docs/examples/tablesdb/create-float-column.md | 3 +- docs/examples/tablesdb/create-index.md | 3 +- .../tablesdb/create-integer-column.md | 3 +- docs/examples/tablesdb/create-ip-column.md | 3 +- docs/examples/tablesdb/create-line-column.md | 3 +- .../tablesdb/create-longtext-column.md | 3 +- .../tablesdb/create-mediumtext-column.md | 3 +- docs/examples/tablesdb/create-point-column.md | 3 +- .../tablesdb/create-polygon-column.md | 3 +- docs/examples/tablesdb/create-query.md | 23 ++ .../tablesdb/create-relationship-column.md | 3 +- .../examples/tablesdb/create-string-column.md | 3 +- docs/examples/tablesdb/create-table.md | 3 +- docs/examples/tablesdb/create-text-column.md | 3 +- docs/examples/tablesdb/create-url-column.md | 3 +- .../tablesdb/create-varchar-column.md | 3 +- docs/examples/tablesdb/create.md | 1 + .../tablesdb/update-big-int-column.md | 3 +- .../tablesdb/update-boolean-column.md | 3 +- .../tablesdb/update-datetime-column.md | 3 +- docs/examples/tablesdb/update-email-column.md | 3 +- docs/examples/tablesdb/update-enum-column.md | 3 +- docs/examples/tablesdb/update-float-column.md | 3 +- .../tablesdb/update-integer-column.md | 3 +- docs/examples/tablesdb/update-ip-column.md | 3 +- docs/examples/tablesdb/update-line-column.md | 3 +- .../tablesdb/update-longtext-column.md | 3 +- .../tablesdb/update-mediumtext-column.md | 3 +- docs/examples/tablesdb/update-point-column.md | 3 +- .../tablesdb/update-polygon-column.md | 3 +- .../tablesdb/update-relationship-column.md | 3 +- .../examples/tablesdb/update-string-column.md | 3 +- docs/examples/tablesdb/update-table.md | 3 +- docs/examples/tablesdb/update-text-column.md | 3 +- docs/examples/tablesdb/update-url-column.md | 3 +- .../tablesdb/update-varchar-column.md | 3 +- docs/examples/tablesdb/update.md | 1 + docs/examples/vectorsdb/create-collection.md | 3 +- docs/examples/vectorsdb/create-index.md | 3 +- docs/examples/vectorsdb/create.md | 1 + docs/examples/vectorsdb/update-collection.md | 3 +- docs/examples/vectorsdb/update.md | 1 + docs/examples/webhooks/create.md | 3 +- docs/examples/webhooks/update.md | 3 +- pyproject.toml | 3 +- setup.py | 5 +- test/services/test_account.py | 16 + test/services/test_databases.py | 64 ++++ test/services/test_documents_db.py | 27 ++ test/services/test_functions.py | 7 + test/services/test_messaging.py | 3 + test/services/test_organization.py | 5 + test/services/test_project.py | 7 + test/services/test_sites.py | 7 + test/services/test_storage.py | 3 + test/services/test_tables_db.py | 64 ++++ test/services/test_vectors_db.py | 8 + test/services/test_webhooks.py | 4 + test/test_query.py | 13 + 223 files changed, 1784 insertions(+), 129 deletions(-) create mode 100644 docs/examples/account/list-memberships.md create mode 100644 docs/examples/databases/create-query.md create mode 100644 docs/examples/documentsdb/create-query.md create mode 100644 docs/examples/tablesdb/create-query.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 06eb25e3..94d8abf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Change Log +## 26.0.0 + +* Breaking: `tables_db`, `documents_db` and `vectors_db` `create` and `update` take `description` before `specification` +* Breaking: `functions.update` takes `description` before `email_enabled` +* Added: `create_query` on `databases`, `documents_db` and `tables_db` to send list queries in the request body +* Added: `account.list_memberships` +* Added: optional `description` on create and update for databases, collections, tables, attributes, columns, indexes, buckets, functions, sites, variables, topics, webhooks and keys +* Added: `description` on the matching models +* Added: `total` on list methods in `activities`, `backups`, `domains`, `mongo`, `mysql` and `postgresql` +* Added: `emails` on `messaging.create_email` and `messaging.update_email` +* Added: `deny_list` on `project.update_password_dictionary_policy`, `update_password_personal_data_policy` and their policy models +* Added: `BillingPlan.functions_allow_cron` +* Updated: package classifiers list Python 3.13 + ## 25.0.0 * Breaking: requests send `X-Appwrite-Response-Format: 2.5.0`; responses follow the 2.5 shapes diff --git a/appwrite/client.py b/appwrite/client.py index 49c7d754..4b4a5a0f 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -19,11 +19,11 @@ def __init__(self): self._endpoint = 'https://cloud.appwrite.io/v1' self._global_headers = { 'content-type': '', - 'user-agent': f'AppwritePythonSDK/25.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', + 'user-agent': f'AppwritePythonSDK/26.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', 'x-sdk-name': 'Python', 'x-sdk-platform': 'server', 'x-sdk-language': 'python', - 'x-sdk-version': '25.0.0', + 'x-sdk-version': '26.0.0', 'X-Appwrite-Response-Format': '2.5.0', } self._config = {} diff --git a/appwrite/models/attribute_bigint.py b/appwrite/models/attribute_bigint.py index 474812db..53639d02 100644 --- a/appwrite/models/attribute_bigint.py +++ b/appwrite/models/attribute_bigint.py @@ -13,6 +13,8 @@ class AttributeBigint(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeBigint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_boolean.py b/appwrite/models/attribute_boolean.py index 7ce326cb..91397ef4 100644 --- a/appwrite/models/attribute_boolean.py +++ b/appwrite/models/attribute_boolean.py @@ -13,6 +13,8 @@ class AttributeBoolean(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributeBoolean(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_datetime.py b/appwrite/models/attribute_datetime.py index cad6726b..fae2b1e6 100644 --- a/appwrite/models/attribute_datetime.py +++ b/appwrite/models/attribute_datetime.py @@ -13,6 +13,8 @@ class AttributeDatetime(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeDatetime(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_email.py b/appwrite/models/attribute_email.py index 4e3c33f6..2e8badcc 100644 --- a/appwrite/models/attribute_email.py +++ b/appwrite/models/attribute_email.py @@ -13,6 +13,8 @@ class AttributeEmail(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeEmail(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_enum.py b/appwrite/models/attribute_enum.py index fdf787fc..89d9db9d 100644 --- a/appwrite/models/attribute_enum.py +++ b/appwrite/models/attribute_enum.py @@ -13,6 +13,8 @@ class AttributeEnum(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeEnum(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_float.py b/appwrite/models/attribute_float.py index 209b42e8..8392f53a 100644 --- a/appwrite/models/attribute_float.py +++ b/appwrite/models/attribute_float.py @@ -13,6 +13,8 @@ class AttributeFloat(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeFloat(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_integer.py b/appwrite/models/attribute_integer.py index f268bb23..609eddac 100644 --- a/appwrite/models/attribute_integer.py +++ b/appwrite/models/attribute_integer.py @@ -13,6 +13,8 @@ class AttributeInteger(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeInteger(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_ip.py b/appwrite/models/attribute_ip.py index 9b334968..50f5a8db 100644 --- a/appwrite/models/attribute_ip.py +++ b/appwrite/models/attribute_ip.py @@ -13,6 +13,8 @@ class AttributeIp(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeIp(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_line.py b/appwrite/models/attribute_line.py index 4fbcba35..28a5e393 100644 --- a/appwrite/models/attribute_line.py +++ b/appwrite/models/attribute_line.py @@ -13,6 +13,8 @@ class AttributeLine(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributeLine(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_longtext.py b/appwrite/models/attribute_longtext.py index fa45bfc5..fe3a0bcb 100644 --- a/appwrite/models/attribute_longtext.py +++ b/appwrite/models/attribute_longtext.py @@ -13,6 +13,8 @@ class AttributeLongtext(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeLongtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_mediumtext.py b/appwrite/models/attribute_mediumtext.py index 59c8ee3f..bfacf192 100644 --- a/appwrite/models/attribute_mediumtext.py +++ b/appwrite/models/attribute_mediumtext.py @@ -13,6 +13,8 @@ class AttributeMediumtext(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeMediumtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_object.py b/appwrite/models/attribute_object.py index 16e736dd..9cfcb9e4 100644 --- a/appwrite/models/attribute_object.py +++ b/appwrite/models/attribute_object.py @@ -13,6 +13,8 @@ class AttributeObject(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -30,6 +32,7 @@ class AttributeObject(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -47,6 +50,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_point.py b/appwrite/models/attribute_point.py index 15c1d931..f93cb980 100644 --- a/appwrite/models/attribute_point.py +++ b/appwrite/models/attribute_point.py @@ -13,6 +13,8 @@ class AttributePoint(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributePoint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_polygon.py b/appwrite/models/attribute_polygon.py index 7d669d70..f7d1c64c 100644 --- a/appwrite/models/attribute_polygon.py +++ b/appwrite/models/attribute_polygon.py @@ -13,6 +13,8 @@ class AttributePolygon(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributePolygon(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_relationship.py b/appwrite/models/attribute_relationship.py index fc549373..de63c8d3 100644 --- a/appwrite/models/attribute_relationship.py +++ b/appwrite/models/attribute_relationship.py @@ -13,6 +13,8 @@ class AttributeRelationship(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -42,6 +44,7 @@ class AttributeRelationship(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -65,6 +68,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_string.py b/appwrite/models/attribute_string.py index 7cca07a0..4790f71b 100644 --- a/appwrite/models/attribute_string.py +++ b/appwrite/models/attribute_string.py @@ -13,6 +13,8 @@ class AttributeString(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeString(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_text.py b/appwrite/models/attribute_text.py index b66f2a50..4fe71e87 100644 --- a/appwrite/models/attribute_text.py +++ b/appwrite/models/attribute_text.py @@ -13,6 +13,8 @@ class AttributeText(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeText(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_url.py b/appwrite/models/attribute_url.py index 50ad0ffe..ff34a0f7 100644 --- a/appwrite/models/attribute_url.py +++ b/appwrite/models/attribute_url.py @@ -13,6 +13,8 @@ class AttributeUrl(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeUrl(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_varchar.py b/appwrite/models/attribute_varchar.py index 65b806c2..b87ef6b0 100644 --- a/appwrite/models/attribute_varchar.py +++ b/appwrite/models/attribute_varchar.py @@ -13,6 +13,8 @@ class AttributeVarchar(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeVarchar(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_vector.py b/appwrite/models/attribute_vector.py index 1b1d921d..33344e95 100644 --- a/appwrite/models/attribute_vector.py +++ b/appwrite/models/attribute_vector.py @@ -13,6 +13,8 @@ class AttributeVector(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributeVector(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/billing_plan.py b/appwrite/models/billing_plan.py index 545e198b..a5e65184 100644 --- a/appwrite/models/billing_plan.py +++ b/appwrite/models/billing_plan.py @@ -155,6 +155,8 @@ class BillingPlan(AppwriteModel): Maximum function and site deployment size in MB functionsintervalminimum : float Shortest function schedule interval allowed, in minutes. 0 allows every interval. + functionsallowcron : bool + Does the plan allow cron function schedules or only intervals. databasesallowencrypt : bool Does the plan support encrypted string attributes or not. limits : Optional[BillingPlanLimits] @@ -238,6 +240,7 @@ class BillingPlan(AppwriteModel): deploymentsize: float = Field(..., alias='deploymentSize') buildsize: float = Field(..., alias='buildSize') functionsintervalminimum: float = Field(..., alias='functionsIntervalMinimum') + functionsallowcron: bool = Field(..., alias='functionsAllowCron') databasesallowencrypt: bool = Field(..., alias='databasesAllowEncrypt') limits: Optional[BillingPlanLimits] = Field(default=None, alias='limits') group: BillingPlanGroup = Field(..., alias='group') @@ -370,6 +373,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['buildSize'] = 0 if 'functionsIntervalMinimum' not in data and 'functionsintervalminimum' not in data: data['functionsIntervalMinimum'] = 0 + if 'functionsAllowCron' not in data and 'functionsallowcron' not in data: + data['functionsAllowCron'] = True if 'databasesAllowEncrypt' not in data and 'databasesallowencrypt' not in data: data['databasesAllowEncrypt'] = False if 'databaseComputeCredit' not in data and 'databasecomputecredit' not in data: diff --git a/appwrite/models/bucket.py b/appwrite/models/bucket.py index 7718a57d..60110ee7 100644 --- a/appwrite/models/bucket.py +++ b/appwrite/models/bucket.py @@ -22,6 +22,8 @@ class Bucket(AppwriteModel): Whether file-level security is enabled. [Learn more about permissions](https://appwrite.io/docs/permissions). name : str Bucket name. + description : str + Optional description for the bucket. enabled : bool Bucket enabled. maximumfilesize : float @@ -46,6 +48,7 @@ class Bucket(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') filesecurity: bool = Field(..., alias='fileSecurity') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') maximumfilesize: float = Field(..., alias='maximumFileSize') allowedfileextensions: List[Any] = Field(..., alias='allowedFileExtensions') @@ -74,6 +77,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['fileSecurity'] = False if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'maximumFileSize' not in data and 'maximumfilesize' not in data: diff --git a/appwrite/models/collection.py b/appwrite/models/collection.py index 419ec48a..57b47caf 100644 --- a/appwrite/models/collection.py +++ b/appwrite/models/collection.py @@ -41,6 +41,8 @@ class Collection(AppwriteModel): Database ID. name : str Collection name. + description : str + Optional description for the collection. enabled : bool Collection enabled. Can be 'enabled' or 'disabled'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys. documentsecurity : bool @@ -61,6 +63,7 @@ class Collection(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') databaseid: str = Field(..., alias='databaseId') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') documentsecurity: bool = Field(..., alias='documentSecurity') attributes: List[ @@ -108,6 +111,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['databaseId'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'documentSecurity' not in data and 'documentsecurity' not in data: diff --git a/appwrite/models/column_bigint.py b/appwrite/models/column_bigint.py index b2fa8225..8fe0857c 100644 --- a/appwrite/models/column_bigint.py +++ b/appwrite/models/column_bigint.py @@ -13,6 +13,8 @@ class ColumnBigint(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnBigint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_boolean.py b/appwrite/models/column_boolean.py index 33ebd132..1ceb8170 100644 --- a/appwrite/models/column_boolean.py +++ b/appwrite/models/column_boolean.py @@ -13,6 +13,8 @@ class ColumnBoolean(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnBoolean(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_datetime.py b/appwrite/models/column_datetime.py index 46c1d04a..c8db0d06 100644 --- a/appwrite/models/column_datetime.py +++ b/appwrite/models/column_datetime.py @@ -13,6 +13,8 @@ class ColumnDatetime(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnDatetime(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_email.py b/appwrite/models/column_email.py index 68094615..3940ef16 100644 --- a/appwrite/models/column_email.py +++ b/appwrite/models/column_email.py @@ -13,6 +13,8 @@ class ColumnEmail(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnEmail(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_enum.py b/appwrite/models/column_enum.py index 3dd815af..081eba50 100644 --- a/appwrite/models/column_enum.py +++ b/appwrite/models/column_enum.py @@ -13,6 +13,8 @@ class ColumnEnum(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnEnum(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_float.py b/appwrite/models/column_float.py index aa089000..6ed443c3 100644 --- a/appwrite/models/column_float.py +++ b/appwrite/models/column_float.py @@ -13,6 +13,8 @@ class ColumnFloat(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnFloat(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_index.py b/appwrite/models/column_index.py index 54419edf..1124ab21 100644 --- a/appwrite/models/column_index.py +++ b/appwrite/models/column_index.py @@ -18,6 +18,8 @@ class ColumnIndex(AppwriteModel): Index update date in ISO 8601 format. key : str Index Key. + description : str + Optional description for the index. type : str Index type. status : str @@ -36,6 +38,7 @@ class ColumnIndex(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: str = Field(..., alias='status') error: str = Field(..., alias='error') @@ -58,6 +61,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'status' not in data: diff --git a/appwrite/models/column_integer.py b/appwrite/models/column_integer.py index 9ad8fbd4..64c9feaf 100644 --- a/appwrite/models/column_integer.py +++ b/appwrite/models/column_integer.py @@ -13,6 +13,8 @@ class ColumnInteger(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnInteger(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_ip.py b/appwrite/models/column_ip.py index fe5de6b6..24b9cdfa 100644 --- a/appwrite/models/column_ip.py +++ b/appwrite/models/column_ip.py @@ -13,6 +13,8 @@ class ColumnIp(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnIp(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_line.py b/appwrite/models/column_line.py index 0216047f..e715deec 100644 --- a/appwrite/models/column_line.py +++ b/appwrite/models/column_line.py @@ -13,6 +13,8 @@ class ColumnLine(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnLine(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_longtext.py b/appwrite/models/column_longtext.py index ce3585fd..8a27318d 100644 --- a/appwrite/models/column_longtext.py +++ b/appwrite/models/column_longtext.py @@ -13,6 +13,8 @@ class ColumnLongtext(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnLongtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_mediumtext.py b/appwrite/models/column_mediumtext.py index 31717475..dfb5c0cc 100644 --- a/appwrite/models/column_mediumtext.py +++ b/appwrite/models/column_mediumtext.py @@ -13,6 +13,8 @@ class ColumnMediumtext(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnMediumtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_point.py b/appwrite/models/column_point.py index 0217c08e..14997cea 100644 --- a/appwrite/models/column_point.py +++ b/appwrite/models/column_point.py @@ -13,6 +13,8 @@ class ColumnPoint(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnPoint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_polygon.py b/appwrite/models/column_polygon.py index 3bcc61c9..1d8c722c 100644 --- a/appwrite/models/column_polygon.py +++ b/appwrite/models/column_polygon.py @@ -13,6 +13,8 @@ class ColumnPolygon(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnPolygon(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_relationship.py b/appwrite/models/column_relationship.py index 5b1f96b9..fd44c287 100644 --- a/appwrite/models/column_relationship.py +++ b/appwrite/models/column_relationship.py @@ -13,6 +13,8 @@ class ColumnRelationship(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -42,6 +44,7 @@ class ColumnRelationship(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -65,6 +68,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_string.py b/appwrite/models/column_string.py index c8d5efcb..a6d8da11 100644 --- a/appwrite/models/column_string.py +++ b/appwrite/models/column_string.py @@ -13,6 +13,8 @@ class ColumnString(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnString(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_text.py b/appwrite/models/column_text.py index 29ea6f5b..6be3b6f5 100644 --- a/appwrite/models/column_text.py +++ b/appwrite/models/column_text.py @@ -13,6 +13,8 @@ class ColumnText(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnText(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_url.py b/appwrite/models/column_url.py index bb51776b..60bfcfa8 100644 --- a/appwrite/models/column_url.py +++ b/appwrite/models/column_url.py @@ -13,6 +13,8 @@ class ColumnUrl(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnUrl(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_varchar.py b/appwrite/models/column_varchar.py index 113ca34f..d09f0c94 100644 --- a/appwrite/models/column_varchar.py +++ b/appwrite/models/column_varchar.py @@ -13,6 +13,8 @@ class ColumnVarchar(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnVarchar(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/database.py b/appwrite/models/database.py index 931cb68d..c17f2a63 100644 --- a/appwrite/models/database.py +++ b/appwrite/models/database.py @@ -18,6 +18,8 @@ class Database(AppwriteModel): Database ID. name : str Database name. + description : str + Optional description for the database. createdat : str Database creation date in ISO 8601 format. updatedat : str @@ -48,6 +50,7 @@ class Database(AppwriteModel): id: str = Field(..., alias='$id') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') enabled: bool = Field(..., alias='enabled') @@ -73,6 +76,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$id'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if '$createdAt' not in data and 'createdat' not in data: data['$createdAt'] = '' if '$updatedAt' not in data and 'updatedat' not in data: diff --git a/appwrite/models/ephemeral_key.py b/appwrite/models/ephemeral_key.py index 7800c687..1fda1395 100644 --- a/appwrite/models/ephemeral_key.py +++ b/appwrite/models/ephemeral_key.py @@ -18,6 +18,8 @@ class EphemeralKey(AppwriteModel): Key update date in ISO 8601 format. name : str Key name. + description : str + Optional description for the key. expire : str Key expiration date in ISO 8601 format. scopes : List[Any] @@ -34,6 +36,7 @@ class EphemeralKey(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') expire: str = Field(..., alias='expire') scopes: List[Any] = Field(..., alias='scopes') secret: str = Field(..., alias='secret') @@ -55,6 +58,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'expire' not in data: data['expire'] = '' if 'scopes' not in data: diff --git a/appwrite/models/function.py b/appwrite/models/function.py index 87862433..c25b1017 100644 --- a/appwrite/models/function.py +++ b/appwrite/models/function.py @@ -21,6 +21,8 @@ class Function(AppwriteModel): Execution permissions. name : str Function name. + description : str + Optional description for the function. enabled : bool Function enabled. live : bool @@ -86,6 +88,7 @@ class Function(AppwriteModel): updatedat: str = Field(..., alias='$updatedAt') execute: List[Any] = Field(..., alias='execute') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') live: bool = Field(..., alias='live') logging: bool = Field(..., alias='logging') @@ -133,6 +136,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['execute'] = [] if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'live' not in data: diff --git a/appwrite/models/index.py b/appwrite/models/index.py index 21613a19..64dadef6 100644 --- a/appwrite/models/index.py +++ b/appwrite/models/index.py @@ -19,6 +19,8 @@ class Index(AppwriteModel): Index update date in ISO 8601 format. key : str Index key. + description : str + Optional description for the index. type : str Index type. status : IndexStatus @@ -37,6 +39,7 @@ class Index(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: IndexStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -59,6 +62,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/key.py b/appwrite/models/key.py index f8e209fe..7888c9de 100644 --- a/appwrite/models/key.py +++ b/appwrite/models/key.py @@ -18,6 +18,8 @@ class Key(AppwriteModel): Key update date in ISO 8601 format. name : str Key name. + description : str + Optional description for the key. expire : str Key expiration date in ISO 8601 format. scopes : List[Any] @@ -34,6 +36,7 @@ class Key(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') expire: str = Field(..., alias='expire') scopes: List[Any] = Field(..., alias='scopes') secret: str = Field(..., alias='secret') @@ -55,6 +58,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'expire' not in data: data['expire'] = '' if 'scopes' not in data: diff --git a/appwrite/models/policy_password_dictionary.py b/appwrite/models/policy_password_dictionary.py index 37bbbeeb..6527f197 100644 --- a/appwrite/models/policy_password_dictionary.py +++ b/appwrite/models/policy_password_dictionary.py @@ -14,10 +14,13 @@ class PolicyPasswordDictionary(AppwriteModel): Policy ID. enabled : bool Whether password dictionary policy is enabled. + denylist : List[Any] + Custom passwords blocked by case-sensitive equality when enabled. """ id: str = Field(..., alias='$id') enabled: bool = Field(..., alias='enabled') + denylist: List[Any] = Field(..., alias='denyList') @model_validator(mode='before') @classmethod @@ -30,4 +33,6 @@ def _apply_defaults(cls, data: Any) -> Any: data['$id'] = '' if 'enabled' not in data: data['enabled'] = False + if 'denyList' not in data and 'denylist' not in data: + data['denyList'] = [] return data diff --git a/appwrite/models/policy_password_personal_data.py b/appwrite/models/policy_password_personal_data.py index 64f12c7c..82f6158e 100644 --- a/appwrite/models/policy_password_personal_data.py +++ b/appwrite/models/policy_password_personal_data.py @@ -14,10 +14,13 @@ class PolicyPasswordPersonalData(AppwriteModel): Policy ID. enabled : bool Whether password personal data policy is enabled. + denylist : List[Any] + Custom terms blocked by case-insensitive substring matching when enabled. """ id: str = Field(..., alias='$id') enabled: bool = Field(..., alias='enabled') + denylist: List[Any] = Field(..., alias='denyList') @model_validator(mode='before') @classmethod @@ -30,4 +33,6 @@ def _apply_defaults(cls, data: Any) -> Any: data['$id'] = '' if 'enabled' not in data: data['enabled'] = False + if 'denyList' not in data and 'denylist' not in data: + data['denyList'] = [] return data diff --git a/appwrite/models/site.py b/appwrite/models/site.py index bef638fc..346e7fae 100644 --- a/appwrite/models/site.py +++ b/appwrite/models/site.py @@ -19,6 +19,8 @@ class Site(AppwriteModel): Site update date in ISO 8601 format. name : str Site name. + description : str + Optional description for the site. enabled : bool Site enabled. live : bool @@ -87,6 +89,7 @@ class Site(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') live: bool = Field(..., alias='live') logging: bool = Field(..., alias='logging') @@ -134,6 +137,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'live' not in data: diff --git a/appwrite/models/table.py b/appwrite/models/table.py index 41c97ef3..944497ab 100644 --- a/appwrite/models/table.py +++ b/appwrite/models/table.py @@ -41,6 +41,8 @@ class Table(AppwriteModel): Database ID. name : str Table name. + description : str + Optional description for the table. enabled : bool Table enabled. Can be 'enabled' or 'disabled'. When disabled, the table is inaccessible to users, but remains accessible to Server SDKs using API keys. rowsecurity : bool @@ -61,6 +63,7 @@ class Table(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') databaseid: str = Field(..., alias='databaseId') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') rowsecurity: bool = Field(..., alias='rowSecurity') columns: List[ @@ -108,6 +111,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['databaseId'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'rowSecurity' not in data and 'rowsecurity' not in data: diff --git a/appwrite/models/topic.py b/appwrite/models/topic.py index b3566eec..f7d3da42 100644 --- a/appwrite/models/topic.py +++ b/appwrite/models/topic.py @@ -18,6 +18,8 @@ class Topic(AppwriteModel): Topic update date in ISO 8601 format. name : str The name of the topic. + description : str + Optional description for the topic. emailtotal : float Total count of email subscribers subscribed to the topic. smstotal : float @@ -36,6 +38,7 @@ class Topic(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') emailtotal: float = Field(..., alias='emailTotal') smstotal: float = Field(..., alias='smsTotal') pushtotal: float = Field(..., alias='pushTotal') @@ -58,6 +61,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'emailTotal' not in data and 'emailtotal' not in data: data['emailTotal'] = 0 if 'smsTotal' not in data and 'smstotal' not in data: diff --git a/appwrite/models/variable.py b/appwrite/models/variable.py index a50d58f0..a667b1c9 100644 --- a/appwrite/models/variable.py +++ b/appwrite/models/variable.py @@ -18,6 +18,8 @@ class Variable(AppwriteModel): Variable creation date in ISO 8601 format. key : str Variable key. + description : str + Optional description for the variable. value : str Variable value. secret : bool @@ -32,6 +34,7 @@ class Variable(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') key: str = Field(..., alias='key') + description: str = Field(..., alias='description') value: str = Field(..., alias='value') secret: bool = Field(..., alias='secret') resourcetype: str = Field(..., alias='resourceType') @@ -52,6 +55,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'value' not in data: data['value'] = '' if 'secret' not in data: diff --git a/appwrite/models/vectorsdb_collection.py b/appwrite/models/vectorsdb_collection.py index e4ca60ff..84098b26 100644 --- a/appwrite/models/vectorsdb_collection.py +++ b/appwrite/models/vectorsdb_collection.py @@ -25,6 +25,8 @@ class VectorsdbCollection(AppwriteModel): Database ID. name : str Collection name. + description : str + Optional description for the collection. enabled : bool Collection enabled. Can be 'enabled' or 'disabled'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys. documentsecurity : bool @@ -47,6 +49,7 @@ class VectorsdbCollection(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') databaseid: str = Field(..., alias='databaseId') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') documentsecurity: bool = Field(..., alias='documentSecurity') attributes: List[ @@ -79,6 +82,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['databaseId'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'documentSecurity' not in data and 'documentsecurity' not in data: diff --git a/appwrite/models/webhook.py b/appwrite/models/webhook.py index 3114336d..db6fc715 100644 --- a/appwrite/models/webhook.py +++ b/appwrite/models/webhook.py @@ -18,6 +18,8 @@ class Webhook(AppwriteModel): Webhook update date in ISO 8601 format. name : str Webhook name. + description : str + Optional description for the webhook. url : str Webhook URL endpoint. events : List[Any] @@ -42,6 +44,7 @@ class Webhook(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') url: str = Field(..., alias='url') events: List[Any] = Field(..., alias='events') tls: bool = Field(..., alias='tls') @@ -67,6 +70,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'url' not in data: data['url'] = '' if 'events' not in data: diff --git a/appwrite/services/account.py b/appwrite/services/account.py index 6891e202..9788e53c 100644 --- a/appwrite/services/account.py +++ b/appwrite/services/account.py @@ -9,6 +9,7 @@ from ..models.oauth2_consent_token_list import Oauth2ConsentTokenList from ..models.oauth2_consent_token import Oauth2ConsentToken from ..models.identity_list import IdentityList +from ..models.membership_list import MembershipList from ..enums.authenticator_type import AuthenticatorType from ..models.mfa_type import MfaType from ..enums.authentication_factor import AuthenticationFactor @@ -539,6 +540,50 @@ def delete_identity( return response + def list_memberships( + self, + queries: Optional[List[str]] = None, + total: Optional[bool] = None, + ) -> MembershipList: + """ + Get the team memberships of the currently logged in user. + + Parameters + ---------- + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + Returns + ------- + MembershipList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/memberships' + api_params = {} + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=MembershipList) + def update_mfa( self, mfa: bool, @@ -1482,6 +1527,8 @@ def update_recovery( Please note that in order to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface. + The new password must satisfy the project's enabled password policies, including custom deny lists. Dictionary entries match the entire password case-sensitively; personal-data terms match substrings case-insensitively. Personal-data validation runs after token verification against the recovered user's ID, name, email, and phone, closing the previous recovery gap for profile-based checks. A rejected password leaves the existing credentials unchanged and the recovery token usable. + Parameters ---------- user_id : str @@ -1583,6 +1630,8 @@ def update_recovery_otp( """ Use this endpoint to complete the user password recovery process using the 6-digit code that was emailed by [createRecoveryOTP](https://appwrite.io/docs/references/cloud/client-web/account#createRecoveryOTP). Pass the **userId** of the user along with the **secret** code from the email and the new **password** to set. If confirmed, this route will return a 200 status code, the code is consumed and the user's password is updated. + The new password must satisfy the project's enabled password policies, including custom deny lists. Dictionary entries match the entire password case-sensitively; personal-data terms match substrings case-insensitively. Personal-data validation runs after token verification against the recovered user's ID, name, email, and phone, closing the previous recovery gap for profile-based checks. A rejected password leaves the existing credentials unchanged and the recovery token usable. + Parameters ---------- user_id : str diff --git a/appwrite/services/activities.py b/appwrite/services/activities.py index 1c33eeef..8556609f 100644 --- a/appwrite/services/activities.py +++ b/appwrite/services/activities.py @@ -15,6 +15,7 @@ def __init__(self, client) -> None: def list_events( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> ActivityEventList: """ List all events for selected filters. @@ -23,6 +24,8 @@ def list_events( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on attributes such as userId, teamId, etc. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- ActivityEventList @@ -38,6 +41,8 @@ def list_events( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/backups.py b/appwrite/services/backups.py index c6e75e98..b33b009c 100644 --- a/appwrite/services/backups.py +++ b/appwrite/services/backups.py @@ -20,6 +20,7 @@ def __init__(self, client) -> None: def list_archives( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupArchiveList: """ List all archives for a project. @@ -28,6 +29,8 @@ def list_archives( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupArchiveList @@ -43,6 +46,8 @@ def list_archives( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -186,6 +191,7 @@ def delete_archive( def list_policies( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List all policies for a project. @@ -194,6 +200,8 @@ def list_policies( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -209,6 +217,8 @@ def list_policies( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -506,6 +516,7 @@ def create_restoration( def list_restorations( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupRestorationList: """ List all backup restorations for a project. @@ -514,6 +525,8 @@ def list_restorations( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupRestorationList @@ -529,6 +542,8 @@ def list_restorations( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/databases.py b/appwrite/services/databases.py index 5e5fa631..8d7c85cb 100644 --- a/appwrite/services/databases.py +++ b/appwrite/services/databases.py @@ -103,6 +103,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> Database: """ Create a new Database. @@ -117,6 +118,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. Returns ------- Database @@ -138,6 +141,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -481,6 +486,7 @@ def update( database_id: str, name: Optional[str] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> Database: """ Update a database by its unique ID. @@ -495,6 +501,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. Returns ------- Database @@ -515,6 +523,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -642,6 +652,7 @@ def create_collection( enabled: Optional[bool] = None, attributes: Optional[List[Dict[str, Any]]] = None, indexes: Optional[List[Dict[str, Any]]] = None, + description: Optional[str] = None, ) -> Collection: """ Create a new Collection. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console. @@ -666,6 +677,8 @@ def create_collection( Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, varchar, text, mediumtext, longtext, integer, bigint, double, boolean, datetime, point, linestring, polygon, email, url, ip, enum), size (integer, required for string and varchar types), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options. indexes : Optional[List[Dict[str, Any]]] Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial, trigram), attributes (array of attribute keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional). + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -698,6 +711,8 @@ def create_collection( api_params['attributes'] = self._normalize_value(attributes) if indexes is not None: api_params['indexes'] = self._normalize_value(indexes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -771,6 +786,7 @@ def update_collection( document_security: Optional[bool] = None, enabled: Optional[bool] = None, purge: Optional[bool] = None, + description: Optional[str] = None, ) -> Collection: """ Update a collection by its unique ID. @@ -793,6 +809,8 @@ def update_collection( Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. purge : Optional[bool] When true, purge all cached list responses for this collection as part of the update. Use this to force readers to see fresh data immediately instead of waiting for the cache TTL to expire. + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -822,6 +840,8 @@ def update_collection( api_params['enabled'] = self._normalize_value(enabled) if purge is not None: api_params['purge'] = self._normalize_value(purge) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -956,6 +976,7 @@ def create_big_int_attribute( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeBigint: """ Create a bigint attribute. Optionally, minimum and maximum values can be provided. @@ -980,6 +1001,8 @@ def create_big_int_attribute( Default value. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBigint @@ -1013,6 +1036,8 @@ def create_big_int_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1038,6 +1063,7 @@ def update_big_int_attribute( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeBigint: """ Update a bigint attribute. Changing the `default` value will not update already existing documents. @@ -1062,6 +1088,8 @@ def update_big_int_attribute( Maximum value new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBigint @@ -1094,6 +1122,8 @@ def update_big_int_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1117,6 +1147,7 @@ def create_boolean_attribute( required: bool, default: Optional[bool] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeBoolean: """ Create a boolean attribute. @@ -1137,6 +1168,8 @@ def create_boolean_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBoolean @@ -1166,6 +1199,8 @@ def create_boolean_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1189,6 +1224,7 @@ def update_boolean_attribute( required: bool, default: Optional[bool], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeBoolean: """ Update a boolean attribute. Changing the `default` value will not update already existing documents. @@ -1209,6 +1245,8 @@ def update_boolean_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBoolean @@ -1237,6 +1275,8 @@ def update_boolean_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1260,6 +1300,7 @@ def create_datetime_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeDatetime: """ Create a date time attribute according to the ISO 8601 standard. @@ -1280,6 +1321,8 @@ def create_datetime_attribute( Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeDatetime @@ -1309,6 +1352,8 @@ def create_datetime_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1332,6 +1377,7 @@ def update_datetime_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeDatetime: """ Update a date time attribute. Changing the `default` value will not update already existing documents. @@ -1352,6 +1398,8 @@ def update_datetime_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeDatetime @@ -1380,6 +1428,8 @@ def update_datetime_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1403,6 +1453,7 @@ def create_email_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeEmail: """ Create an email attribute. @@ -1423,6 +1474,8 @@ def create_email_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEmail @@ -1452,6 +1505,8 @@ def create_email_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1475,6 +1530,7 @@ def update_email_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeEmail: """ Update an email attribute. Changing the `default` value will not update already existing documents. @@ -1495,6 +1551,8 @@ def update_email_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEmail @@ -1523,6 +1581,8 @@ def update_email_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1547,6 +1607,7 @@ def create_enum_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeEnum: """ Create an enum attribute. The `elements` param acts as a white-list of accepted values for this attribute. @@ -1569,6 +1630,8 @@ def create_enum_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEnum @@ -1601,6 +1664,8 @@ def create_enum_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1625,6 +1690,7 @@ def update_enum_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeEnum: """ Update an enum attribute. Changing the `default` value will not update already existing documents. @@ -1647,6 +1713,8 @@ def update_enum_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEnum @@ -1678,6 +1746,8 @@ def update_enum_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1703,6 +1773,7 @@ def create_float_attribute( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeFloat: """ Create a float attribute. Optionally, minimum and maximum values can be provided. @@ -1727,6 +1798,8 @@ def create_float_attribute( Default value. Cannot be set when required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeFloat @@ -1760,6 +1833,8 @@ def create_float_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1785,6 +1860,7 @@ def update_float_attribute( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeFloat: """ Update a float attribute. Changing the `default` value will not update already existing documents. @@ -1809,6 +1885,8 @@ def update_float_attribute( Maximum value. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeFloat @@ -1841,6 +1919,8 @@ def update_float_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1866,6 +1946,7 @@ def create_integer_attribute( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeInteger: """ Create an integer attribute. Optionally, minimum and maximum values can be provided. @@ -1890,6 +1971,8 @@ def create_integer_attribute( Default value. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeInteger @@ -1923,6 +2006,8 @@ def create_integer_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1948,6 +2033,7 @@ def update_integer_attribute( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeInteger: """ Update an integer attribute. Changing the `default` value will not update already existing documents. @@ -1972,6 +2058,8 @@ def update_integer_attribute( Maximum value new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeInteger @@ -2004,6 +2092,8 @@ def update_integer_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2027,6 +2117,7 @@ def create_ip_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeIp: """ Create IP address attribute. @@ -2047,6 +2138,8 @@ def create_ip_attribute( Default value. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeIp @@ -2076,6 +2169,8 @@ def create_ip_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2099,6 +2194,7 @@ def update_ip_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeIp: """ Update an ip attribute. Changing the `default` value will not update already existing documents. @@ -2119,6 +2215,8 @@ def update_ip_attribute( Default value. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeIp @@ -2147,6 +2245,8 @@ def update_ip_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2169,6 +2269,7 @@ def create_line_attribute( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> AttributeLine: """ Create a geometric line attribute. @@ -2187,6 +2288,8 @@ def create_line_attribute( Is attribute required? default : Optional[List[List[Any]]] Default value for attribute when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when attribute is required. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLine @@ -2214,6 +2317,8 @@ def create_line_attribute( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2237,6 +2342,7 @@ def update_line_attribute( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeLine: """ Update a line attribute. Changing the `default` value will not update already existing documents. @@ -2257,6 +2363,8 @@ def update_line_attribute( Default value for attribute when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLine @@ -2286,6 +2394,8 @@ def update_line_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2310,6 +2420,7 @@ def create_longtext_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeLongtext: """ Create a longtext attribute. @@ -2332,6 +2443,8 @@ def create_longtext_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLongtext @@ -2363,6 +2476,8 @@ def create_longtext_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2386,6 +2501,7 @@ def update_longtext_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeLongtext: """ Update a longtext attribute. Changing the `default` value will not update already existing documents. @@ -2406,6 +2522,8 @@ def update_longtext_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLongtext @@ -2434,6 +2552,8 @@ def update_longtext_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2458,6 +2578,7 @@ def create_mediumtext_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeMediumtext: """ Create a mediumtext attribute. @@ -2480,6 +2601,8 @@ def create_mediumtext_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeMediumtext @@ -2511,6 +2634,8 @@ def create_mediumtext_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2534,6 +2659,7 @@ def update_mediumtext_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeMediumtext: """ Update a mediumtext attribute. Changing the `default` value will not update already existing documents. @@ -2554,6 +2680,8 @@ def update_mediumtext_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeMediumtext @@ -2582,6 +2710,8 @@ def update_mediumtext_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2604,6 +2734,7 @@ def create_point_attribute( key: str, required: bool, default: Optional[List[float]] = None, + description: Optional[str] = None, ) -> AttributePoint: """ Create a geometric point attribute. @@ -2622,6 +2753,8 @@ def create_point_attribute( Is attribute required? default : Optional[List[float]] Default value for attribute when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when attribute is required. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePoint @@ -2649,6 +2782,8 @@ def create_point_attribute( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2672,6 +2807,7 @@ def update_point_attribute( required: bool, default: Optional[List[float]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributePoint: """ Update a point attribute. Changing the `default` value will not update already existing documents. @@ -2692,6 +2828,8 @@ def update_point_attribute( Default value for attribute when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePoint @@ -2721,6 +2859,8 @@ def update_point_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2743,6 +2883,7 @@ def create_polygon_attribute( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> AttributePolygon: """ Create a geometric polygon attribute. @@ -2761,6 +2902,8 @@ def create_polygon_attribute( Is attribute required? default : Optional[List[List[Any]]] Default value for attribute when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when attribute is required. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePolygon @@ -2788,6 +2931,8 @@ def create_polygon_attribute( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2811,6 +2956,7 @@ def update_polygon_attribute( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributePolygon: """ Update a polygon attribute. Changing the `default` value will not update already existing documents. @@ -2831,6 +2977,8 @@ def update_polygon_attribute( Default value for attribute when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePolygon @@ -2860,6 +3008,8 @@ def update_polygon_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2885,6 +3035,7 @@ def create_relationship_attribute( key: Optional[str] = None, two_way_key: Optional[str] = None, on_delete: Optional[RelationMutate] = None, + description: Optional[str] = None, ) -> AttributeRelationship: """ Create relationship attribute. [Learn more about relationship attributes](https://appwrite.io/docs/databases-relationships#relationship-attributes). @@ -2909,6 +3060,8 @@ def create_relationship_attribute( Two Way Attribute Key. on_delete : Optional[RelationMutate] Delete constraint. Possible values are: cascade, restrict, setNull. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeRelationship @@ -2942,6 +3095,8 @@ def create_relationship_attribute( api_params['twoWayKey'] = self._normalize_value(two_way_key) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2964,6 +3119,7 @@ def update_relationship_attribute( key: str, on_delete: Optional[RelationMutate] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeRelationship: """ Update relationship attribute. [Learn more about relationship attributes](https://appwrite.io/docs/databases-relationships#relationship-attributes). @@ -2982,6 +3138,8 @@ def update_relationship_attribute( Delete constraint. Possible values are: cascade, restrict, setNull. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeRelationship @@ -3008,6 +3166,8 @@ def update_relationship_attribute( api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3033,6 +3193,7 @@ def create_string_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeString: """ Create a string attribute. @@ -3057,6 +3218,8 @@ def create_string_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeString @@ -3091,6 +3254,8 @@ def create_string_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3115,6 +3280,7 @@ def update_string_attribute( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeString: """ Update a string attribute. Changing the `default` value will not update already existing documents. @@ -3137,6 +3303,8 @@ def update_string_attribute( Maximum size of the string attribute. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeString @@ -3167,6 +3335,8 @@ def update_string_attribute( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3191,6 +3361,7 @@ def create_text_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeText: """ Create a text attribute. @@ -3213,6 +3384,8 @@ def create_text_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeText @@ -3244,6 +3417,8 @@ def create_text_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3267,6 +3442,7 @@ def update_text_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeText: """ Update a text attribute. Changing the `default` value will not update already existing documents. @@ -3287,6 +3463,8 @@ def update_text_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeText @@ -3315,6 +3493,8 @@ def update_text_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3338,6 +3518,7 @@ def create_url_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeUrl: """ Create a URL attribute. @@ -3358,6 +3539,8 @@ def create_url_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeUrl @@ -3387,6 +3570,8 @@ def create_url_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3410,6 +3595,7 @@ def update_url_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeUrl: """ Update an url attribute. Changing the `default` value will not update already existing documents. @@ -3430,6 +3616,8 @@ def update_url_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeUrl @@ -3458,6 +3646,8 @@ def update_url_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3483,6 +3673,7 @@ def create_varchar_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeVarchar: """ Create a varchar attribute. @@ -3507,6 +3698,8 @@ def create_varchar_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeVarchar @@ -3541,6 +3734,8 @@ def create_varchar_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3565,6 +3760,7 @@ def update_varchar_attribute( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeVarchar: """ Update a varchar attribute. Changing the `default` value will not update already existing documents. @@ -3587,6 +3783,8 @@ def update_varchar_attribute( Maximum size of the varchar attribute. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeVarchar @@ -3617,6 +3815,8 @@ def update_varchar_attribute( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3831,6 +4031,8 @@ def list_documents( """ Get a list of all the user's documents in a given collection. You can use the query params to filter your results. + Documents returned by aggregate, groupBy or distinct queries hold only the attributes they select, group or aggregate, typed as a document types them, with a joined attribute named by its join alias (`alias.attribute`). They have no `$id`, `$sequence`, `$createdAt`, `$updatedAt`, `$permissions`, `$databaseId` or `$collectionId` unless they select or group it, and neither does a right or full join document that no document of this collection matched. + .. deprecated::1.8.0 This API has been deprecated since 1.8.0. Please use `tablesDB.list_rows` instead. Parameters @@ -3846,7 +4048,7 @@ def list_documents( total : Optional[bool] When set to false, the total count returned will be 0 and will not be calculated. ttl : Optional[float] - TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Must be between 0 and 86400 (24 hours). + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). model_type : Type[T], optional Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. @@ -4229,6 +4431,80 @@ def delete_documents( return DocumentList.with_data(response, model_type) + @deprecated("This API has been deprecated since 1.8.0. Please use `tablesDB.create_query` instead.") + def create_query( + self, + database_id: str, + collection_id: str, + queries: Optional[List[str]] = None, + transaction_id: Optional[str] = None, + total: Optional[bool] = None, + ttl: Optional[float] = None, + model_type: Type[T] = dict, + ) -> DocumentList[T]: + """ + Get a list of all the user's documents in a given collection using a POST request. This behaves identically to the list documents endpoint but accepts the queries in the request body, allowing much larger `queries` arrays than can fit in a URL query string. + + .. deprecated::1.8.0 + This API has been deprecated since 1.8.0. Please use `tablesDB.create_query` instead. + Parameters + ---------- + database_id : str + Database ID. + collection_id : str + Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + transaction_id : Optional[str] + Transaction ID to read uncommitted changes within the transaction. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + ttl : Optional[float] + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + DocumentList[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/databases/{databaseId}/collections/{collectionId}/documents/query' + api_params = {} + if database_id is None or database_id == '': + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None or collection_id == '': + raise AppwriteException('Missing required parameter: "collection_id"') + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) + if total is not None: + api_params['total'] = self._normalize_value(total) + if ttl is not None: + api_params['ttl'] = self._normalize_value(ttl) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return DocumentList.with_data(response, model_type) + @deprecated("This API has been deprecated since 1.8.0. Please use `tablesDB.get_row` instead.") def get_document( self, @@ -4740,6 +5016,7 @@ def create_index( attributes: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> Index: """ Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. @@ -4763,6 +5040,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- Index @@ -4795,6 +5074,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', diff --git a/appwrite/services/documents_db.py b/appwrite/services/documents_db.py index ecef7255..234f2b66 100644 --- a/appwrite/services/documents_db.py +++ b/appwrite/services/documents_db.py @@ -78,6 +78,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -93,6 +94,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Defaults to `serverless`, which creates the database on the shared pool. Any other value provisions a dedicated database on that specification. replicas : Optional[float] @@ -120,6 +123,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -478,6 +483,7 @@ def update( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -493,6 +499,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Resizing between dedicated specifications changes cpu, memory, storage and the connection ceiling via a rolling cutover with zero downtime. Moving a `serverless` database onto a dedicated specification is a data migration, not a resize. replicas : Optional[float] @@ -520,6 +528,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -646,6 +656,7 @@ def create_collection( enabled: Optional[bool] = None, attributes: Optional[List[Dict[str, Any]]] = None, indexes: Optional[List[Dict[str, Any]]] = None, + description: Optional[str] = None, ) -> Collection: """ Create a new Collection. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console. @@ -668,6 +679,8 @@ def create_collection( Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, varchar, text, mediumtext, longtext, integer, bigint, double, boolean, datetime, point, linestring, polygon, email, url, ip, enum), size (integer, required for string and varchar types), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options. indexes : Optional[List[Dict[str, Any]]] Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional). + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -700,6 +713,8 @@ def create_collection( api_params['attributes'] = self._normalize_value(attributes) if indexes is not None: api_params['indexes'] = self._normalize_value(indexes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -769,6 +784,7 @@ def update_collection( document_security: Optional[bool] = None, enabled: Optional[bool] = None, purge: Optional[bool] = None, + description: Optional[str] = None, ) -> Collection: """ Update a collection by its unique ID. @@ -789,6 +805,8 @@ def update_collection( Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. purge : Optional[bool] When true, purge all cached list responses for this collection as part of the update. Use this to force readers to see fresh data immediately instead of waiting for the cache TTL to expire. + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -819,6 +837,8 @@ def update_collection( api_params['enabled'] = self._normalize_value(enabled) if purge is not None: api_params['purge'] = self._normalize_value(purge) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1274,6 +1294,77 @@ def delete_documents( return DocumentList.with_data(response, model_type) + def create_query( + self, + database_id: str, + collection_id: str, + queries: Optional[List[str]] = None, + transaction_id: Optional[str] = None, + total: Optional[bool] = None, + ttl: Optional[float] = None, + model_type: Type[T] = dict, + ) -> DocumentList[T]: + """ + Get a list of all the user's documents in a given collection using a POST request. This behaves identically to the list documents endpoint but accepts the queries in the request body, allowing much larger `queries` arrays than can fit in a URL query string. + + Parameters + ---------- + database_id : str + Database ID. + collection_id : str + Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + transaction_id : Optional[str] + Transaction ID to read uncommitted changes within the transaction. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + ttl : Optional[float] + TTL (seconds) for cached responses when caching is enabled for select queries. Must be between 0 and 86400 (24 hours). + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + DocumentList[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/documentsdb/{databaseId}/collections/{collectionId}/documents/query' + api_params = {} + if database_id is None or database_id == '': + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None or collection_id == '': + raise AppwriteException('Missing required parameter: "collection_id"') + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) + if total is not None: + api_params['total'] = self._normalize_value(total) + if ttl is not None: + api_params['ttl'] = self._normalize_value(ttl) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return DocumentList.with_data(response, model_type) + def get_document( self, database_id: str, @@ -1763,6 +1854,7 @@ def create_index( attributes: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> Index: """ Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. @@ -1784,6 +1876,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- Index @@ -1816,6 +1910,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', diff --git a/appwrite/services/domains.py b/appwrite/services/domains.py index 6ca12b76..bce1dc23 100644 --- a/appwrite/services/domains.py +++ b/appwrite/services/domains.py @@ -22,6 +22,7 @@ def list( self, queries: Optional[List[str]] = None, search: Optional[str] = None, + total: Optional[bool] = None, ) -> DomainsList: """ List all domains registered for this project. This endpoint supports pagination. @@ -32,6 +33,8 @@ def list( Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on attributes such as domain name, teamInternalId, expiration, etc. search : Optional[str] Search term to filter your list results. Max length: 256 chars. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DomainsList @@ -49,6 +52,8 @@ def list( api_params['queries'] = self._normalize_value(queries) if search is not None: api_params['search'] = self._normalize_value(search) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -880,6 +885,7 @@ def list_records( self, domain_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DnsRecordsList: """ List DNS records for a given domain. You can use this endpoint to list all the DNS records @@ -891,6 +897,8 @@ def list_records( Domain unique ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. You may filter on attributes such as type, name, value, etc. Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DnsRecordsList @@ -909,6 +917,8 @@ def list_records( api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/functions.py b/appwrite/services/functions.py index 4a7b0ca7..fa109998 100644 --- a/appwrite/services/functions.py +++ b/appwrite/services/functions.py @@ -101,6 +101,7 @@ def create( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, interval: Optional[float] = None, + description: Optional[str] = None, ) -> Function: """ Create a new function. You can pass a list of [permissions](https://appwrite.io/docs/permissions) to allow different project users or team with access to execute the function using the client API. @@ -153,6 +154,8 @@ def create( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. + description : Optional[str] + Optional description for the function. Returns ------- Function @@ -215,6 +218,8 @@ def create( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if interval is not None: api_params['interval'] = self._normalize_value(interval) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -377,6 +382,7 @@ def update( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, interval: Optional[float] = None, + description: Optional[str] = None, email_enabled: Optional[bool] = None, ) -> Function: """ @@ -430,6 +436,8 @@ def update( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. When omitted, the current interval is kept unless schedule is set. + description : Optional[str] + Optional description for the function. email_enabled : Optional[bool] Enable inbound email triggering for this function. When false or omitted on create, SMTP recipients are rejected. Opt-in; default is off. Returns @@ -493,6 +501,8 @@ def update( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if interval is not None: api_params['interval'] = self._normalize_value(interval) + if description is not None: + api_params['description'] = self._normalize_value(description) if email_enabled is not None: api_params['emailEnabled'] = self._normalize_value(email_enabled) @@ -1387,6 +1397,7 @@ def create_variable( key: str, value: str, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Create a new function environment variable. These variables can be accessed in the function at runtime as environment variables. @@ -1403,6 +1414,8 @@ def create_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only functions can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1430,6 +1443,8 @@ def create_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1497,6 +1512,7 @@ def update_variable( key: Optional[str] = None, value: Optional[str] = None, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Update variable by its unique ID. @@ -1513,6 +1529,8 @@ def update_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only functions can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1538,6 +1556,8 @@ def update_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/messaging.py b/appwrite/services/messaging.py index 6b864262..df5083f7 100644 --- a/appwrite/services/messaging.py +++ b/appwrite/services/messaging.py @@ -86,6 +86,7 @@ def create_email( scheduled_at: Optional[str] = None, reply_to_email: Optional[str] = None, reply_to_name: Optional[str] = None, + emails: Optional[List[str]] = None, ) -> Message: """ Create a new email message. @@ -120,6 +121,8 @@ def create_email( Email address to reply to. If not set, defaults to the sender email address. reply_to_name : Optional[str] Name of the reply to recipient. If not set, defaults to the sender name. + emails : Optional[List[str]] + List of email addresses to send the message to. Use this to deliver to recipients that are not backed by an Appwrite user, target, or topic. Returns ------- Message @@ -164,6 +167,8 @@ def create_email( api_params['replyToEmail'] = self._normalize_value(reply_to_email) if reply_to_name is not None: api_params['replyToName'] = self._normalize_value(reply_to_name) + if emails is not None: + api_params['emails'] = self._normalize_value(emails) response = self.client.call( 'post', @@ -194,6 +199,7 @@ def update_email( attachments: Optional[List[str]] = None, reply_to_email: Optional[str] = None, reply_to_name: Optional[str] = None, + emails: Optional[List[str]] = None, ) -> Message: """ Update an email message by its unique ID. This endpoint only works on messages that are in draft status. Messages that are already processing, sent, or failed cannot be updated. @@ -228,6 +234,8 @@ def update_email( Email address to reply to. Pass an empty string to restore the provider or sender default. reply_to_name : Optional[str] Name of the reply to recipient. Pass an empty string to restore the provider or sender default. + emails : Optional[List[str]] + List of email addresses to send the message to. Use this to deliver to recipients that are not backed by an Appwrite user, target, or topic. Returns ------- Message @@ -270,6 +278,8 @@ def update_email( api_params['replyToEmail'] = self._normalize_value(reply_to_email) if reply_to_name is not None: api_params['replyToName'] = self._normalize_value(reply_to_name) + if emails is not None: + api_params['emails'] = self._normalize_value(emails) response = self.client.call( 'patch', @@ -2936,6 +2946,7 @@ def create_topic( subscribe: Optional[List[str]] = None, qos: Optional[float] = None, expiry: Optional[float] = None, + description: Optional[str] = None, ) -> Topic: """ Create a new topic. @@ -2952,6 +2963,8 @@ def create_topic( MQTT delivery quality of service for this topic. 0 is fire-and-forget (delivered only to clients connected at publish time). 1 persists each message and replays it when a client reconnects without having acknowledged it. Null lets the subscriber choose. expiry : Optional[float] Message retention in seconds for offline delivery. Max 7 days (604800). Null inherits the lowest expiry of the project's enabled Appwrite providers, or the 7-day ledger window if none sets one. + description : Optional[str] + Optional description for the topic. Returns ------- Topic @@ -2977,6 +2990,8 @@ def create_topic( api_params['qos'] = self._normalize_value(qos) if expiry is not None: api_params['expiry'] = self._normalize_value(expiry) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3038,6 +3053,7 @@ def update_topic( subscribe: Optional[List[str]] = None, qos: Optional[float] = None, expiry: Optional[float] = None, + description: Optional[str] = None, ) -> Topic: """ Update a topic by its unique ID. @@ -3054,6 +3070,8 @@ def update_topic( MQTT delivery quality of service for this topic. 0 is fire-and-forget (delivered only to clients connected at publish time). 1 persists each message and replays it when a client reconnects without having acknowledged it. Null lets the subscriber choose. expiry : Optional[float] Message retention in seconds for offline delivery. Max 7 days (604800). Null inherits the lowest expiry of the project's enabled Appwrite providers, or the 7-day ledger window if none sets one. + description : Optional[str] + Optional description for the topic. Returns ------- Topic @@ -3078,6 +3096,8 @@ def update_topic( api_params['qos'] = self._normalize_value(qos) if expiry is not None: api_params['expiry'] = self._normalize_value(expiry) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', diff --git a/appwrite/services/mongo.py b/appwrite/services/mongo.py index 7bb92df1..f377fe6c 100644 --- a/appwrite/services/mongo.py +++ b/appwrite/services/mongo.py @@ -29,6 +29,7 @@ def __init__(self, client) -> None: def list( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseList: """ List all dedicated databases. Results support pagination. @@ -37,6 +38,8 @@ def list( ---------- queries : Optional[List[str]] Array of query strings. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseList @@ -52,6 +55,8 @@ def list( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -433,6 +438,7 @@ def list_backups( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseBackupList: """ List all backups for a dedicated database. Results can be filtered by status and type. @@ -443,6 +449,8 @@ def list_backups( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, type, databaseId + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseBackupList @@ -461,6 +469,8 @@ def list_backups( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -524,6 +534,7 @@ def list_backup_policies( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List scheduled backup policies for a dedicated database. @@ -534,6 +545,8 @@ def list_backup_policies( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -552,6 +565,8 @@ def list_backup_policies( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/mysql.py b/appwrite/services/mysql.py index 4e70e83f..0e402fe2 100644 --- a/appwrite/services/mysql.py +++ b/appwrite/services/mysql.py @@ -31,6 +31,7 @@ def __init__(self, client) -> None: def list( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseList: """ List all dedicated databases. Results support pagination. @@ -39,6 +40,8 @@ def list( ---------- queries : Optional[List[str]] Array of query strings. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseList @@ -54,6 +57,8 @@ def list( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -435,6 +440,7 @@ def list_backups( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseBackupList: """ List all backups for a dedicated database. Results can be filtered by status and type. @@ -445,6 +451,8 @@ def list_backups( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, type, databaseId + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseBackupList @@ -463,6 +471,8 @@ def list_backups( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -526,6 +536,7 @@ def list_backup_policies( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List scheduled backup policies for a dedicated database. @@ -536,6 +547,8 @@ def list_backup_policies( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -554,6 +567,8 @@ def list_backup_policies( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/organization.py b/appwrite/services/organization.py index 0e31be2b..ecb7efdb 100644 --- a/appwrite/services/organization.py +++ b/appwrite/services/organization.py @@ -986,6 +986,7 @@ def update_project_key( name: str, scopes: List[ProjectKeyScopes], expire: Optional[str] = None, + description: Optional[str] = None, ) -> Key: """ Update a project key by its unique ID. Use this endpoint to update the name, scopes, or expiration time of an API key. @@ -1002,6 +1003,8 @@ def update_project_key( Key scopes list. Maximum of 200 scopes are allowed. expire : Optional[str] Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration. + description : Optional[str] + Optional description for the key. Returns ------- Key @@ -1029,6 +1032,8 @@ def update_project_key( api_params['scopes'] = self._normalize_value(scopes) if expire is not None: api_params['expire'] = self._normalize_value(expire) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/postgresql.py b/appwrite/services/postgresql.py index f361d9dd..a5692e05 100644 --- a/appwrite/services/postgresql.py +++ b/appwrite/services/postgresql.py @@ -32,6 +32,7 @@ def __init__(self, client) -> None: def list( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseList: """ List all dedicated databases. Results support pagination. @@ -40,6 +41,8 @@ def list( ---------- queries : Optional[List[str]] Array of query strings. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseList @@ -55,6 +58,8 @@ def list( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -436,6 +441,7 @@ def list_backups( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseBackupList: """ List all backups for a dedicated database. Results can be filtered by status and type. @@ -446,6 +452,8 @@ def list_backups( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, type, databaseId + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseBackupList @@ -464,6 +472,8 @@ def list_backups( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -527,6 +537,7 @@ def list_backup_policies( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List scheduled backup policies for a dedicated database. @@ -537,6 +548,8 @@ def list_backup_policies( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -555,6 +568,8 @@ def list_backup_policies( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/project.py b/appwrite/services/project.py index 3de8bfc2..d1d6da2d 100644 --- a/appwrite/services/project.py +++ b/appwrite/services/project.py @@ -581,6 +581,7 @@ def update_key( name: str, scopes: List[ProjectKeyScopes], expire: Optional[str] = None, + description: Optional[str] = None, ) -> Key: """ Update a key by its unique ID. Use this endpoint to update the name, scopes, or expiration time of an API key. @@ -595,6 +596,8 @@ def update_key( Key scopes list. Maximum of 200 scopes are allowed. expire : Optional[str] Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration. + description : Optional[str] + Optional description for the key. Returns ------- Key @@ -619,6 +622,8 @@ def update_key( api_params['scopes'] = self._normalize_value(scopes) if expire is not None: api_params['expire'] = self._normalize_value(expire) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -4982,14 +4987,19 @@ def update_passkey_policy( def update_password_dictionary_policy( self, enabled: bool, + deny_list: Optional[List[str]] = None, ) -> ProjectModel: """ Updating this policy allows you to control if new passwords are checked against most common passwords dictionary. When enabled, and user changes their password, password must not be contained in the dictionary. + Custom denyList entries use case-sensitive equality and apply only while this policy is enabled. Omit denyList or pass null to preserve it, pass an array to replace it, or pass [] to clear it. Entries are stored without trimming or deduplication. Existing passwords remain valid for sign-in and password confirmation. The combined serialized project authentication configuration must fit within 16,384 characters, even when each list meets its entry limits. + Parameters ---------- enabled : bool Toggle password dictionary policy. Set to true if you want password change to block passwords in the dictionary, or false to allow them. When changing this policy, existing passwords remain valid. + deny_list : Optional[List[str]] + Custom blocked values using case-sensitive equality. Up to 100 entries of 1 to 256 characters. Omit or pass null to preserve, an array to replace, or [] to clear. Combined authentication settings must fit within 16,384 characters. Returns ------- ProjectModel @@ -5006,6 +5016,8 @@ def update_password_dictionary_policy( if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') api_params['enabled'] = self._normalize_value(enabled) + if deny_list is not None: + api_params['denyList'] = self._normalize_value(deny_list) response = self.client.call( 'patch', @@ -5064,14 +5076,20 @@ def update_password_history_policy( def update_password_personal_data_policy( self, enabled: bool, + deny_list: Optional[List[str]] = None, ) -> ProjectModel: """ Updating this policy allows you to control if password strength is checked against personal data. When enabled, and user sets or changes their password, the password must not contain user ID, name, email or phone number. + Custom denyList entries use case-insensitive substring matching and apply only while this policy is enabled. Omit denyList or pass null to preserve it, pass an array to replace it, or pass [] to clear it. Entries are stored without trimming or deduplication. Existing passwords remain valid for sign-in and password confirmation. The combined serialized project authentication configuration must fit within 16,384 characters, even when each list meets its entry limits. + Password recovery also checks the recovered user profile, closing the previous gap in profile-based personal-data validation. + Parameters ---------- enabled : bool Toggle password personal data policy. Set to true if you want to block passwords including user's personal data, or false to allow it. When changing this policy, existing passwords remain valid. + deny_list : Optional[List[str]] + Custom blocked values using case-insensitive substring matching. Up to 100 entries of 1 to 256 characters. Omit or pass null to preserve, an array to replace, or [] to clear. Combined authentication settings must fit within 16,384 characters. Returns ------- ProjectModel @@ -5088,6 +5106,8 @@ def update_password_personal_data_policy( if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') api_params['enabled'] = self._normalize_value(enabled) + if deny_list is not None: + api_params['denyList'] = self._normalize_value(deny_list) response = self.client.call( 'patch', @@ -6010,6 +6030,7 @@ def create_variable( key: str, value: str, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Create a new project environment variable. These variables can be accessed by all functions and sites in the project. @@ -6024,6 +6045,8 @@ def create_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only projects can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -6048,6 +6071,8 @@ def create_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -6108,6 +6133,7 @@ def update_variable( key: Optional[str] = None, value: Optional[str] = None, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Update variable by its unique ID. @@ -6122,6 +6148,8 @@ def update_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only projects can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -6144,6 +6172,8 @@ def update_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/sites.py b/appwrite/services/sites.py index 952e4b27..f3320934 100644 --- a/appwrite/services/sites.py +++ b/appwrite/services/sites.py @@ -103,6 +103,7 @@ def create( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, scopes: Optional[List[ProjectKeyScopes]] = None, + description: Optional[str] = None, ) -> Site: """ Create a new site. @@ -157,6 +158,8 @@ def create( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. + description : Optional[str] + Optional description for the site. Returns ------- Site @@ -222,6 +225,8 @@ def create( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if scopes is not None: api_params['scopes'] = self._normalize_value(scopes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -385,6 +390,7 @@ def update( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, scopes: Optional[List[ProjectKeyScopes]] = None, + description: Optional[str] = None, ) -> Site: """ Update site by its unique ID. @@ -439,6 +445,8 @@ def update( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. + description : Optional[str] + Optional description for the site. Returns ------- Site @@ -503,6 +511,8 @@ def update( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if scopes is not None: api_params['scopes'] = self._normalize_value(scopes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1321,6 +1331,7 @@ def create_variable( key: str, value: str, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Create a new site variable. These variables can be accessed during build and runtime (server-side rendering) as environment variables. @@ -1337,6 +1348,8 @@ def create_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only sites can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1364,6 +1377,8 @@ def create_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1431,6 +1446,7 @@ def update_variable( key: Optional[str] = None, value: Optional[str] = None, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Update variable by its unique ID. @@ -1447,6 +1463,8 @@ def update_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only sites can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1472,6 +1490,8 @@ def update_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/storage.py b/appwrite/services/storage.py index a095f6d2..7ea5cfbc 100644 --- a/appwrite/services/storage.py +++ b/appwrite/services/storage.py @@ -80,6 +80,7 @@ def create_bucket( encryption: Optional[bool] = None, antivirus: Optional[bool] = None, transformations: Optional[bool] = None, + description: Optional[str] = None, ) -> Bucket: """ Create a new storage bucket. @@ -108,6 +109,8 @@ def create_bucket( Is virus scanning enabled? For file size above 20MB AntiVirus scanning is skipped even if it's enabled transformations : Optional[bool] Are image transformations enabled? + description : Optional[str] + Optional description for the bucket. Returns ------- Bucket @@ -145,6 +148,8 @@ def create_bucket( api_params['antivirus'] = self._normalize_value(antivirus) if transformations is not None: api_params['transformations'] = self._normalize_value(transformations) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -212,6 +217,7 @@ def update_bucket( encryption: Optional[bool] = None, antivirus: Optional[bool] = None, transformations: Optional[bool] = None, + description: Optional[str] = None, ) -> Bucket: """ Update a storage bucket by its unique ID. @@ -240,6 +246,8 @@ def update_bucket( Is virus scanning enabled? For file size above 20MB AntiVirus scanning is skipped even if it's enabled transformations : Optional[bool] Are image transformations enabled? + description : Optional[str] + Optional description for the bucket. Returns ------- Bucket @@ -277,6 +285,8 @@ def update_bucket( api_params['antivirus'] = self._normalize_value(antivirus) if transformations is not None: api_params['transformations'] = self._normalize_value(transformations) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/tables_db.py b/appwrite/services/tables_db.py index 6a90c3a8..2110880d 100644 --- a/appwrite/services/tables_db.py +++ b/appwrite/services/tables_db.py @@ -106,6 +106,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -121,6 +122,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Defaults to `serverless`, which creates the database on the shared pool. Any other value provisions a dedicated database on that specification. replicas : Optional[float] @@ -148,6 +151,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -506,6 +511,7 @@ def update( database_id: str, name: Optional[str] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -521,6 +527,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Resizing between dedicated specifications changes cpu, memory, storage and the connection ceiling via a rolling cutover with zero downtime. Moving a `serverless` database onto a dedicated specification is a data migration, not a resize. replicas : Optional[float] @@ -547,6 +555,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -1086,6 +1096,7 @@ def create_table( enabled: Optional[bool] = None, columns: Optional[List[Dict[str, Any]]] = None, indexes: Optional[List[Dict[str, Any]]] = None, + description: Optional[str] = None, ) -> Table: """ Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable) API or directly from your database console. @@ -1108,6 +1119,8 @@ def create_table( Array of column definitions to create. Each column should contain: key (string), type (string: string, varchar, text, mediumtext, longtext, integer, bigint, double, boolean, datetime, point, linestring, polygon, email, url, ip, enum), size (integer, required for string and varchar types), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options. indexes : Optional[List[Dict[str, Any]]] Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial, trigram), attributes (array of column keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional). + description : Optional[str] + Optional description for the table. Returns ------- Table @@ -1140,6 +1153,8 @@ def create_table( api_params['columns'] = self._normalize_value(columns) if indexes is not None: api_params['indexes'] = self._normalize_value(indexes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1209,6 +1224,7 @@ def update_table( row_security: Optional[bool] = None, enabled: Optional[bool] = None, purge: Optional[bool] = None, + description: Optional[str] = None, ) -> Table: """ Update a table by its unique ID. @@ -1229,6 +1245,8 @@ def update_table( Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled. purge : Optional[bool] When true, purge all cached list responses for this table as part of the update. Use this to force readers to see fresh data immediately instead of waiting for the cache TTL to expire. + description : Optional[str] + Optional description for the table. Returns ------- Table @@ -1258,6 +1276,8 @@ def update_table( api_params['enabled'] = self._normalize_value(enabled) if purge is not None: api_params['purge'] = self._normalize_value(purge) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1385,6 +1405,7 @@ def create_big_int_column( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnBigint: """ Create a bigint column. Optionally, minimum and maximum values can be provided. @@ -1407,6 +1428,8 @@ def create_big_int_column( Default value. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnBigint @@ -1440,6 +1463,8 @@ def create_big_int_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1464,6 +1489,7 @@ def update_big_int_column( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnBigint: """ Update a bigint column. Changing the `default` value will not update already existing rows. @@ -1486,6 +1512,8 @@ def update_big_int_column( Maximum value new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnBigint @@ -1518,6 +1546,8 @@ def update_big_int_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1540,6 +1570,7 @@ def create_boolean_column( required: bool, default: Optional[bool] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnBoolean: """ Create a boolean column. @@ -1558,6 +1589,8 @@ def create_boolean_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnBoolean @@ -1587,6 +1620,8 @@ def create_boolean_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1609,6 +1644,7 @@ def update_boolean_column( required: bool, default: Optional[bool], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnBoolean: """ Update a boolean column. Changing the `default` value will not update already existing rows. @@ -1627,6 +1663,8 @@ def update_boolean_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnBoolean @@ -1655,6 +1693,8 @@ def update_boolean_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1677,6 +1717,7 @@ def create_datetime_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnDatetime: """ Create a date time column according to the ISO 8601 standard. @@ -1695,6 +1736,8 @@ def create_datetime_column( Default value for the column in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnDatetime @@ -1724,6 +1767,8 @@ def create_datetime_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1746,6 +1791,7 @@ def update_datetime_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnDatetime: """ Update a date time column. Changing the `default` value will not update already existing rows. @@ -1764,6 +1810,8 @@ def update_datetime_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnDatetime @@ -1792,6 +1840,8 @@ def update_datetime_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1814,6 +1864,7 @@ def create_email_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnEmail: """ Create an email column. @@ -1832,6 +1883,8 @@ def create_email_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnEmail @@ -1861,6 +1914,8 @@ def create_email_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1883,6 +1938,7 @@ def update_email_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnEmail: """ Update an email column. Changing the `default` value will not update already existing rows. @@ -1901,6 +1957,8 @@ def update_email_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnEmail @@ -1929,6 +1987,8 @@ def update_email_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1952,6 +2012,7 @@ def create_enum_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnEnum: """ Create an enumeration column. The `elements` param acts as a white-list of accepted values for this column. @@ -1972,6 +2033,8 @@ def create_enum_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnEnum @@ -2004,6 +2067,8 @@ def create_enum_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2027,6 +2092,7 @@ def update_enum_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnEnum: """ Update an enum column. Changing the `default` value will not update already existing rows. @@ -2047,6 +2113,8 @@ def update_enum_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnEnum @@ -2078,6 +2146,8 @@ def update_enum_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2102,6 +2172,7 @@ def create_float_column( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnFloat: """ Create a float column. Optionally, minimum and maximum values can be provided. @@ -2124,6 +2195,8 @@ def create_float_column( Default value. Cannot be set when required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnFloat @@ -2157,6 +2230,8 @@ def create_float_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2181,6 +2256,7 @@ def update_float_column( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnFloat: """ Update a float column. Changing the `default` value will not update already existing rows. @@ -2203,6 +2279,8 @@ def update_float_column( Maximum value new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnFloat @@ -2235,6 +2313,8 @@ def update_float_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2259,6 +2339,7 @@ def create_integer_column( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnInteger: """ Create an integer column. Optionally, minimum and maximum values can be provided. @@ -2281,6 +2362,8 @@ def create_integer_column( Default value. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnInteger @@ -2314,6 +2397,8 @@ def create_integer_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2338,6 +2423,7 @@ def update_integer_column( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnInteger: """ Update an integer column. Changing the `default` value will not update already existing rows. @@ -2360,6 +2446,8 @@ def update_integer_column( Maximum value new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnInteger @@ -2392,6 +2480,8 @@ def update_integer_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2414,6 +2504,7 @@ def create_ip_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnIp: """ Create IP address column. @@ -2432,6 +2523,8 @@ def create_ip_column( Default value. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnIp @@ -2461,6 +2554,8 @@ def create_ip_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2483,6 +2578,7 @@ def update_ip_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnIp: """ Update an ip column. Changing the `default` value will not update already existing rows. @@ -2501,6 +2597,8 @@ def update_ip_column( Default value. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnIp @@ -2529,6 +2627,8 @@ def update_ip_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2550,6 +2650,7 @@ def create_line_column( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> ColumnLine: """ Create a geometric line column. @@ -2566,6 +2667,8 @@ def create_line_column( Is column required? default : Optional[List[List[Any]]] Default value for column when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when column is required. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLine @@ -2593,6 +2696,8 @@ def create_line_column( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2615,6 +2720,7 @@ def update_line_column( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnLine: """ Update a line column. Changing the `default` value will not update already existing rows. @@ -2633,6 +2739,8 @@ def update_line_column( Default value for column when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLine @@ -2662,6 +2770,8 @@ def update_line_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2685,6 +2795,7 @@ def create_longtext_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnLongtext: """ Create a longtext column. @@ -2705,6 +2816,8 @@ def create_longtext_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLongtext @@ -2736,6 +2849,8 @@ def create_longtext_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2758,6 +2873,7 @@ def update_longtext_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnLongtext: """ Update a longtext column. Changing the `default` value will not update already existing rows. @@ -2776,6 +2892,8 @@ def update_longtext_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLongtext @@ -2804,6 +2922,8 @@ def update_longtext_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2827,6 +2947,7 @@ def create_mediumtext_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnMediumtext: """ Create a mediumtext column. @@ -2847,6 +2968,8 @@ def create_mediumtext_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnMediumtext @@ -2878,6 +3001,8 @@ def create_mediumtext_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2900,6 +3025,7 @@ def update_mediumtext_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnMediumtext: """ Update a mediumtext column. Changing the `default` value will not update already existing rows. @@ -2918,6 +3044,8 @@ def update_mediumtext_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnMediumtext @@ -2946,6 +3074,8 @@ def update_mediumtext_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2967,6 +3097,7 @@ def create_point_column( key: str, required: bool, default: Optional[List[float]] = None, + description: Optional[str] = None, ) -> ColumnPoint: """ Create a geometric point column. @@ -2983,6 +3114,8 @@ def create_point_column( Is column required? default : Optional[List[float]] Default value for column when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when column is required. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPoint @@ -3010,6 +3143,8 @@ def create_point_column( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3032,6 +3167,7 @@ def update_point_column( required: bool, default: Optional[List[float]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnPoint: """ Update a point column. Changing the `default` value will not update already existing rows. @@ -3050,6 +3186,8 @@ def update_point_column( Default value for column when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPoint @@ -3079,6 +3217,8 @@ def update_point_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3100,6 +3240,7 @@ def create_polygon_column( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> ColumnPolygon: """ Create a geometric polygon column. @@ -3116,6 +3257,8 @@ def create_polygon_column( Is column required? default : Optional[List[List[Any]]] Default value for column when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when column is required. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPolygon @@ -3143,6 +3286,8 @@ def create_polygon_column( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3165,6 +3310,7 @@ def update_polygon_column( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnPolygon: """ Update a polygon column. Changing the `default` value will not update already existing rows. @@ -3183,6 +3329,8 @@ def update_polygon_column( Default value for column when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPolygon @@ -3212,6 +3360,8 @@ def update_polygon_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3236,6 +3386,7 @@ def create_relationship_column( key: Optional[str] = None, two_way_key: Optional[str] = None, on_delete: Optional[RelationMutate] = None, + description: Optional[str] = None, ) -> ColumnRelationship: """ Create relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). @@ -3258,6 +3409,8 @@ def create_relationship_column( Two Way Column Key. on_delete : Optional[RelationMutate] Delete constraint. Possible values are: cascade, restrict, setNull. + description : Optional[str] + Optional description for the column. Returns ------- ColumnRelationship @@ -3291,6 +3444,8 @@ def create_relationship_column( api_params['twoWayKey'] = self._normalize_value(two_way_key) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3312,6 +3467,7 @@ def update_relationship_column( key: str, on_delete: Optional[RelationMutate] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnRelationship: """ Update relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). @@ -3328,6 +3484,8 @@ def update_relationship_column( Delete constraint. Possible values are: cascade, restrict, setNull. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnRelationship @@ -3354,6 +3512,8 @@ def update_relationship_column( api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3379,6 +3539,7 @@ def create_string_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnString: """ Create a string column. @@ -3403,6 +3564,8 @@ def create_string_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnString @@ -3437,6 +3600,8 @@ def create_string_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3461,6 +3626,7 @@ def update_string_column( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnString: """ Update a string column. Changing the `default` value will not update already existing rows. @@ -3483,6 +3649,8 @@ def update_string_column( Maximum size of the string column. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnString @@ -3513,6 +3681,8 @@ def update_string_column( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3536,6 +3706,7 @@ def create_text_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnText: """ Create a text column. @@ -3556,6 +3727,8 @@ def create_text_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnText @@ -3587,6 +3760,8 @@ def create_text_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3609,6 +3784,7 @@ def update_text_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnText: """ Update a text column. Changing the `default` value will not update already existing rows. @@ -3627,6 +3803,8 @@ def update_text_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnText @@ -3655,6 +3833,8 @@ def update_text_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3677,6 +3857,7 @@ def create_url_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnUrl: """ Create a URL column. @@ -3695,6 +3876,8 @@ def create_url_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnUrl @@ -3724,6 +3907,8 @@ def create_url_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3746,6 +3931,7 @@ def update_url_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnUrl: """ Update an url column. Changing the `default` value will not update already existing rows. @@ -3764,6 +3950,8 @@ def update_url_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnUrl @@ -3792,6 +3980,8 @@ def update_url_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3816,6 +4006,7 @@ def create_varchar_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnVarchar: """ Create a varchar column. @@ -3838,6 +4029,8 @@ def create_varchar_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnVarchar @@ -3872,6 +4065,8 @@ def create_varchar_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3895,6 +4090,7 @@ def update_varchar_column( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnVarchar: """ Update a varchar column. Changing the `default` value will not update already existing rows. @@ -3915,6 +4111,8 @@ def update_varchar_column( Maximum size of the varchar column. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnVarchar @@ -3945,6 +4143,8 @@ def update_varchar_column( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -4204,6 +4404,7 @@ def create_index( columns: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> ColumnIndex: """ Creates an index on the columns listed. Your index should include all the columns you will query in a single request. @@ -4225,6 +4426,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- ColumnIndex @@ -4257,6 +4460,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -4389,6 +4594,8 @@ def list_rows( """ Get a list of all the user's rows in a given table. You can use the query params to filter your results. + Rows returned by aggregate, groupBy or distinct queries hold only the columns they select, group or aggregate, typed as a row types them, with a joined column named by its join alias (`alias.column`). They have no `$id`, `$sequence`, `$createdAt`, `$updatedAt`, `$permissions`, `$databaseId` or `$tableId` unless they select or group it, and neither does a right or full join row that no row of this table matched. + Parameters ---------- database_id : str @@ -4402,7 +4609,7 @@ def list_rows( total : Optional[bool] When set to false, the total count returned will be 0 and will not be calculated. ttl : Optional[float] - TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, table, schema version (columns and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; row writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Must be between 0 and 86400 (24 hours). + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, table, schema version (columns and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; row writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). model_type : Type[T], optional Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. @@ -4770,6 +4977,77 @@ def delete_rows( return RowList.with_data(response, model_type) + def create_query( + self, + database_id: str, + table_id: str, + queries: Optional[List[str]] = None, + transaction_id: Optional[str] = None, + total: Optional[bool] = None, + ttl: Optional[float] = None, + model_type: Type[T] = dict, + ) -> RowList[T]: + """ + Get a list of all the user's rows in a given table using a POST request. This behaves identically to the list rows endpoint but accepts the queries in the request body, allowing much larger `queries` arrays than can fit in a URL query string. + + Parameters + ---------- + database_id : str + Database ID. + table_id : str + Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/products/databases/tables#create-table). + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + transaction_id : Optional[str] + Transaction ID to read uncommitted changes within the transaction. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + ttl : Optional[float] + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, table, schema version (columns and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; row writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + RowList[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/tablesdb/{databaseId}/tables/{tableId}/rows/query' + api_params = {} + if database_id is None or database_id == '': + raise AppwriteException('Missing required parameter: "database_id"') + if table_id is None or table_id == '': + raise AppwriteException('Missing required parameter: "table_id"') + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) + if total is not None: + api_params['total'] = self._normalize_value(total) + if ttl is not None: + api_params['ttl'] = self._normalize_value(ttl) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return RowList.with_data(response, model_type) + def get_row( self, database_id: str, diff --git a/appwrite/services/vectors_db.py b/appwrite/services/vectors_db.py index 1cea0603..eda5f00d 100644 --- a/appwrite/services/vectors_db.py +++ b/appwrite/services/vectors_db.py @@ -78,6 +78,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -93,6 +94,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Defaults to `serverless`, which creates the database on the shared pool. Any other value provisions a dedicated database on that specification. replicas : Optional[float] @@ -120,6 +123,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -478,6 +483,7 @@ def update( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -493,6 +499,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Resizing between dedicated specifications changes cpu, memory, storage and the connection ceiling via a rolling cutover with zero downtime. Moving a `serverless` database onto a dedicated specification is a data migration, not a resize. replicas : Optional[float] @@ -520,6 +528,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -645,6 +655,7 @@ def create_collection( permissions: Optional[List[str]] = None, document_security: Optional[bool] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> VectorsdbCollection: """ Create a new Collection. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console. @@ -665,6 +676,8 @@ def create_collection( Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions). enabled : Optional[bool] Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. + description : Optional[str] + Optional description for the collection. Returns ------- VectorsdbCollection @@ -696,6 +709,8 @@ def create_collection( api_params['documentSecurity'] = self._normalize_value(document_security) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -765,6 +780,7 @@ def update_collection( permissions: Optional[List[str]] = None, document_security: Optional[bool] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> VectorsdbCollection: """ Update a collection by its unique ID. @@ -785,6 +801,8 @@ def update_collection( Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions). enabled : Optional[bool] Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. + description : Optional[str] + Optional description for the collection. Returns ------- VectorsdbCollection @@ -815,6 +833,8 @@ def update_collection( api_params['documentSecurity'] = self._normalize_value(document_security) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1674,6 +1694,7 @@ def create_index( attributes: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> Index: """ Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. @@ -1695,6 +1716,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- Index @@ -1727,6 +1750,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', diff --git a/appwrite/services/webhooks.py b/appwrite/services/webhooks.py index 6caa4745..41f4ef49 100644 --- a/appwrite/services/webhooks.py +++ b/appwrite/services/webhooks.py @@ -67,6 +67,7 @@ def create( auth_username: Optional[str] = None, auth_password: Optional[str] = None, secret: Optional[str] = None, + description: Optional[str] = None, ) -> Webhook: """ Create a new webhook. Use this endpoint to configure a URL that will receive events from Appwrite when specific events occur. @@ -91,6 +92,8 @@ def create( Webhook HTTP password. Max length: 2048 chars. secret : Optional[str] Webhook secret key. If not provided, a new key will be generated automatically. Key must be at least 8 characters long, and at max 256 characters. + description : Optional[str] + Optional description for the webhook. Returns ------- Webhook @@ -126,6 +129,8 @@ def create( api_params['authPassword'] = self._normalize_value(auth_password) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -190,6 +195,7 @@ def update( tls: Optional[bool] = None, auth_username: Optional[str] = None, auth_password: Optional[str] = None, + description: Optional[str] = None, ) -> Webhook: """ Update a webhook by its unique ID. Use this endpoint to update the URL, events, or status of an existing webhook. @@ -212,6 +218,8 @@ def update( Webhook HTTP user. Max length: 255 chars. auth_password : Optional[str] Webhook HTTP password. Max length: 2048 chars. Omit to keep the current password; it is cleared when the URL changes or TLS verification is disabled. + description : Optional[str] + Optional description for the webhook. Returns ------- Webhook @@ -245,6 +253,8 @@ def update( api_params['authUsername'] = self._normalize_value(auth_username) if auth_password is not None: api_params['authPassword'] = self._normalize_value(auth_password) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/docs/examples/account/list-memberships.md b/docs/examples/account/list-memberships.md new file mode 100644 index 00000000..2238f602 --- /dev/null +++ b/docs/examples/account/list-memberships.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account +from appwrite.models import MembershipList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +account = Account(client) + +result: MembershipList = account.list_memberships( + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/activities/list-events.md b/docs/examples/activities/list-events.md index 17f3b67d..14dd6f7d 100644 --- a/docs/examples/activities/list-events.md +++ b/docs/examples/activities/list-events.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key activities = Activities(client) result: ActivityEventList = activities.list_events( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/backups/list-archives.md b/docs/examples/backups/list-archives.md index 3d179923..42674b17 100644 --- a/docs/examples/backups/list-archives.md +++ b/docs/examples/backups/list-archives.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key backups = Backups(client) result: BackupArchiveList = backups.list_archives( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/backups/list-policies.md b/docs/examples/backups/list-policies.md index bcfb6734..65bc8c9f 100644 --- a/docs/examples/backups/list-policies.md +++ b/docs/examples/backups/list-policies.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key backups = Backups(client) result: BackupPolicyList = backups.list_policies( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/backups/list-restorations.md b/docs/examples/backups/list-restorations.md index c353a50e..4556933b 100644 --- a/docs/examples/backups/list-restorations.md +++ b/docs/examples/backups/list-restorations.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key backups = Backups(client) result: BackupRestorationList = backups.list_restorations( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-big-int-attribute.md b/docs/examples/databases/create-big-int-attribute.md index e55ab28c..18025ca5 100644 --- a/docs/examples/databases/create-big-int-attribute.md +++ b/docs/examples/databases/create-big-int-attribute.md @@ -18,7 +18,8 @@ result: AttributeBigint = databases.create_big_int_attribute( min = 0, # optional max = 1000000, # optional default = 0, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-boolean-attribute.md b/docs/examples/databases/create-boolean-attribute.md index 7c66b322..5a9a946b 100644 --- a/docs/examples/databases/create-boolean-attribute.md +++ b/docs/examples/databases/create-boolean-attribute.md @@ -16,7 +16,8 @@ result: AttributeBoolean = databases.create_boolean_attribute( key = '', required = False, default = False, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-collection.md b/docs/examples/databases/create-collection.md index 7e9afa0f..655144e3 100644 --- a/docs/examples/databases/create-collection.md +++ b/docs/examples/databases/create-collection.md @@ -20,7 +20,8 @@ result: Collection = databases.create_collection( document_security = False, # optional enabled = False, # optional attributes = [], # optional - indexes = [] # optional + indexes = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-datetime-attribute.md b/docs/examples/databases/create-datetime-attribute.md index d6e5a437..4e4c3702 100644 --- a/docs/examples/databases/create-datetime-attribute.md +++ b/docs/examples/databases/create-datetime-attribute.md @@ -16,7 +16,8 @@ result: AttributeDatetime = databases.create_datetime_attribute( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-email-attribute.md b/docs/examples/databases/create-email-attribute.md index 8330bb9a..4745d038 100644 --- a/docs/examples/databases/create-email-attribute.md +++ b/docs/examples/databases/create-email-attribute.md @@ -16,7 +16,8 @@ result: AttributeEmail = databases.create_email_attribute( key = '', required = False, default = 'email@example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-enum-attribute.md b/docs/examples/databases/create-enum-attribute.md index 4710819f..d6c6f6de 100644 --- a/docs/examples/databases/create-enum-attribute.md +++ b/docs/examples/databases/create-enum-attribute.md @@ -17,7 +17,8 @@ result: AttributeEnum = databases.create_enum_attribute( elements = ["active", "inactive"], required = False, default = 'active', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-float-attribute.md b/docs/examples/databases/create-float-attribute.md index e7155081..dd76cec2 100644 --- a/docs/examples/databases/create-float-attribute.md +++ b/docs/examples/databases/create-float-attribute.md @@ -18,7 +18,8 @@ result: AttributeFloat = databases.create_float_attribute( min = 0, # optional max = 100, # optional default = 10.5, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-index.md b/docs/examples/databases/create-index.md index 8733b72a..8102336b 100644 --- a/docs/examples/databases/create-index.md +++ b/docs/examples/databases/create-index.md @@ -19,7 +19,8 @@ result: Index = databases.create_index( type = DatabasesIndexType.KEY, attributes = ["username"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-integer-attribute.md b/docs/examples/databases/create-integer-attribute.md index c46e2295..f89a52d2 100644 --- a/docs/examples/databases/create-integer-attribute.md +++ b/docs/examples/databases/create-integer-attribute.md @@ -18,7 +18,8 @@ result: AttributeInteger = databases.create_integer_attribute( min = 0, # optional max = 100, # optional default = 10, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-ip-attribute.md b/docs/examples/databases/create-ip-attribute.md index 34542fa9..19f18397 100644 --- a/docs/examples/databases/create-ip-attribute.md +++ b/docs/examples/databases/create-ip-attribute.md @@ -16,7 +16,8 @@ result: AttributeIp = databases.create_ip_attribute( key = '', required = False, default = '192.0.2.0', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-line-attribute.md b/docs/examples/databases/create-line-attribute.md index 6745498a..77f1fa6d 100644 --- a/docs/examples/databases/create-line-attribute.md +++ b/docs/examples/databases/create-line-attribute.md @@ -15,7 +15,8 @@ result: AttributeLine = databases.create_line_attribute( collection_id = '', key = '', required = False, - default = [[1, 2], [3, 4], [5, 6]] # optional + default = [[1, 2], [3, 4], [5, 6]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-longtext-attribute.md b/docs/examples/databases/create-longtext-attribute.md index 70b0eb48..a8cdecc5 100644 --- a/docs/examples/databases/create-longtext-attribute.md +++ b/docs/examples/databases/create-longtext-attribute.md @@ -17,7 +17,8 @@ result: AttributeLongtext = databases.create_longtext_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-mediumtext-attribute.md b/docs/examples/databases/create-mediumtext-attribute.md index cf3011ac..68a04431 100644 --- a/docs/examples/databases/create-mediumtext-attribute.md +++ b/docs/examples/databases/create-mediumtext-attribute.md @@ -17,7 +17,8 @@ result: AttributeMediumtext = databases.create_mediumtext_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-point-attribute.md b/docs/examples/databases/create-point-attribute.md index a3d12715..91859e78 100644 --- a/docs/examples/databases/create-point-attribute.md +++ b/docs/examples/databases/create-point-attribute.md @@ -15,7 +15,8 @@ result: AttributePoint = databases.create_point_attribute( collection_id = '', key = '', required = False, - default = [1, 2] # optional + default = [1, 2], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-polygon-attribute.md b/docs/examples/databases/create-polygon-attribute.md index 4e6145f5..6651bd3e 100644 --- a/docs/examples/databases/create-polygon-attribute.md +++ b/docs/examples/databases/create-polygon-attribute.md @@ -15,7 +15,8 @@ result: AttributePolygon = databases.create_polygon_attribute( collection_id = '', key = '', required = False, - default = [[[1, 2], [3, 4], [5, 6], [1, 2]]] # optional + default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-query.md b/docs/examples/databases/create-query.md new file mode 100644 index 00000000..917b4254 --- /dev/null +++ b/docs/examples/databases/create-query.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.databases import Databases +from appwrite.models import DocumentList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +databases = Databases(client) + +result: DocumentList = databases.create_query( + database_id = '', + collection_id = '', + queries = [], # optional + transaction_id = '', # optional + total = False, # optional + ttl = 0 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/databases/create-relationship-attribute.md b/docs/examples/databases/create-relationship-attribute.md index 48821cac..a04c6e1b 100644 --- a/docs/examples/databases/create-relationship-attribute.md +++ b/docs/examples/databases/create-relationship-attribute.md @@ -20,7 +20,8 @@ result: AttributeRelationship = databases.create_relationship_attribute( two_way = False, # optional key = '', # optional two_way_key = '', # optional - on_delete = RelationMutate.CASCADE # optional + on_delete = RelationMutate.CASCADE, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-string-attribute.md b/docs/examples/databases/create-string-attribute.md index 84089243..17d8bae1 100644 --- a/docs/examples/databases/create-string-attribute.md +++ b/docs/examples/databases/create-string-attribute.md @@ -18,7 +18,8 @@ result: AttributeString = databases.create_string_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-text-attribute.md b/docs/examples/databases/create-text-attribute.md index 195650ee..0271b051 100644 --- a/docs/examples/databases/create-text-attribute.md +++ b/docs/examples/databases/create-text-attribute.md @@ -17,7 +17,8 @@ result: AttributeText = databases.create_text_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-url-attribute.md b/docs/examples/databases/create-url-attribute.md index 2fdaadac..b1b823cf 100644 --- a/docs/examples/databases/create-url-attribute.md +++ b/docs/examples/databases/create-url-attribute.md @@ -16,7 +16,8 @@ result: AttributeUrl = databases.create_url_attribute( key = '', required = False, default = 'https://example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-varchar-attribute.md b/docs/examples/databases/create-varchar-attribute.md index b0a32221..4ebe389a 100644 --- a/docs/examples/databases/create-varchar-attribute.md +++ b/docs/examples/databases/create-varchar-attribute.md @@ -18,7 +18,8 @@ result: AttributeVarchar = databases.create_varchar_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create.md b/docs/examples/databases/create.md index 68a5b374..6d8b67bc 100644 --- a/docs/examples/databases/create.md +++ b/docs/examples/databases/create.md @@ -13,7 +13,8 @@ databases = Databases(client) result: Database = databases.create( database_id = '', name = '', - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-big-int-attribute.md b/docs/examples/databases/update-big-int-attribute.md index 84ecb942..c6390c47 100644 --- a/docs/examples/databases/update-big-int-attribute.md +++ b/docs/examples/databases/update-big-int-attribute.md @@ -18,7 +18,8 @@ result: AttributeBigint = databases.update_big_int_attribute( default = 0, min = 0, # optional max = 1000000, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-boolean-attribute.md b/docs/examples/databases/update-boolean-attribute.md index 26ac8023..cf61992a 100644 --- a/docs/examples/databases/update-boolean-attribute.md +++ b/docs/examples/databases/update-boolean-attribute.md @@ -16,7 +16,8 @@ result: AttributeBoolean = databases.update_boolean_attribute( key = '', required = False, default = False, - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-collection.md b/docs/examples/databases/update-collection.md index 85d50789..70599c47 100644 --- a/docs/examples/databases/update-collection.md +++ b/docs/examples/databases/update-collection.md @@ -19,7 +19,8 @@ result: Collection = databases.update_collection( permissions = [Permission.read(Role.any())], # optional document_security = False, # optional enabled = False, # optional - purge = False # optional + purge = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-datetime-attribute.md b/docs/examples/databases/update-datetime-attribute.md index cf80c1d7..8c93de1f 100644 --- a/docs/examples/databases/update-datetime-attribute.md +++ b/docs/examples/databases/update-datetime-attribute.md @@ -16,7 +16,8 @@ result: AttributeDatetime = databases.update_datetime_attribute( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-email-attribute.md b/docs/examples/databases/update-email-attribute.md index 6a6b2d52..0317082f 100644 --- a/docs/examples/databases/update-email-attribute.md +++ b/docs/examples/databases/update-email-attribute.md @@ -16,7 +16,8 @@ result: AttributeEmail = databases.update_email_attribute( key = '', required = False, default = 'email@example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-enum-attribute.md b/docs/examples/databases/update-enum-attribute.md index 822f712c..fe9d8fad 100644 --- a/docs/examples/databases/update-enum-attribute.md +++ b/docs/examples/databases/update-enum-attribute.md @@ -17,7 +17,8 @@ result: AttributeEnum = databases.update_enum_attribute( elements = ["active", "inactive"], required = False, default = 'active', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-float-attribute.md b/docs/examples/databases/update-float-attribute.md index 4497a4a5..5fc8bf32 100644 --- a/docs/examples/databases/update-float-attribute.md +++ b/docs/examples/databases/update-float-attribute.md @@ -18,7 +18,8 @@ result: AttributeFloat = databases.update_float_attribute( default = 10.5, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-integer-attribute.md b/docs/examples/databases/update-integer-attribute.md index 61fe8bcc..88bc6301 100644 --- a/docs/examples/databases/update-integer-attribute.md +++ b/docs/examples/databases/update-integer-attribute.md @@ -18,7 +18,8 @@ result: AttributeInteger = databases.update_integer_attribute( default = 10, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-ip-attribute.md b/docs/examples/databases/update-ip-attribute.md index 89945ee3..822f58a9 100644 --- a/docs/examples/databases/update-ip-attribute.md +++ b/docs/examples/databases/update-ip-attribute.md @@ -16,7 +16,8 @@ result: AttributeIp = databases.update_ip_attribute( key = '', required = False, default = '192.0.2.0', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-line-attribute.md b/docs/examples/databases/update-line-attribute.md index 96d6b4e6..5bda52b3 100644 --- a/docs/examples/databases/update-line-attribute.md +++ b/docs/examples/databases/update-line-attribute.md @@ -16,7 +16,8 @@ result: AttributeLine = databases.update_line_attribute( key = '', required = False, default = [[1, 2], [3, 4], [5, 6]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-longtext-attribute.md b/docs/examples/databases/update-longtext-attribute.md index d039a6e0..38671d67 100644 --- a/docs/examples/databases/update-longtext-attribute.md +++ b/docs/examples/databases/update-longtext-attribute.md @@ -16,7 +16,8 @@ result: AttributeLongtext = databases.update_longtext_attribute( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-mediumtext-attribute.md b/docs/examples/databases/update-mediumtext-attribute.md index dc960fc6..ff2364b6 100644 --- a/docs/examples/databases/update-mediumtext-attribute.md +++ b/docs/examples/databases/update-mediumtext-attribute.md @@ -16,7 +16,8 @@ result: AttributeMediumtext = databases.update_mediumtext_attribute( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-point-attribute.md b/docs/examples/databases/update-point-attribute.md index bf6a94d8..bf91ea2d 100644 --- a/docs/examples/databases/update-point-attribute.md +++ b/docs/examples/databases/update-point-attribute.md @@ -16,7 +16,8 @@ result: AttributePoint = databases.update_point_attribute( key = '', required = False, default = [1, 2], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-polygon-attribute.md b/docs/examples/databases/update-polygon-attribute.md index 2dd1194c..2a8d94b0 100644 --- a/docs/examples/databases/update-polygon-attribute.md +++ b/docs/examples/databases/update-polygon-attribute.md @@ -16,7 +16,8 @@ result: AttributePolygon = databases.update_polygon_attribute( key = '', required = False, default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-relationship-attribute.md b/docs/examples/databases/update-relationship-attribute.md index cc328d63..c39fa987 100644 --- a/docs/examples/databases/update-relationship-attribute.md +++ b/docs/examples/databases/update-relationship-attribute.md @@ -16,7 +16,8 @@ result: AttributeRelationship = databases.update_relationship_attribute( collection_id = '', key = '', on_delete = RelationMutate.CASCADE, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-string-attribute.md b/docs/examples/databases/update-string-attribute.md index bfe14df1..2989a4cc 100644 --- a/docs/examples/databases/update-string-attribute.md +++ b/docs/examples/databases/update-string-attribute.md @@ -17,7 +17,8 @@ result: AttributeString = databases.update_string_attribute( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-text-attribute.md b/docs/examples/databases/update-text-attribute.md index 01f66d13..c28dd7f0 100644 --- a/docs/examples/databases/update-text-attribute.md +++ b/docs/examples/databases/update-text-attribute.md @@ -16,7 +16,8 @@ result: AttributeText = databases.update_text_attribute( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-url-attribute.md b/docs/examples/databases/update-url-attribute.md index 9f05a4a4..4f772b97 100644 --- a/docs/examples/databases/update-url-attribute.md +++ b/docs/examples/databases/update-url-attribute.md @@ -16,7 +16,8 @@ result: AttributeUrl = databases.update_url_attribute( key = '', required = False, default = 'https://example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-varchar-attribute.md b/docs/examples/databases/update-varchar-attribute.md index b844a712..5fb7a73f 100644 --- a/docs/examples/databases/update-varchar-attribute.md +++ b/docs/examples/databases/update-varchar-attribute.md @@ -17,7 +17,8 @@ result: AttributeVarchar = databases.update_varchar_attribute( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update.md b/docs/examples/databases/update.md index 73458d4f..ae7b33eb 100644 --- a/docs/examples/databases/update.md +++ b/docs/examples/databases/update.md @@ -13,7 +13,8 @@ databases = Databases(client) result: Database = databases.update( database_id = '', name = '', # optional - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/create-collection.md b/docs/examples/documentsdb/create-collection.md index b717460f..da77bced 100644 --- a/docs/examples/documentsdb/create-collection.md +++ b/docs/examples/documentsdb/create-collection.md @@ -20,7 +20,8 @@ result: Collection = documents_db.create_collection( document_security = False, # optional enabled = False, # optional attributes = [], # optional - indexes = [] # optional + indexes = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/create-index.md b/docs/examples/documentsdb/create-index.md index 51eea435..4e9361cd 100644 --- a/docs/examples/documentsdb/create-index.md +++ b/docs/examples/documentsdb/create-index.md @@ -19,7 +19,8 @@ result: Index = documents_db.create_index( type = DocumentsDBIndexType.KEY, attributes = ["username"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/create-query.md b/docs/examples/documentsdb/create-query.md new file mode 100644 index 00000000..80f1298c --- /dev/null +++ b/docs/examples/documentsdb/create-query.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.models import DocumentList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +documents_db = DocumentsDB(client) + +result: DocumentList = documents_db.create_query( + database_id = '', + collection_id = '', + queries = [], # optional + transaction_id = '', # optional + total = False, # optional + ttl = 0 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/documentsdb/create.md b/docs/examples/documentsdb/create.md index b8da9d4b..9d424527 100644 --- a/docs/examples/documentsdb/create.md +++ b/docs/examples/documentsdb/create.md @@ -14,6 +14,7 @@ result: Database = documents_db.create( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/documentsdb/update-collection.md b/docs/examples/documentsdb/update-collection.md index 955fb28f..0c3d010f 100644 --- a/docs/examples/documentsdb/update-collection.md +++ b/docs/examples/documentsdb/update-collection.md @@ -19,7 +19,8 @@ result: Collection = documents_db.update_collection( permissions = [Permission.read(Role.any())], # optional document_security = False, # optional enabled = False, # optional - purge = False # optional + purge = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/update.md b/docs/examples/documentsdb/update.md index 03d7e861..3cd8bea3 100644 --- a/docs/examples/documentsdb/update.md +++ b/docs/examples/documentsdb/update.md @@ -14,6 +14,7 @@ result: Database = documents_db.update( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/domains/list-records.md b/docs/examples/domains/list-records.md index 9d40d255..94275a49 100644 --- a/docs/examples/domains/list-records.md +++ b/docs/examples/domains/list-records.md @@ -12,7 +12,8 @@ domains = Domains(client) result: DnsRecordsList = domains.list_records( domain_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/domains/list.md b/docs/examples/domains/list.md index f0d15a75..602fa974 100644 --- a/docs/examples/domains/list.md +++ b/docs/examples/domains/list.md @@ -12,7 +12,8 @@ domains = Domains(client) result: DomainsList = domains.list( queries = [], # optional - search = '' # optional + search = '', # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/create-variable.md b/docs/examples/functions/create-variable.md index da323f97..6f596772 100644 --- a/docs/examples/functions/create-variable.md +++ b/docs/examples/functions/create-variable.md @@ -15,7 +15,8 @@ result: Variable = functions.create_variable( variable_id = '', key = '', value = '', - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/create.md b/docs/examples/functions/create.md index 46652c4a..4cc28c82 100644 --- a/docs/examples/functions/create.md +++ b/docs/examples/functions/create.md @@ -35,7 +35,8 @@ result: Function = functions.create( build_specification = 's-1vcpu-512mb', # optional runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional - interval = 0 # optional + interval = 0, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/update-variable.md b/docs/examples/functions/update-variable.md index 6aa2f98c..2368f0d2 100644 --- a/docs/examples/functions/update-variable.md +++ b/docs/examples/functions/update-variable.md @@ -15,7 +15,8 @@ result: Variable = functions.update_variable( variable_id = '', key = '', # optional value = '', # optional - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/update.md b/docs/examples/functions/update.md index 425158bb..2efde2fb 100644 --- a/docs/examples/functions/update.md +++ b/docs/examples/functions/update.md @@ -36,6 +36,7 @@ result: Function = functions.update( runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional interval = 0, # optional + description = '', # optional email_enabled = False # optional ) diff --git a/docs/examples/messaging/create-email.md b/docs/examples/messaging/create-email.md index c8f278dd..6cf8f14d 100644 --- a/docs/examples/messaging/create-email.md +++ b/docs/examples/messaging/create-email.md @@ -24,7 +24,8 @@ result: Message = messaging.create_email( html = False, # optional scheduled_at = '2020-10-15T06:38:00.000+00:00', # optional reply_to_email = 'email@example.com', # optional - reply_to_name = '' # optional + reply_to_name = '', # optional + emails = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/messaging/create-topic.md b/docs/examples/messaging/create-topic.md index 93388456..6e45f73a 100644 --- a/docs/examples/messaging/create-topic.md +++ b/docs/examples/messaging/create-topic.md @@ -15,7 +15,8 @@ result: Topic = messaging.create_topic( name = '', subscribe = ["any"], # optional qos = 0, # optional - expiry = 0 # optional + expiry = 0, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/messaging/update-email.md b/docs/examples/messaging/update-email.md index bf8c5ba8..38a59902 100644 --- a/docs/examples/messaging/update-email.md +++ b/docs/examples/messaging/update-email.md @@ -24,7 +24,8 @@ result: Message = messaging.update_email( scheduled_at = '2020-10-15T06:38:00.000+00:00', # optional attachments = [], # optional reply_to_email = 'email@example.com', # optional - reply_to_name = '' # optional + reply_to_name = '', # optional + emails = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/messaging/update-topic.md b/docs/examples/messaging/update-topic.md index 4870903a..65213a5c 100644 --- a/docs/examples/messaging/update-topic.md +++ b/docs/examples/messaging/update-topic.md @@ -15,7 +15,8 @@ result: Topic = messaging.update_topic( name = '', # optional subscribe = ["any"], # optional qos = 0, # optional - expiry = 0 # optional + expiry = 0, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/mongo/list-backup-policies.md b/docs/examples/mongo/list-backup-policies.md index c023cc1d..e79ffd33 100644 --- a/docs/examples/mongo/list-backup-policies.md +++ b/docs/examples/mongo/list-backup-policies.md @@ -12,7 +12,8 @@ mongo = Mongo(client) result: BackupPolicyList = mongo.list_backup_policies( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mongo/list-backups.md b/docs/examples/mongo/list-backups.md index bf896520..86b6bc3f 100644 --- a/docs/examples/mongo/list-backups.md +++ b/docs/examples/mongo/list-backups.md @@ -12,7 +12,8 @@ mongo = Mongo(client) result: DedicatedDatabaseBackupList = mongo.list_backups( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mongo/list.md b/docs/examples/mongo/list.md index b956cebe..c31a18c3 100644 --- a/docs/examples/mongo/list.md +++ b/docs/examples/mongo/list.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key mongo = Mongo(client) result: DedicatedDatabaseList = mongo.list( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/list-backup-policies.md b/docs/examples/mysql/list-backup-policies.md index 0b5328d4..0297517f 100644 --- a/docs/examples/mysql/list-backup-policies.md +++ b/docs/examples/mysql/list-backup-policies.md @@ -12,7 +12,8 @@ mysql = Mysql(client) result: BackupPolicyList = mysql.list_backup_policies( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/list-backups.md b/docs/examples/mysql/list-backups.md index 5a533951..0dc15e5b 100644 --- a/docs/examples/mysql/list-backups.md +++ b/docs/examples/mysql/list-backups.md @@ -12,7 +12,8 @@ mysql = Mysql(client) result: DedicatedDatabaseBackupList = mysql.list_backups( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/list.md b/docs/examples/mysql/list.md index 865e474f..b5aba207 100644 --- a/docs/examples/mysql/list.md +++ b/docs/examples/mysql/list.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key mysql = Mysql(client) result: DedicatedDatabaseList = mysql.list( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/organization/update-project-key.md b/docs/examples/organization/update-project-key.md index 7f01ddf5..ea537952 100644 --- a/docs/examples/organization/update-project-key.md +++ b/docs/examples/organization/update-project-key.md @@ -16,7 +16,8 @@ result: Key = organization.update_project_key( key_id = '', name = '', scopes = [ProjectKeyScopes.USERS_READ], - expire = '2020-10-15T06:38:00.000+00:00' # optional + expire = '2020-10-15T06:38:00.000+00:00', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/postgresql/list-backup-policies.md b/docs/examples/postgresql/list-backup-policies.md index c9000c59..2c10f860 100644 --- a/docs/examples/postgresql/list-backup-policies.md +++ b/docs/examples/postgresql/list-backup-policies.md @@ -12,7 +12,8 @@ postgresql = Postgresql(client) result: BackupPolicyList = postgresql.list_backup_policies( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/postgresql/list-backups.md b/docs/examples/postgresql/list-backups.md index 85622373..ea05800f 100644 --- a/docs/examples/postgresql/list-backups.md +++ b/docs/examples/postgresql/list-backups.md @@ -12,7 +12,8 @@ postgresql = Postgresql(client) result: DedicatedDatabaseBackupList = postgresql.list_backups( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/postgresql/list.md b/docs/examples/postgresql/list.md index fae83f96..41d53428 100644 --- a/docs/examples/postgresql/list.md +++ b/docs/examples/postgresql/list.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key postgresql = Postgresql(client) result: DedicatedDatabaseList = postgresql.list( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/project/create-variable.md b/docs/examples/project/create-variable.md index 668e3548..4b73077c 100644 --- a/docs/examples/project/create-variable.md +++ b/docs/examples/project/create-variable.md @@ -14,7 +14,8 @@ result: Variable = project.create_variable( variable_id = '', key = '', value = '', - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-key.md b/docs/examples/project/update-key.md index a8aa7ccf..12faf365 100644 --- a/docs/examples/project/update-key.md +++ b/docs/examples/project/update-key.md @@ -15,7 +15,8 @@ result: Key = project.update_key( key_id = '', name = '', scopes = [ProjectKeyScopes.USERS_READ], - expire = '2020-10-15T06:38:00.000+00:00' # optional + expire = '2020-10-15T06:38:00.000+00:00', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-password-dictionary-policy.md b/docs/examples/project/update-password-dictionary-policy.md index 93e652be..53a8d71e 100644 --- a/docs/examples/project/update-password-dictionary-policy.md +++ b/docs/examples/project/update-password-dictionary-policy.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key project = Project(client) result: ProjectModel = project.update_password_dictionary_policy( - enabled = False + enabled = False, + deny_list = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-password-personal-data-policy.md b/docs/examples/project/update-password-personal-data-policy.md index 507bdac4..d9587b6a 100644 --- a/docs/examples/project/update-password-personal-data-policy.md +++ b/docs/examples/project/update-password-personal-data-policy.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key project = Project(client) result: ProjectModel = project.update_password_personal_data_policy( - enabled = False + enabled = False, + deny_list = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-variable.md b/docs/examples/project/update-variable.md index 3f4dac0f..abe4ae74 100644 --- a/docs/examples/project/update-variable.md +++ b/docs/examples/project/update-variable.md @@ -14,7 +14,8 @@ result: Variable = project.update_variable( variable_id = '', key = '', # optional value = '', # optional - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/create-variable.md b/docs/examples/sites/create-variable.md index ca6d9e57..7174c1d1 100644 --- a/docs/examples/sites/create-variable.md +++ b/docs/examples/sites/create-variable.md @@ -15,7 +15,8 @@ result: Variable = sites.create_variable( variable_id = '', key = '', value = '', - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/create.md b/docs/examples/sites/create.md index 4d8e03bc..c993e6fb 100644 --- a/docs/examples/sites/create.md +++ b/docs/examples/sites/create.md @@ -38,7 +38,8 @@ result: Site = sites.create( build_specification = 's-1vcpu-512mb', # optional runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional - scopes = [ProjectKeyScopes.PROJECT_READ] # optional + scopes = [ProjectKeyScopes.PROJECT_READ], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/update-variable.md b/docs/examples/sites/update-variable.md index 637d2220..9293b47d 100644 --- a/docs/examples/sites/update-variable.md +++ b/docs/examples/sites/update-variable.md @@ -15,7 +15,8 @@ result: Variable = sites.update_variable( variable_id = '', key = '', # optional value = '', # optional - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/update.md b/docs/examples/sites/update.md index 59ac85ce..f6f5bbdd 100644 --- a/docs/examples/sites/update.md +++ b/docs/examples/sites/update.md @@ -38,7 +38,8 @@ result: Site = sites.update( build_specification = 's-1vcpu-512mb', # optional runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional - scopes = [ProjectKeyScopes.PROJECT_READ] # optional + scopes = [ProjectKeyScopes.PROJECT_READ], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/storage/create-bucket.md b/docs/examples/storage/create-bucket.md index 76ec2f0e..9c6af7cb 100644 --- a/docs/examples/storage/create-bucket.md +++ b/docs/examples/storage/create-bucket.md @@ -24,7 +24,8 @@ result: Bucket = storage.create_bucket( compression = Compression.NONE, # optional encryption = False, # optional antivirus = False, # optional - transformations = False # optional + transformations = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/storage/update-bucket.md b/docs/examples/storage/update-bucket.md index c9222517..db26467d 100644 --- a/docs/examples/storage/update-bucket.md +++ b/docs/examples/storage/update-bucket.md @@ -24,7 +24,8 @@ result: Bucket = storage.update_bucket( compression = Compression.NONE, # optional encryption = False, # optional antivirus = False, # optional - transformations = False # optional + transformations = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-big-int-column.md b/docs/examples/tablesdb/create-big-int-column.md index cff66960..2ebb4287 100644 --- a/docs/examples/tablesdb/create-big-int-column.md +++ b/docs/examples/tablesdb/create-big-int-column.md @@ -18,7 +18,8 @@ result: ColumnBigint = tables_db.create_big_int_column( min = 0, # optional max = 1000000, # optional default = 0, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-boolean-column.md b/docs/examples/tablesdb/create-boolean-column.md index b325095d..c5b62657 100644 --- a/docs/examples/tablesdb/create-boolean-column.md +++ b/docs/examples/tablesdb/create-boolean-column.md @@ -16,7 +16,8 @@ result: ColumnBoolean = tables_db.create_boolean_column( key = '', required = False, default = False, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-datetime-column.md b/docs/examples/tablesdb/create-datetime-column.md index a4a565d0..30463a6b 100644 --- a/docs/examples/tablesdb/create-datetime-column.md +++ b/docs/examples/tablesdb/create-datetime-column.md @@ -16,7 +16,8 @@ result: ColumnDatetime = tables_db.create_datetime_column( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-email-column.md b/docs/examples/tablesdb/create-email-column.md index 7315689c..cae71254 100644 --- a/docs/examples/tablesdb/create-email-column.md +++ b/docs/examples/tablesdb/create-email-column.md @@ -16,7 +16,8 @@ result: ColumnEmail = tables_db.create_email_column( key = '', required = False, default = 'email@example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-enum-column.md b/docs/examples/tablesdb/create-enum-column.md index 3e4cb1b9..645aa8fb 100644 --- a/docs/examples/tablesdb/create-enum-column.md +++ b/docs/examples/tablesdb/create-enum-column.md @@ -17,7 +17,8 @@ result: ColumnEnum = tables_db.create_enum_column( elements = ["active", "inactive"], required = False, default = 'active', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-float-column.md b/docs/examples/tablesdb/create-float-column.md index 0edd6a26..b57def0a 100644 --- a/docs/examples/tablesdb/create-float-column.md +++ b/docs/examples/tablesdb/create-float-column.md @@ -18,7 +18,8 @@ result: ColumnFloat = tables_db.create_float_column( min = 0, # optional max = 100, # optional default = 10.5, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-index.md b/docs/examples/tablesdb/create-index.md index 8ecf506d..f953466f 100644 --- a/docs/examples/tablesdb/create-index.md +++ b/docs/examples/tablesdb/create-index.md @@ -19,7 +19,8 @@ result: ColumnIndex = tables_db.create_index( type = TablesDBIndexType.KEY, columns = ["username"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-integer-column.md b/docs/examples/tablesdb/create-integer-column.md index bbe76889..8b989252 100644 --- a/docs/examples/tablesdb/create-integer-column.md +++ b/docs/examples/tablesdb/create-integer-column.md @@ -18,7 +18,8 @@ result: ColumnInteger = tables_db.create_integer_column( min = 0, # optional max = 100, # optional default = 10, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-ip-column.md b/docs/examples/tablesdb/create-ip-column.md index a0a47e67..9c510780 100644 --- a/docs/examples/tablesdb/create-ip-column.md +++ b/docs/examples/tablesdb/create-ip-column.md @@ -16,7 +16,8 @@ result: ColumnIp = tables_db.create_ip_column( key = '', required = False, default = '192.0.2.0', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-line-column.md b/docs/examples/tablesdb/create-line-column.md index 2fc64c4f..e32562f8 100644 --- a/docs/examples/tablesdb/create-line-column.md +++ b/docs/examples/tablesdb/create-line-column.md @@ -15,7 +15,8 @@ result: ColumnLine = tables_db.create_line_column( table_id = '', key = '', required = False, - default = [[1, 2], [3, 4], [5, 6]] # optional + default = [[1, 2], [3, 4], [5, 6]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-longtext-column.md b/docs/examples/tablesdb/create-longtext-column.md index 1438ff8a..57ed86db 100644 --- a/docs/examples/tablesdb/create-longtext-column.md +++ b/docs/examples/tablesdb/create-longtext-column.md @@ -17,7 +17,8 @@ result: ColumnLongtext = tables_db.create_longtext_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-mediumtext-column.md b/docs/examples/tablesdb/create-mediumtext-column.md index 6729b329..261d801a 100644 --- a/docs/examples/tablesdb/create-mediumtext-column.md +++ b/docs/examples/tablesdb/create-mediumtext-column.md @@ -17,7 +17,8 @@ result: ColumnMediumtext = tables_db.create_mediumtext_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-point-column.md b/docs/examples/tablesdb/create-point-column.md index 17c1e447..e1fdb9e7 100644 --- a/docs/examples/tablesdb/create-point-column.md +++ b/docs/examples/tablesdb/create-point-column.md @@ -15,7 +15,8 @@ result: ColumnPoint = tables_db.create_point_column( table_id = '', key = '', required = False, - default = [1, 2] # optional + default = [1, 2], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-polygon-column.md b/docs/examples/tablesdb/create-polygon-column.md index 86fca304..08c2c1af 100644 --- a/docs/examples/tablesdb/create-polygon-column.md +++ b/docs/examples/tablesdb/create-polygon-column.md @@ -15,7 +15,8 @@ result: ColumnPolygon = tables_db.create_polygon_column( table_id = '', key = '', required = False, - default = [[[1, 2], [3, 4], [5, 6], [1, 2]]] # optional + default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-query.md b/docs/examples/tablesdb/create-query.md new file mode 100644 index 00000000..cdc0ddd5 --- /dev/null +++ b/docs/examples/tablesdb/create-query.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import RowList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +tables_db = TablesDB(client) + +result: RowList = tables_db.create_query( + database_id = '', + table_id = '', + queries = [], # optional + transaction_id = '', # optional + total = False, # optional + ttl = 0 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/tablesdb/create-relationship-column.md b/docs/examples/tablesdb/create-relationship-column.md index 32fc4ba1..1287da28 100644 --- a/docs/examples/tablesdb/create-relationship-column.md +++ b/docs/examples/tablesdb/create-relationship-column.md @@ -20,7 +20,8 @@ result: ColumnRelationship = tables_db.create_relationship_column( two_way = False, # optional key = '', # optional two_way_key = '', # optional - on_delete = RelationMutate.CASCADE # optional + on_delete = RelationMutate.CASCADE, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-string-column.md b/docs/examples/tablesdb/create-string-column.md index 85decfde..27a96af2 100644 --- a/docs/examples/tablesdb/create-string-column.md +++ b/docs/examples/tablesdb/create-string-column.md @@ -18,7 +18,8 @@ result: ColumnString = tables_db.create_string_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-table.md b/docs/examples/tablesdb/create-table.md index 029fb281..5ddf3b99 100644 --- a/docs/examples/tablesdb/create-table.md +++ b/docs/examples/tablesdb/create-table.md @@ -20,7 +20,8 @@ result: Table = tables_db.create_table( row_security = False, # optional enabled = False, # optional columns = [], # optional - indexes = [] # optional + indexes = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-text-column.md b/docs/examples/tablesdb/create-text-column.md index f3c8df3d..f7c63339 100644 --- a/docs/examples/tablesdb/create-text-column.md +++ b/docs/examples/tablesdb/create-text-column.md @@ -17,7 +17,8 @@ result: ColumnText = tables_db.create_text_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-url-column.md b/docs/examples/tablesdb/create-url-column.md index 4bfb858f..ae9a5ecc 100644 --- a/docs/examples/tablesdb/create-url-column.md +++ b/docs/examples/tablesdb/create-url-column.md @@ -16,7 +16,8 @@ result: ColumnUrl = tables_db.create_url_column( key = '', required = False, default = 'https://example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-varchar-column.md b/docs/examples/tablesdb/create-varchar-column.md index 2fb6338c..d181df49 100644 --- a/docs/examples/tablesdb/create-varchar-column.md +++ b/docs/examples/tablesdb/create-varchar-column.md @@ -18,7 +18,8 @@ result: ColumnVarchar = tables_db.create_varchar_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create.md b/docs/examples/tablesdb/create.md index ca668875..ff89d119 100644 --- a/docs/examples/tablesdb/create.md +++ b/docs/examples/tablesdb/create.md @@ -14,6 +14,7 @@ result: Database = tables_db.create( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/tablesdb/update-big-int-column.md b/docs/examples/tablesdb/update-big-int-column.md index f4e6e90d..76343d4c 100644 --- a/docs/examples/tablesdb/update-big-int-column.md +++ b/docs/examples/tablesdb/update-big-int-column.md @@ -18,7 +18,8 @@ result: ColumnBigint = tables_db.update_big_int_column( default = 0, min = 0, # optional max = 1000000, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-boolean-column.md b/docs/examples/tablesdb/update-boolean-column.md index 9352362c..8b2f0be8 100644 --- a/docs/examples/tablesdb/update-boolean-column.md +++ b/docs/examples/tablesdb/update-boolean-column.md @@ -16,7 +16,8 @@ result: ColumnBoolean = tables_db.update_boolean_column( key = '', required = False, default = False, - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-datetime-column.md b/docs/examples/tablesdb/update-datetime-column.md index 70cd5754..10f3146e 100644 --- a/docs/examples/tablesdb/update-datetime-column.md +++ b/docs/examples/tablesdb/update-datetime-column.md @@ -16,7 +16,8 @@ result: ColumnDatetime = tables_db.update_datetime_column( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-email-column.md b/docs/examples/tablesdb/update-email-column.md index 94036baf..272d0c00 100644 --- a/docs/examples/tablesdb/update-email-column.md +++ b/docs/examples/tablesdb/update-email-column.md @@ -16,7 +16,8 @@ result: ColumnEmail = tables_db.update_email_column( key = '', required = False, default = 'email@example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-enum-column.md b/docs/examples/tablesdb/update-enum-column.md index 0ac9ac91..41ca8bcf 100644 --- a/docs/examples/tablesdb/update-enum-column.md +++ b/docs/examples/tablesdb/update-enum-column.md @@ -17,7 +17,8 @@ result: ColumnEnum = tables_db.update_enum_column( elements = ["active", "inactive"], required = False, default = 'active', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-float-column.md b/docs/examples/tablesdb/update-float-column.md index 08e39be5..e851722f 100644 --- a/docs/examples/tablesdb/update-float-column.md +++ b/docs/examples/tablesdb/update-float-column.md @@ -18,7 +18,8 @@ result: ColumnFloat = tables_db.update_float_column( default = 10.5, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-integer-column.md b/docs/examples/tablesdb/update-integer-column.md index cd6831fc..6f7c7916 100644 --- a/docs/examples/tablesdb/update-integer-column.md +++ b/docs/examples/tablesdb/update-integer-column.md @@ -18,7 +18,8 @@ result: ColumnInteger = tables_db.update_integer_column( default = 10, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-ip-column.md b/docs/examples/tablesdb/update-ip-column.md index c3570fb1..f94638c3 100644 --- a/docs/examples/tablesdb/update-ip-column.md +++ b/docs/examples/tablesdb/update-ip-column.md @@ -16,7 +16,8 @@ result: ColumnIp = tables_db.update_ip_column( key = '', required = False, default = '192.0.2.0', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-line-column.md b/docs/examples/tablesdb/update-line-column.md index 052a12c0..8ef8353a 100644 --- a/docs/examples/tablesdb/update-line-column.md +++ b/docs/examples/tablesdb/update-line-column.md @@ -16,7 +16,8 @@ result: ColumnLine = tables_db.update_line_column( key = '', required = False, default = [[1, 2], [3, 4], [5, 6]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-longtext-column.md b/docs/examples/tablesdb/update-longtext-column.md index 8e44bd03..154e7b36 100644 --- a/docs/examples/tablesdb/update-longtext-column.md +++ b/docs/examples/tablesdb/update-longtext-column.md @@ -16,7 +16,8 @@ result: ColumnLongtext = tables_db.update_longtext_column( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-mediumtext-column.md b/docs/examples/tablesdb/update-mediumtext-column.md index 2412dc84..2c92bd4f 100644 --- a/docs/examples/tablesdb/update-mediumtext-column.md +++ b/docs/examples/tablesdb/update-mediumtext-column.md @@ -16,7 +16,8 @@ result: ColumnMediumtext = tables_db.update_mediumtext_column( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-point-column.md b/docs/examples/tablesdb/update-point-column.md index 5368394e..a232c2e1 100644 --- a/docs/examples/tablesdb/update-point-column.md +++ b/docs/examples/tablesdb/update-point-column.md @@ -16,7 +16,8 @@ result: ColumnPoint = tables_db.update_point_column( key = '', required = False, default = [1, 2], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-polygon-column.md b/docs/examples/tablesdb/update-polygon-column.md index b7af58ae..1237ffe1 100644 --- a/docs/examples/tablesdb/update-polygon-column.md +++ b/docs/examples/tablesdb/update-polygon-column.md @@ -16,7 +16,8 @@ result: ColumnPolygon = tables_db.update_polygon_column( key = '', required = False, default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-relationship-column.md b/docs/examples/tablesdb/update-relationship-column.md index 67fb6063..3bc94157 100644 --- a/docs/examples/tablesdb/update-relationship-column.md +++ b/docs/examples/tablesdb/update-relationship-column.md @@ -16,7 +16,8 @@ result: ColumnRelationship = tables_db.update_relationship_column( table_id = '', key = '', on_delete = RelationMutate.CASCADE, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-string-column.md b/docs/examples/tablesdb/update-string-column.md index f3375237..3dddf79b 100644 --- a/docs/examples/tablesdb/update-string-column.md +++ b/docs/examples/tablesdb/update-string-column.md @@ -17,7 +17,8 @@ result: ColumnString = tables_db.update_string_column( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-table.md b/docs/examples/tablesdb/update-table.md index ee6bba02..e1d8ff90 100644 --- a/docs/examples/tablesdb/update-table.md +++ b/docs/examples/tablesdb/update-table.md @@ -19,7 +19,8 @@ result: Table = tables_db.update_table( permissions = [Permission.read(Role.any())], # optional row_security = False, # optional enabled = False, # optional - purge = False # optional + purge = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-text-column.md b/docs/examples/tablesdb/update-text-column.md index 21e03944..b7999d41 100644 --- a/docs/examples/tablesdb/update-text-column.md +++ b/docs/examples/tablesdb/update-text-column.md @@ -16,7 +16,8 @@ result: ColumnText = tables_db.update_text_column( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-url-column.md b/docs/examples/tablesdb/update-url-column.md index 2553957d..c4fcff52 100644 --- a/docs/examples/tablesdb/update-url-column.md +++ b/docs/examples/tablesdb/update-url-column.md @@ -16,7 +16,8 @@ result: ColumnUrl = tables_db.update_url_column( key = '', required = False, default = 'https://example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-varchar-column.md b/docs/examples/tablesdb/update-varchar-column.md index 3f03b42f..88133183 100644 --- a/docs/examples/tablesdb/update-varchar-column.md +++ b/docs/examples/tablesdb/update-varchar-column.md @@ -17,7 +17,8 @@ result: ColumnVarchar = tables_db.update_varchar_column( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update.md b/docs/examples/tablesdb/update.md index 7fc0c930..7b8b2225 100644 --- a/docs/examples/tablesdb/update.md +++ b/docs/examples/tablesdb/update.md @@ -14,6 +14,7 @@ result: Database = tables_db.update( database_id = '', name = '', # optional enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/vectorsdb/create-collection.md b/docs/examples/vectorsdb/create-collection.md index 6dcdeb24..6011be07 100644 --- a/docs/examples/vectorsdb/create-collection.md +++ b/docs/examples/vectorsdb/create-collection.md @@ -19,7 +19,8 @@ result: VectorsdbCollection = vectors_db.create_collection( dimension = 4, permissions = [Permission.read(Role.any())], # optional document_security = False, # optional - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/vectorsdb/create-index.md b/docs/examples/vectorsdb/create-index.md index 71e650a6..a1fcd6f7 100644 --- a/docs/examples/vectorsdb/create-index.md +++ b/docs/examples/vectorsdb/create-index.md @@ -19,7 +19,8 @@ result: Index = vectors_db.create_index( type = VectorsDBIndexType.HNSW_EUCLIDEAN, attributes = ["embeddings"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/vectorsdb/create.md b/docs/examples/vectorsdb/create.md index a77a3a38..b133cb18 100644 --- a/docs/examples/vectorsdb/create.md +++ b/docs/examples/vectorsdb/create.md @@ -14,6 +14,7 @@ result: Database = vectors_db.create( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/vectorsdb/update-collection.md b/docs/examples/vectorsdb/update-collection.md index 7093c0b2..e4c925e1 100644 --- a/docs/examples/vectorsdb/update-collection.md +++ b/docs/examples/vectorsdb/update-collection.md @@ -19,7 +19,8 @@ result: VectorsdbCollection = vectors_db.update_collection( dimension = 4, # optional permissions = [Permission.read(Role.any())], # optional document_security = False, # optional - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/vectorsdb/update.md b/docs/examples/vectorsdb/update.md index 23ae9429..25ee897b 100644 --- a/docs/examples/vectorsdb/update.md +++ b/docs/examples/vectorsdb/update.md @@ -14,6 +14,7 @@ result: Database = vectors_db.update( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/webhooks/create.md b/docs/examples/webhooks/create.md index 1585ef2b..fddd87eb 100644 --- a/docs/examples/webhooks/create.md +++ b/docs/examples/webhooks/create.md @@ -19,7 +19,8 @@ result: Webhook = webhooks.create( tls = False, # optional auth_username = '', # optional auth_password = 'password', # optional - secret = '' # optional + secret = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/webhooks/update.md b/docs/examples/webhooks/update.md index 647e8618..7fa2311e 100644 --- a/docs/examples/webhooks/update.md +++ b/docs/examples/webhooks/update.md @@ -18,7 +18,8 @@ result: Webhook = webhooks.update( enabled = False, # optional tls = False, # optional auth_username = '', # optional - auth_password = 'password' # optional + auth_password = 'password', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/pyproject.toml b/pyproject.toml index f9897243..b75f237c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "appwrite" -version = "25.0.0" +version = "26.0.0" description = "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API" readme = "README.md" requires-python = ">=3.9" @@ -30,6 +30,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] [project.optional-dependencies] diff --git a/setup.py b/setup.py index dc433792..88f54765 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='appwrite', packages=setuptools.find_packages(), - version='25.0.0', + version='26.0.0', license='BSD-3-Clause', description='Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API', long_description=long_description, @@ -18,7 +18,7 @@ maintainer='Appwrite Team', maintainer_email='team@appwrite.io', url='https://appwrite.io/support', - download_url='https://github.com/appwrite/sdk-for-python/archive/25.0.0.tar.gz', + download_url='https://github.com/appwrite/sdk-for-python/archive/26.0.0.tar.gz', install_requires=[ 'requests', 'pydantic>=2,<3', @@ -35,5 +35,6 @@ 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ], ) diff --git a/test/services/test_account.py b/test/services/test_account.py index 04fb136f..e37d5b47 100644 --- a/test/services/test_account.py +++ b/test/services/test_account.py @@ -261,6 +261,22 @@ def test_delete_identity(self, m): ) self.assertEqual(response, data) + @requests_mock.Mocker() + def test_list_memberships(self, m): + data = { + "total": 5.0, + "memberships": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.account.list_memberships() + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_update_mfa(self, m): data = { diff --git a/test/services/test_databases.py b/test/services/test_databases.py index 733f8f08..14d81297 100644 --- a/test/services/test_databases.py +++ b/test/services/test_databases.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -175,6 +176,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -197,6 +199,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -256,6 +259,7 @@ def test_create_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -286,6 +290,7 @@ def test_get_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -315,6 +320,7 @@ def test_update_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -374,6 +380,7 @@ def test_list_attributes(self, m): def test_create_big_int_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -400,6 +407,7 @@ def test_create_big_int_attribute(self, m): def test_update_big_int_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -427,6 +435,7 @@ def test_update_big_int_attribute(self, m): def test_create_boolean_attribute(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -453,6 +462,7 @@ def test_create_boolean_attribute(self, m): def test_update_boolean_attribute(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -480,6 +490,7 @@ def test_update_boolean_attribute(self, m): def test_create_datetime_attribute(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -507,6 +518,7 @@ def test_create_datetime_attribute(self, m): def test_update_datetime_attribute(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -535,6 +547,7 @@ def test_update_datetime_attribute(self, m): def test_create_email_attribute(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -562,6 +575,7 @@ def test_create_email_attribute(self, m): def test_update_email_attribute(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -590,6 +604,7 @@ def test_update_email_attribute(self, m): def test_create_enum_attribute(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -619,6 +634,7 @@ def test_create_enum_attribute(self, m): def test_update_enum_attribute(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -649,6 +665,7 @@ def test_update_enum_attribute(self, m): def test_create_float_attribute(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -675,6 +692,7 @@ def test_create_float_attribute(self, m): def test_update_float_attribute(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -702,6 +720,7 @@ def test_update_float_attribute(self, m): def test_create_integer_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -728,6 +747,7 @@ def test_create_integer_attribute(self, m): def test_update_integer_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -755,6 +775,7 @@ def test_update_integer_attribute(self, m): def test_create_ip_attribute(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -782,6 +803,7 @@ def test_create_ip_attribute(self, m): def test_update_ip_attribute(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -810,6 +832,7 @@ def test_update_ip_attribute(self, m): def test_create_line_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -836,6 +859,7 @@ def test_create_line_attribute(self, m): def test_update_line_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -862,6 +886,7 @@ def test_update_line_attribute(self, m): def test_create_longtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -888,6 +913,7 @@ def test_create_longtext_attribute(self, m): def test_update_longtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -915,6 +941,7 @@ def test_update_longtext_attribute(self, m): def test_create_mediumtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -941,6 +968,7 @@ def test_create_mediumtext_attribute(self, m): def test_update_mediumtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -968,6 +996,7 @@ def test_update_mediumtext_attribute(self, m): def test_create_point_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -994,6 +1023,7 @@ def test_create_point_attribute(self, m): def test_update_point_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1020,6 +1050,7 @@ def test_update_point_attribute(self, m): def test_create_polygon_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1046,6 +1077,7 @@ def test_create_polygon_attribute(self, m): def test_update_polygon_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1072,6 +1104,7 @@ def test_update_polygon_attribute(self, m): def test_create_relationship_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1104,6 +1137,7 @@ def test_create_relationship_attribute(self, m): def test_update_relationship_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1135,6 +1169,7 @@ def test_update_relationship_attribute(self, m): def test_create_string_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1163,6 +1198,7 @@ def test_create_string_attribute(self, m): def test_update_string_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1191,6 +1227,7 @@ def test_update_string_attribute(self, m): def test_create_text_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1217,6 +1254,7 @@ def test_create_text_attribute(self, m): def test_update_text_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1244,6 +1282,7 @@ def test_update_text_attribute(self, m): def test_create_url_attribute(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1271,6 +1310,7 @@ def test_create_url_attribute(self, m): def test_update_url_attribute(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1299,6 +1339,7 @@ def test_update_url_attribute(self, m): def test_create_varchar_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1327,6 +1368,7 @@ def test_create_varchar_attribute(self, m): def test_update_varchar_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1355,6 +1397,7 @@ def test_update_varchar_attribute(self, m): def test_get_attribute(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -1517,6 +1560,25 @@ def test_delete_documents(self, m): ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_create_query(self, m): + data = { + "total": 5.0, + "documents": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.databases.create_query( + '', + '', + ) + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_get_document(self, m): data = { @@ -1692,6 +1754,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -1721,6 +1784,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", diff --git a/test/services/test_documents_db.py b/test/services/test_documents_db.py index fb451c82..c9840596 100644 --- a/test/services/test_documents_db.py +++ b/test/services/test_documents_db.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -191,6 +192,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -213,6 +215,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -273,6 +276,7 @@ def test_create_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -303,6 +307,7 @@ def test_get_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -332,6 +337,7 @@ def test_update_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -493,6 +499,25 @@ def test_delete_documents(self, m): ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_create_query(self, m): + data = { + "total": 5.0, + "documents": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.documents_db.create_query( + '', + '', + ) + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_get_document(self, m): data = { @@ -668,6 +693,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -697,6 +723,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", diff --git a/test/services/test_functions.py b/test/services/test_functions.py index d515e891..1f63e700 100644 --- a/test/services/test_functions.py +++ b/test/services/test_functions.py @@ -38,6 +38,7 @@ def test_create(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -121,6 +122,7 @@ def test_get(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -170,6 +172,7 @@ def test_update(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -235,6 +238,7 @@ def test_update_function_deployment(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -725,6 +729,7 @@ def test_create_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -752,6 +757,7 @@ def test_get_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -777,6 +783,7 @@ def test_update_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", diff --git a/test/services/test_messaging.py b/test/services/test_messaging.py index 03c86ea6..d22f32d6 100644 --- a/test/services/test_messaging.py +++ b/test/services/test_messaging.py @@ -1165,6 +1165,7 @@ def test_create_topic(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "events", + "description": "Order status updates sent to customers.", "emailTotal": 100.0, "smsTotal": 100.0, "pushTotal": 100.0, @@ -1190,6 +1191,7 @@ def test_get_topic(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "events", + "description": "Order status updates sent to customers.", "emailTotal": 100.0, "smsTotal": 100.0, "pushTotal": 100.0, @@ -1214,6 +1216,7 @@ def test_update_topic(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "events", + "description": "Order status updates sent to customers.", "emailTotal": 100.0, "smsTotal": 100.0, "pushTotal": 100.0, diff --git a/test/services/test_organization.py b/test/services/test_organization.py index 85ed9358..41206a71 100644 --- a/test/services/test_organization.py +++ b/test/services/test_organization.py @@ -92,6 +92,7 @@ def test_get(self, m): "deploymentSize": 30.0, "buildSize": 2000.0, "functionsIntervalMinimum": 60.0, + "functionsAllowCron": True, "databasesAllowEncrypt": True, "group": "pro", "databaseComputeCredit": 10, @@ -190,6 +191,7 @@ def test_update(self, m): "deploymentSize": 30.0, "buildSize": 2000.0, "functionsIntervalMinimum": 60.0, + "functionsAllowCron": True, "databasesAllowEncrypt": True, "group": "pro", "databaseComputeCredit": 10, @@ -640,6 +642,7 @@ def test_create_ephemeral_project_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -667,6 +670,7 @@ def test_get_project_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -693,6 +697,7 @@ def test_update_project_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", diff --git a/test/services/test_project.py b/test/services/test_project.py index 669f0013..d0c94701 100644 --- a/test/services/test_project.py +++ b/test/services/test_project.py @@ -292,6 +292,7 @@ def test_create_ephemeral_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -318,6 +319,7 @@ def test_get_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -343,6 +345,7 @@ def test_update_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -2546,6 +2549,7 @@ def test_get_policy(self, m): data = { "$id": "password-dictionary", "enabled": True, + "denyList": [], } headers = {'Content-Type': 'application/json'} m.request( @@ -2787,6 +2791,7 @@ def test_create_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -2813,6 +2818,7 @@ def test_get_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -2837,6 +2843,7 @@ def test_update_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", diff --git a/test/services/test_sites.py b/test/services/test_sites.py index d7b7f32c..a1e4354e 100644 --- a/test/services/test_sites.py +++ b/test/services/test_sites.py @@ -37,6 +37,7 @@ def test_create(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -123,6 +124,7 @@ def test_get(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -174,6 +176,7 @@ def test_update(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -242,6 +245,7 @@ def test_update_site_deployment(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -700,6 +704,7 @@ def test_create_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -727,6 +732,7 @@ def test_get_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -752,6 +758,7 @@ def test_update_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", diff --git a/test/services/test_storage.py b/test/services/test_storage.py index d2e56d29..d368d214 100644 --- a/test/services/test_storage.py +++ b/test/services/test_storage.py @@ -39,6 +39,7 @@ def test_create_bucket(self, m): "$permissions": [], "fileSecurity": True, "name": "Documents", + "description": "Invoices and receipts uploaded by customers.", "enabled": True, "maximumFileSize": 100.0, "allowedFileExtensions": [], @@ -70,6 +71,7 @@ def test_get_bucket(self, m): "$permissions": [], "fileSecurity": True, "name": "Documents", + "description": "Invoices and receipts uploaded by customers.", "enabled": True, "maximumFileSize": 100.0, "allowedFileExtensions": [], @@ -100,6 +102,7 @@ def test_update_bucket(self, m): "$permissions": [], "fileSecurity": True, "name": "Documents", + "description": "Invoices and receipts uploaded by customers.", "enabled": True, "maximumFileSize": 100.0, "allowedFileExtensions": [], diff --git a/test/services/test_tables_db.py b/test/services/test_tables_db.py index b2e71905..31151822 100644 --- a/test/services/test_tables_db.py +++ b/test/services/test_tables_db.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -191,6 +192,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -213,6 +215,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -536,6 +539,7 @@ def test_create_table(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Table", + "description": "Movies available in the catalog.", "enabled": True, "rowSecurity": True, "columns": [], @@ -566,6 +570,7 @@ def test_get_table(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Table", + "description": "Movies available in the catalog.", "enabled": True, "rowSecurity": True, "columns": [], @@ -595,6 +600,7 @@ def test_update_table(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Table", + "description": "Movies available in the catalog.", "enabled": True, "rowSecurity": True, "columns": [], @@ -654,6 +660,7 @@ def test_list_columns(self, m): def test_create_big_int_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -680,6 +687,7 @@ def test_create_big_int_column(self, m): def test_update_big_int_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -707,6 +715,7 @@ def test_update_big_int_column(self, m): def test_create_boolean_column(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -733,6 +742,7 @@ def test_create_boolean_column(self, m): def test_update_boolean_column(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -760,6 +770,7 @@ def test_update_boolean_column(self, m): def test_create_datetime_column(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -787,6 +798,7 @@ def test_create_datetime_column(self, m): def test_update_datetime_column(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -815,6 +827,7 @@ def test_update_datetime_column(self, m): def test_create_email_column(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -842,6 +855,7 @@ def test_create_email_column(self, m): def test_update_email_column(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -870,6 +884,7 @@ def test_update_email_column(self, m): def test_create_enum_column(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -899,6 +914,7 @@ def test_create_enum_column(self, m): def test_update_enum_column(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -929,6 +945,7 @@ def test_update_enum_column(self, m): def test_create_float_column(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -955,6 +972,7 @@ def test_create_float_column(self, m): def test_update_float_column(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -982,6 +1000,7 @@ def test_update_float_column(self, m): def test_create_integer_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -1008,6 +1027,7 @@ def test_create_integer_column(self, m): def test_update_integer_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -1035,6 +1055,7 @@ def test_update_integer_column(self, m): def test_create_ip_column(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1062,6 +1083,7 @@ def test_create_ip_column(self, m): def test_update_ip_column(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1090,6 +1112,7 @@ def test_update_ip_column(self, m): def test_create_line_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1116,6 +1139,7 @@ def test_create_line_column(self, m): def test_update_line_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1142,6 +1166,7 @@ def test_update_line_column(self, m): def test_create_longtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1168,6 +1193,7 @@ def test_create_longtext_column(self, m): def test_update_longtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1195,6 +1221,7 @@ def test_update_longtext_column(self, m): def test_create_mediumtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1221,6 +1248,7 @@ def test_create_mediumtext_column(self, m): def test_update_mediumtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1248,6 +1276,7 @@ def test_update_mediumtext_column(self, m): def test_create_point_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1274,6 +1303,7 @@ def test_create_point_column(self, m): def test_update_point_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1300,6 +1330,7 @@ def test_update_point_column(self, m): def test_create_polygon_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1326,6 +1357,7 @@ def test_create_polygon_column(self, m): def test_update_polygon_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1352,6 +1384,7 @@ def test_update_polygon_column(self, m): def test_create_relationship_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1384,6 +1417,7 @@ def test_create_relationship_column(self, m): def test_update_relationship_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1415,6 +1449,7 @@ def test_update_relationship_column(self, m): def test_create_string_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1443,6 +1478,7 @@ def test_create_string_column(self, m): def test_update_string_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1471,6 +1507,7 @@ def test_update_string_column(self, m): def test_create_text_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1497,6 +1534,7 @@ def test_create_text_column(self, m): def test_update_text_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1524,6 +1562,7 @@ def test_update_text_column(self, m): def test_create_url_column(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1551,6 +1590,7 @@ def test_create_url_column(self, m): def test_update_url_column(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1579,6 +1619,7 @@ def test_update_url_column(self, m): def test_create_varchar_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1607,6 +1648,7 @@ def test_create_varchar_column(self, m): def test_update_varchar_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1635,6 +1677,7 @@ def test_update_varchar_column(self, m): def test_get_column(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -1699,6 +1742,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -1728,6 +1772,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -1889,6 +1934,25 @@ def test_delete_rows(self, m): ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_create_query(self, m): + data = { + "total": 5.0, + "rows": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.tables_db.create_query( + '', + '', + ) + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_get_row(self, m): data = { diff --git a/test/services/test_vectors_db.py b/test/services/test_vectors_db.py index 5ed5c005..2c6f2240 100644 --- a/test/services/test_vectors_db.py +++ b/test/services/test_vectors_db.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -191,6 +192,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -213,6 +215,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -273,6 +276,7 @@ def test_create_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -305,6 +309,7 @@ def test_get_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -335,6 +340,7 @@ def test_update_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -637,6 +643,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -666,6 +673,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", diff --git a/test/services/test_webhooks.py b/test/services/test_webhooks.py index 9d5d6237..0f65c6b2 100644 --- a/test/services/test_webhooks.py +++ b/test/services/test_webhooks.py @@ -37,6 +37,7 @@ def test_create(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, @@ -69,6 +70,7 @@ def test_get(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, @@ -98,6 +100,7 @@ def test_update(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, @@ -145,6 +148,7 @@ def test_update_secret(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, diff --git a/test/test_query.py b/test/test_query.py index b6a92bbc..ae47a4b1 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -267,6 +267,19 @@ def test_cross_join(self): parsed(Query.cross_join('orders', 'ord')), {'method': 'crossJoin', 'attribute': 'orders', 'values': ['ord']} ) + def test_nested_join_without_alias_leaves_the_alias_to_the_server(self): + query = json.loads(Query.join('orders', [Query.on('$id', 'customerId')])) + self.assertEqual(query['method'], 'join') + self.assertEqual(query['attribute'], 'orders') + self.assertEqual(len(query['values']), 1) + self.assertEqual(query['values'][0]['method'], 'on') + + def test_cross_join_without_alias_leaves_the_alias_to_the_server(self): + query = json.loads(Query.cross_join('orders')) + self.assertEqual(query['method'], 'crossJoin') + self.assertEqual(query['attribute'], 'orders') + self.assertEqual(query.get('values') or [], []) + def test_not_covers(self): self.assertEqual( parsed(Query.not_covers('location', [1, 2])), From 49a5eda0fd6d0ba98dd2d6b84f1e2b971a7843bd Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Wed, 21 Oct 2026 12:40:59 +1300 Subject: [PATCH 2/4] chore: update Python SDK to 26.0.0 --- CHANGELOG.md | 15 + appwrite/client.py | 4 +- appwrite/models/attribute_bigint.py | 5 + appwrite/models/attribute_boolean.py | 5 + appwrite/models/attribute_datetime.py | 5 + appwrite/models/attribute_email.py | 5 + appwrite/models/attribute_enum.py | 5 + appwrite/models/attribute_float.py | 5 + appwrite/models/attribute_integer.py | 5 + appwrite/models/attribute_ip.py | 5 + appwrite/models/attribute_line.py | 5 + appwrite/models/attribute_longtext.py | 5 + appwrite/models/attribute_mediumtext.py | 5 + appwrite/models/attribute_object.py | 5 + appwrite/models/attribute_point.py | 5 + appwrite/models/attribute_polygon.py | 5 + appwrite/models/attribute_relationship.py | 5 + appwrite/models/attribute_string.py | 5 + appwrite/models/attribute_text.py | 5 + appwrite/models/attribute_url.py | 5 + appwrite/models/attribute_varchar.py | 5 + appwrite/models/attribute_vector.py | 5 + appwrite/models/billing_plan.py | 5 + appwrite/models/bucket.py | 10 + appwrite/models/collection.py | 5 + appwrite/models/column_bigint.py | 5 + appwrite/models/column_boolean.py | 5 + appwrite/models/column_datetime.py | 5 + appwrite/models/column_email.py | 5 + appwrite/models/column_enum.py | 5 + appwrite/models/column_float.py | 5 + appwrite/models/column_index.py | 5 + appwrite/models/column_integer.py | 5 + appwrite/models/column_ip.py | 5 + appwrite/models/column_line.py | 5 + appwrite/models/column_longtext.py | 5 + appwrite/models/column_mediumtext.py | 5 + appwrite/models/column_point.py | 5 + appwrite/models/column_polygon.py | 5 + appwrite/models/column_relationship.py | 5 + appwrite/models/column_string.py | 5 + appwrite/models/column_text.py | 5 + appwrite/models/column_url.py | 5 + appwrite/models/column_varchar.py | 5 + appwrite/models/database.py | 5 + appwrite/models/ephemeral_key.py | 5 + appwrite/models/function.py | 5 + appwrite/models/index.py | 5 + appwrite/models/key.py | 5 + appwrite/models/policy_password_dictionary.py | 5 + .../models/policy_password_personal_data.py | 5 + appwrite/models/site.py | 5 + appwrite/models/table.py | 5 + appwrite/models/topic.py | 5 + appwrite/models/variable.py | 5 + appwrite/models/vectorsdb_collection.py | 5 + appwrite/models/webhook.py | 5 + appwrite/services/account.py | 49 +++ appwrite/services/activities.py | 5 + appwrite/services/backups.py | 15 + appwrite/services/databases.py | 283 +++++++++++++++++- appwrite/services/documents_db.py | 96 ++++++ appwrite/services/domains.py | 10 + appwrite/services/functions.py | 20 ++ appwrite/services/messaging.py | 20 ++ appwrite/services/mongo.py | 15 + appwrite/services/mysql.py | 15 + appwrite/services/organization.py | 5 + appwrite/services/postgresql.py | 15 + appwrite/services/project.py | 30 ++ appwrite/services/sites.py | 20 ++ appwrite/services/storage.py | 22 +- appwrite/services/tables_db.py | 280 ++++++++++++++++- appwrite/services/vectors_db.py | 25 ++ appwrite/services/webhooks.py | 10 + docs/examples/account/list-memberships.md | 19 ++ docs/examples/activities/list-events.md | 3 +- docs/examples/backups/list-archives.md | 3 +- docs/examples/backups/list-policies.md | 3 +- docs/examples/backups/list-restorations.md | 3 +- .../databases/create-big-int-attribute.md | 3 +- .../databases/create-boolean-attribute.md | 3 +- docs/examples/databases/create-collection.md | 3 +- .../databases/create-datetime-attribute.md | 3 +- .../databases/create-email-attribute.md | 3 +- .../databases/create-enum-attribute.md | 3 +- .../databases/create-float-attribute.md | 3 +- docs/examples/databases/create-index.md | 3 +- .../databases/create-integer-attribute.md | 3 +- .../examples/databases/create-ip-attribute.md | 3 +- .../databases/create-line-attribute.md | 3 +- .../databases/create-longtext-attribute.md | 3 +- .../databases/create-mediumtext-attribute.md | 3 +- .../databases/create-point-attribute.md | 3 +- .../databases/create-polygon-attribute.md | 3 +- docs/examples/databases/create-query.md | 23 ++ .../create-relationship-attribute.md | 3 +- .../databases/create-string-attribute.md | 3 +- .../databases/create-text-attribute.md | 3 +- .../databases/create-url-attribute.md | 3 +- .../databases/create-varchar-attribute.md | 3 +- docs/examples/databases/create.md | 3 +- .../databases/update-big-int-attribute.md | 3 +- .../databases/update-boolean-attribute.md | 3 +- docs/examples/databases/update-collection.md | 3 +- .../databases/update-datetime-attribute.md | 3 +- .../databases/update-email-attribute.md | 3 +- .../databases/update-enum-attribute.md | 3 +- .../databases/update-float-attribute.md | 3 +- .../databases/update-integer-attribute.md | 3 +- .../examples/databases/update-ip-attribute.md | 3 +- .../databases/update-line-attribute.md | 3 +- .../databases/update-longtext-attribute.md | 3 +- .../databases/update-mediumtext-attribute.md | 3 +- .../databases/update-point-attribute.md | 3 +- .../databases/update-polygon-attribute.md | 3 +- .../update-relationship-attribute.md | 3 +- .../databases/update-string-attribute.md | 3 +- .../databases/update-text-attribute.md | 3 +- .../databases/update-url-attribute.md | 3 +- .../databases/update-varchar-attribute.md | 3 +- docs/examples/databases/update.md | 3 +- .../examples/documentsdb/create-collection.md | 3 +- docs/examples/documentsdb/create-index.md | 3 +- docs/examples/documentsdb/create-query.md | 23 ++ docs/examples/documentsdb/create.md | 1 + .../examples/documentsdb/update-collection.md | 3 +- docs/examples/documentsdb/update.md | 1 + docs/examples/domains/list-records.md | 3 +- docs/examples/domains/list.md | 3 +- docs/examples/functions/create-variable.md | 3 +- docs/examples/functions/create.md | 3 +- docs/examples/functions/update-variable.md | 3 +- docs/examples/functions/update.md | 1 + docs/examples/messaging/create-email.md | 3 +- docs/examples/messaging/create-topic.md | 3 +- docs/examples/messaging/update-email.md | 3 +- docs/examples/messaging/update-topic.md | 3 +- docs/examples/mongo/list-backup-policies.md | 3 +- docs/examples/mongo/list-backups.md | 3 +- docs/examples/mongo/list.md | 3 +- docs/examples/mysql/list-backup-policies.md | 3 +- docs/examples/mysql/list-backups.md | 3 +- docs/examples/mysql/list.md | 3 +- .../organization/update-project-key.md | 3 +- .../postgresql/list-backup-policies.md | 3 +- docs/examples/postgresql/list-backups.md | 3 +- docs/examples/postgresql/list.md | 3 +- docs/examples/project/create-variable.md | 3 +- docs/examples/project/update-key.md | 3 +- .../update-password-dictionary-policy.md | 3 +- .../update-password-personal-data-policy.md | 3 +- docs/examples/project/update-variable.md | 3 +- docs/examples/sites/create-variable.md | 3 +- docs/examples/sites/create.md | 3 +- docs/examples/sites/update-variable.md | 3 +- docs/examples/sites/update.md | 3 +- docs/examples/storage/create-bucket.md | 4 +- docs/examples/storage/update-bucket.md | 4 +- .../tablesdb/create-big-int-column.md | 3 +- .../tablesdb/create-boolean-column.md | 3 +- .../tablesdb/create-datetime-column.md | 3 +- docs/examples/tablesdb/create-email-column.md | 3 +- docs/examples/tablesdb/create-enum-column.md | 3 +- docs/examples/tablesdb/create-float-column.md | 3 +- docs/examples/tablesdb/create-index.md | 3 +- .../tablesdb/create-integer-column.md | 3 +- docs/examples/tablesdb/create-ip-column.md | 3 +- docs/examples/tablesdb/create-line-column.md | 3 +- .../tablesdb/create-longtext-column.md | 3 +- .../tablesdb/create-mediumtext-column.md | 3 +- docs/examples/tablesdb/create-point-column.md | 3 +- .../tablesdb/create-polygon-column.md | 3 +- docs/examples/tablesdb/create-query.md | 23 ++ .../tablesdb/create-relationship-column.md | 3 +- .../examples/tablesdb/create-string-column.md | 3 +- docs/examples/tablesdb/create-table.md | 3 +- docs/examples/tablesdb/create-text-column.md | 3 +- docs/examples/tablesdb/create-url-column.md | 3 +- .../tablesdb/create-varchar-column.md | 3 +- docs/examples/tablesdb/create.md | 1 + .../tablesdb/update-big-int-column.md | 3 +- .../tablesdb/update-boolean-column.md | 3 +- .../tablesdb/update-datetime-column.md | 3 +- docs/examples/tablesdb/update-email-column.md | 3 +- docs/examples/tablesdb/update-enum-column.md | 3 +- docs/examples/tablesdb/update-float-column.md | 3 +- .../tablesdb/update-integer-column.md | 3 +- docs/examples/tablesdb/update-ip-column.md | 3 +- docs/examples/tablesdb/update-line-column.md | 3 +- .../tablesdb/update-longtext-column.md | 3 +- .../tablesdb/update-mediumtext-column.md | 3 +- docs/examples/tablesdb/update-point-column.md | 3 +- .../tablesdb/update-polygon-column.md | 3 +- .../tablesdb/update-relationship-column.md | 3 +- .../examples/tablesdb/update-string-column.md | 3 +- docs/examples/tablesdb/update-table.md | 3 +- docs/examples/tablesdb/update-text-column.md | 3 +- docs/examples/tablesdb/update-url-column.md | 3 +- .../tablesdb/update-varchar-column.md | 3 +- docs/examples/tablesdb/update.md | 1 + docs/examples/vectorsdb/create-collection.md | 3 +- docs/examples/vectorsdb/create-index.md | 3 +- docs/examples/vectorsdb/create.md | 1 + docs/examples/vectorsdb/update-collection.md | 3 +- docs/examples/vectorsdb/update.md | 1 + docs/examples/webhooks/create.md | 3 +- docs/examples/webhooks/update.md | 3 +- pyproject.toml | 3 +- setup.py | 5 +- test/services/test_account.py | 16 + test/services/test_databases.py | 64 ++++ test/services/test_documents_db.py | 27 ++ test/services/test_functions.py | 7 + test/services/test_messaging.py | 3 + test/services/test_organization.py | 5 + test/services/test_project.py | 7 + test/services/test_sites.py | 7 + test/services/test_storage.py | 6 + test/services/test_tables_db.py | 64 ++++ test/services/test_vectors_db.py | 8 + test/services/test_webhooks.py | 4 + test/test_query.py | 13 + 223 files changed, 1806 insertions(+), 130 deletions(-) create mode 100644 docs/examples/account/list-memberships.md create mode 100644 docs/examples/databases/create-query.md create mode 100644 docs/examples/documentsdb/create-query.md create mode 100644 docs/examples/tablesdb/create-query.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 06eb25e3..8f0982d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## 26.0.0 + +* Breaking: `tables_db`, `documents_db` and `vectors_db` `create` and `update` take `description` before `specification` +* Breaking: `functions.update` takes `description` before `email_enabled` +* Added: `create_query` on `databases`, `documents_db` and `tables_db` to send list queries in the request body +* Added: `account.list_memberships` +* Added: optional `description` on create and update for databases, collections, tables, attributes, columns, indexes, buckets, functions, sites, variables, topics, webhooks and keys +* Added: `description` on the matching models +* Added: `total` on list methods in `activities`, `backups`, `domains`, `mongo`, `mysql` and `postgresql` +* Added: `emails` on `messaging.create_email` and `messaging.update_email` +* Added: `deny_list` on `project.update_password_dictionary_policy`, `update_password_personal_data_policy` and their policy models +* Added: `BillingPlan.functions_allow_cron` +* Added: `strip_metadata` on `storage.create_bucket`, `update_bucket` and the `Bucket` model +* Updated: package classifiers list Python 3.13 + ## 25.0.0 * Breaking: requests send `X-Appwrite-Response-Format: 2.5.0`; responses follow the 2.5 shapes diff --git a/appwrite/client.py b/appwrite/client.py index 49c7d754..4b4a5a0f 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -19,11 +19,11 @@ def __init__(self): self._endpoint = 'https://cloud.appwrite.io/v1' self._global_headers = { 'content-type': '', - 'user-agent': f'AppwritePythonSDK/25.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', + 'user-agent': f'AppwritePythonSDK/26.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', 'x-sdk-name': 'Python', 'x-sdk-platform': 'server', 'x-sdk-language': 'python', - 'x-sdk-version': '25.0.0', + 'x-sdk-version': '26.0.0', 'X-Appwrite-Response-Format': '2.5.0', } self._config = {} diff --git a/appwrite/models/attribute_bigint.py b/appwrite/models/attribute_bigint.py index 474812db..53639d02 100644 --- a/appwrite/models/attribute_bigint.py +++ b/appwrite/models/attribute_bigint.py @@ -13,6 +13,8 @@ class AttributeBigint(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeBigint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_boolean.py b/appwrite/models/attribute_boolean.py index 7ce326cb..91397ef4 100644 --- a/appwrite/models/attribute_boolean.py +++ b/appwrite/models/attribute_boolean.py @@ -13,6 +13,8 @@ class AttributeBoolean(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributeBoolean(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_datetime.py b/appwrite/models/attribute_datetime.py index cad6726b..fae2b1e6 100644 --- a/appwrite/models/attribute_datetime.py +++ b/appwrite/models/attribute_datetime.py @@ -13,6 +13,8 @@ class AttributeDatetime(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeDatetime(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_email.py b/appwrite/models/attribute_email.py index 4e3c33f6..2e8badcc 100644 --- a/appwrite/models/attribute_email.py +++ b/appwrite/models/attribute_email.py @@ -13,6 +13,8 @@ class AttributeEmail(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeEmail(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_enum.py b/appwrite/models/attribute_enum.py index fdf787fc..89d9db9d 100644 --- a/appwrite/models/attribute_enum.py +++ b/appwrite/models/attribute_enum.py @@ -13,6 +13,8 @@ class AttributeEnum(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeEnum(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_float.py b/appwrite/models/attribute_float.py index 209b42e8..8392f53a 100644 --- a/appwrite/models/attribute_float.py +++ b/appwrite/models/attribute_float.py @@ -13,6 +13,8 @@ class AttributeFloat(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeFloat(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_integer.py b/appwrite/models/attribute_integer.py index f268bb23..609eddac 100644 --- a/appwrite/models/attribute_integer.py +++ b/appwrite/models/attribute_integer.py @@ -13,6 +13,8 @@ class AttributeInteger(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeInteger(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_ip.py b/appwrite/models/attribute_ip.py index 9b334968..50f5a8db 100644 --- a/appwrite/models/attribute_ip.py +++ b/appwrite/models/attribute_ip.py @@ -13,6 +13,8 @@ class AttributeIp(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeIp(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_line.py b/appwrite/models/attribute_line.py index 4fbcba35..28a5e393 100644 --- a/appwrite/models/attribute_line.py +++ b/appwrite/models/attribute_line.py @@ -13,6 +13,8 @@ class AttributeLine(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributeLine(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_longtext.py b/appwrite/models/attribute_longtext.py index fa45bfc5..fe3a0bcb 100644 --- a/appwrite/models/attribute_longtext.py +++ b/appwrite/models/attribute_longtext.py @@ -13,6 +13,8 @@ class AttributeLongtext(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeLongtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_mediumtext.py b/appwrite/models/attribute_mediumtext.py index 59c8ee3f..bfacf192 100644 --- a/appwrite/models/attribute_mediumtext.py +++ b/appwrite/models/attribute_mediumtext.py @@ -13,6 +13,8 @@ class AttributeMediumtext(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeMediumtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_object.py b/appwrite/models/attribute_object.py index 16e736dd..9cfcb9e4 100644 --- a/appwrite/models/attribute_object.py +++ b/appwrite/models/attribute_object.py @@ -13,6 +13,8 @@ class AttributeObject(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -30,6 +32,7 @@ class AttributeObject(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -47,6 +50,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_point.py b/appwrite/models/attribute_point.py index 15c1d931..f93cb980 100644 --- a/appwrite/models/attribute_point.py +++ b/appwrite/models/attribute_point.py @@ -13,6 +13,8 @@ class AttributePoint(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributePoint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_polygon.py b/appwrite/models/attribute_polygon.py index 7d669d70..f7d1c64c 100644 --- a/appwrite/models/attribute_polygon.py +++ b/appwrite/models/attribute_polygon.py @@ -13,6 +13,8 @@ class AttributePolygon(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributePolygon(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_relationship.py b/appwrite/models/attribute_relationship.py index fc549373..de63c8d3 100644 --- a/appwrite/models/attribute_relationship.py +++ b/appwrite/models/attribute_relationship.py @@ -13,6 +13,8 @@ class AttributeRelationship(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -42,6 +44,7 @@ class AttributeRelationship(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -65,6 +68,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_string.py b/appwrite/models/attribute_string.py index 7cca07a0..4790f71b 100644 --- a/appwrite/models/attribute_string.py +++ b/appwrite/models/attribute_string.py @@ -13,6 +13,8 @@ class AttributeString(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeString(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_text.py b/appwrite/models/attribute_text.py index b66f2a50..4fe71e87 100644 --- a/appwrite/models/attribute_text.py +++ b/appwrite/models/attribute_text.py @@ -13,6 +13,8 @@ class AttributeText(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeText(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_url.py b/appwrite/models/attribute_url.py index 50ad0ffe..ff34a0f7 100644 --- a/appwrite/models/attribute_url.py +++ b/appwrite/models/attribute_url.py @@ -13,6 +13,8 @@ class AttributeUrl(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -34,6 +36,7 @@ class AttributeUrl(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_varchar.py b/appwrite/models/attribute_varchar.py index 65b806c2..b87ef6b0 100644 --- a/appwrite/models/attribute_varchar.py +++ b/appwrite/models/attribute_varchar.py @@ -13,6 +13,8 @@ class AttributeVarchar(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -36,6 +38,7 @@ class AttributeVarchar(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/attribute_vector.py b/appwrite/models/attribute_vector.py index 1b1d921d..33344e95 100644 --- a/appwrite/models/attribute_vector.py +++ b/appwrite/models/attribute_vector.py @@ -13,6 +13,8 @@ class AttributeVector(AppwriteModel): ---------- key : str Attribute Key. + description : str + Optional description for the attribute. type : str Attribute type. status : AttributeStatus @@ -32,6 +34,7 @@ class AttributeVector(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: AttributeStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/billing_plan.py b/appwrite/models/billing_plan.py index 545e198b..a5e65184 100644 --- a/appwrite/models/billing_plan.py +++ b/appwrite/models/billing_plan.py @@ -155,6 +155,8 @@ class BillingPlan(AppwriteModel): Maximum function and site deployment size in MB functionsintervalminimum : float Shortest function schedule interval allowed, in minutes. 0 allows every interval. + functionsallowcron : bool + Does the plan allow cron function schedules or only intervals. databasesallowencrypt : bool Does the plan support encrypted string attributes or not. limits : Optional[BillingPlanLimits] @@ -238,6 +240,7 @@ class BillingPlan(AppwriteModel): deploymentsize: float = Field(..., alias='deploymentSize') buildsize: float = Field(..., alias='buildSize') functionsintervalminimum: float = Field(..., alias='functionsIntervalMinimum') + functionsallowcron: bool = Field(..., alias='functionsAllowCron') databasesallowencrypt: bool = Field(..., alias='databasesAllowEncrypt') limits: Optional[BillingPlanLimits] = Field(default=None, alias='limits') group: BillingPlanGroup = Field(..., alias='group') @@ -370,6 +373,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['buildSize'] = 0 if 'functionsIntervalMinimum' not in data and 'functionsintervalminimum' not in data: data['functionsIntervalMinimum'] = 0 + if 'functionsAllowCron' not in data and 'functionsallowcron' not in data: + data['functionsAllowCron'] = True if 'databasesAllowEncrypt' not in data and 'databasesallowencrypt' not in data: data['databasesAllowEncrypt'] = False if 'databaseComputeCredit' not in data and 'databasecomputecredit' not in data: diff --git a/appwrite/models/bucket.py b/appwrite/models/bucket.py index 7718a57d..be6a7c37 100644 --- a/appwrite/models/bucket.py +++ b/appwrite/models/bucket.py @@ -22,6 +22,8 @@ class Bucket(AppwriteModel): Whether file-level security is enabled. [Learn more about permissions](https://appwrite.io/docs/permissions). name : str Bucket name. + description : str + Optional description for the bucket. enabled : bool Bucket enabled. maximumfilesize : float @@ -36,6 +38,8 @@ class Bucket(AppwriteModel): Virus scanning is enabled. transformations : bool Image transformations are enabled. + stripmetadata : bool + Metadata such as location, device and capture time is removed from uploaded images. totalsize : float Total size of this bucket in bytes. """ @@ -46,6 +50,7 @@ class Bucket(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') filesecurity: bool = Field(..., alias='fileSecurity') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') maximumfilesize: float = Field(..., alias='maximumFileSize') allowedfileextensions: List[Any] = Field(..., alias='allowedFileExtensions') @@ -53,6 +58,7 @@ class Bucket(AppwriteModel): encryption: bool = Field(..., alias='encryption') antivirus: bool = Field(..., alias='antivirus') transformations: bool = Field(..., alias='transformations') + stripmetadata: bool = Field(..., alias='stripMetadata') totalsize: float = Field(..., alias='totalSize') @model_validator(mode='before') @@ -74,6 +80,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['fileSecurity'] = False if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'maximumFileSize' not in data and 'maximumfilesize' not in data: @@ -88,6 +96,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['antivirus'] = True if 'transformations' not in data: data['transformations'] = True + if 'stripMetadata' not in data and 'stripmetadata' not in data: + data['stripMetadata'] = False if 'totalSize' not in data and 'totalsize' not in data: data['totalSize'] = 0 return data diff --git a/appwrite/models/collection.py b/appwrite/models/collection.py index 419ec48a..57b47caf 100644 --- a/appwrite/models/collection.py +++ b/appwrite/models/collection.py @@ -41,6 +41,8 @@ class Collection(AppwriteModel): Database ID. name : str Collection name. + description : str + Optional description for the collection. enabled : bool Collection enabled. Can be 'enabled' or 'disabled'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys. documentsecurity : bool @@ -61,6 +63,7 @@ class Collection(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') databaseid: str = Field(..., alias='databaseId') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') documentsecurity: bool = Field(..., alias='documentSecurity') attributes: List[ @@ -108,6 +111,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['databaseId'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'documentSecurity' not in data and 'documentsecurity' not in data: diff --git a/appwrite/models/column_bigint.py b/appwrite/models/column_bigint.py index b2fa8225..8fe0857c 100644 --- a/appwrite/models/column_bigint.py +++ b/appwrite/models/column_bigint.py @@ -13,6 +13,8 @@ class ColumnBigint(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnBigint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_boolean.py b/appwrite/models/column_boolean.py index 33ebd132..1ceb8170 100644 --- a/appwrite/models/column_boolean.py +++ b/appwrite/models/column_boolean.py @@ -13,6 +13,8 @@ class ColumnBoolean(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnBoolean(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_datetime.py b/appwrite/models/column_datetime.py index 46c1d04a..c8db0d06 100644 --- a/appwrite/models/column_datetime.py +++ b/appwrite/models/column_datetime.py @@ -13,6 +13,8 @@ class ColumnDatetime(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnDatetime(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_email.py b/appwrite/models/column_email.py index 68094615..3940ef16 100644 --- a/appwrite/models/column_email.py +++ b/appwrite/models/column_email.py @@ -13,6 +13,8 @@ class ColumnEmail(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnEmail(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_enum.py b/appwrite/models/column_enum.py index 3dd815af..081eba50 100644 --- a/appwrite/models/column_enum.py +++ b/appwrite/models/column_enum.py @@ -13,6 +13,8 @@ class ColumnEnum(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnEnum(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_float.py b/appwrite/models/column_float.py index aa089000..6ed443c3 100644 --- a/appwrite/models/column_float.py +++ b/appwrite/models/column_float.py @@ -13,6 +13,8 @@ class ColumnFloat(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnFloat(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_index.py b/appwrite/models/column_index.py index 54419edf..1124ab21 100644 --- a/appwrite/models/column_index.py +++ b/appwrite/models/column_index.py @@ -18,6 +18,8 @@ class ColumnIndex(AppwriteModel): Index update date in ISO 8601 format. key : str Index Key. + description : str + Optional description for the index. type : str Index type. status : str @@ -36,6 +38,7 @@ class ColumnIndex(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: str = Field(..., alias='status') error: str = Field(..., alias='error') @@ -58,6 +61,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'status' not in data: diff --git a/appwrite/models/column_integer.py b/appwrite/models/column_integer.py index 9ad8fbd4..64c9feaf 100644 --- a/appwrite/models/column_integer.py +++ b/appwrite/models/column_integer.py @@ -13,6 +13,8 @@ class ColumnInteger(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnInteger(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_ip.py b/appwrite/models/column_ip.py index fe5de6b6..24b9cdfa 100644 --- a/appwrite/models/column_ip.py +++ b/appwrite/models/column_ip.py @@ -13,6 +13,8 @@ class ColumnIp(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnIp(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_line.py b/appwrite/models/column_line.py index 0216047f..e715deec 100644 --- a/appwrite/models/column_line.py +++ b/appwrite/models/column_line.py @@ -13,6 +13,8 @@ class ColumnLine(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnLine(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_longtext.py b/appwrite/models/column_longtext.py index ce3585fd..8a27318d 100644 --- a/appwrite/models/column_longtext.py +++ b/appwrite/models/column_longtext.py @@ -13,6 +13,8 @@ class ColumnLongtext(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnLongtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_mediumtext.py b/appwrite/models/column_mediumtext.py index 31717475..dfb5c0cc 100644 --- a/appwrite/models/column_mediumtext.py +++ b/appwrite/models/column_mediumtext.py @@ -13,6 +13,8 @@ class ColumnMediumtext(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnMediumtext(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_point.py b/appwrite/models/column_point.py index 0217c08e..14997cea 100644 --- a/appwrite/models/column_point.py +++ b/appwrite/models/column_point.py @@ -13,6 +13,8 @@ class ColumnPoint(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnPoint(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_polygon.py b/appwrite/models/column_polygon.py index 3bcc61c9..1d8c722c 100644 --- a/appwrite/models/column_polygon.py +++ b/appwrite/models/column_polygon.py @@ -13,6 +13,8 @@ class ColumnPolygon(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -32,6 +34,7 @@ class ColumnPolygon(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -50,6 +53,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_relationship.py b/appwrite/models/column_relationship.py index 5b1f96b9..fd44c287 100644 --- a/appwrite/models/column_relationship.py +++ b/appwrite/models/column_relationship.py @@ -13,6 +13,8 @@ class ColumnRelationship(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -42,6 +44,7 @@ class ColumnRelationship(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -65,6 +68,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_string.py b/appwrite/models/column_string.py index c8d5efcb..a6d8da11 100644 --- a/appwrite/models/column_string.py +++ b/appwrite/models/column_string.py @@ -13,6 +13,8 @@ class ColumnString(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnString(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_text.py b/appwrite/models/column_text.py index 29ea6f5b..6be3b6f5 100644 --- a/appwrite/models/column_text.py +++ b/appwrite/models/column_text.py @@ -13,6 +13,8 @@ class ColumnText(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnText(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_url.py b/appwrite/models/column_url.py index bb51776b..60bfcfa8 100644 --- a/appwrite/models/column_url.py +++ b/appwrite/models/column_url.py @@ -13,6 +13,8 @@ class ColumnUrl(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -34,6 +36,7 @@ class ColumnUrl(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -53,6 +56,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/column_varchar.py b/appwrite/models/column_varchar.py index 113ca34f..d09f0c94 100644 --- a/appwrite/models/column_varchar.py +++ b/appwrite/models/column_varchar.py @@ -13,6 +13,8 @@ class ColumnVarchar(AppwriteModel): ---------- key : str Column Key. + description : str + Optional description for the column. type : str Column type. status : ColumnStatus @@ -36,6 +38,7 @@ class ColumnVarchar(AppwriteModel): """ key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: ColumnStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -56,6 +59,8 @@ def _apply_defaults(cls, data: Any) -> Any: data = dict(data) if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/database.py b/appwrite/models/database.py index 931cb68d..c17f2a63 100644 --- a/appwrite/models/database.py +++ b/appwrite/models/database.py @@ -18,6 +18,8 @@ class Database(AppwriteModel): Database ID. name : str Database name. + description : str + Optional description for the database. createdat : str Database creation date in ISO 8601 format. updatedat : str @@ -48,6 +50,7 @@ class Database(AppwriteModel): id: str = Field(..., alias='$id') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') enabled: bool = Field(..., alias='enabled') @@ -73,6 +76,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$id'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if '$createdAt' not in data and 'createdat' not in data: data['$createdAt'] = '' if '$updatedAt' not in data and 'updatedat' not in data: diff --git a/appwrite/models/ephemeral_key.py b/appwrite/models/ephemeral_key.py index 7800c687..1fda1395 100644 --- a/appwrite/models/ephemeral_key.py +++ b/appwrite/models/ephemeral_key.py @@ -18,6 +18,8 @@ class EphemeralKey(AppwriteModel): Key update date in ISO 8601 format. name : str Key name. + description : str + Optional description for the key. expire : str Key expiration date in ISO 8601 format. scopes : List[Any] @@ -34,6 +36,7 @@ class EphemeralKey(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') expire: str = Field(..., alias='expire') scopes: List[Any] = Field(..., alias='scopes') secret: str = Field(..., alias='secret') @@ -55,6 +58,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'expire' not in data: data['expire'] = '' if 'scopes' not in data: diff --git a/appwrite/models/function.py b/appwrite/models/function.py index 87862433..c25b1017 100644 --- a/appwrite/models/function.py +++ b/appwrite/models/function.py @@ -21,6 +21,8 @@ class Function(AppwriteModel): Execution permissions. name : str Function name. + description : str + Optional description for the function. enabled : bool Function enabled. live : bool @@ -86,6 +88,7 @@ class Function(AppwriteModel): updatedat: str = Field(..., alias='$updatedAt') execute: List[Any] = Field(..., alias='execute') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') live: bool = Field(..., alias='live') logging: bool = Field(..., alias='logging') @@ -133,6 +136,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['execute'] = [] if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'live' not in data: diff --git a/appwrite/models/index.py b/appwrite/models/index.py index 21613a19..64dadef6 100644 --- a/appwrite/models/index.py +++ b/appwrite/models/index.py @@ -19,6 +19,8 @@ class Index(AppwriteModel): Index update date in ISO 8601 format. key : str Index key. + description : str + Optional description for the index. type : str Index type. status : IndexStatus @@ -37,6 +39,7 @@ class Index(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') key: str = Field(..., alias='key') + description: str = Field(..., alias='description') type: str = Field(..., alias='type') status: IndexStatus = Field(..., alias='status') error: str = Field(..., alias='error') @@ -59,6 +62,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'type' not in data: data['type'] = '' if 'error' not in data: diff --git a/appwrite/models/key.py b/appwrite/models/key.py index f8e209fe..7888c9de 100644 --- a/appwrite/models/key.py +++ b/appwrite/models/key.py @@ -18,6 +18,8 @@ class Key(AppwriteModel): Key update date in ISO 8601 format. name : str Key name. + description : str + Optional description for the key. expire : str Key expiration date in ISO 8601 format. scopes : List[Any] @@ -34,6 +36,7 @@ class Key(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') expire: str = Field(..., alias='expire') scopes: List[Any] = Field(..., alias='scopes') secret: str = Field(..., alias='secret') @@ -55,6 +58,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'expire' not in data: data['expire'] = '' if 'scopes' not in data: diff --git a/appwrite/models/policy_password_dictionary.py b/appwrite/models/policy_password_dictionary.py index 37bbbeeb..6527f197 100644 --- a/appwrite/models/policy_password_dictionary.py +++ b/appwrite/models/policy_password_dictionary.py @@ -14,10 +14,13 @@ class PolicyPasswordDictionary(AppwriteModel): Policy ID. enabled : bool Whether password dictionary policy is enabled. + denylist : List[Any] + Custom passwords blocked by case-sensitive equality when enabled. """ id: str = Field(..., alias='$id') enabled: bool = Field(..., alias='enabled') + denylist: List[Any] = Field(..., alias='denyList') @model_validator(mode='before') @classmethod @@ -30,4 +33,6 @@ def _apply_defaults(cls, data: Any) -> Any: data['$id'] = '' if 'enabled' not in data: data['enabled'] = False + if 'denyList' not in data and 'denylist' not in data: + data['denyList'] = [] return data diff --git a/appwrite/models/policy_password_personal_data.py b/appwrite/models/policy_password_personal_data.py index 64f12c7c..82f6158e 100644 --- a/appwrite/models/policy_password_personal_data.py +++ b/appwrite/models/policy_password_personal_data.py @@ -14,10 +14,13 @@ class PolicyPasswordPersonalData(AppwriteModel): Policy ID. enabled : bool Whether password personal data policy is enabled. + denylist : List[Any] + Custom terms blocked by case-insensitive substring matching when enabled. """ id: str = Field(..., alias='$id') enabled: bool = Field(..., alias='enabled') + denylist: List[Any] = Field(..., alias='denyList') @model_validator(mode='before') @classmethod @@ -30,4 +33,6 @@ def _apply_defaults(cls, data: Any) -> Any: data['$id'] = '' if 'enabled' not in data: data['enabled'] = False + if 'denyList' not in data and 'denylist' not in data: + data['denyList'] = [] return data diff --git a/appwrite/models/site.py b/appwrite/models/site.py index bef638fc..346e7fae 100644 --- a/appwrite/models/site.py +++ b/appwrite/models/site.py @@ -19,6 +19,8 @@ class Site(AppwriteModel): Site update date in ISO 8601 format. name : str Site name. + description : str + Optional description for the site. enabled : bool Site enabled. live : bool @@ -87,6 +89,7 @@ class Site(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') live: bool = Field(..., alias='live') logging: bool = Field(..., alias='logging') @@ -134,6 +137,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'live' not in data: diff --git a/appwrite/models/table.py b/appwrite/models/table.py index 41c97ef3..944497ab 100644 --- a/appwrite/models/table.py +++ b/appwrite/models/table.py @@ -41,6 +41,8 @@ class Table(AppwriteModel): Database ID. name : str Table name. + description : str + Optional description for the table. enabled : bool Table enabled. Can be 'enabled' or 'disabled'. When disabled, the table is inaccessible to users, but remains accessible to Server SDKs using API keys. rowsecurity : bool @@ -61,6 +63,7 @@ class Table(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') databaseid: str = Field(..., alias='databaseId') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') rowsecurity: bool = Field(..., alias='rowSecurity') columns: List[ @@ -108,6 +111,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['databaseId'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'rowSecurity' not in data and 'rowsecurity' not in data: diff --git a/appwrite/models/topic.py b/appwrite/models/topic.py index b3566eec..f7d3da42 100644 --- a/appwrite/models/topic.py +++ b/appwrite/models/topic.py @@ -18,6 +18,8 @@ class Topic(AppwriteModel): Topic update date in ISO 8601 format. name : str The name of the topic. + description : str + Optional description for the topic. emailtotal : float Total count of email subscribers subscribed to the topic. smstotal : float @@ -36,6 +38,7 @@ class Topic(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') emailtotal: float = Field(..., alias='emailTotal') smstotal: float = Field(..., alias='smsTotal') pushtotal: float = Field(..., alias='pushTotal') @@ -58,6 +61,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'emailTotal' not in data and 'emailtotal' not in data: data['emailTotal'] = 0 if 'smsTotal' not in data and 'smstotal' not in data: diff --git a/appwrite/models/variable.py b/appwrite/models/variable.py index a50d58f0..a667b1c9 100644 --- a/appwrite/models/variable.py +++ b/appwrite/models/variable.py @@ -18,6 +18,8 @@ class Variable(AppwriteModel): Variable creation date in ISO 8601 format. key : str Variable key. + description : str + Optional description for the variable. value : str Variable value. secret : bool @@ -32,6 +34,7 @@ class Variable(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') key: str = Field(..., alias='key') + description: str = Field(..., alias='description') value: str = Field(..., alias='value') secret: bool = Field(..., alias='secret') resourcetype: str = Field(..., alias='resourceType') @@ -52,6 +55,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'key' not in data: data['key'] = '' + if 'description' not in data: + data['description'] = '' if 'value' not in data: data['value'] = '' if 'secret' not in data: diff --git a/appwrite/models/vectorsdb_collection.py b/appwrite/models/vectorsdb_collection.py index e4ca60ff..84098b26 100644 --- a/appwrite/models/vectorsdb_collection.py +++ b/appwrite/models/vectorsdb_collection.py @@ -25,6 +25,8 @@ class VectorsdbCollection(AppwriteModel): Database ID. name : str Collection name. + description : str + Optional description for the collection. enabled : bool Collection enabled. Can be 'enabled' or 'disabled'. When disabled, the collection is inaccessible to users, but remains accessible to Server SDKs using API keys. documentsecurity : bool @@ -47,6 +49,7 @@ class VectorsdbCollection(AppwriteModel): permissions: List[Any] = Field(..., alias='$permissions') databaseid: str = Field(..., alias='databaseId') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') enabled: bool = Field(..., alias='enabled') documentsecurity: bool = Field(..., alias='documentSecurity') attributes: List[ @@ -79,6 +82,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['databaseId'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'enabled' not in data: data['enabled'] = True if 'documentSecurity' not in data and 'documentsecurity' not in data: diff --git a/appwrite/models/webhook.py b/appwrite/models/webhook.py index 3114336d..db6fc715 100644 --- a/appwrite/models/webhook.py +++ b/appwrite/models/webhook.py @@ -18,6 +18,8 @@ class Webhook(AppwriteModel): Webhook update date in ISO 8601 format. name : str Webhook name. + description : str + Optional description for the webhook. url : str Webhook URL endpoint. events : List[Any] @@ -42,6 +44,7 @@ class Webhook(AppwriteModel): createdat: str = Field(..., alias='$createdAt') updatedat: str = Field(..., alias='$updatedAt') name: str = Field(..., alias='name') + description: str = Field(..., alias='description') url: str = Field(..., alias='url') events: List[Any] = Field(..., alias='events') tls: bool = Field(..., alias='tls') @@ -67,6 +70,8 @@ def _apply_defaults(cls, data: Any) -> Any: data['$updatedAt'] = '' if 'name' not in data: data['name'] = '' + if 'description' not in data: + data['description'] = '' if 'url' not in data: data['url'] = '' if 'events' not in data: diff --git a/appwrite/services/account.py b/appwrite/services/account.py index 6891e202..9788e53c 100644 --- a/appwrite/services/account.py +++ b/appwrite/services/account.py @@ -9,6 +9,7 @@ from ..models.oauth2_consent_token_list import Oauth2ConsentTokenList from ..models.oauth2_consent_token import Oauth2ConsentToken from ..models.identity_list import IdentityList +from ..models.membership_list import MembershipList from ..enums.authenticator_type import AuthenticatorType from ..models.mfa_type import MfaType from ..enums.authentication_factor import AuthenticationFactor @@ -539,6 +540,50 @@ def delete_identity( return response + def list_memberships( + self, + queries: Optional[List[str]] = None, + total: Optional[bool] = None, + ) -> MembershipList: + """ + Get the team memberships of the currently logged in user. + + Parameters + ---------- + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + Returns + ------- + MembershipList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/memberships' + api_params = {} + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=MembershipList) + def update_mfa( self, mfa: bool, @@ -1482,6 +1527,8 @@ def update_recovery( Please note that in order to avoid a [Redirect Attack](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md) the only valid redirect URLs are the ones from domains you have set when adding your platforms in the console interface. + The new password must satisfy the project's enabled password policies, including custom deny lists. Dictionary entries match the entire password case-sensitively; personal-data terms match substrings case-insensitively. Personal-data validation runs after token verification against the recovered user's ID, name, email, and phone, closing the previous recovery gap for profile-based checks. A rejected password leaves the existing credentials unchanged and the recovery token usable. + Parameters ---------- user_id : str @@ -1583,6 +1630,8 @@ def update_recovery_otp( """ Use this endpoint to complete the user password recovery process using the 6-digit code that was emailed by [createRecoveryOTP](https://appwrite.io/docs/references/cloud/client-web/account#createRecoveryOTP). Pass the **userId** of the user along with the **secret** code from the email and the new **password** to set. If confirmed, this route will return a 200 status code, the code is consumed and the user's password is updated. + The new password must satisfy the project's enabled password policies, including custom deny lists. Dictionary entries match the entire password case-sensitively; personal-data terms match substrings case-insensitively. Personal-data validation runs after token verification against the recovered user's ID, name, email, and phone, closing the previous recovery gap for profile-based checks. A rejected password leaves the existing credentials unchanged and the recovery token usable. + Parameters ---------- user_id : str diff --git a/appwrite/services/activities.py b/appwrite/services/activities.py index 1c33eeef..8556609f 100644 --- a/appwrite/services/activities.py +++ b/appwrite/services/activities.py @@ -15,6 +15,7 @@ def __init__(self, client) -> None: def list_events( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> ActivityEventList: """ List all events for selected filters. @@ -23,6 +24,8 @@ def list_events( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on attributes such as userId, teamId, etc. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- ActivityEventList @@ -38,6 +41,8 @@ def list_events( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/backups.py b/appwrite/services/backups.py index c6e75e98..b33b009c 100644 --- a/appwrite/services/backups.py +++ b/appwrite/services/backups.py @@ -20,6 +20,7 @@ def __init__(self, client) -> None: def list_archives( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupArchiveList: """ List all archives for a project. @@ -28,6 +29,8 @@ def list_archives( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupArchiveList @@ -43,6 +46,8 @@ def list_archives( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -186,6 +191,7 @@ def delete_archive( def list_policies( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List all policies for a project. @@ -194,6 +200,8 @@ def list_policies( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -209,6 +217,8 @@ def list_policies( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -506,6 +516,7 @@ def create_restoration( def list_restorations( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupRestorationList: """ List all backup restorations for a project. @@ -514,6 +525,8 @@ def list_restorations( ---------- queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupRestorationList @@ -529,6 +542,8 @@ def list_restorations( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/databases.py b/appwrite/services/databases.py index 5e5fa631..179b6b20 100644 --- a/appwrite/services/databases.py +++ b/appwrite/services/databases.py @@ -103,6 +103,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> Database: """ Create a new Database. @@ -117,6 +118,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. Returns ------- Database @@ -138,6 +141,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -481,6 +486,7 @@ def update( database_id: str, name: Optional[str] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> Database: """ Update a database by its unique ID. @@ -495,6 +501,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. Returns ------- Database @@ -515,6 +523,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -642,6 +652,7 @@ def create_collection( enabled: Optional[bool] = None, attributes: Optional[List[Dict[str, Any]]] = None, indexes: Optional[List[Dict[str, Any]]] = None, + description: Optional[str] = None, ) -> Collection: """ Create a new Collection. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console. @@ -666,6 +677,8 @@ def create_collection( Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, varchar, text, mediumtext, longtext, integer, bigint, double, boolean, datetime, point, linestring, polygon, email, url, ip, enum), size (integer, required for string and varchar types), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options. indexes : Optional[List[Dict[str, Any]]] Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial, trigram), attributes (array of attribute keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional). + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -698,6 +711,8 @@ def create_collection( api_params['attributes'] = self._normalize_value(attributes) if indexes is not None: api_params['indexes'] = self._normalize_value(indexes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -771,6 +786,7 @@ def update_collection( document_security: Optional[bool] = None, enabled: Optional[bool] = None, purge: Optional[bool] = None, + description: Optional[str] = None, ) -> Collection: """ Update a collection by its unique ID. @@ -793,6 +809,8 @@ def update_collection( Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. purge : Optional[bool] When true, purge all cached list responses for this collection as part of the update. Use this to force readers to see fresh data immediately instead of waiting for the cache TTL to expire. + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -822,6 +840,8 @@ def update_collection( api_params['enabled'] = self._normalize_value(enabled) if purge is not None: api_params['purge'] = self._normalize_value(purge) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -956,6 +976,7 @@ def create_big_int_attribute( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeBigint: """ Create a bigint attribute. Optionally, minimum and maximum values can be provided. @@ -980,6 +1001,8 @@ def create_big_int_attribute( Default value. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBigint @@ -1013,6 +1036,8 @@ def create_big_int_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1038,6 +1063,7 @@ def update_big_int_attribute( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeBigint: """ Update a bigint attribute. Changing the `default` value will not update already existing documents. @@ -1062,6 +1088,8 @@ def update_big_int_attribute( Maximum value new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBigint @@ -1094,6 +1122,8 @@ def update_big_int_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1117,6 +1147,7 @@ def create_boolean_attribute( required: bool, default: Optional[bool] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeBoolean: """ Create a boolean attribute. @@ -1137,6 +1168,8 @@ def create_boolean_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBoolean @@ -1166,6 +1199,8 @@ def create_boolean_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1189,6 +1224,7 @@ def update_boolean_attribute( required: bool, default: Optional[bool], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeBoolean: """ Update a boolean attribute. Changing the `default` value will not update already existing documents. @@ -1209,6 +1245,8 @@ def update_boolean_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeBoolean @@ -1237,6 +1275,8 @@ def update_boolean_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1260,6 +1300,7 @@ def create_datetime_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeDatetime: """ Create a date time attribute according to the ISO 8601 standard. @@ -1280,6 +1321,8 @@ def create_datetime_attribute( Default value for the attribute in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeDatetime @@ -1309,6 +1352,8 @@ def create_datetime_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1332,6 +1377,7 @@ def update_datetime_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeDatetime: """ Update a date time attribute. Changing the `default` value will not update already existing documents. @@ -1352,6 +1398,8 @@ def update_datetime_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeDatetime @@ -1380,6 +1428,8 @@ def update_datetime_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1403,6 +1453,7 @@ def create_email_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeEmail: """ Create an email attribute. @@ -1423,6 +1474,8 @@ def create_email_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEmail @@ -1452,6 +1505,8 @@ def create_email_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1475,6 +1530,7 @@ def update_email_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeEmail: """ Update an email attribute. Changing the `default` value will not update already existing documents. @@ -1495,6 +1551,8 @@ def update_email_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEmail @@ -1523,6 +1581,8 @@ def update_email_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1547,6 +1607,7 @@ def create_enum_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeEnum: """ Create an enum attribute. The `elements` param acts as a white-list of accepted values for this attribute. @@ -1569,6 +1630,8 @@ def create_enum_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEnum @@ -1601,6 +1664,8 @@ def create_enum_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1625,6 +1690,7 @@ def update_enum_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeEnum: """ Update an enum attribute. Changing the `default` value will not update already existing documents. @@ -1647,6 +1713,8 @@ def update_enum_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeEnum @@ -1678,6 +1746,8 @@ def update_enum_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1703,6 +1773,7 @@ def create_float_attribute( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeFloat: """ Create a float attribute. Optionally, minimum and maximum values can be provided. @@ -1727,6 +1798,8 @@ def create_float_attribute( Default value. Cannot be set when required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeFloat @@ -1760,6 +1833,8 @@ def create_float_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1785,6 +1860,7 @@ def update_float_attribute( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeFloat: """ Update a float attribute. Changing the `default` value will not update already existing documents. @@ -1809,6 +1885,8 @@ def update_float_attribute( Maximum value. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeFloat @@ -1841,6 +1919,8 @@ def update_float_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1866,6 +1946,7 @@ def create_integer_attribute( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeInteger: """ Create an integer attribute. Optionally, minimum and maximum values can be provided. @@ -1890,6 +1971,8 @@ def create_integer_attribute( Default value. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeInteger @@ -1923,6 +2006,8 @@ def create_integer_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1948,6 +2033,7 @@ def update_integer_attribute( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeInteger: """ Update an integer attribute. Changing the `default` value will not update already existing documents. @@ -1972,6 +2058,8 @@ def update_integer_attribute( Maximum value new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeInteger @@ -2004,6 +2092,8 @@ def update_integer_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2027,6 +2117,7 @@ def create_ip_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeIp: """ Create IP address attribute. @@ -2047,6 +2138,8 @@ def create_ip_attribute( Default value. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeIp @@ -2076,6 +2169,8 @@ def create_ip_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2099,6 +2194,7 @@ def update_ip_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeIp: """ Update an ip attribute. Changing the `default` value will not update already existing documents. @@ -2119,6 +2215,8 @@ def update_ip_attribute( Default value. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeIp @@ -2147,6 +2245,8 @@ def update_ip_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2169,6 +2269,7 @@ def create_line_attribute( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> AttributeLine: """ Create a geometric line attribute. @@ -2187,6 +2288,8 @@ def create_line_attribute( Is attribute required? default : Optional[List[List[Any]]] Default value for attribute when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when attribute is required. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLine @@ -2214,6 +2317,8 @@ def create_line_attribute( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2237,6 +2342,7 @@ def update_line_attribute( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeLine: """ Update a line attribute. Changing the `default` value will not update already existing documents. @@ -2257,6 +2363,8 @@ def update_line_attribute( Default value for attribute when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLine @@ -2286,6 +2394,8 @@ def update_line_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2310,6 +2420,7 @@ def create_longtext_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeLongtext: """ Create a longtext attribute. @@ -2332,6 +2443,8 @@ def create_longtext_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLongtext @@ -2363,6 +2476,8 @@ def create_longtext_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2386,6 +2501,7 @@ def update_longtext_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeLongtext: """ Update a longtext attribute. Changing the `default` value will not update already existing documents. @@ -2406,6 +2522,8 @@ def update_longtext_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeLongtext @@ -2434,6 +2552,8 @@ def update_longtext_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2458,6 +2578,7 @@ def create_mediumtext_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeMediumtext: """ Create a mediumtext attribute. @@ -2480,6 +2601,8 @@ def create_mediumtext_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeMediumtext @@ -2511,6 +2634,8 @@ def create_mediumtext_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2534,6 +2659,7 @@ def update_mediumtext_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeMediumtext: """ Update a mediumtext attribute. Changing the `default` value will not update already existing documents. @@ -2554,6 +2680,8 @@ def update_mediumtext_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeMediumtext @@ -2582,6 +2710,8 @@ def update_mediumtext_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2604,6 +2734,7 @@ def create_point_attribute( key: str, required: bool, default: Optional[List[float]] = None, + description: Optional[str] = None, ) -> AttributePoint: """ Create a geometric point attribute. @@ -2622,6 +2753,8 @@ def create_point_attribute( Is attribute required? default : Optional[List[float]] Default value for attribute when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when attribute is required. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePoint @@ -2649,6 +2782,8 @@ def create_point_attribute( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2672,6 +2807,7 @@ def update_point_attribute( required: bool, default: Optional[List[float]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributePoint: """ Update a point attribute. Changing the `default` value will not update already existing documents. @@ -2692,6 +2828,8 @@ def update_point_attribute( Default value for attribute when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePoint @@ -2721,6 +2859,8 @@ def update_point_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2743,6 +2883,7 @@ def create_polygon_attribute( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> AttributePolygon: """ Create a geometric polygon attribute. @@ -2761,6 +2902,8 @@ def create_polygon_attribute( Is attribute required? default : Optional[List[List[Any]]] Default value for attribute when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when attribute is required. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePolygon @@ -2788,6 +2931,8 @@ def create_polygon_attribute( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2811,6 +2956,7 @@ def update_polygon_attribute( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributePolygon: """ Update a polygon attribute. Changing the `default` value will not update already existing documents. @@ -2831,6 +2977,8 @@ def update_polygon_attribute( Default value for attribute when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when attribute is required. new_key : Optional[str] New attribute key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributePolygon @@ -2860,6 +3008,8 @@ def update_polygon_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2885,6 +3035,7 @@ def create_relationship_attribute( key: Optional[str] = None, two_way_key: Optional[str] = None, on_delete: Optional[RelationMutate] = None, + description: Optional[str] = None, ) -> AttributeRelationship: """ Create relationship attribute. [Learn more about relationship attributes](https://appwrite.io/docs/databases-relationships#relationship-attributes). @@ -2909,6 +3060,8 @@ def create_relationship_attribute( Two Way Attribute Key. on_delete : Optional[RelationMutate] Delete constraint. Possible values are: cascade, restrict, setNull. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeRelationship @@ -2942,6 +3095,8 @@ def create_relationship_attribute( api_params['twoWayKey'] = self._normalize_value(two_way_key) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2964,6 +3119,7 @@ def update_relationship_attribute( key: str, on_delete: Optional[RelationMutate] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeRelationship: """ Update relationship attribute. [Learn more about relationship attributes](https://appwrite.io/docs/databases-relationships#relationship-attributes). @@ -2982,6 +3138,8 @@ def update_relationship_attribute( Delete constraint. Possible values are: cascade, restrict, setNull. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeRelationship @@ -3008,6 +3166,8 @@ def update_relationship_attribute( api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3033,6 +3193,7 @@ def create_string_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeString: """ Create a string attribute. @@ -3057,6 +3218,8 @@ def create_string_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeString @@ -3091,6 +3254,8 @@ def create_string_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3115,6 +3280,7 @@ def update_string_attribute( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeString: """ Update a string attribute. Changing the `default` value will not update already existing documents. @@ -3137,6 +3303,8 @@ def update_string_attribute( Maximum size of the string attribute. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeString @@ -3167,6 +3335,8 @@ def update_string_attribute( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3191,6 +3361,7 @@ def create_text_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeText: """ Create a text attribute. @@ -3213,6 +3384,8 @@ def create_text_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeText @@ -3244,6 +3417,8 @@ def create_text_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3267,6 +3442,7 @@ def update_text_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeText: """ Update a text attribute. Changing the `default` value will not update already existing documents. @@ -3287,6 +3463,8 @@ def update_text_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeText @@ -3315,6 +3493,8 @@ def update_text_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3338,6 +3518,7 @@ def create_url_attribute( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeUrl: """ Create a URL attribute. @@ -3358,6 +3539,8 @@ def create_url_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. array : Optional[bool] Is attribute an array? + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeUrl @@ -3387,6 +3570,8 @@ def create_url_attribute( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3410,6 +3595,7 @@ def update_url_attribute( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeUrl: """ Update an url attribute. Changing the `default` value will not update already existing documents. @@ -3430,6 +3616,8 @@ def update_url_attribute( Default value for attribute when not provided. Cannot be set when attribute is required. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeUrl @@ -3458,6 +3646,8 @@ def update_url_attribute( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3483,6 +3673,7 @@ def create_varchar_attribute( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> AttributeVarchar: """ Create a varchar attribute. @@ -3507,6 +3698,8 @@ def create_varchar_attribute( Is attribute an array? encrypt : Optional[bool] Toggle encryption for the attribute. Encryption enhances security by not storing any plain text values in the database. However, encrypted attributes cannot be queried. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeVarchar @@ -3541,6 +3734,8 @@ def create_varchar_attribute( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3565,6 +3760,7 @@ def update_varchar_attribute( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> AttributeVarchar: """ Update a varchar attribute. Changing the `default` value will not update already existing documents. @@ -3587,6 +3783,8 @@ def update_varchar_attribute( Maximum size of the varchar attribute. new_key : Optional[str] New Attribute Key. + description : Optional[str] + Optional description for the attribute. Returns ------- AttributeVarchar @@ -3617,6 +3815,8 @@ def update_varchar_attribute( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3831,6 +4031,8 @@ def list_documents( """ Get a list of all the user's documents in a given collection. You can use the query params to filter your results. + Documents returned by aggregate, groupBy or distinct queries hold only the attributes they select, group or aggregate, typed as a document types them, with a joined attribute named by its join alias (`alias.attribute`). The response omits their `$id`, `$sequence`, `$createdAt`, `$updatedAt`, `$permissions`, `$databaseId` and `$collectionId` unless they select or group it, and those of a right or full join document that no document of this collection matched. SDK document models fill omitted system attributes with empty values. + .. deprecated::1.8.0 This API has been deprecated since 1.8.0. Please use `tablesDB.list_rows` instead. Parameters @@ -3846,7 +4048,7 @@ def list_documents( total : Optional[bool] When set to false, the total count returned will be 0 and will not be calculated. ttl : Optional[float] - TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Must be between 0 and 86400 (24 hours). + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). model_type : Type[T], optional Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. @@ -4229,6 +4431,80 @@ def delete_documents( return DocumentList.with_data(response, model_type) + @deprecated("This API has been deprecated since 1.8.0. Please use `tablesDB.create_query` instead.") + def create_query( + self, + database_id: str, + collection_id: str, + queries: Optional[List[str]] = None, + transaction_id: Optional[str] = None, + total: Optional[bool] = None, + ttl: Optional[float] = None, + model_type: Type[T] = dict, + ) -> DocumentList[T]: + """ + Get a list of all the user's documents in a given collection using a POST request. This behaves identically to the list documents endpoint but accepts the queries in the request body, allowing much larger `queries` arrays than can fit in a URL query string. + + .. deprecated::1.8.0 + This API has been deprecated since 1.8.0. Please use `tablesDB.create_query` instead. + Parameters + ---------- + database_id : str + Database ID. + collection_id : str + Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + transaction_id : Optional[str] + Transaction ID to read uncommitted changes within the transaction. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + ttl : Optional[float] + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + DocumentList[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/databases/{databaseId}/collections/{collectionId}/documents/query' + api_params = {} + if database_id is None or database_id == '': + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None or collection_id == '': + raise AppwriteException('Missing required parameter: "collection_id"') + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) + if total is not None: + api_params['total'] = self._normalize_value(total) + if ttl is not None: + api_params['ttl'] = self._normalize_value(ttl) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return DocumentList.with_data(response, model_type) + @deprecated("This API has been deprecated since 1.8.0. Please use `tablesDB.get_row` instead.") def get_document( self, @@ -4740,6 +5016,7 @@ def create_index( attributes: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> Index: """ Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. @@ -4763,6 +5040,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- Index @@ -4795,6 +5074,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', diff --git a/appwrite/services/documents_db.py b/appwrite/services/documents_db.py index ecef7255..234f2b66 100644 --- a/appwrite/services/documents_db.py +++ b/appwrite/services/documents_db.py @@ -78,6 +78,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -93,6 +94,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Defaults to `serverless`, which creates the database on the shared pool. Any other value provisions a dedicated database on that specification. replicas : Optional[float] @@ -120,6 +123,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -478,6 +483,7 @@ def update( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -493,6 +499,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Resizing between dedicated specifications changes cpu, memory, storage and the connection ceiling via a rolling cutover with zero downtime. Moving a `serverless` database onto a dedicated specification is a data migration, not a resize. replicas : Optional[float] @@ -520,6 +528,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -646,6 +656,7 @@ def create_collection( enabled: Optional[bool] = None, attributes: Optional[List[Dict[str, Any]]] = None, indexes: Optional[List[Dict[str, Any]]] = None, + description: Optional[str] = None, ) -> Collection: """ Create a new Collection. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console. @@ -668,6 +679,8 @@ def create_collection( Array of attribute definitions to create. Each attribute should contain: key (string), type (string: string, varchar, text, mediumtext, longtext, integer, bigint, double, boolean, datetime, point, linestring, polygon, email, url, ip, enum), size (integer, required for string and varchar types), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options. indexes : Optional[List[Dict[str, Any]]] Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial), attributes (array of attribute keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional). + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -700,6 +713,8 @@ def create_collection( api_params['attributes'] = self._normalize_value(attributes) if indexes is not None: api_params['indexes'] = self._normalize_value(indexes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -769,6 +784,7 @@ def update_collection( document_security: Optional[bool] = None, enabled: Optional[bool] = None, purge: Optional[bool] = None, + description: Optional[str] = None, ) -> Collection: """ Update a collection by its unique ID. @@ -789,6 +805,8 @@ def update_collection( Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. purge : Optional[bool] When true, purge all cached list responses for this collection as part of the update. Use this to force readers to see fresh data immediately instead of waiting for the cache TTL to expire. + description : Optional[str] + Optional description for the collection. Returns ------- Collection @@ -819,6 +837,8 @@ def update_collection( api_params['enabled'] = self._normalize_value(enabled) if purge is not None: api_params['purge'] = self._normalize_value(purge) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1274,6 +1294,77 @@ def delete_documents( return DocumentList.with_data(response, model_type) + def create_query( + self, + database_id: str, + collection_id: str, + queries: Optional[List[str]] = None, + transaction_id: Optional[str] = None, + total: Optional[bool] = None, + ttl: Optional[float] = None, + model_type: Type[T] = dict, + ) -> DocumentList[T]: + """ + Get a list of all the user's documents in a given collection using a POST request. This behaves identically to the list documents endpoint but accepts the queries in the request body, allowing much larger `queries` arrays than can fit in a URL query string. + + Parameters + ---------- + database_id : str + Database ID. + collection_id : str + Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection). + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + transaction_id : Optional[str] + Transaction ID to read uncommitted changes within the transaction. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + ttl : Optional[float] + TTL (seconds) for cached responses when caching is enabled for select queries. Must be between 0 and 86400 (24 hours). + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + DocumentList[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/documentsdb/{databaseId}/collections/{collectionId}/documents/query' + api_params = {} + if database_id is None or database_id == '': + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None or collection_id == '': + raise AppwriteException('Missing required parameter: "collection_id"') + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) + if total is not None: + api_params['total'] = self._normalize_value(total) + if ttl is not None: + api_params['ttl'] = self._normalize_value(ttl) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return DocumentList.with_data(response, model_type) + def get_document( self, database_id: str, @@ -1763,6 +1854,7 @@ def create_index( attributes: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> Index: """ Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. @@ -1784,6 +1876,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- Index @@ -1816,6 +1910,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', diff --git a/appwrite/services/domains.py b/appwrite/services/domains.py index 6ca12b76..bce1dc23 100644 --- a/appwrite/services/domains.py +++ b/appwrite/services/domains.py @@ -22,6 +22,7 @@ def list( self, queries: Optional[List[str]] = None, search: Optional[str] = None, + total: Optional[bool] = None, ) -> DomainsList: """ List all domains registered for this project. This endpoint supports pagination. @@ -32,6 +33,8 @@ def list( Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on attributes such as domain name, teamInternalId, expiration, etc. search : Optional[str] Search term to filter your list results. Max length: 256 chars. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DomainsList @@ -49,6 +52,8 @@ def list( api_params['queries'] = self._normalize_value(queries) if search is not None: api_params['search'] = self._normalize_value(search) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -880,6 +885,7 @@ def list_records( self, domain_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DnsRecordsList: """ List DNS records for a given domain. You can use this endpoint to list all the DNS records @@ -891,6 +897,8 @@ def list_records( Domain unique ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. You may filter on attributes such as type, name, value, etc. Maximum of 100 queries are allowed, each 4096 characters long. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DnsRecordsList @@ -909,6 +917,8 @@ def list_records( api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/functions.py b/appwrite/services/functions.py index 4a7b0ca7..fa109998 100644 --- a/appwrite/services/functions.py +++ b/appwrite/services/functions.py @@ -101,6 +101,7 @@ def create( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, interval: Optional[float] = None, + description: Optional[str] = None, ) -> Function: """ Create a new function. You can pass a list of [permissions](https://appwrite.io/docs/permissions) to allow different project users or team with access to execute the function using the client API. @@ -153,6 +154,8 @@ def create( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. + description : Optional[str] + Optional description for the function. Returns ------- Function @@ -215,6 +218,8 @@ def create( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if interval is not None: api_params['interval'] = self._normalize_value(interval) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -377,6 +382,7 @@ def update( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, interval: Optional[float] = None, + description: Optional[str] = None, email_enabled: Optional[bool] = None, ) -> Function: """ @@ -430,6 +436,8 @@ def update( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. When omitted, the current interval is kept unless schedule is set. + description : Optional[str] + Optional description for the function. email_enabled : Optional[bool] Enable inbound email triggering for this function. When false or omitted on create, SMTP recipients are rejected. Opt-in; default is off. Returns @@ -493,6 +501,8 @@ def update( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if interval is not None: api_params['interval'] = self._normalize_value(interval) + if description is not None: + api_params['description'] = self._normalize_value(description) if email_enabled is not None: api_params['emailEnabled'] = self._normalize_value(email_enabled) @@ -1387,6 +1397,7 @@ def create_variable( key: str, value: str, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Create a new function environment variable. These variables can be accessed in the function at runtime as environment variables. @@ -1403,6 +1414,8 @@ def create_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only functions can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1430,6 +1443,8 @@ def create_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1497,6 +1512,7 @@ def update_variable( key: Optional[str] = None, value: Optional[str] = None, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Update variable by its unique ID. @@ -1513,6 +1529,8 @@ def update_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only functions can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1538,6 +1556,8 @@ def update_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/messaging.py b/appwrite/services/messaging.py index 6b864262..df5083f7 100644 --- a/appwrite/services/messaging.py +++ b/appwrite/services/messaging.py @@ -86,6 +86,7 @@ def create_email( scheduled_at: Optional[str] = None, reply_to_email: Optional[str] = None, reply_to_name: Optional[str] = None, + emails: Optional[List[str]] = None, ) -> Message: """ Create a new email message. @@ -120,6 +121,8 @@ def create_email( Email address to reply to. If not set, defaults to the sender email address. reply_to_name : Optional[str] Name of the reply to recipient. If not set, defaults to the sender name. + emails : Optional[List[str]] + List of email addresses to send the message to. Use this to deliver to recipients that are not backed by an Appwrite user, target, or topic. Returns ------- Message @@ -164,6 +167,8 @@ def create_email( api_params['replyToEmail'] = self._normalize_value(reply_to_email) if reply_to_name is not None: api_params['replyToName'] = self._normalize_value(reply_to_name) + if emails is not None: + api_params['emails'] = self._normalize_value(emails) response = self.client.call( 'post', @@ -194,6 +199,7 @@ def update_email( attachments: Optional[List[str]] = None, reply_to_email: Optional[str] = None, reply_to_name: Optional[str] = None, + emails: Optional[List[str]] = None, ) -> Message: """ Update an email message by its unique ID. This endpoint only works on messages that are in draft status. Messages that are already processing, sent, or failed cannot be updated. @@ -228,6 +234,8 @@ def update_email( Email address to reply to. Pass an empty string to restore the provider or sender default. reply_to_name : Optional[str] Name of the reply to recipient. Pass an empty string to restore the provider or sender default. + emails : Optional[List[str]] + List of email addresses to send the message to. Use this to deliver to recipients that are not backed by an Appwrite user, target, or topic. Returns ------- Message @@ -270,6 +278,8 @@ def update_email( api_params['replyToEmail'] = self._normalize_value(reply_to_email) if reply_to_name is not None: api_params['replyToName'] = self._normalize_value(reply_to_name) + if emails is not None: + api_params['emails'] = self._normalize_value(emails) response = self.client.call( 'patch', @@ -2936,6 +2946,7 @@ def create_topic( subscribe: Optional[List[str]] = None, qos: Optional[float] = None, expiry: Optional[float] = None, + description: Optional[str] = None, ) -> Topic: """ Create a new topic. @@ -2952,6 +2963,8 @@ def create_topic( MQTT delivery quality of service for this topic. 0 is fire-and-forget (delivered only to clients connected at publish time). 1 persists each message and replays it when a client reconnects without having acknowledged it. Null lets the subscriber choose. expiry : Optional[float] Message retention in seconds for offline delivery. Max 7 days (604800). Null inherits the lowest expiry of the project's enabled Appwrite providers, or the 7-day ledger window if none sets one. + description : Optional[str] + Optional description for the topic. Returns ------- Topic @@ -2977,6 +2990,8 @@ def create_topic( api_params['qos'] = self._normalize_value(qos) if expiry is not None: api_params['expiry'] = self._normalize_value(expiry) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3038,6 +3053,7 @@ def update_topic( subscribe: Optional[List[str]] = None, qos: Optional[float] = None, expiry: Optional[float] = None, + description: Optional[str] = None, ) -> Topic: """ Update a topic by its unique ID. @@ -3054,6 +3070,8 @@ def update_topic( MQTT delivery quality of service for this topic. 0 is fire-and-forget (delivered only to clients connected at publish time). 1 persists each message and replays it when a client reconnects without having acknowledged it. Null lets the subscriber choose. expiry : Optional[float] Message retention in seconds for offline delivery. Max 7 days (604800). Null inherits the lowest expiry of the project's enabled Appwrite providers, or the 7-day ledger window if none sets one. + description : Optional[str] + Optional description for the topic. Returns ------- Topic @@ -3078,6 +3096,8 @@ def update_topic( api_params['qos'] = self._normalize_value(qos) if expiry is not None: api_params['expiry'] = self._normalize_value(expiry) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', diff --git a/appwrite/services/mongo.py b/appwrite/services/mongo.py index 7bb92df1..f377fe6c 100644 --- a/appwrite/services/mongo.py +++ b/appwrite/services/mongo.py @@ -29,6 +29,7 @@ def __init__(self, client) -> None: def list( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseList: """ List all dedicated databases. Results support pagination. @@ -37,6 +38,8 @@ def list( ---------- queries : Optional[List[str]] Array of query strings. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseList @@ -52,6 +55,8 @@ def list( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -433,6 +438,7 @@ def list_backups( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseBackupList: """ List all backups for a dedicated database. Results can be filtered by status and type. @@ -443,6 +449,8 @@ def list_backups( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, type, databaseId + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseBackupList @@ -461,6 +469,8 @@ def list_backups( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -524,6 +534,7 @@ def list_backup_policies( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List scheduled backup policies for a dedicated database. @@ -534,6 +545,8 @@ def list_backup_policies( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -552,6 +565,8 @@ def list_backup_policies( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/mysql.py b/appwrite/services/mysql.py index 4e70e83f..0e402fe2 100644 --- a/appwrite/services/mysql.py +++ b/appwrite/services/mysql.py @@ -31,6 +31,7 @@ def __init__(self, client) -> None: def list( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseList: """ List all dedicated databases. Results support pagination. @@ -39,6 +40,8 @@ def list( ---------- queries : Optional[List[str]] Array of query strings. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseList @@ -54,6 +57,8 @@ def list( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -435,6 +440,7 @@ def list_backups( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseBackupList: """ List all backups for a dedicated database. Results can be filtered by status and type. @@ -445,6 +451,8 @@ def list_backups( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, type, databaseId + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseBackupList @@ -463,6 +471,8 @@ def list_backups( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -526,6 +536,7 @@ def list_backup_policies( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List scheduled backup policies for a dedicated database. @@ -536,6 +547,8 @@ def list_backup_policies( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -554,6 +567,8 @@ def list_backup_policies( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/organization.py b/appwrite/services/organization.py index 0e31be2b..ecb7efdb 100644 --- a/appwrite/services/organization.py +++ b/appwrite/services/organization.py @@ -986,6 +986,7 @@ def update_project_key( name: str, scopes: List[ProjectKeyScopes], expire: Optional[str] = None, + description: Optional[str] = None, ) -> Key: """ Update a project key by its unique ID. Use this endpoint to update the name, scopes, or expiration time of an API key. @@ -1002,6 +1003,8 @@ def update_project_key( Key scopes list. Maximum of 200 scopes are allowed. expire : Optional[str] Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration. + description : Optional[str] + Optional description for the key. Returns ------- Key @@ -1029,6 +1032,8 @@ def update_project_key( api_params['scopes'] = self._normalize_value(scopes) if expire is not None: api_params['expire'] = self._normalize_value(expire) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/postgresql.py b/appwrite/services/postgresql.py index f361d9dd..a5692e05 100644 --- a/appwrite/services/postgresql.py +++ b/appwrite/services/postgresql.py @@ -32,6 +32,7 @@ def __init__(self, client) -> None: def list( self, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseList: """ List all dedicated databases. Results support pagination. @@ -40,6 +41,8 @@ def list( ---------- queries : Optional[List[str]] Array of query strings. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseList @@ -55,6 +58,8 @@ def list( api_params = {} if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -436,6 +441,7 @@ def list_backups( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> DedicatedDatabaseBackupList: """ List all backups for a dedicated database. Results can be filtered by status and type. @@ -446,6 +452,8 @@ def list_backups( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, type, databaseId + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- DedicatedDatabaseBackupList @@ -464,6 +472,8 @@ def list_backups( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', @@ -527,6 +537,7 @@ def list_backup_policies( self, database_id: str, queries: Optional[List[str]] = None, + total: Optional[bool] = None, ) -> BackupPolicyList: """ List scheduled backup policies for a dedicated database. @@ -537,6 +548,8 @@ def list_backup_policies( Database ID. queries : Optional[List[str]] Array of query strings generated using the Query class provided by the SDK. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. Returns ------- BackupPolicyList @@ -555,6 +568,8 @@ def list_backup_policies( api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) if queries is not None: api_params['queries'] = self._normalize_value(queries) + if total is not None: + api_params['total'] = self._normalize_value(total) response = self.client.call( 'get', diff --git a/appwrite/services/project.py b/appwrite/services/project.py index 3de8bfc2..d1d6da2d 100644 --- a/appwrite/services/project.py +++ b/appwrite/services/project.py @@ -581,6 +581,7 @@ def update_key( name: str, scopes: List[ProjectKeyScopes], expire: Optional[str] = None, + description: Optional[str] = None, ) -> Key: """ Update a key by its unique ID. Use this endpoint to update the name, scopes, or expiration time of an API key. @@ -595,6 +596,8 @@ def update_key( Key scopes list. Maximum of 200 scopes are allowed. expire : Optional[str] Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration. + description : Optional[str] + Optional description for the key. Returns ------- Key @@ -619,6 +622,8 @@ def update_key( api_params['scopes'] = self._normalize_value(scopes) if expire is not None: api_params['expire'] = self._normalize_value(expire) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -4982,14 +4987,19 @@ def update_passkey_policy( def update_password_dictionary_policy( self, enabled: bool, + deny_list: Optional[List[str]] = None, ) -> ProjectModel: """ Updating this policy allows you to control if new passwords are checked against most common passwords dictionary. When enabled, and user changes their password, password must not be contained in the dictionary. + Custom denyList entries use case-sensitive equality and apply only while this policy is enabled. Omit denyList or pass null to preserve it, pass an array to replace it, or pass [] to clear it. Entries are stored without trimming or deduplication. Existing passwords remain valid for sign-in and password confirmation. The combined serialized project authentication configuration must fit within 16,384 characters, even when each list meets its entry limits. + Parameters ---------- enabled : bool Toggle password dictionary policy. Set to true if you want password change to block passwords in the dictionary, or false to allow them. When changing this policy, existing passwords remain valid. + deny_list : Optional[List[str]] + Custom blocked values using case-sensitive equality. Up to 100 entries of 1 to 256 characters. Omit or pass null to preserve, an array to replace, or [] to clear. Combined authentication settings must fit within 16,384 characters. Returns ------- ProjectModel @@ -5006,6 +5016,8 @@ def update_password_dictionary_policy( if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') api_params['enabled'] = self._normalize_value(enabled) + if deny_list is not None: + api_params['denyList'] = self._normalize_value(deny_list) response = self.client.call( 'patch', @@ -5064,14 +5076,20 @@ def update_password_history_policy( def update_password_personal_data_policy( self, enabled: bool, + deny_list: Optional[List[str]] = None, ) -> ProjectModel: """ Updating this policy allows you to control if password strength is checked against personal data. When enabled, and user sets or changes their password, the password must not contain user ID, name, email or phone number. + Custom denyList entries use case-insensitive substring matching and apply only while this policy is enabled. Omit denyList or pass null to preserve it, pass an array to replace it, or pass [] to clear it. Entries are stored without trimming or deduplication. Existing passwords remain valid for sign-in and password confirmation. The combined serialized project authentication configuration must fit within 16,384 characters, even when each list meets its entry limits. + Password recovery also checks the recovered user profile, closing the previous gap in profile-based personal-data validation. + Parameters ---------- enabled : bool Toggle password personal data policy. Set to true if you want to block passwords including user's personal data, or false to allow it. When changing this policy, existing passwords remain valid. + deny_list : Optional[List[str]] + Custom blocked values using case-insensitive substring matching. Up to 100 entries of 1 to 256 characters. Omit or pass null to preserve, an array to replace, or [] to clear. Combined authentication settings must fit within 16,384 characters. Returns ------- ProjectModel @@ -5088,6 +5106,8 @@ def update_password_personal_data_policy( if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') api_params['enabled'] = self._normalize_value(enabled) + if deny_list is not None: + api_params['denyList'] = self._normalize_value(deny_list) response = self.client.call( 'patch', @@ -6010,6 +6030,7 @@ def create_variable( key: str, value: str, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Create a new project environment variable. These variables can be accessed by all functions and sites in the project. @@ -6024,6 +6045,8 @@ def create_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only projects can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -6048,6 +6071,8 @@ def create_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -6108,6 +6133,7 @@ def update_variable( key: Optional[str] = None, value: Optional[str] = None, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Update variable by its unique ID. @@ -6122,6 +6148,8 @@ def update_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only projects can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -6144,6 +6172,8 @@ def update_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/sites.py b/appwrite/services/sites.py index 952e4b27..f3320934 100644 --- a/appwrite/services/sites.py +++ b/appwrite/services/sites.py @@ -103,6 +103,7 @@ def create( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, scopes: Optional[List[ProjectKeyScopes]] = None, + description: Optional[str] = None, ) -> Site: """ Create a new site. @@ -157,6 +158,8 @@ def create( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. + description : Optional[str] + Optional description for the site. Returns ------- Site @@ -222,6 +225,8 @@ def create( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if scopes is not None: api_params['scopes'] = self._normalize_value(scopes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -385,6 +390,7 @@ def update( runtime_specification: Optional[str] = None, deployment_retention: Optional[float] = None, scopes: Optional[List[ProjectKeyScopes]] = None, + description: Optional[str] = None, ) -> Site: """ Update site by its unique ID. @@ -439,6 +445,8 @@ def update( Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. + description : Optional[str] + Optional description for the site. Returns ------- Site @@ -503,6 +511,8 @@ def update( api_params['deploymentRetention'] = self._normalize_value(deployment_retention) if scopes is not None: api_params['scopes'] = self._normalize_value(scopes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1321,6 +1331,7 @@ def create_variable( key: str, value: str, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Create a new site variable. These variables can be accessed during build and runtime (server-side rendering) as environment variables. @@ -1337,6 +1348,8 @@ def create_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only sites can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1364,6 +1377,8 @@ def create_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1431,6 +1446,7 @@ def update_variable( key: Optional[str] = None, value: Optional[str] = None, secret: Optional[bool] = None, + description: Optional[str] = None, ) -> Variable: """ Update variable by its unique ID. @@ -1447,6 +1463,8 @@ def update_variable( Variable value. Max length: 8192 chars. secret : Optional[bool] Secret variables can be updated or deleted, but only sites can read them during build and runtime. + description : Optional[str] + Optional description for the variable. Returns ------- Variable @@ -1472,6 +1490,8 @@ def update_variable( api_params['value'] = self._normalize_value(value) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/appwrite/services/storage.py b/appwrite/services/storage.py index a095f6d2..2981f526 100644 --- a/appwrite/services/storage.py +++ b/appwrite/services/storage.py @@ -30,7 +30,7 @@ def list_buckets( Parameters ---------- queries : Optional[List[str]] - Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: enabled, name, fileSecurity, maximumFileSize, encryption, antivirus, transformations + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: enabled, name, fileSecurity, maximumFileSize, encryption, antivirus, transformations, stripMetadata search : Optional[str] Search term to filter your list results. Max length: 256 chars. total : Optional[bool] @@ -80,6 +80,8 @@ def create_bucket( encryption: Optional[bool] = None, antivirus: Optional[bool] = None, transformations: Optional[bool] = None, + description: Optional[str] = None, + strip_metadata: Optional[bool] = None, ) -> Bucket: """ Create a new storage bucket. @@ -108,6 +110,10 @@ def create_bucket( Is virus scanning enabled? For file size above 20MB AntiVirus scanning is skipped even if it's enabled transformations : Optional[bool] Are image transformations enabled? + description : Optional[str] + Optional description for the bucket. + strip_metadata : Optional[bool] + Remove metadata such as location, device and capture time from uploaded images. The color profile and orientation are kept. For file size above 20MB metadata is not removed even if it's enabled. Returns ------- Bucket @@ -145,6 +151,10 @@ def create_bucket( api_params['antivirus'] = self._normalize_value(antivirus) if transformations is not None: api_params['transformations'] = self._normalize_value(transformations) + if description is not None: + api_params['description'] = self._normalize_value(description) + if strip_metadata is not None: + api_params['stripMetadata'] = self._normalize_value(strip_metadata) response = self.client.call( 'post', @@ -212,6 +222,8 @@ def update_bucket( encryption: Optional[bool] = None, antivirus: Optional[bool] = None, transformations: Optional[bool] = None, + description: Optional[str] = None, + strip_metadata: Optional[bool] = None, ) -> Bucket: """ Update a storage bucket by its unique ID. @@ -240,6 +252,10 @@ def update_bucket( Is virus scanning enabled? For file size above 20MB AntiVirus scanning is skipped even if it's enabled transformations : Optional[bool] Are image transformations enabled? + description : Optional[str] + Optional description for the bucket. + strip_metadata : Optional[bool] + Remove metadata such as location, device and capture time from uploaded images. The color profile and orientation are kept. For file size above 20MB metadata is not removed even if it's enabled. By default, the current setting is kept. Returns ------- Bucket @@ -277,6 +293,10 @@ def update_bucket( api_params['antivirus'] = self._normalize_value(antivirus) if transformations is not None: api_params['transformations'] = self._normalize_value(transformations) + if description is not None: + api_params['description'] = self._normalize_value(description) + if strip_metadata is not None: + api_params['stripMetadata'] = self._normalize_value(strip_metadata) response = self.client.call( 'put', diff --git a/appwrite/services/tables_db.py b/appwrite/services/tables_db.py index 6a90c3a8..19f181a5 100644 --- a/appwrite/services/tables_db.py +++ b/appwrite/services/tables_db.py @@ -106,6 +106,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -121,6 +122,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Defaults to `serverless`, which creates the database on the shared pool. Any other value provisions a dedicated database on that specification. replicas : Optional[float] @@ -148,6 +151,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -506,6 +511,7 @@ def update( database_id: str, name: Optional[str] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -521,6 +527,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Resizing between dedicated specifications changes cpu, memory, storage and the connection ceiling via a rolling cutover with zero downtime. Moving a `serverless` database onto a dedicated specification is a data migration, not a resize. replicas : Optional[float] @@ -547,6 +555,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -1086,6 +1096,7 @@ def create_table( enabled: Optional[bool] = None, columns: Optional[List[Dict[str, Any]]] = None, indexes: Optional[List[Dict[str, Any]]] = None, + description: Optional[str] = None, ) -> Table: """ Create a new Table. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/references/cloud/server-dart/tablesDB#createTable) API or directly from your database console. @@ -1108,6 +1119,8 @@ def create_table( Array of column definitions to create. Each column should contain: key (string), type (string: string, varchar, text, mediumtext, longtext, integer, bigint, double, boolean, datetime, point, linestring, polygon, email, url, ip, enum), size (integer, required for string and varchar types), required (boolean, optional), default (mixed, optional), array (boolean, optional), and type-specific options. indexes : Optional[List[Dict[str, Any]]] Array of index definitions to create. Each index should contain: key (string), type (string: key, fulltext, unique, spatial, trigram), attributes (array of column keys), orders (array of ASC/DESC, optional), and lengths (array of integers, optional). + description : Optional[str] + Optional description for the table. Returns ------- Table @@ -1140,6 +1153,8 @@ def create_table( api_params['columns'] = self._normalize_value(columns) if indexes is not None: api_params['indexes'] = self._normalize_value(indexes) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1209,6 +1224,7 @@ def update_table( row_security: Optional[bool] = None, enabled: Optional[bool] = None, purge: Optional[bool] = None, + description: Optional[str] = None, ) -> Table: """ Update a table by its unique ID. @@ -1229,6 +1245,8 @@ def update_table( Is table enabled? When set to 'disabled', users cannot access the table but Server SDKs with and API key can still read and write to the table. No data is lost when this is toggled. purge : Optional[bool] When true, purge all cached list responses for this table as part of the update. Use this to force readers to see fresh data immediately instead of waiting for the cache TTL to expire. + description : Optional[str] + Optional description for the table. Returns ------- Table @@ -1258,6 +1276,8 @@ def update_table( api_params['enabled'] = self._normalize_value(enabled) if purge is not None: api_params['purge'] = self._normalize_value(purge) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1385,6 +1405,7 @@ def create_big_int_column( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnBigint: """ Create a bigint column. Optionally, minimum and maximum values can be provided. @@ -1407,6 +1428,8 @@ def create_big_int_column( Default value. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnBigint @@ -1440,6 +1463,8 @@ def create_big_int_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1464,6 +1489,7 @@ def update_big_int_column( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnBigint: """ Update a bigint column. Changing the `default` value will not update already existing rows. @@ -1486,6 +1512,8 @@ def update_big_int_column( Maximum value new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnBigint @@ -1518,6 +1546,8 @@ def update_big_int_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1540,6 +1570,7 @@ def create_boolean_column( required: bool, default: Optional[bool] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnBoolean: """ Create a boolean column. @@ -1558,6 +1589,8 @@ def create_boolean_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnBoolean @@ -1587,6 +1620,8 @@ def create_boolean_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1609,6 +1644,7 @@ def update_boolean_column( required: bool, default: Optional[bool], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnBoolean: """ Update a boolean column. Changing the `default` value will not update already existing rows. @@ -1627,6 +1663,8 @@ def update_boolean_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnBoolean @@ -1655,6 +1693,8 @@ def update_boolean_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1677,6 +1717,7 @@ def create_datetime_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnDatetime: """ Create a date time column according to the ISO 8601 standard. @@ -1695,6 +1736,8 @@ def create_datetime_column( Default value for the column in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnDatetime @@ -1724,6 +1767,8 @@ def create_datetime_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1746,6 +1791,7 @@ def update_datetime_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnDatetime: """ Update a date time column. Changing the `default` value will not update already existing rows. @@ -1764,6 +1810,8 @@ def update_datetime_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnDatetime @@ -1792,6 +1840,8 @@ def update_datetime_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1814,6 +1864,7 @@ def create_email_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnEmail: """ Create an email column. @@ -1832,6 +1883,8 @@ def create_email_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnEmail @@ -1861,6 +1914,8 @@ def create_email_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -1883,6 +1938,7 @@ def update_email_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnEmail: """ Update an email column. Changing the `default` value will not update already existing rows. @@ -1901,6 +1957,8 @@ def update_email_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnEmail @@ -1929,6 +1987,8 @@ def update_email_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -1952,6 +2012,7 @@ def create_enum_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnEnum: """ Create an enumeration column. The `elements` param acts as a white-list of accepted values for this column. @@ -1972,6 +2033,8 @@ def create_enum_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnEnum @@ -2004,6 +2067,8 @@ def create_enum_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2027,6 +2092,7 @@ def update_enum_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnEnum: """ Update an enum column. Changing the `default` value will not update already existing rows. @@ -2047,6 +2113,8 @@ def update_enum_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnEnum @@ -2078,6 +2146,8 @@ def update_enum_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2102,6 +2172,7 @@ def create_float_column( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnFloat: """ Create a float column. Optionally, minimum and maximum values can be provided. @@ -2124,6 +2195,8 @@ def create_float_column( Default value. Cannot be set when required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnFloat @@ -2157,6 +2230,8 @@ def create_float_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2181,6 +2256,7 @@ def update_float_column( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnFloat: """ Update a float column. Changing the `default` value will not update already existing rows. @@ -2203,6 +2279,8 @@ def update_float_column( Maximum value new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnFloat @@ -2235,6 +2313,8 @@ def update_float_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2259,6 +2339,7 @@ def create_integer_column( max: Optional[float] = None, default: Optional[float] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnInteger: """ Create an integer column. Optionally, minimum and maximum values can be provided. @@ -2281,6 +2362,8 @@ def create_integer_column( Default value. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnInteger @@ -2314,6 +2397,8 @@ def create_integer_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2338,6 +2423,7 @@ def update_integer_column( min: Optional[float] = None, max: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnInteger: """ Update an integer column. Changing the `default` value will not update already existing rows. @@ -2360,6 +2446,8 @@ def update_integer_column( Maximum value new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnInteger @@ -2392,6 +2480,8 @@ def update_integer_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2414,6 +2504,7 @@ def create_ip_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnIp: """ Create IP address column. @@ -2432,6 +2523,8 @@ def create_ip_column( Default value. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnIp @@ -2461,6 +2554,8 @@ def create_ip_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2483,6 +2578,7 @@ def update_ip_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnIp: """ Update an ip column. Changing the `default` value will not update already existing rows. @@ -2501,6 +2597,8 @@ def update_ip_column( Default value. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnIp @@ -2529,6 +2627,8 @@ def update_ip_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2550,6 +2650,7 @@ def create_line_column( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> ColumnLine: """ Create a geometric line column. @@ -2566,6 +2667,8 @@ def create_line_column( Is column required? default : Optional[List[List[Any]]] Default value for column when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when column is required. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLine @@ -2593,6 +2696,8 @@ def create_line_column( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2615,6 +2720,7 @@ def update_line_column( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnLine: """ Update a line column. Changing the `default` value will not update already existing rows. @@ -2633,6 +2739,8 @@ def update_line_column( Default value for column when not provided, two-dimensional array of coordinate pairs, [[longitude, latitude], [longitude, latitude], …], listing the vertices of the line in order. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLine @@ -2662,6 +2770,8 @@ def update_line_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2685,6 +2795,7 @@ def create_longtext_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnLongtext: """ Create a longtext column. @@ -2705,6 +2816,8 @@ def create_longtext_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLongtext @@ -2736,6 +2849,8 @@ def create_longtext_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2758,6 +2873,7 @@ def update_longtext_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnLongtext: """ Update a longtext column. Changing the `default` value will not update already existing rows. @@ -2776,6 +2892,8 @@ def update_longtext_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnLongtext @@ -2804,6 +2922,8 @@ def update_longtext_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2827,6 +2947,7 @@ def create_mediumtext_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnMediumtext: """ Create a mediumtext column. @@ -2847,6 +2968,8 @@ def create_mediumtext_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnMediumtext @@ -2878,6 +3001,8 @@ def create_mediumtext_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -2900,6 +3025,7 @@ def update_mediumtext_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnMediumtext: """ Update a mediumtext column. Changing the `default` value will not update already existing rows. @@ -2918,6 +3044,8 @@ def update_mediumtext_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnMediumtext @@ -2946,6 +3074,8 @@ def update_mediumtext_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -2967,6 +3097,7 @@ def create_point_column( key: str, required: bool, default: Optional[List[float]] = None, + description: Optional[str] = None, ) -> ColumnPoint: """ Create a geometric point column. @@ -2983,6 +3114,8 @@ def create_point_column( Is column required? default : Optional[List[float]] Default value for column when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when column is required. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPoint @@ -3010,6 +3143,8 @@ def create_point_column( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3032,6 +3167,7 @@ def update_point_column( required: bool, default: Optional[List[float]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnPoint: """ Update a point column. Changing the `default` value will not update already existing rows. @@ -3050,6 +3186,8 @@ def update_point_column( Default value for column when not provided, array of two numbers [longitude, latitude], representing a single coordinate. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPoint @@ -3079,6 +3217,8 @@ def update_point_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3100,6 +3240,7 @@ def create_polygon_column( key: str, required: bool, default: Optional[List[List[Any]]] = None, + description: Optional[str] = None, ) -> ColumnPolygon: """ Create a geometric polygon column. @@ -3116,6 +3257,8 @@ def create_polygon_column( Is column required? default : Optional[List[List[Any]]] Default value for column when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when column is required. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPolygon @@ -3143,6 +3286,8 @@ def create_polygon_column( api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3165,6 +3310,7 @@ def update_polygon_column( required: bool, default: Optional[List[List[Any]]] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnPolygon: """ Update a polygon column. Changing the `default` value will not update already existing rows. @@ -3183,6 +3329,8 @@ def update_polygon_column( Default value for column when not provided, three-dimensional array where the outer array holds one or more linear rings, [[[longitude, latitude], …], …], the first ring is the exterior boundary, any additional rings are interior holes, and each ring must start and end with the same coordinate pair. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnPolygon @@ -3212,6 +3360,8 @@ def update_polygon_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3236,6 +3386,7 @@ def create_relationship_column( key: Optional[str] = None, two_way_key: Optional[str] = None, on_delete: Optional[RelationMutate] = None, + description: Optional[str] = None, ) -> ColumnRelationship: """ Create relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). @@ -3258,6 +3409,8 @@ def create_relationship_column( Two Way Column Key. on_delete : Optional[RelationMutate] Delete constraint. Possible values are: cascade, restrict, setNull. + description : Optional[str] + Optional description for the column. Returns ------- ColumnRelationship @@ -3291,6 +3444,8 @@ def create_relationship_column( api_params['twoWayKey'] = self._normalize_value(two_way_key) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3312,6 +3467,7 @@ def update_relationship_column( key: str, on_delete: Optional[RelationMutate] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnRelationship: """ Update relationship column. [Learn more about relationship columns](https://appwrite.io/docs/databases-relationships#relationship-columns). @@ -3328,6 +3484,8 @@ def update_relationship_column( Delete constraint. Possible values are: cascade, restrict, setNull. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnRelationship @@ -3354,6 +3512,8 @@ def update_relationship_column( api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3379,6 +3539,7 @@ def create_string_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnString: """ Create a string column. @@ -3403,6 +3564,8 @@ def create_string_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnString @@ -3437,6 +3600,8 @@ def create_string_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3461,6 +3626,7 @@ def update_string_column( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnString: """ Update a string column. Changing the `default` value will not update already existing rows. @@ -3483,6 +3649,8 @@ def update_string_column( Maximum size of the string column. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnString @@ -3513,6 +3681,8 @@ def update_string_column( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3536,6 +3706,7 @@ def create_text_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnText: """ Create a text column. @@ -3556,6 +3727,8 @@ def create_text_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnText @@ -3587,6 +3760,8 @@ def create_text_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3609,6 +3784,7 @@ def update_text_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnText: """ Update a text column. Changing the `default` value will not update already existing rows. @@ -3627,6 +3803,8 @@ def update_text_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnText @@ -3655,6 +3833,8 @@ def update_text_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3677,6 +3857,7 @@ def create_url_column( required: bool, default: Optional[str] = None, array: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnUrl: """ Create a URL column. @@ -3695,6 +3876,8 @@ def create_url_column( Default value for column when not provided. Cannot be set when column is required. array : Optional[bool] Is column an array? + description : Optional[str] + Optional description for the column. Returns ------- ColumnUrl @@ -3724,6 +3907,8 @@ def create_url_column( api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3746,6 +3931,7 @@ def update_url_column( required: bool, default: Optional[str], new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnUrl: """ Update an url column. Changing the `default` value will not update already existing rows. @@ -3764,6 +3950,8 @@ def update_url_column( Default value for column when not provided. Cannot be set when column is required. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnUrl @@ -3792,6 +3980,8 @@ def update_url_column( api_params['default'] = self._normalize_value(default) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -3816,6 +4006,7 @@ def create_varchar_column( default: Optional[str] = None, array: Optional[bool] = None, encrypt: Optional[bool] = None, + description: Optional[str] = None, ) -> ColumnVarchar: """ Create a varchar column. @@ -3838,6 +4029,8 @@ def create_varchar_column( Is column an array? encrypt : Optional[bool] Toggle encryption for the column. Encryption enhances security by not storing any plain text values in the database. However, encrypted columns cannot be queried. + description : Optional[str] + Optional description for the column. Returns ------- ColumnVarchar @@ -3872,6 +4065,8 @@ def create_varchar_column( api_params['array'] = self._normalize_value(array) if encrypt is not None: api_params['encrypt'] = self._normalize_value(encrypt) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -3895,6 +4090,7 @@ def update_varchar_column( default: Optional[str], size: Optional[float] = None, new_key: Optional[str] = None, + description: Optional[str] = None, ) -> ColumnVarchar: """ Update a varchar column. Changing the `default` value will not update already existing rows. @@ -3915,6 +4111,8 @@ def update_varchar_column( Maximum size of the varchar column. new_key : Optional[str] New Column Key. + description : Optional[str] + Optional description for the column. Returns ------- ColumnVarchar @@ -3945,6 +4143,8 @@ def update_varchar_column( api_params['size'] = self._normalize_value(size) if new_key is not None: api_params['newKey'] = self._normalize_value(new_key) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'patch', @@ -4204,6 +4404,7 @@ def create_index( columns: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> ColumnIndex: """ Creates an index on the columns listed. Your index should include all the columns you will query in a single request. @@ -4225,6 +4426,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- ColumnIndex @@ -4257,6 +4460,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -4389,6 +4594,8 @@ def list_rows( """ Get a list of all the user's rows in a given table. You can use the query params to filter your results. + Rows returned by aggregate, groupBy or distinct queries hold only the columns they select, group or aggregate, typed as a row types them, with a joined column named by its join alias (`alias.column`). The response omits their `$id`, `$sequence`, `$createdAt`, `$updatedAt`, `$permissions`, `$databaseId` and `$tableId` unless they select or group it, and those of a right or full join row that no row of this table matched. SDK row models fill omitted system columns with empty values. + Parameters ---------- database_id : str @@ -4402,7 +4609,7 @@ def list_rows( total : Optional[bool] When set to false, the total count returned will be 0 and will not be calculated. ttl : Optional[float] - TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, table, schema version (columns and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; row writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Must be between 0 and 86400 (24 hours). + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, table, schema version (columns and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; row writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). model_type : Type[T], optional Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. @@ -4770,6 +4977,77 @@ def delete_rows( return RowList.with_data(response, model_type) + def create_query( + self, + database_id: str, + table_id: str, + queries: Optional[List[str]] = None, + transaction_id: Optional[str] = None, + total: Optional[bool] = None, + ttl: Optional[float] = None, + model_type: Type[T] = dict, + ) -> RowList[T]: + """ + Get a list of all the user's rows in a given table using a POST request. This behaves identically to the list rows endpoint but accepts the queries in the request body, allowing much larger `queries` arrays than can fit in a URL query string. + + Parameters + ---------- + database_id : str + Database ID. + table_id : str + Table ID. You can create a new table using the TablesDB service [server integration](https://appwrite.io/docs/products/databases/tables#create-table). + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. + transaction_id : Optional[str] + Transaction ID to read uncommitted changes within the transaction. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + ttl : Optional[float] + TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, table, schema version (columns and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; row writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Lists with join queries are not cached. Must be between 0 and 86400 (24 hours). + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + RowList[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/tablesdb/{databaseId}/tables/{tableId}/rows/query' + api_params = {} + if database_id is None or database_id == '': + raise AppwriteException('Missing required parameter: "database_id"') + if table_id is None or table_id == '': + raise AppwriteException('Missing required parameter: "table_id"') + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) + if total is not None: + api_params['total'] = self._normalize_value(total) + if ttl is not None: + api_params['ttl'] = self._normalize_value(ttl) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return RowList.with_data(response, model_type) + def get_row( self, database_id: str, diff --git a/appwrite/services/vectors_db.py b/appwrite/services/vectors_db.py index 1cea0603..eda5f00d 100644 --- a/appwrite/services/vectors_db.py +++ b/appwrite/services/vectors_db.py @@ -78,6 +78,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -93,6 +94,8 @@ def create( Database name. Max length: 128 chars. enabled : Optional[bool] Is the database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Defaults to `serverless`, which creates the database on the shared pool. Any other value provisions a dedicated database on that specification. replicas : Optional[float] @@ -120,6 +123,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -478,6 +483,7 @@ def update( database_id: str, name: str, enabled: Optional[bool] = None, + description: Optional[str] = None, specification: Optional[str] = None, replicas: Optional[float] = None, sync_mode: Optional[str] = None, @@ -493,6 +499,8 @@ def update( Database name. Max length: 128 chars. enabled : Optional[bool] Is database enabled? When set to 'disabled', users cannot access the database but Server SDKs with an API key can still read and write to the database. No data is lost when this is toggled. + description : Optional[str] + Optional description for the database. specification : Optional[str] Database specification. Resizing between dedicated specifications changes cpu, memory, storage and the connection ceiling via a rolling cutover with zero downtime. Moving a `serverless` database onto a dedicated specification is a data migration, not a resize. replicas : Optional[float] @@ -520,6 +528,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) if specification is not None: api_params['specification'] = self._normalize_value(specification) if replicas is not None: @@ -645,6 +655,7 @@ def create_collection( permissions: Optional[List[str]] = None, document_security: Optional[bool] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> VectorsdbCollection: """ Create a new Collection. Before using this route, you should create a new database resource using either a [server integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) API or directly from your database console. @@ -665,6 +676,8 @@ def create_collection( Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions). enabled : Optional[bool] Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. + description : Optional[str] + Optional description for the collection. Returns ------- VectorsdbCollection @@ -696,6 +709,8 @@ def create_collection( api_params['documentSecurity'] = self._normalize_value(document_security) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -765,6 +780,7 @@ def update_collection( permissions: Optional[List[str]] = None, document_security: Optional[bool] = None, enabled: Optional[bool] = None, + description: Optional[str] = None, ) -> VectorsdbCollection: """ Update a collection by its unique ID. @@ -785,6 +801,8 @@ def update_collection( Enables configuring permissions for individual documents. A user needs one of document or collection level permissions to access a document. [Learn more about permissions](https://appwrite.io/docs/permissions). enabled : Optional[bool] Is collection enabled? When set to 'disabled', users cannot access the collection but Server SDKs with and API key can still read and write to the collection. No data is lost when this is toggled. + description : Optional[str] + Optional description for the collection. Returns ------- VectorsdbCollection @@ -815,6 +833,8 @@ def update_collection( api_params['documentSecurity'] = self._normalize_value(document_security) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', @@ -1674,6 +1694,7 @@ def create_index( attributes: List[str], orders: Optional[List[OrderBy]] = None, lengths: Optional[List[float]] = None, + description: Optional[str] = None, ) -> Index: """ Creates an index on the attributes listed. Your index should include all the attributes you will query in a single request. @@ -1695,6 +1716,8 @@ def create_index( Array of index orders. Maximum of 100 orders are allowed. lengths : Optional[List[float]] Length of index. Maximum of 100 + description : Optional[str] + Optional description for the index. Returns ------- Index @@ -1727,6 +1750,8 @@ def create_index( api_params['orders'] = self._normalize_value(orders) if lengths is not None: api_params['lengths'] = self._normalize_value(lengths) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', diff --git a/appwrite/services/webhooks.py b/appwrite/services/webhooks.py index 6caa4745..41f4ef49 100644 --- a/appwrite/services/webhooks.py +++ b/appwrite/services/webhooks.py @@ -67,6 +67,7 @@ def create( auth_username: Optional[str] = None, auth_password: Optional[str] = None, secret: Optional[str] = None, + description: Optional[str] = None, ) -> Webhook: """ Create a new webhook. Use this endpoint to configure a URL that will receive events from Appwrite when specific events occur. @@ -91,6 +92,8 @@ def create( Webhook HTTP password. Max length: 2048 chars. secret : Optional[str] Webhook secret key. If not provided, a new key will be generated automatically. Key must be at least 8 characters long, and at max 256 characters. + description : Optional[str] + Optional description for the webhook. Returns ------- Webhook @@ -126,6 +129,8 @@ def create( api_params['authPassword'] = self._normalize_value(auth_password) if secret is not None: api_params['secret'] = self._normalize_value(secret) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'post', @@ -190,6 +195,7 @@ def update( tls: Optional[bool] = None, auth_username: Optional[str] = None, auth_password: Optional[str] = None, + description: Optional[str] = None, ) -> Webhook: """ Update a webhook by its unique ID. Use this endpoint to update the URL, events, or status of an existing webhook. @@ -212,6 +218,8 @@ def update( Webhook HTTP user. Max length: 255 chars. auth_password : Optional[str] Webhook HTTP password. Max length: 2048 chars. Omit to keep the current password; it is cleared when the URL changes or TLS verification is disabled. + description : Optional[str] + Optional description for the webhook. Returns ------- Webhook @@ -245,6 +253,8 @@ def update( api_params['authUsername'] = self._normalize_value(auth_username) if auth_password is not None: api_params['authPassword'] = self._normalize_value(auth_password) + if description is not None: + api_params['description'] = self._normalize_value(description) response = self.client.call( 'put', diff --git a/docs/examples/account/list-memberships.md b/docs/examples/account/list-memberships.md new file mode 100644 index 00000000..2238f602 --- /dev/null +++ b/docs/examples/account/list-memberships.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account +from appwrite.models import MembershipList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +account = Account(client) + +result: MembershipList = account.list_memberships( + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/activities/list-events.md b/docs/examples/activities/list-events.md index 17f3b67d..14dd6f7d 100644 --- a/docs/examples/activities/list-events.md +++ b/docs/examples/activities/list-events.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key activities = Activities(client) result: ActivityEventList = activities.list_events( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/backups/list-archives.md b/docs/examples/backups/list-archives.md index 3d179923..42674b17 100644 --- a/docs/examples/backups/list-archives.md +++ b/docs/examples/backups/list-archives.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key backups = Backups(client) result: BackupArchiveList = backups.list_archives( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/backups/list-policies.md b/docs/examples/backups/list-policies.md index bcfb6734..65bc8c9f 100644 --- a/docs/examples/backups/list-policies.md +++ b/docs/examples/backups/list-policies.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key backups = Backups(client) result: BackupPolicyList = backups.list_policies( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/backups/list-restorations.md b/docs/examples/backups/list-restorations.md index c353a50e..4556933b 100644 --- a/docs/examples/backups/list-restorations.md +++ b/docs/examples/backups/list-restorations.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key backups = Backups(client) result: BackupRestorationList = backups.list_restorations( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-big-int-attribute.md b/docs/examples/databases/create-big-int-attribute.md index e55ab28c..18025ca5 100644 --- a/docs/examples/databases/create-big-int-attribute.md +++ b/docs/examples/databases/create-big-int-attribute.md @@ -18,7 +18,8 @@ result: AttributeBigint = databases.create_big_int_attribute( min = 0, # optional max = 1000000, # optional default = 0, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-boolean-attribute.md b/docs/examples/databases/create-boolean-attribute.md index 7c66b322..5a9a946b 100644 --- a/docs/examples/databases/create-boolean-attribute.md +++ b/docs/examples/databases/create-boolean-attribute.md @@ -16,7 +16,8 @@ result: AttributeBoolean = databases.create_boolean_attribute( key = '', required = False, default = False, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-collection.md b/docs/examples/databases/create-collection.md index 7e9afa0f..655144e3 100644 --- a/docs/examples/databases/create-collection.md +++ b/docs/examples/databases/create-collection.md @@ -20,7 +20,8 @@ result: Collection = databases.create_collection( document_security = False, # optional enabled = False, # optional attributes = [], # optional - indexes = [] # optional + indexes = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-datetime-attribute.md b/docs/examples/databases/create-datetime-attribute.md index d6e5a437..4e4c3702 100644 --- a/docs/examples/databases/create-datetime-attribute.md +++ b/docs/examples/databases/create-datetime-attribute.md @@ -16,7 +16,8 @@ result: AttributeDatetime = databases.create_datetime_attribute( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-email-attribute.md b/docs/examples/databases/create-email-attribute.md index 8330bb9a..4745d038 100644 --- a/docs/examples/databases/create-email-attribute.md +++ b/docs/examples/databases/create-email-attribute.md @@ -16,7 +16,8 @@ result: AttributeEmail = databases.create_email_attribute( key = '', required = False, default = 'email@example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-enum-attribute.md b/docs/examples/databases/create-enum-attribute.md index 4710819f..d6c6f6de 100644 --- a/docs/examples/databases/create-enum-attribute.md +++ b/docs/examples/databases/create-enum-attribute.md @@ -17,7 +17,8 @@ result: AttributeEnum = databases.create_enum_attribute( elements = ["active", "inactive"], required = False, default = 'active', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-float-attribute.md b/docs/examples/databases/create-float-attribute.md index e7155081..dd76cec2 100644 --- a/docs/examples/databases/create-float-attribute.md +++ b/docs/examples/databases/create-float-attribute.md @@ -18,7 +18,8 @@ result: AttributeFloat = databases.create_float_attribute( min = 0, # optional max = 100, # optional default = 10.5, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-index.md b/docs/examples/databases/create-index.md index 8733b72a..8102336b 100644 --- a/docs/examples/databases/create-index.md +++ b/docs/examples/databases/create-index.md @@ -19,7 +19,8 @@ result: Index = databases.create_index( type = DatabasesIndexType.KEY, attributes = ["username"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-integer-attribute.md b/docs/examples/databases/create-integer-attribute.md index c46e2295..f89a52d2 100644 --- a/docs/examples/databases/create-integer-attribute.md +++ b/docs/examples/databases/create-integer-attribute.md @@ -18,7 +18,8 @@ result: AttributeInteger = databases.create_integer_attribute( min = 0, # optional max = 100, # optional default = 10, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-ip-attribute.md b/docs/examples/databases/create-ip-attribute.md index 34542fa9..19f18397 100644 --- a/docs/examples/databases/create-ip-attribute.md +++ b/docs/examples/databases/create-ip-attribute.md @@ -16,7 +16,8 @@ result: AttributeIp = databases.create_ip_attribute( key = '', required = False, default = '192.0.2.0', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-line-attribute.md b/docs/examples/databases/create-line-attribute.md index 6745498a..77f1fa6d 100644 --- a/docs/examples/databases/create-line-attribute.md +++ b/docs/examples/databases/create-line-attribute.md @@ -15,7 +15,8 @@ result: AttributeLine = databases.create_line_attribute( collection_id = '', key = '', required = False, - default = [[1, 2], [3, 4], [5, 6]] # optional + default = [[1, 2], [3, 4], [5, 6]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-longtext-attribute.md b/docs/examples/databases/create-longtext-attribute.md index 70b0eb48..a8cdecc5 100644 --- a/docs/examples/databases/create-longtext-attribute.md +++ b/docs/examples/databases/create-longtext-attribute.md @@ -17,7 +17,8 @@ result: AttributeLongtext = databases.create_longtext_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-mediumtext-attribute.md b/docs/examples/databases/create-mediumtext-attribute.md index cf3011ac..68a04431 100644 --- a/docs/examples/databases/create-mediumtext-attribute.md +++ b/docs/examples/databases/create-mediumtext-attribute.md @@ -17,7 +17,8 @@ result: AttributeMediumtext = databases.create_mediumtext_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-point-attribute.md b/docs/examples/databases/create-point-attribute.md index a3d12715..91859e78 100644 --- a/docs/examples/databases/create-point-attribute.md +++ b/docs/examples/databases/create-point-attribute.md @@ -15,7 +15,8 @@ result: AttributePoint = databases.create_point_attribute( collection_id = '', key = '', required = False, - default = [1, 2] # optional + default = [1, 2], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-polygon-attribute.md b/docs/examples/databases/create-polygon-attribute.md index 4e6145f5..6651bd3e 100644 --- a/docs/examples/databases/create-polygon-attribute.md +++ b/docs/examples/databases/create-polygon-attribute.md @@ -15,7 +15,8 @@ result: AttributePolygon = databases.create_polygon_attribute( collection_id = '', key = '', required = False, - default = [[[1, 2], [3, 4], [5, 6], [1, 2]]] # optional + default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-query.md b/docs/examples/databases/create-query.md new file mode 100644 index 00000000..917b4254 --- /dev/null +++ b/docs/examples/databases/create-query.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.databases import Databases +from appwrite.models import DocumentList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +databases = Databases(client) + +result: DocumentList = databases.create_query( + database_id = '', + collection_id = '', + queries = [], # optional + transaction_id = '', # optional + total = False, # optional + ttl = 0 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/databases/create-relationship-attribute.md b/docs/examples/databases/create-relationship-attribute.md index 48821cac..a04c6e1b 100644 --- a/docs/examples/databases/create-relationship-attribute.md +++ b/docs/examples/databases/create-relationship-attribute.md @@ -20,7 +20,8 @@ result: AttributeRelationship = databases.create_relationship_attribute( two_way = False, # optional key = '', # optional two_way_key = '', # optional - on_delete = RelationMutate.CASCADE # optional + on_delete = RelationMutate.CASCADE, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-string-attribute.md b/docs/examples/databases/create-string-attribute.md index 84089243..17d8bae1 100644 --- a/docs/examples/databases/create-string-attribute.md +++ b/docs/examples/databases/create-string-attribute.md @@ -18,7 +18,8 @@ result: AttributeString = databases.create_string_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-text-attribute.md b/docs/examples/databases/create-text-attribute.md index 195650ee..0271b051 100644 --- a/docs/examples/databases/create-text-attribute.md +++ b/docs/examples/databases/create-text-attribute.md @@ -17,7 +17,8 @@ result: AttributeText = databases.create_text_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-url-attribute.md b/docs/examples/databases/create-url-attribute.md index 2fdaadac..b1b823cf 100644 --- a/docs/examples/databases/create-url-attribute.md +++ b/docs/examples/databases/create-url-attribute.md @@ -16,7 +16,8 @@ result: AttributeUrl = databases.create_url_attribute( key = '', required = False, default = 'https://example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create-varchar-attribute.md b/docs/examples/databases/create-varchar-attribute.md index b0a32221..4ebe389a 100644 --- a/docs/examples/databases/create-varchar-attribute.md +++ b/docs/examples/databases/create-varchar-attribute.md @@ -18,7 +18,8 @@ result: AttributeVarchar = databases.create_varchar_attribute( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/create.md b/docs/examples/databases/create.md index 68a5b374..6d8b67bc 100644 --- a/docs/examples/databases/create.md +++ b/docs/examples/databases/create.md @@ -13,7 +13,8 @@ databases = Databases(client) result: Database = databases.create( database_id = '', name = '', - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-big-int-attribute.md b/docs/examples/databases/update-big-int-attribute.md index 84ecb942..c6390c47 100644 --- a/docs/examples/databases/update-big-int-attribute.md +++ b/docs/examples/databases/update-big-int-attribute.md @@ -18,7 +18,8 @@ result: AttributeBigint = databases.update_big_int_attribute( default = 0, min = 0, # optional max = 1000000, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-boolean-attribute.md b/docs/examples/databases/update-boolean-attribute.md index 26ac8023..cf61992a 100644 --- a/docs/examples/databases/update-boolean-attribute.md +++ b/docs/examples/databases/update-boolean-attribute.md @@ -16,7 +16,8 @@ result: AttributeBoolean = databases.update_boolean_attribute( key = '', required = False, default = False, - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-collection.md b/docs/examples/databases/update-collection.md index 85d50789..70599c47 100644 --- a/docs/examples/databases/update-collection.md +++ b/docs/examples/databases/update-collection.md @@ -19,7 +19,8 @@ result: Collection = databases.update_collection( permissions = [Permission.read(Role.any())], # optional document_security = False, # optional enabled = False, # optional - purge = False # optional + purge = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-datetime-attribute.md b/docs/examples/databases/update-datetime-attribute.md index cf80c1d7..8c93de1f 100644 --- a/docs/examples/databases/update-datetime-attribute.md +++ b/docs/examples/databases/update-datetime-attribute.md @@ -16,7 +16,8 @@ result: AttributeDatetime = databases.update_datetime_attribute( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-email-attribute.md b/docs/examples/databases/update-email-attribute.md index 6a6b2d52..0317082f 100644 --- a/docs/examples/databases/update-email-attribute.md +++ b/docs/examples/databases/update-email-attribute.md @@ -16,7 +16,8 @@ result: AttributeEmail = databases.update_email_attribute( key = '', required = False, default = 'email@example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-enum-attribute.md b/docs/examples/databases/update-enum-attribute.md index 822f712c..fe9d8fad 100644 --- a/docs/examples/databases/update-enum-attribute.md +++ b/docs/examples/databases/update-enum-attribute.md @@ -17,7 +17,8 @@ result: AttributeEnum = databases.update_enum_attribute( elements = ["active", "inactive"], required = False, default = 'active', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-float-attribute.md b/docs/examples/databases/update-float-attribute.md index 4497a4a5..5fc8bf32 100644 --- a/docs/examples/databases/update-float-attribute.md +++ b/docs/examples/databases/update-float-attribute.md @@ -18,7 +18,8 @@ result: AttributeFloat = databases.update_float_attribute( default = 10.5, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-integer-attribute.md b/docs/examples/databases/update-integer-attribute.md index 61fe8bcc..88bc6301 100644 --- a/docs/examples/databases/update-integer-attribute.md +++ b/docs/examples/databases/update-integer-attribute.md @@ -18,7 +18,8 @@ result: AttributeInteger = databases.update_integer_attribute( default = 10, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-ip-attribute.md b/docs/examples/databases/update-ip-attribute.md index 89945ee3..822f58a9 100644 --- a/docs/examples/databases/update-ip-attribute.md +++ b/docs/examples/databases/update-ip-attribute.md @@ -16,7 +16,8 @@ result: AttributeIp = databases.update_ip_attribute( key = '', required = False, default = '192.0.2.0', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-line-attribute.md b/docs/examples/databases/update-line-attribute.md index 96d6b4e6..5bda52b3 100644 --- a/docs/examples/databases/update-line-attribute.md +++ b/docs/examples/databases/update-line-attribute.md @@ -16,7 +16,8 @@ result: AttributeLine = databases.update_line_attribute( key = '', required = False, default = [[1, 2], [3, 4], [5, 6]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-longtext-attribute.md b/docs/examples/databases/update-longtext-attribute.md index d039a6e0..38671d67 100644 --- a/docs/examples/databases/update-longtext-attribute.md +++ b/docs/examples/databases/update-longtext-attribute.md @@ -16,7 +16,8 @@ result: AttributeLongtext = databases.update_longtext_attribute( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-mediumtext-attribute.md b/docs/examples/databases/update-mediumtext-attribute.md index dc960fc6..ff2364b6 100644 --- a/docs/examples/databases/update-mediumtext-attribute.md +++ b/docs/examples/databases/update-mediumtext-attribute.md @@ -16,7 +16,8 @@ result: AttributeMediumtext = databases.update_mediumtext_attribute( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-point-attribute.md b/docs/examples/databases/update-point-attribute.md index bf6a94d8..bf91ea2d 100644 --- a/docs/examples/databases/update-point-attribute.md +++ b/docs/examples/databases/update-point-attribute.md @@ -16,7 +16,8 @@ result: AttributePoint = databases.update_point_attribute( key = '', required = False, default = [1, 2], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-polygon-attribute.md b/docs/examples/databases/update-polygon-attribute.md index 2dd1194c..2a8d94b0 100644 --- a/docs/examples/databases/update-polygon-attribute.md +++ b/docs/examples/databases/update-polygon-attribute.md @@ -16,7 +16,8 @@ result: AttributePolygon = databases.update_polygon_attribute( key = '', required = False, default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-relationship-attribute.md b/docs/examples/databases/update-relationship-attribute.md index cc328d63..c39fa987 100644 --- a/docs/examples/databases/update-relationship-attribute.md +++ b/docs/examples/databases/update-relationship-attribute.md @@ -16,7 +16,8 @@ result: AttributeRelationship = databases.update_relationship_attribute( collection_id = '', key = '', on_delete = RelationMutate.CASCADE, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-string-attribute.md b/docs/examples/databases/update-string-attribute.md index bfe14df1..2989a4cc 100644 --- a/docs/examples/databases/update-string-attribute.md +++ b/docs/examples/databases/update-string-attribute.md @@ -17,7 +17,8 @@ result: AttributeString = databases.update_string_attribute( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-text-attribute.md b/docs/examples/databases/update-text-attribute.md index 01f66d13..c28dd7f0 100644 --- a/docs/examples/databases/update-text-attribute.md +++ b/docs/examples/databases/update-text-attribute.md @@ -16,7 +16,8 @@ result: AttributeText = databases.update_text_attribute( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-url-attribute.md b/docs/examples/databases/update-url-attribute.md index 9f05a4a4..4f772b97 100644 --- a/docs/examples/databases/update-url-attribute.md +++ b/docs/examples/databases/update-url-attribute.md @@ -16,7 +16,8 @@ result: AttributeUrl = databases.update_url_attribute( key = '', required = False, default = 'https://example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update-varchar-attribute.md b/docs/examples/databases/update-varchar-attribute.md index b844a712..5fb7a73f 100644 --- a/docs/examples/databases/update-varchar-attribute.md +++ b/docs/examples/databases/update-varchar-attribute.md @@ -17,7 +17,8 @@ result: AttributeVarchar = databases.update_varchar_attribute( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/databases/update.md b/docs/examples/databases/update.md index 73458d4f..ae7b33eb 100644 --- a/docs/examples/databases/update.md +++ b/docs/examples/databases/update.md @@ -13,7 +13,8 @@ databases = Databases(client) result: Database = databases.update( database_id = '', name = '', # optional - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/create-collection.md b/docs/examples/documentsdb/create-collection.md index b717460f..da77bced 100644 --- a/docs/examples/documentsdb/create-collection.md +++ b/docs/examples/documentsdb/create-collection.md @@ -20,7 +20,8 @@ result: Collection = documents_db.create_collection( document_security = False, # optional enabled = False, # optional attributes = [], # optional - indexes = [] # optional + indexes = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/create-index.md b/docs/examples/documentsdb/create-index.md index 51eea435..4e9361cd 100644 --- a/docs/examples/documentsdb/create-index.md +++ b/docs/examples/documentsdb/create-index.md @@ -19,7 +19,8 @@ result: Index = documents_db.create_index( type = DocumentsDBIndexType.KEY, attributes = ["username"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/create-query.md b/docs/examples/documentsdb/create-query.md new file mode 100644 index 00000000..80f1298c --- /dev/null +++ b/docs/examples/documentsdb/create-query.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.models import DocumentList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +documents_db = DocumentsDB(client) + +result: DocumentList = documents_db.create_query( + database_id = '', + collection_id = '', + queries = [], # optional + transaction_id = '', # optional + total = False, # optional + ttl = 0 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/documentsdb/create.md b/docs/examples/documentsdb/create.md index b8da9d4b..9d424527 100644 --- a/docs/examples/documentsdb/create.md +++ b/docs/examples/documentsdb/create.md @@ -14,6 +14,7 @@ result: Database = documents_db.create( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/documentsdb/update-collection.md b/docs/examples/documentsdb/update-collection.md index 955fb28f..0c3d010f 100644 --- a/docs/examples/documentsdb/update-collection.md +++ b/docs/examples/documentsdb/update-collection.md @@ -19,7 +19,8 @@ result: Collection = documents_db.update_collection( permissions = [Permission.read(Role.any())], # optional document_security = False, # optional enabled = False, # optional - purge = False # optional + purge = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/documentsdb/update.md b/docs/examples/documentsdb/update.md index 03d7e861..3cd8bea3 100644 --- a/docs/examples/documentsdb/update.md +++ b/docs/examples/documentsdb/update.md @@ -14,6 +14,7 @@ result: Database = documents_db.update( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/domains/list-records.md b/docs/examples/domains/list-records.md index 9d40d255..94275a49 100644 --- a/docs/examples/domains/list-records.md +++ b/docs/examples/domains/list-records.md @@ -12,7 +12,8 @@ domains = Domains(client) result: DnsRecordsList = domains.list_records( domain_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/domains/list.md b/docs/examples/domains/list.md index f0d15a75..602fa974 100644 --- a/docs/examples/domains/list.md +++ b/docs/examples/domains/list.md @@ -12,7 +12,8 @@ domains = Domains(client) result: DomainsList = domains.list( queries = [], # optional - search = '' # optional + search = '', # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/create-variable.md b/docs/examples/functions/create-variable.md index da323f97..6f596772 100644 --- a/docs/examples/functions/create-variable.md +++ b/docs/examples/functions/create-variable.md @@ -15,7 +15,8 @@ result: Variable = functions.create_variable( variable_id = '', key = '', value = '', - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/create.md b/docs/examples/functions/create.md index 46652c4a..4cc28c82 100644 --- a/docs/examples/functions/create.md +++ b/docs/examples/functions/create.md @@ -35,7 +35,8 @@ result: Function = functions.create( build_specification = 's-1vcpu-512mb', # optional runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional - interval = 0 # optional + interval = 0, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/update-variable.md b/docs/examples/functions/update-variable.md index 6aa2f98c..2368f0d2 100644 --- a/docs/examples/functions/update-variable.md +++ b/docs/examples/functions/update-variable.md @@ -15,7 +15,8 @@ result: Variable = functions.update_variable( variable_id = '', key = '', # optional value = '', # optional - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/update.md b/docs/examples/functions/update.md index 425158bb..2efde2fb 100644 --- a/docs/examples/functions/update.md +++ b/docs/examples/functions/update.md @@ -36,6 +36,7 @@ result: Function = functions.update( runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional interval = 0, # optional + description = '', # optional email_enabled = False # optional ) diff --git a/docs/examples/messaging/create-email.md b/docs/examples/messaging/create-email.md index c8f278dd..6cf8f14d 100644 --- a/docs/examples/messaging/create-email.md +++ b/docs/examples/messaging/create-email.md @@ -24,7 +24,8 @@ result: Message = messaging.create_email( html = False, # optional scheduled_at = '2020-10-15T06:38:00.000+00:00', # optional reply_to_email = 'email@example.com', # optional - reply_to_name = '' # optional + reply_to_name = '', # optional + emails = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/messaging/create-topic.md b/docs/examples/messaging/create-topic.md index 93388456..6e45f73a 100644 --- a/docs/examples/messaging/create-topic.md +++ b/docs/examples/messaging/create-topic.md @@ -15,7 +15,8 @@ result: Topic = messaging.create_topic( name = '', subscribe = ["any"], # optional qos = 0, # optional - expiry = 0 # optional + expiry = 0, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/messaging/update-email.md b/docs/examples/messaging/update-email.md index bf8c5ba8..38a59902 100644 --- a/docs/examples/messaging/update-email.md +++ b/docs/examples/messaging/update-email.md @@ -24,7 +24,8 @@ result: Message = messaging.update_email( scheduled_at = '2020-10-15T06:38:00.000+00:00', # optional attachments = [], # optional reply_to_email = 'email@example.com', # optional - reply_to_name = '' # optional + reply_to_name = '', # optional + emails = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/messaging/update-topic.md b/docs/examples/messaging/update-topic.md index 4870903a..65213a5c 100644 --- a/docs/examples/messaging/update-topic.md +++ b/docs/examples/messaging/update-topic.md @@ -15,7 +15,8 @@ result: Topic = messaging.update_topic( name = '', # optional subscribe = ["any"], # optional qos = 0, # optional - expiry = 0 # optional + expiry = 0, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/mongo/list-backup-policies.md b/docs/examples/mongo/list-backup-policies.md index c023cc1d..e79ffd33 100644 --- a/docs/examples/mongo/list-backup-policies.md +++ b/docs/examples/mongo/list-backup-policies.md @@ -12,7 +12,8 @@ mongo = Mongo(client) result: BackupPolicyList = mongo.list_backup_policies( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mongo/list-backups.md b/docs/examples/mongo/list-backups.md index bf896520..86b6bc3f 100644 --- a/docs/examples/mongo/list-backups.md +++ b/docs/examples/mongo/list-backups.md @@ -12,7 +12,8 @@ mongo = Mongo(client) result: DedicatedDatabaseBackupList = mongo.list_backups( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mongo/list.md b/docs/examples/mongo/list.md index b956cebe..c31a18c3 100644 --- a/docs/examples/mongo/list.md +++ b/docs/examples/mongo/list.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key mongo = Mongo(client) result: DedicatedDatabaseList = mongo.list( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/list-backup-policies.md b/docs/examples/mysql/list-backup-policies.md index 0b5328d4..0297517f 100644 --- a/docs/examples/mysql/list-backup-policies.md +++ b/docs/examples/mysql/list-backup-policies.md @@ -12,7 +12,8 @@ mysql = Mysql(client) result: BackupPolicyList = mysql.list_backup_policies( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/list-backups.md b/docs/examples/mysql/list-backups.md index 5a533951..0dc15e5b 100644 --- a/docs/examples/mysql/list-backups.md +++ b/docs/examples/mysql/list-backups.md @@ -12,7 +12,8 @@ mysql = Mysql(client) result: DedicatedDatabaseBackupList = mysql.list_backups( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/list.md b/docs/examples/mysql/list.md index 865e474f..b5aba207 100644 --- a/docs/examples/mysql/list.md +++ b/docs/examples/mysql/list.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key mysql = Mysql(client) result: DedicatedDatabaseList = mysql.list( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/organization/update-project-key.md b/docs/examples/organization/update-project-key.md index 7f01ddf5..ea537952 100644 --- a/docs/examples/organization/update-project-key.md +++ b/docs/examples/organization/update-project-key.md @@ -16,7 +16,8 @@ result: Key = organization.update_project_key( key_id = '', name = '', scopes = [ProjectKeyScopes.USERS_READ], - expire = '2020-10-15T06:38:00.000+00:00' # optional + expire = '2020-10-15T06:38:00.000+00:00', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/postgresql/list-backup-policies.md b/docs/examples/postgresql/list-backup-policies.md index c9000c59..2c10f860 100644 --- a/docs/examples/postgresql/list-backup-policies.md +++ b/docs/examples/postgresql/list-backup-policies.md @@ -12,7 +12,8 @@ postgresql = Postgresql(client) result: BackupPolicyList = postgresql.list_backup_policies( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/postgresql/list-backups.md b/docs/examples/postgresql/list-backups.md index 85622373..ea05800f 100644 --- a/docs/examples/postgresql/list-backups.md +++ b/docs/examples/postgresql/list-backups.md @@ -12,7 +12,8 @@ postgresql = Postgresql(client) result: DedicatedDatabaseBackupList = postgresql.list_backups( database_id = '', - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/postgresql/list.md b/docs/examples/postgresql/list.md index fae83f96..41d53428 100644 --- a/docs/examples/postgresql/list.md +++ b/docs/examples/postgresql/list.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key postgresql = Postgresql(client) result: DedicatedDatabaseList = postgresql.list( - queries = [] # optional + queries = [], # optional + total = False # optional ) print(result.model_dump()) diff --git a/docs/examples/project/create-variable.md b/docs/examples/project/create-variable.md index 668e3548..4b73077c 100644 --- a/docs/examples/project/create-variable.md +++ b/docs/examples/project/create-variable.md @@ -14,7 +14,8 @@ result: Variable = project.create_variable( variable_id = '', key = '', value = '', - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-key.md b/docs/examples/project/update-key.md index a8aa7ccf..12faf365 100644 --- a/docs/examples/project/update-key.md +++ b/docs/examples/project/update-key.md @@ -15,7 +15,8 @@ result: Key = project.update_key( key_id = '', name = '', scopes = [ProjectKeyScopes.USERS_READ], - expire = '2020-10-15T06:38:00.000+00:00' # optional + expire = '2020-10-15T06:38:00.000+00:00', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-password-dictionary-policy.md b/docs/examples/project/update-password-dictionary-policy.md index 93e652be..53a8d71e 100644 --- a/docs/examples/project/update-password-dictionary-policy.md +++ b/docs/examples/project/update-password-dictionary-policy.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key project = Project(client) result: ProjectModel = project.update_password_dictionary_policy( - enabled = False + enabled = False, + deny_list = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-password-personal-data-policy.md b/docs/examples/project/update-password-personal-data-policy.md index 507bdac4..d9587b6a 100644 --- a/docs/examples/project/update-password-personal-data-policy.md +++ b/docs/examples/project/update-password-personal-data-policy.md @@ -11,7 +11,8 @@ client.set_key('') # Your secret API key project = Project(client) result: ProjectModel = project.update_password_personal_data_policy( - enabled = False + enabled = False, + deny_list = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/project/update-variable.md b/docs/examples/project/update-variable.md index 3f4dac0f..abe4ae74 100644 --- a/docs/examples/project/update-variable.md +++ b/docs/examples/project/update-variable.md @@ -14,7 +14,8 @@ result: Variable = project.update_variable( variable_id = '', key = '', # optional value = '', # optional - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/create-variable.md b/docs/examples/sites/create-variable.md index ca6d9e57..7174c1d1 100644 --- a/docs/examples/sites/create-variable.md +++ b/docs/examples/sites/create-variable.md @@ -15,7 +15,8 @@ result: Variable = sites.create_variable( variable_id = '', key = '', value = '', - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/create.md b/docs/examples/sites/create.md index 4d8e03bc..c993e6fb 100644 --- a/docs/examples/sites/create.md +++ b/docs/examples/sites/create.md @@ -38,7 +38,8 @@ result: Site = sites.create( build_specification = 's-1vcpu-512mb', # optional runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional - scopes = [ProjectKeyScopes.PROJECT_READ] # optional + scopes = [ProjectKeyScopes.PROJECT_READ], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/update-variable.md b/docs/examples/sites/update-variable.md index 637d2220..9293b47d 100644 --- a/docs/examples/sites/update-variable.md +++ b/docs/examples/sites/update-variable.md @@ -15,7 +15,8 @@ result: Variable = sites.update_variable( variable_id = '', key = '', # optional value = '', # optional - secret = False # optional + secret = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/update.md b/docs/examples/sites/update.md index 59ac85ce..f6f5bbdd 100644 --- a/docs/examples/sites/update.md +++ b/docs/examples/sites/update.md @@ -38,7 +38,8 @@ result: Site = sites.update( build_specification = 's-1vcpu-512mb', # optional runtime_specification = 's-1vcpu-512mb', # optional deployment_retention = 0, # optional - scopes = [ProjectKeyScopes.PROJECT_READ] # optional + scopes = [ProjectKeyScopes.PROJECT_READ], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/storage/create-bucket.md b/docs/examples/storage/create-bucket.md index 76ec2f0e..5800deee 100644 --- a/docs/examples/storage/create-bucket.md +++ b/docs/examples/storage/create-bucket.md @@ -24,7 +24,9 @@ result: Bucket = storage.create_bucket( compression = Compression.NONE, # optional encryption = False, # optional antivirus = False, # optional - transformations = False # optional + transformations = False, # optional + description = '', # optional + strip_metadata = False # optional ) print(result.model_dump()) diff --git a/docs/examples/storage/update-bucket.md b/docs/examples/storage/update-bucket.md index c9222517..b7906a36 100644 --- a/docs/examples/storage/update-bucket.md +++ b/docs/examples/storage/update-bucket.md @@ -24,7 +24,9 @@ result: Bucket = storage.update_bucket( compression = Compression.NONE, # optional encryption = False, # optional antivirus = False, # optional - transformations = False # optional + transformations = False, # optional + description = '', # optional + strip_metadata = False # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-big-int-column.md b/docs/examples/tablesdb/create-big-int-column.md index cff66960..2ebb4287 100644 --- a/docs/examples/tablesdb/create-big-int-column.md +++ b/docs/examples/tablesdb/create-big-int-column.md @@ -18,7 +18,8 @@ result: ColumnBigint = tables_db.create_big_int_column( min = 0, # optional max = 1000000, # optional default = 0, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-boolean-column.md b/docs/examples/tablesdb/create-boolean-column.md index b325095d..c5b62657 100644 --- a/docs/examples/tablesdb/create-boolean-column.md +++ b/docs/examples/tablesdb/create-boolean-column.md @@ -16,7 +16,8 @@ result: ColumnBoolean = tables_db.create_boolean_column( key = '', required = False, default = False, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-datetime-column.md b/docs/examples/tablesdb/create-datetime-column.md index a4a565d0..30463a6b 100644 --- a/docs/examples/tablesdb/create-datetime-column.md +++ b/docs/examples/tablesdb/create-datetime-column.md @@ -16,7 +16,8 @@ result: ColumnDatetime = tables_db.create_datetime_column( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-email-column.md b/docs/examples/tablesdb/create-email-column.md index 7315689c..cae71254 100644 --- a/docs/examples/tablesdb/create-email-column.md +++ b/docs/examples/tablesdb/create-email-column.md @@ -16,7 +16,8 @@ result: ColumnEmail = tables_db.create_email_column( key = '', required = False, default = 'email@example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-enum-column.md b/docs/examples/tablesdb/create-enum-column.md index 3e4cb1b9..645aa8fb 100644 --- a/docs/examples/tablesdb/create-enum-column.md +++ b/docs/examples/tablesdb/create-enum-column.md @@ -17,7 +17,8 @@ result: ColumnEnum = tables_db.create_enum_column( elements = ["active", "inactive"], required = False, default = 'active', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-float-column.md b/docs/examples/tablesdb/create-float-column.md index 0edd6a26..b57def0a 100644 --- a/docs/examples/tablesdb/create-float-column.md +++ b/docs/examples/tablesdb/create-float-column.md @@ -18,7 +18,8 @@ result: ColumnFloat = tables_db.create_float_column( min = 0, # optional max = 100, # optional default = 10.5, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-index.md b/docs/examples/tablesdb/create-index.md index 8ecf506d..f953466f 100644 --- a/docs/examples/tablesdb/create-index.md +++ b/docs/examples/tablesdb/create-index.md @@ -19,7 +19,8 @@ result: ColumnIndex = tables_db.create_index( type = TablesDBIndexType.KEY, columns = ["username"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-integer-column.md b/docs/examples/tablesdb/create-integer-column.md index bbe76889..8b989252 100644 --- a/docs/examples/tablesdb/create-integer-column.md +++ b/docs/examples/tablesdb/create-integer-column.md @@ -18,7 +18,8 @@ result: ColumnInteger = tables_db.create_integer_column( min = 0, # optional max = 100, # optional default = 10, # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-ip-column.md b/docs/examples/tablesdb/create-ip-column.md index a0a47e67..9c510780 100644 --- a/docs/examples/tablesdb/create-ip-column.md +++ b/docs/examples/tablesdb/create-ip-column.md @@ -16,7 +16,8 @@ result: ColumnIp = tables_db.create_ip_column( key = '', required = False, default = '192.0.2.0', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-line-column.md b/docs/examples/tablesdb/create-line-column.md index 2fc64c4f..e32562f8 100644 --- a/docs/examples/tablesdb/create-line-column.md +++ b/docs/examples/tablesdb/create-line-column.md @@ -15,7 +15,8 @@ result: ColumnLine = tables_db.create_line_column( table_id = '', key = '', required = False, - default = [[1, 2], [3, 4], [5, 6]] # optional + default = [[1, 2], [3, 4], [5, 6]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-longtext-column.md b/docs/examples/tablesdb/create-longtext-column.md index 1438ff8a..57ed86db 100644 --- a/docs/examples/tablesdb/create-longtext-column.md +++ b/docs/examples/tablesdb/create-longtext-column.md @@ -17,7 +17,8 @@ result: ColumnLongtext = tables_db.create_longtext_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-mediumtext-column.md b/docs/examples/tablesdb/create-mediumtext-column.md index 6729b329..261d801a 100644 --- a/docs/examples/tablesdb/create-mediumtext-column.md +++ b/docs/examples/tablesdb/create-mediumtext-column.md @@ -17,7 +17,8 @@ result: ColumnMediumtext = tables_db.create_mediumtext_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-point-column.md b/docs/examples/tablesdb/create-point-column.md index 17c1e447..e1fdb9e7 100644 --- a/docs/examples/tablesdb/create-point-column.md +++ b/docs/examples/tablesdb/create-point-column.md @@ -15,7 +15,8 @@ result: ColumnPoint = tables_db.create_point_column( table_id = '', key = '', required = False, - default = [1, 2] # optional + default = [1, 2], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-polygon-column.md b/docs/examples/tablesdb/create-polygon-column.md index 86fca304..08c2c1af 100644 --- a/docs/examples/tablesdb/create-polygon-column.md +++ b/docs/examples/tablesdb/create-polygon-column.md @@ -15,7 +15,8 @@ result: ColumnPolygon = tables_db.create_polygon_column( table_id = '', key = '', required = False, - default = [[[1, 2], [3, 4], [5, 6], [1, 2]]] # optional + default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-query.md b/docs/examples/tablesdb/create-query.md new file mode 100644 index 00000000..cdc0ddd5 --- /dev/null +++ b/docs/examples/tablesdb/create-query.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import RowList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_session('') # The user session to authenticate with + +tables_db = TablesDB(client) + +result: RowList = tables_db.create_query( + database_id = '', + table_id = '', + queries = [], # optional + transaction_id = '', # optional + total = False, # optional + ttl = 0 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/tablesdb/create-relationship-column.md b/docs/examples/tablesdb/create-relationship-column.md index 32fc4ba1..1287da28 100644 --- a/docs/examples/tablesdb/create-relationship-column.md +++ b/docs/examples/tablesdb/create-relationship-column.md @@ -20,7 +20,8 @@ result: ColumnRelationship = tables_db.create_relationship_column( two_way = False, # optional key = '', # optional two_way_key = '', # optional - on_delete = RelationMutate.CASCADE # optional + on_delete = RelationMutate.CASCADE, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-string-column.md b/docs/examples/tablesdb/create-string-column.md index 85decfde..27a96af2 100644 --- a/docs/examples/tablesdb/create-string-column.md +++ b/docs/examples/tablesdb/create-string-column.md @@ -18,7 +18,8 @@ result: ColumnString = tables_db.create_string_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-table.md b/docs/examples/tablesdb/create-table.md index 029fb281..5ddf3b99 100644 --- a/docs/examples/tablesdb/create-table.md +++ b/docs/examples/tablesdb/create-table.md @@ -20,7 +20,8 @@ result: Table = tables_db.create_table( row_security = False, # optional enabled = False, # optional columns = [], # optional - indexes = [] # optional + indexes = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-text-column.md b/docs/examples/tablesdb/create-text-column.md index f3c8df3d..f7c63339 100644 --- a/docs/examples/tablesdb/create-text-column.md +++ b/docs/examples/tablesdb/create-text-column.md @@ -17,7 +17,8 @@ result: ColumnText = tables_db.create_text_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-url-column.md b/docs/examples/tablesdb/create-url-column.md index 4bfb858f..ae9a5ecc 100644 --- a/docs/examples/tablesdb/create-url-column.md +++ b/docs/examples/tablesdb/create-url-column.md @@ -16,7 +16,8 @@ result: ColumnUrl = tables_db.create_url_column( key = '', required = False, default = 'https://example.com', # optional - array = False # optional + array = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create-varchar-column.md b/docs/examples/tablesdb/create-varchar-column.md index 2fb6338c..d181df49 100644 --- a/docs/examples/tablesdb/create-varchar-column.md +++ b/docs/examples/tablesdb/create-varchar-column.md @@ -18,7 +18,8 @@ result: ColumnVarchar = tables_db.create_varchar_column( required = False, default = 'Hello World', # optional array = False, # optional - encrypt = False # optional + encrypt = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/create.md b/docs/examples/tablesdb/create.md index ca668875..ff89d119 100644 --- a/docs/examples/tablesdb/create.md +++ b/docs/examples/tablesdb/create.md @@ -14,6 +14,7 @@ result: Database = tables_db.create( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/tablesdb/update-big-int-column.md b/docs/examples/tablesdb/update-big-int-column.md index f4e6e90d..76343d4c 100644 --- a/docs/examples/tablesdb/update-big-int-column.md +++ b/docs/examples/tablesdb/update-big-int-column.md @@ -18,7 +18,8 @@ result: ColumnBigint = tables_db.update_big_int_column( default = 0, min = 0, # optional max = 1000000, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-boolean-column.md b/docs/examples/tablesdb/update-boolean-column.md index 9352362c..8b2f0be8 100644 --- a/docs/examples/tablesdb/update-boolean-column.md +++ b/docs/examples/tablesdb/update-boolean-column.md @@ -16,7 +16,8 @@ result: ColumnBoolean = tables_db.update_boolean_column( key = '', required = False, default = False, - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-datetime-column.md b/docs/examples/tablesdb/update-datetime-column.md index 70cd5754..10f3146e 100644 --- a/docs/examples/tablesdb/update-datetime-column.md +++ b/docs/examples/tablesdb/update-datetime-column.md @@ -16,7 +16,8 @@ result: ColumnDatetime = tables_db.update_datetime_column( key = '', required = False, default = '2020-10-15T06:38:00.000+00:00', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-email-column.md b/docs/examples/tablesdb/update-email-column.md index 94036baf..272d0c00 100644 --- a/docs/examples/tablesdb/update-email-column.md +++ b/docs/examples/tablesdb/update-email-column.md @@ -16,7 +16,8 @@ result: ColumnEmail = tables_db.update_email_column( key = '', required = False, default = 'email@example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-enum-column.md b/docs/examples/tablesdb/update-enum-column.md index 0ac9ac91..41ca8bcf 100644 --- a/docs/examples/tablesdb/update-enum-column.md +++ b/docs/examples/tablesdb/update-enum-column.md @@ -17,7 +17,8 @@ result: ColumnEnum = tables_db.update_enum_column( elements = ["active", "inactive"], required = False, default = 'active', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-float-column.md b/docs/examples/tablesdb/update-float-column.md index 08e39be5..e851722f 100644 --- a/docs/examples/tablesdb/update-float-column.md +++ b/docs/examples/tablesdb/update-float-column.md @@ -18,7 +18,8 @@ result: ColumnFloat = tables_db.update_float_column( default = 10.5, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-integer-column.md b/docs/examples/tablesdb/update-integer-column.md index cd6831fc..6f7c7916 100644 --- a/docs/examples/tablesdb/update-integer-column.md +++ b/docs/examples/tablesdb/update-integer-column.md @@ -18,7 +18,8 @@ result: ColumnInteger = tables_db.update_integer_column( default = 10, min = 0, # optional max = 100, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-ip-column.md b/docs/examples/tablesdb/update-ip-column.md index c3570fb1..f94638c3 100644 --- a/docs/examples/tablesdb/update-ip-column.md +++ b/docs/examples/tablesdb/update-ip-column.md @@ -16,7 +16,8 @@ result: ColumnIp = tables_db.update_ip_column( key = '', required = False, default = '192.0.2.0', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-line-column.md b/docs/examples/tablesdb/update-line-column.md index 052a12c0..8ef8353a 100644 --- a/docs/examples/tablesdb/update-line-column.md +++ b/docs/examples/tablesdb/update-line-column.md @@ -16,7 +16,8 @@ result: ColumnLine = tables_db.update_line_column( key = '', required = False, default = [[1, 2], [3, 4], [5, 6]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-longtext-column.md b/docs/examples/tablesdb/update-longtext-column.md index 8e44bd03..154e7b36 100644 --- a/docs/examples/tablesdb/update-longtext-column.md +++ b/docs/examples/tablesdb/update-longtext-column.md @@ -16,7 +16,8 @@ result: ColumnLongtext = tables_db.update_longtext_column( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-mediumtext-column.md b/docs/examples/tablesdb/update-mediumtext-column.md index 2412dc84..2c92bd4f 100644 --- a/docs/examples/tablesdb/update-mediumtext-column.md +++ b/docs/examples/tablesdb/update-mediumtext-column.md @@ -16,7 +16,8 @@ result: ColumnMediumtext = tables_db.update_mediumtext_column( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-point-column.md b/docs/examples/tablesdb/update-point-column.md index 5368394e..a232c2e1 100644 --- a/docs/examples/tablesdb/update-point-column.md +++ b/docs/examples/tablesdb/update-point-column.md @@ -16,7 +16,8 @@ result: ColumnPoint = tables_db.update_point_column( key = '', required = False, default = [1, 2], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-polygon-column.md b/docs/examples/tablesdb/update-polygon-column.md index b7af58ae..1237ffe1 100644 --- a/docs/examples/tablesdb/update-polygon-column.md +++ b/docs/examples/tablesdb/update-polygon-column.md @@ -16,7 +16,8 @@ result: ColumnPolygon = tables_db.update_polygon_column( key = '', required = False, default = [[[1, 2], [3, 4], [5, 6], [1, 2]]], # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-relationship-column.md b/docs/examples/tablesdb/update-relationship-column.md index 67fb6063..3bc94157 100644 --- a/docs/examples/tablesdb/update-relationship-column.md +++ b/docs/examples/tablesdb/update-relationship-column.md @@ -16,7 +16,8 @@ result: ColumnRelationship = tables_db.update_relationship_column( table_id = '', key = '', on_delete = RelationMutate.CASCADE, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-string-column.md b/docs/examples/tablesdb/update-string-column.md index f3375237..3dddf79b 100644 --- a/docs/examples/tablesdb/update-string-column.md +++ b/docs/examples/tablesdb/update-string-column.md @@ -17,7 +17,8 @@ result: ColumnString = tables_db.update_string_column( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-table.md b/docs/examples/tablesdb/update-table.md index ee6bba02..e1d8ff90 100644 --- a/docs/examples/tablesdb/update-table.md +++ b/docs/examples/tablesdb/update-table.md @@ -19,7 +19,8 @@ result: Table = tables_db.update_table( permissions = [Permission.read(Role.any())], # optional row_security = False, # optional enabled = False, # optional - purge = False # optional + purge = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-text-column.md b/docs/examples/tablesdb/update-text-column.md index 21e03944..b7999d41 100644 --- a/docs/examples/tablesdb/update-text-column.md +++ b/docs/examples/tablesdb/update-text-column.md @@ -16,7 +16,8 @@ result: ColumnText = tables_db.update_text_column( key = '', required = False, default = 'Hello World', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-url-column.md b/docs/examples/tablesdb/update-url-column.md index 2553957d..c4fcff52 100644 --- a/docs/examples/tablesdb/update-url-column.md +++ b/docs/examples/tablesdb/update-url-column.md @@ -16,7 +16,8 @@ result: ColumnUrl = tables_db.update_url_column( key = '', required = False, default = 'https://example.com', - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update-varchar-column.md b/docs/examples/tablesdb/update-varchar-column.md index 3f03b42f..88133183 100644 --- a/docs/examples/tablesdb/update-varchar-column.md +++ b/docs/examples/tablesdb/update-varchar-column.md @@ -17,7 +17,8 @@ result: ColumnVarchar = tables_db.update_varchar_column( required = False, default = 'Hello World', size = 1, # optional - new_key = '' # optional + new_key = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/update.md b/docs/examples/tablesdb/update.md index 7fc0c930..7b8b2225 100644 --- a/docs/examples/tablesdb/update.md +++ b/docs/examples/tablesdb/update.md @@ -14,6 +14,7 @@ result: Database = tables_db.update( database_id = '', name = '', # optional enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/vectorsdb/create-collection.md b/docs/examples/vectorsdb/create-collection.md index 6dcdeb24..6011be07 100644 --- a/docs/examples/vectorsdb/create-collection.md +++ b/docs/examples/vectorsdb/create-collection.md @@ -19,7 +19,8 @@ result: VectorsdbCollection = vectors_db.create_collection( dimension = 4, permissions = [Permission.read(Role.any())], # optional document_security = False, # optional - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/vectorsdb/create-index.md b/docs/examples/vectorsdb/create-index.md index 71e650a6..a1fcd6f7 100644 --- a/docs/examples/vectorsdb/create-index.md +++ b/docs/examples/vectorsdb/create-index.md @@ -19,7 +19,8 @@ result: Index = vectors_db.create_index( type = VectorsDBIndexType.HNSW_EUCLIDEAN, attributes = ["embeddings"], orders = [OrderBy.ASC], # optional - lengths = [] # optional + lengths = [], # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/vectorsdb/create.md b/docs/examples/vectorsdb/create.md index a77a3a38..b133cb18 100644 --- a/docs/examples/vectorsdb/create.md +++ b/docs/examples/vectorsdb/create.md @@ -14,6 +14,7 @@ result: Database = vectors_db.create( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/vectorsdb/update-collection.md b/docs/examples/vectorsdb/update-collection.md index 7093c0b2..e4c925e1 100644 --- a/docs/examples/vectorsdb/update-collection.md +++ b/docs/examples/vectorsdb/update-collection.md @@ -19,7 +19,8 @@ result: VectorsdbCollection = vectors_db.update_collection( dimension = 4, # optional permissions = [Permission.read(Role.any())], # optional document_security = False, # optional - enabled = False # optional + enabled = False, # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/vectorsdb/update.md b/docs/examples/vectorsdb/update.md index 23ae9429..25ee897b 100644 --- a/docs/examples/vectorsdb/update.md +++ b/docs/examples/vectorsdb/update.md @@ -14,6 +14,7 @@ result: Database = vectors_db.update( database_id = '', name = '', enabled = False, # optional + description = '', # optional specification = 'serverless', # optional replicas = 0, # optional sync_mode = 'async' # optional diff --git a/docs/examples/webhooks/create.md b/docs/examples/webhooks/create.md index 1585ef2b..fddd87eb 100644 --- a/docs/examples/webhooks/create.md +++ b/docs/examples/webhooks/create.md @@ -19,7 +19,8 @@ result: Webhook = webhooks.create( tls = False, # optional auth_username = '', # optional auth_password = 'password', # optional - secret = '' # optional + secret = '', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/webhooks/update.md b/docs/examples/webhooks/update.md index 647e8618..7fa2311e 100644 --- a/docs/examples/webhooks/update.md +++ b/docs/examples/webhooks/update.md @@ -18,7 +18,8 @@ result: Webhook = webhooks.update( enabled = False, # optional tls = False, # optional auth_username = '', # optional - auth_password = 'password' # optional + auth_password = 'password', # optional + description = '' # optional ) print(result.model_dump()) diff --git a/pyproject.toml b/pyproject.toml index f9897243..b75f237c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "appwrite" -version = "25.0.0" +version = "26.0.0" description = "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API" readme = "README.md" requires-python = ">=3.9" @@ -30,6 +30,7 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] [project.optional-dependencies] diff --git a/setup.py b/setup.py index dc433792..88f54765 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='appwrite', packages=setuptools.find_packages(), - version='25.0.0', + version='26.0.0', license='BSD-3-Clause', description='Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API', long_description=long_description, @@ -18,7 +18,7 @@ maintainer='Appwrite Team', maintainer_email='team@appwrite.io', url='https://appwrite.io/support', - download_url='https://github.com/appwrite/sdk-for-python/archive/25.0.0.tar.gz', + download_url='https://github.com/appwrite/sdk-for-python/archive/26.0.0.tar.gz', install_requires=[ 'requests', 'pydantic>=2,<3', @@ -35,5 +35,6 @@ 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', ], ) diff --git a/test/services/test_account.py b/test/services/test_account.py index 04fb136f..e37d5b47 100644 --- a/test/services/test_account.py +++ b/test/services/test_account.py @@ -261,6 +261,22 @@ def test_delete_identity(self, m): ) self.assertEqual(response, data) + @requests_mock.Mocker() + def test_list_memberships(self, m): + data = { + "total": 5.0, + "memberships": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.account.list_memberships() + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_update_mfa(self, m): data = { diff --git a/test/services/test_databases.py b/test/services/test_databases.py index 733f8f08..14d81297 100644 --- a/test/services/test_databases.py +++ b/test/services/test_databases.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -175,6 +176,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -197,6 +199,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -256,6 +259,7 @@ def test_create_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -286,6 +290,7 @@ def test_get_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -315,6 +320,7 @@ def test_update_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -374,6 +380,7 @@ def test_list_attributes(self, m): def test_create_big_int_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -400,6 +407,7 @@ def test_create_big_int_attribute(self, m): def test_update_big_int_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -427,6 +435,7 @@ def test_update_big_int_attribute(self, m): def test_create_boolean_attribute(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -453,6 +462,7 @@ def test_create_boolean_attribute(self, m): def test_update_boolean_attribute(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -480,6 +490,7 @@ def test_update_boolean_attribute(self, m): def test_create_datetime_attribute(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -507,6 +518,7 @@ def test_create_datetime_attribute(self, m): def test_update_datetime_attribute(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -535,6 +547,7 @@ def test_update_datetime_attribute(self, m): def test_create_email_attribute(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -562,6 +575,7 @@ def test_create_email_attribute(self, m): def test_update_email_attribute(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -590,6 +604,7 @@ def test_update_email_attribute(self, m): def test_create_enum_attribute(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -619,6 +634,7 @@ def test_create_enum_attribute(self, m): def test_update_enum_attribute(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -649,6 +665,7 @@ def test_update_enum_attribute(self, m): def test_create_float_attribute(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -675,6 +692,7 @@ def test_create_float_attribute(self, m): def test_update_float_attribute(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -702,6 +720,7 @@ def test_update_float_attribute(self, m): def test_create_integer_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -728,6 +747,7 @@ def test_create_integer_attribute(self, m): def test_update_integer_attribute(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -755,6 +775,7 @@ def test_update_integer_attribute(self, m): def test_create_ip_attribute(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -782,6 +803,7 @@ def test_create_ip_attribute(self, m): def test_update_ip_attribute(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -810,6 +832,7 @@ def test_update_ip_attribute(self, m): def test_create_line_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -836,6 +859,7 @@ def test_create_line_attribute(self, m): def test_update_line_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -862,6 +886,7 @@ def test_update_line_attribute(self, m): def test_create_longtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -888,6 +913,7 @@ def test_create_longtext_attribute(self, m): def test_update_longtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -915,6 +941,7 @@ def test_update_longtext_attribute(self, m): def test_create_mediumtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -941,6 +968,7 @@ def test_create_mediumtext_attribute(self, m): def test_update_mediumtext_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -968,6 +996,7 @@ def test_update_mediumtext_attribute(self, m): def test_create_point_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -994,6 +1023,7 @@ def test_create_point_attribute(self, m): def test_update_point_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1020,6 +1050,7 @@ def test_update_point_attribute(self, m): def test_create_polygon_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1046,6 +1077,7 @@ def test_create_polygon_attribute(self, m): def test_update_polygon_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1072,6 +1104,7 @@ def test_update_polygon_attribute(self, m): def test_create_relationship_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1104,6 +1137,7 @@ def test_create_relationship_attribute(self, m): def test_update_relationship_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1135,6 +1169,7 @@ def test_update_relationship_attribute(self, m): def test_create_string_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1163,6 +1198,7 @@ def test_create_string_attribute(self, m): def test_update_string_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1191,6 +1227,7 @@ def test_update_string_attribute(self, m): def test_create_text_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1217,6 +1254,7 @@ def test_create_text_attribute(self, m): def test_update_text_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1244,6 +1282,7 @@ def test_update_text_attribute(self, m): def test_create_url_attribute(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1271,6 +1310,7 @@ def test_create_url_attribute(self, m): def test_update_url_attribute(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1299,6 +1339,7 @@ def test_update_url_attribute(self, m): def test_create_varchar_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1327,6 +1368,7 @@ def test_create_varchar_attribute(self, m): def test_update_varchar_attribute(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1355,6 +1397,7 @@ def test_update_varchar_attribute(self, m): def test_get_attribute(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -1517,6 +1560,25 @@ def test_delete_documents(self, m): ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_create_query(self, m): + data = { + "total": 5.0, + "documents": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.databases.create_query( + '', + '', + ) + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_get_document(self, m): data = { @@ -1692,6 +1754,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -1721,6 +1784,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", diff --git a/test/services/test_documents_db.py b/test/services/test_documents_db.py index fb451c82..c9840596 100644 --- a/test/services/test_documents_db.py +++ b/test/services/test_documents_db.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -191,6 +192,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -213,6 +215,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -273,6 +276,7 @@ def test_create_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -303,6 +307,7 @@ def test_get_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -332,6 +337,7 @@ def test_update_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -493,6 +499,25 @@ def test_delete_documents(self, m): ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_create_query(self, m): + data = { + "total": 5.0, + "documents": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.documents_db.create_query( + '', + '', + ) + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_get_document(self, m): data = { @@ -668,6 +693,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -697,6 +723,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", diff --git a/test/services/test_functions.py b/test/services/test_functions.py index d515e891..1f63e700 100644 --- a/test/services/test_functions.py +++ b/test/services/test_functions.py @@ -38,6 +38,7 @@ def test_create(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -121,6 +122,7 @@ def test_get(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -170,6 +172,7 @@ def test_update(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -235,6 +238,7 @@ def test_update_function_deployment(self, m): "$updatedAt": "2020-10-15T06:38:00.000+00:00", "execute": [], "name": "My Function", + "description": "Sends a welcome email to newly registered users.", "enabled": True, "live": True, "logging": True, @@ -725,6 +729,7 @@ def test_create_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -752,6 +757,7 @@ def test_get_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -777,6 +783,7 @@ def test_update_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", diff --git a/test/services/test_messaging.py b/test/services/test_messaging.py index 03c86ea6..d22f32d6 100644 --- a/test/services/test_messaging.py +++ b/test/services/test_messaging.py @@ -1165,6 +1165,7 @@ def test_create_topic(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "events", + "description": "Order status updates sent to customers.", "emailTotal": 100.0, "smsTotal": 100.0, "pushTotal": 100.0, @@ -1190,6 +1191,7 @@ def test_get_topic(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "events", + "description": "Order status updates sent to customers.", "emailTotal": 100.0, "smsTotal": 100.0, "pushTotal": 100.0, @@ -1214,6 +1216,7 @@ def test_update_topic(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "events", + "description": "Order status updates sent to customers.", "emailTotal": 100.0, "smsTotal": 100.0, "pushTotal": 100.0, diff --git a/test/services/test_organization.py b/test/services/test_organization.py index 85ed9358..41206a71 100644 --- a/test/services/test_organization.py +++ b/test/services/test_organization.py @@ -92,6 +92,7 @@ def test_get(self, m): "deploymentSize": 30.0, "buildSize": 2000.0, "functionsIntervalMinimum": 60.0, + "functionsAllowCron": True, "databasesAllowEncrypt": True, "group": "pro", "databaseComputeCredit": 10, @@ -190,6 +191,7 @@ def test_update(self, m): "deploymentSize": 30.0, "buildSize": 2000.0, "functionsIntervalMinimum": 60.0, + "functionsAllowCron": True, "databasesAllowEncrypt": True, "group": "pro", "databaseComputeCredit": 10, @@ -640,6 +642,7 @@ def test_create_ephemeral_project_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -667,6 +670,7 @@ def test_get_project_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -693,6 +697,7 @@ def test_update_project_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", diff --git a/test/services/test_project.py b/test/services/test_project.py index 669f0013..d0c94701 100644 --- a/test/services/test_project.py +++ b/test/services/test_project.py @@ -292,6 +292,7 @@ def test_create_ephemeral_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -318,6 +319,7 @@ def test_get_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -343,6 +345,7 @@ def test_update_key(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My API Key", + "description": "Used by the nightly backup job.", "expire": "2020-10-15T06:38:00.000+00:00", "scopes": [], "secret": "919c2d18fb5d4...a2ae413da83346ad2", @@ -2546,6 +2549,7 @@ def test_get_policy(self, m): data = { "$id": "password-dictionary", "enabled": True, + "denyList": [], } headers = {'Content-Type': 'application/json'} m.request( @@ -2787,6 +2791,7 @@ def test_create_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -2813,6 +2818,7 @@ def test_get_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -2837,6 +2843,7 @@ def test_update_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", diff --git a/test/services/test_sites.py b/test/services/test_sites.py index d7b7f32c..a1e4354e 100644 --- a/test/services/test_sites.py +++ b/test/services/test_sites.py @@ -37,6 +37,7 @@ def test_create(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -123,6 +124,7 @@ def test_get(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -174,6 +176,7 @@ def test_update(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -242,6 +245,7 @@ def test_update_site_deployment(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Site", + "description": "Marketing site for the spring launch.", "enabled": True, "live": True, "logging": True, @@ -700,6 +704,7 @@ def test_create_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -727,6 +732,7 @@ def test_get_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", @@ -752,6 +758,7 @@ def test_update_variable(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "API_KEY", + "description": "Stripe secret key used at checkout.", "value": "myPa$$word1", "secret": True, "resourceType": "function", diff --git a/test/services/test_storage.py b/test/services/test_storage.py index d2e56d29..15c0da0a 100644 --- a/test/services/test_storage.py +++ b/test/services/test_storage.py @@ -39,6 +39,7 @@ def test_create_bucket(self, m): "$permissions": [], "fileSecurity": True, "name": "Documents", + "description": "Invoices and receipts uploaded by customers.", "enabled": True, "maximumFileSize": 100.0, "allowedFileExtensions": [], @@ -46,6 +47,7 @@ def test_create_bucket(self, m): "encryption": True, "antivirus": True, "transformations": True, + "stripMetadata": True, "totalSize": 128.0, } headers = {'Content-Type': 'application/json'} @@ -70,6 +72,7 @@ def test_get_bucket(self, m): "$permissions": [], "fileSecurity": True, "name": "Documents", + "description": "Invoices and receipts uploaded by customers.", "enabled": True, "maximumFileSize": 100.0, "allowedFileExtensions": [], @@ -77,6 +80,7 @@ def test_get_bucket(self, m): "encryption": True, "antivirus": True, "transformations": True, + "stripMetadata": True, "totalSize": 128.0, } headers = {'Content-Type': 'application/json'} @@ -100,6 +104,7 @@ def test_update_bucket(self, m): "$permissions": [], "fileSecurity": True, "name": "Documents", + "description": "Invoices and receipts uploaded by customers.", "enabled": True, "maximumFileSize": 100.0, "allowedFileExtensions": [], @@ -107,6 +112,7 @@ def test_update_bucket(self, m): "encryption": True, "antivirus": True, "transformations": True, + "stripMetadata": True, "totalSize": 128.0, } headers = {'Content-Type': 'application/json'} diff --git a/test/services/test_tables_db.py b/test/services/test_tables_db.py index b2e71905..31151822 100644 --- a/test/services/test_tables_db.py +++ b/test/services/test_tables_db.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -191,6 +192,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -213,6 +215,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -536,6 +539,7 @@ def test_create_table(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Table", + "description": "Movies available in the catalog.", "enabled": True, "rowSecurity": True, "columns": [], @@ -566,6 +570,7 @@ def test_get_table(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Table", + "description": "Movies available in the catalog.", "enabled": True, "rowSecurity": True, "columns": [], @@ -595,6 +600,7 @@ def test_update_table(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Table", + "description": "Movies available in the catalog.", "enabled": True, "rowSecurity": True, "columns": [], @@ -654,6 +660,7 @@ def test_list_columns(self, m): def test_create_big_int_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -680,6 +687,7 @@ def test_create_big_int_column(self, m): def test_update_big_int_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "bigint", "status": "available", "error": "string", @@ -707,6 +715,7 @@ def test_update_big_int_column(self, m): def test_create_boolean_column(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -733,6 +742,7 @@ def test_create_boolean_column(self, m): def test_update_boolean_column(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -760,6 +770,7 @@ def test_update_boolean_column(self, m): def test_create_datetime_column(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -787,6 +798,7 @@ def test_create_datetime_column(self, m): def test_update_datetime_column(self, m): data = { "key": "birthDay", + "description": "Name shown on the public profile.", "type": "datetime", "status": "available", "error": "string", @@ -815,6 +827,7 @@ def test_update_datetime_column(self, m): def test_create_email_column(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -842,6 +855,7 @@ def test_create_email_column(self, m): def test_update_email_column(self, m): data = { "key": "userEmail", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -870,6 +884,7 @@ def test_update_email_column(self, m): def test_create_enum_column(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -899,6 +914,7 @@ def test_create_enum_column(self, m): def test_update_enum_column(self, m): data = { "key": "status", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -929,6 +945,7 @@ def test_update_enum_column(self, m): def test_create_float_column(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -955,6 +972,7 @@ def test_create_float_column(self, m): def test_update_float_column(self, m): data = { "key": "percentageCompleted", + "description": "Name shown on the public profile.", "type": "double", "status": "available", "error": "string", @@ -982,6 +1000,7 @@ def test_update_float_column(self, m): def test_create_integer_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -1008,6 +1027,7 @@ def test_create_integer_column(self, m): def test_update_integer_column(self, m): data = { "key": "count", + "description": "Name shown on the public profile.", "type": "integer", "status": "available", "error": "string", @@ -1035,6 +1055,7 @@ def test_update_integer_column(self, m): def test_create_ip_column(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1062,6 +1083,7 @@ def test_create_ip_column(self, m): def test_update_ip_column(self, m): data = { "key": "ipAddress", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1090,6 +1112,7 @@ def test_update_ip_column(self, m): def test_create_line_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1116,6 +1139,7 @@ def test_create_line_column(self, m): def test_update_line_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1142,6 +1166,7 @@ def test_update_line_column(self, m): def test_create_longtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1168,6 +1193,7 @@ def test_create_longtext_column(self, m): def test_update_longtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1195,6 +1221,7 @@ def test_update_longtext_column(self, m): def test_create_mediumtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1221,6 +1248,7 @@ def test_create_mediumtext_column(self, m): def test_update_mediumtext_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1248,6 +1276,7 @@ def test_update_mediumtext_column(self, m): def test_create_point_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1274,6 +1303,7 @@ def test_create_point_column(self, m): def test_update_point_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1300,6 +1330,7 @@ def test_update_point_column(self, m): def test_create_polygon_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1326,6 +1357,7 @@ def test_create_polygon_column(self, m): def test_update_polygon_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1352,6 +1384,7 @@ def test_update_polygon_column(self, m): def test_create_relationship_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1384,6 +1417,7 @@ def test_create_relationship_column(self, m): def test_update_relationship_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1415,6 +1449,7 @@ def test_update_relationship_column(self, m): def test_create_string_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1443,6 +1478,7 @@ def test_create_string_column(self, m): def test_update_string_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1471,6 +1507,7 @@ def test_update_string_column(self, m): def test_create_text_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1497,6 +1534,7 @@ def test_create_text_column(self, m): def test_update_text_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1524,6 +1562,7 @@ def test_update_text_column(self, m): def test_create_url_column(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1551,6 +1590,7 @@ def test_create_url_column(self, m): def test_update_url_column(self, m): data = { "key": "githubUrl", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1579,6 +1619,7 @@ def test_update_url_column(self, m): def test_create_varchar_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1607,6 +1648,7 @@ def test_create_varchar_column(self, m): def test_update_varchar_column(self, m): data = { "key": "fullName", + "description": "Name shown on the public profile.", "type": "string", "status": "available", "error": "string", @@ -1635,6 +1677,7 @@ def test_update_varchar_column(self, m): def test_get_column(self, m): data = { "key": "isEnabled", + "description": "Name shown on the public profile.", "type": "boolean", "status": "available", "error": "string", @@ -1699,6 +1742,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -1728,6 +1772,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -1889,6 +1934,25 @@ def test_delete_rows(self, m): ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_create_query(self, m): + data = { + "total": 5.0, + "rows": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + requests_mock.ANY, + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.tables_db.create_query( + '', + '', + ) + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_get_row(self, m): data = { diff --git a/test/services/test_vectors_db.py b/test/services/test_vectors_db.py index 5ed5c005..2c6f2240 100644 --- a/test/services/test_vectors_db.py +++ b/test/services/test_vectors_db.py @@ -35,6 +35,7 @@ def test_create(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -191,6 +192,7 @@ def test_get(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -213,6 +215,7 @@ def test_update(self, m): data = { "$id": "5e5ea5c16897e", "name": "My Database", + "description": "Customer orders and invoices.", "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, @@ -273,6 +276,7 @@ def test_create_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -305,6 +309,7 @@ def test_get_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -335,6 +340,7 @@ def test_update_collection(self, m): "$permissions": [], "databaseId": "5e5ea5c16897e", "name": "My Collection", + "description": "Movies available in the catalog.", "enabled": True, "documentSecurity": True, "attributes": [], @@ -637,6 +643,7 @@ def test_create_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", @@ -666,6 +673,7 @@ def test_get_index(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "key": "index1", + "description": "Speeds up lookups by email.", "type": "primary", "status": "available", "error": "string", diff --git a/test/services/test_webhooks.py b/test/services/test_webhooks.py index 9d5d6237..0f65c6b2 100644 --- a/test/services/test_webhooks.py +++ b/test/services/test_webhooks.py @@ -37,6 +37,7 @@ def test_create(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, @@ -69,6 +70,7 @@ def test_get(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, @@ -98,6 +100,7 @@ def test_update(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, @@ -145,6 +148,7 @@ def test_update_secret(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "name": "My Webhook", + "description": "Notifies the billing service about new users.", "url": "https:\/\/example.com\/webhook", "events": [], "tls": True, diff --git a/test/test_query.py b/test/test_query.py index b6a92bbc..ae47a4b1 100644 --- a/test/test_query.py +++ b/test/test_query.py @@ -267,6 +267,19 @@ def test_cross_join(self): parsed(Query.cross_join('orders', 'ord')), {'method': 'crossJoin', 'attribute': 'orders', 'values': ['ord']} ) + def test_nested_join_without_alias_leaves_the_alias_to_the_server(self): + query = json.loads(Query.join('orders', [Query.on('$id', 'customerId')])) + self.assertEqual(query['method'], 'join') + self.assertEqual(query['attribute'], 'orders') + self.assertEqual(len(query['values']), 1) + self.assertEqual(query['values'][0]['method'], 'on') + + def test_cross_join_without_alias_leaves_the_alias_to_the_server(self): + query = json.loads(Query.cross_join('orders')) + self.assertEqual(query['method'], 'crossJoin') + self.assertEqual(query['attribute'], 'orders') + self.assertEqual(query.get('values') or [], []) + def test_not_covers(self): self.assertEqual( parsed(Query.not_covers('location', [1, 2])), From a572d7f7f3bb5ffdc5486b7d6c886ed1aa992355 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Tue, 27 Oct 2026 15:59:47 +0000 Subject: [PATCH 3/4] chore: update Python SDK to 27.1.0 --- CHANGELOG.md | 13 + README.md | 14 +- appwrite/client.py | 20 +- appwrite/encoders/value_class_encoder.py | 20 + appwrite/enums/appwrite_migration_resource.py | 44 + appwrite/enums/build_runtime.py | 4 + appwrite/enums/firebase_migration_resource.py | 14 + appwrite/enums/n_host_migration_resource.py | 15 + appwrite/enums/on_duplicate.py | 7 + appwrite/enums/project_policy_id.py | 1 + appwrite/enums/runtime.py | 4 + appwrite/enums/supabase_migration_resource.py | 15 + appwrite/models/__init__.py | 8 + appwrite/models/billing_plan.py | 9 + appwrite/models/dns_record.py | 2 +- appwrite/models/migration.py | 128 + appwrite/models/migration_list.py | 34 + appwrite/models/migration_report.py | 168 ++ .../models/policy_deny_realtime_guests.py | 33 + appwrite/models/policy_list.py | 4 +- appwrite/models/provider.py | 2 +- appwrite/service.py | 9 + appwrite/services/account.py | 38 +- appwrite/services/activities.py | 2 +- appwrite/services/advisor.py | 10 +- appwrite/services/apps.py | 50 +- appwrite/services/avatars.py | 6 +- appwrite/services/backups.py | 12 +- appwrite/services/databases.py | 314 +-- appwrite/services/documents_db.py | 126 +- appwrite/services/domains.py | 152 +- appwrite/services/functions.py | 66 +- appwrite/services/messaging.py | 60 +- appwrite/services/migrations.py | 955 +++++++ appwrite/services/mongo.py | 74 +- appwrite/services/mysql.py | 80 +- appwrite/services/oauth2.py | 28 +- appwrite/services/organization.py | 34 +- appwrite/services/postgresql.py | 88 +- appwrite/services/presences.py | 8 +- appwrite/services/project.py | 132 +- appwrite/services/proxy.py | 6 +- appwrite/services/sites.py | 64 +- appwrite/services/storage.py | 34 +- appwrite/services/tables_db.py | 338 +-- appwrite/services/teams.py | 46 +- appwrite/services/tokens.py | 14 +- appwrite/services/users.py | 100 +- appwrite/services/vectors_db.py | 110 +- appwrite/services/waf.py | 34 +- appwrite/services/webhooks.py | 8 +- docs/examples/backups/update-policy.md | 2 +- .../migrations/create-appwrite-migration.md | 24 + docs/examples/migrations/create-csv-export.md | 27 + docs/examples/migrations/create-csv-import.md | 24 + .../migrations/create-firebase-migration.md | 20 + .../examples/migrations/create-json-export.md | 23 + .../examples/migrations/create-json-import.md | 24 + .../migrations/create-n-host-migration.md | 26 + .../migrations/create-supabase-migration.md | 25 + docs/examples/migrations/delete.md | 15 + .../migrations/get-firebase-report.md | 20 + docs/examples/migrations/get-n-host-report.md | 26 + .../migrations/get-supabase-report.md | 25 + docs/examples/migrations/get.md | 18 + docs/examples/migrations/list.md | 20 + docs/examples/migrations/retry.md | 18 + docs/examples/mongo/update-backup-policy.md | 2 +- docs/examples/mongo/update-backup-storage.md | 2 +- docs/examples/mysql/update-backup-policy.md | 2 +- docs/examples/mysql/update-backup-storage.md | 2 +- docs/examples/oauth2/authorize-post.md | 2 +- docs/examples/oauth2/authorize.md | 2 +- .../postgresql/update-backup-policy.md | 2 +- .../postgresql/update-backup-storage.md | 2 +- docs/examples/project/delete-sessions.md | 13 + docs/examples/project/get-policy.md | 3 +- .../update-deny-realtime-guests-policy.md | 18 + docs/examples/storage/update-file.md | 2 +- docs/examples/users/create-argon-2-user.md | 2 +- docs/examples/users/create-bcrypt-user.md | 2 +- docs/examples/users/create-ph-pass-user.md | 2 +- docs/examples/users/create-scrypt-user.md | 2 +- pyproject.toml | 2 +- setup.py | 4 +- test/services/test_account.py | 1036 ++++++- test/services/test_activities.py | 32 +- test/services/test_advisor.py | 69 +- test/services/test_apps.py | 343 ++- test/services/test_avatars.py | 257 +- test/services/test_backups.py | 195 +- test/services/test_databases.py | 1484 +++++++++- test/services/test_documents_db.py | 652 ++++- test/services/test_domains.py | 719 ++++- test/services/test_embeddings.py | 19 +- test/services/test_functions.py | 513 +++- test/services/test_graphql.py | 32 +- test/services/test_locale.py | 145 +- test/services/test_messaging.py | 1414 +++++++++- test/services/test_migrations.py | 847 ++++++ test/services/test_mongo.py | 536 +++- test/services/test_mysql.py | 595 +++- test/services/test_oauth2.py | 335 ++- test/services/test_organization.py | 340 ++- test/services/test_postgresql.py | 630 ++++- test/services/test_presences.py | 91 +- test/services/test_project.py | 2424 +++++++++++++++-- test/services/test_proxy.py | 158 +- test/services/test_sites.py | 486 +++- test/services/test_storage.py | 271 +- test/services/test_tables_db.py | 1619 ++++++++++- test/services/test_teams.py | 269 +- test/services/test_tokens.py | 71 +- test/services/test_users.py | 785 +++++- test/services/test_vectors_db.py | 609 ++++- test/services/test_waf.py | 353 ++- test/services/test_webhooks.py | 115 +- 117 files changed, 19284 insertions(+), 2120 deletions(-) create mode 100644 appwrite/enums/appwrite_migration_resource.py create mode 100644 appwrite/enums/firebase_migration_resource.py create mode 100644 appwrite/enums/n_host_migration_resource.py create mode 100644 appwrite/enums/on_duplicate.py create mode 100644 appwrite/enums/supabase_migration_resource.py create mode 100644 appwrite/models/migration.py create mode 100644 appwrite/models/migration_list.py create mode 100644 appwrite/models/migration_report.py create mode 100644 appwrite/models/policy_deny_realtime_guests.py create mode 100644 appwrite/services/migrations.py create mode 100644 docs/examples/migrations/create-appwrite-migration.md create mode 100644 docs/examples/migrations/create-csv-export.md create mode 100644 docs/examples/migrations/create-csv-import.md create mode 100644 docs/examples/migrations/create-firebase-migration.md create mode 100644 docs/examples/migrations/create-json-export.md create mode 100644 docs/examples/migrations/create-json-import.md create mode 100644 docs/examples/migrations/create-n-host-migration.md create mode 100644 docs/examples/migrations/create-supabase-migration.md create mode 100644 docs/examples/migrations/delete.md create mode 100644 docs/examples/migrations/get-firebase-report.md create mode 100644 docs/examples/migrations/get-n-host-report.md create mode 100644 docs/examples/migrations/get-supabase-report.md create mode 100644 docs/examples/migrations/get.md create mode 100644 docs/examples/migrations/list.md create mode 100644 docs/examples/migrations/retry.md create mode 100644 docs/examples/project/delete-sessions.md create mode 100644 docs/examples/project/update-deny-realtime-guests-policy.md create mode 100644 test/services/test_migrations.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e69013cf..0452cc05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## 27.1.0 + +* Added: `migrations` service to run Appwrite, Firebase, NHost, Supabase, CSV and JSON migrations, plus reports, retries and listing +* Added: `Migration`, `MigrationList` and `MigrationReport` models and the `AppwriteMigrationResource`, `FirebaseMigrationResource`, `NHostMigrationResource`, `SupabaseMigrationResource` and `OnDuplicate` enums +* Added: `project.update_deny_realtime_guests_policy`, the `PolicyDenyRealtimeGuests` model and `ProjectPolicyId.DENY_REALTIME_GUESTS` +* Added: `project.delete_sessions` to sign out every user in the project +* Added: `BillingPlan.deploymentretention`, `deploymentretentionmax` and `deploymentsretentioncount` +* Added: `python-3.15`, `deno-2.9`, `go-1.27` and `rust-1.99` to `Runtime` and `BuildRuntime` +* Updated: requests send `X-Appwrite-Response-Format: 2.6.2`; README targets Appwrite 2.6.x +* Updated: redirects are no longer followed; a 3xx raises `AppwriteException` naming the `Location` +* Fixed: path parameters are percent-encoded as one segment; `.` and `..` raise `AppwriteException` +* Fixed: the chunked-upload resume lookup encodes the file ID + ## 27.0.0 This release updates the HTTP dependency requirements and raises the minimum supported Python version. diff --git a/README.md b/README.md index b23533eb..91ab58a5 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Appwrite Python SDK ![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-2.5.0-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-2.6.2-blue.svg?style=flat-square) [![X Account](https://img.shields.io/badge/follow-%40appwrite-000000?logo=x&style=flat-square)](https://x.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -**This SDK targets Appwrite server version 2.5.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-python/releases) when APIs differ. +**This SDK targets Appwrite server version 2.6.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-python/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 Python 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) @@ -27,6 +27,16 @@ uv add appwrite ## Getting started +### Requirements and installation + +Use Python 3.10 or later. Create and activate a virtual environment with that interpreter, then install the SDK: + +```sh +python -m pip install --upgrade appwrite +``` + +The SDK requires `requests>=2.34.2,<3` and `urllib3>=2.8.0,<3`. Update application pins or constraints that exclude these versions. + ### Initialize the client Create a client with your Appwrite endpoint, project ID and an [API key](https://appwrite.io/docs/partners/project/api-keys). Find the endpoint and project ID in your project's **Settings** page, and create an API key with only the scopes you need under **API keys** in the project sidebar. diff --git a/appwrite/client.py b/appwrite/client.py index 47f8757d..6de8491b 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -6,6 +6,7 @@ import requests from concurrent.futures import ThreadPoolExecutor, as_completed from threading import Lock +from urllib.parse import quote from .input_file import InputFile from .exception import AppwriteException from .encoders.value_class_encoder import ValueClassEncoder @@ -19,12 +20,12 @@ def __init__(self): self._endpoint = 'https://cloud.appwrite.io/v1' self._global_headers = { 'content-type': '', - 'user-agent': f'AppwritePythonSDK/27.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', + 'user-agent': f'AppwritePythonSDK/27.1.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', 'x-sdk-name': 'Python', 'x-sdk-platform': 'server', 'x-sdk-language': 'python', - 'x-sdk-version': '27.0.0', - 'X-Appwrite-Response-Format': '2.5.0', + 'x-sdk-version': '27.1.0', + 'X-Appwrite-Response-Format': '2.6.2', } self._config = {} @@ -180,11 +181,18 @@ def call(self, method, path='', headers=None, params=None, response_type='json') files=files, headers=headers, verify=(not self._self_signed), - allow_redirects=False if response_type == 'location' else True, + # A redirect would carry the API key to wherever it points, possibly another host. + allow_redirects=False, ) response.raise_for_status() + if response_type != 'location' and 300 <= response.status_code < 400: + location = response.headers.get('Location', '') + raise AppwriteException( + f'Redirect to {location} not followed', response.status_code, None, response.text + ) + warnings = response.headers.get('x-appwrite-warning') if warnings: for warning in warnings.split(';'): @@ -202,6 +210,8 @@ def call(self, method, path='', headers=None, params=None, response_type='json') return response.json() return response._content + except AppwriteException: + raise except Exception as e: if response != None: content_type = response.headers['Content-Type'] @@ -250,7 +260,7 @@ def chunked_upload( if upload_id: try: - result = self.call('get', path + '/' + upload_id, headers) + result = self.call('get', path + '/' + quote(upload_id, safe="!*'()"), headers) counter = result['chunksUploaded'] except: pass diff --git a/appwrite/encoders/value_class_encoder.py b/appwrite/encoders/value_class_encoder.py index f157b094..7cef1cc6 100644 --- a/appwrite/encoders/value_class_encoder.py +++ b/appwrite/encoders/value_class_encoder.py @@ -27,6 +27,11 @@ from ..enums.execution_method import ExecutionMethod from ..enums.message_priority import MessagePriority from ..enums.smtp_encryption import SmtpEncryption +from ..enums.appwrite_migration_resource import AppwriteMigrationResource +from ..enums.on_duplicate import OnDuplicate +from ..enums.firebase_migration_resource import FirebaseMigrationResource +from ..enums.n_host_migration_resource import NHostMigrationResource +from ..enums.supabase_migration_resource import SupabaseMigrationResource from ..enums.region import Region from ..enums.project_auth_method_id import ProjectAuthMethodId from ..enums.project_o_auth2_auth0_prompt import ProjectOAuth2Auth0Prompt @@ -173,6 +178,21 @@ def default(self, o): if isinstance(o, SmtpEncryption): return o.value + if isinstance(o, AppwriteMigrationResource): + return o.value + + if isinstance(o, OnDuplicate): + return o.value + + if isinstance(o, FirebaseMigrationResource): + return o.value + + if isinstance(o, NHostMigrationResource): + return o.value + + if isinstance(o, SupabaseMigrationResource): + return o.value + if isinstance(o, Region): return o.value diff --git a/appwrite/enums/appwrite_migration_resource.py b/appwrite/enums/appwrite_migration_resource.py new file mode 100644 index 00000000..c9cecc84 --- /dev/null +++ b/appwrite/enums/appwrite_migration_resource.py @@ -0,0 +1,44 @@ +from enum import Enum + + +class AppwriteMigrationResource(Enum): + USER = "user" + TEAM = "team" + MEMBERSHIP = "membership" + AUTH_METHODS = "auth-methods" + POLICIES = "policies" + OAUTH2_PROVIDER = "oauth2-provider" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + INDEX = "index" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + DOCUMENTSDB = "documentsdb" + VECTORSDB = "vectorsdb" + BUCKET = "bucket" + FILE = "file" + FUNCTION = "function" + DEPLOYMENT = "deployment" + ENVIRONMENT_VARIABLE = "environment-variable" + PROVIDER = "provider" + TOPIC = "topic" + SUBSCRIBER = "subscriber" + MESSAGE = "message" + SITE = "site" + SITE_DEPLOYMENT = "site-deployment" + SITE_VARIABLE = "site-variable" + PLATFORM = "platform" + API_KEY = "api-key" + WEBHOOK = "webhook" + SMTP = "smtp" + BACKUP_POLICY = "backup-policy" + PROJECT_VARIABLE = "project-variable" + PROJECT_PROTOCOLS = "project-protocols" + PROJECT_LABELS = "project-labels" + PROJECT_SERVICES = "project-services" + PROJECT_EMAIL_TEMPLATE = "project-email-template" + PROJECT_ENVIRONMENT = "project-environment" + RULE = "rule" diff --git a/appwrite/enums/build_runtime.py b/appwrite/enums/build_runtime.py index 4bf67543..8a5cf305 100644 --- a/appwrite/enums/build_runtime.py +++ b/appwrite/enums/build_runtime.py @@ -31,6 +31,7 @@ class BuildRuntime(Enum): PYTHON_3_12 = "python-3.12" PYTHON_3_13 = "python-3.13" PYTHON_3_14 = "python-3.14" + PYTHON_3_15 = "python-3.15" PYTHON_ML_3_11 = "python-ml-3.11" PYTHON_ML_3_12 = "python-ml-3.12" PYTHON_ML_3_13 = "python-ml-3.13" @@ -39,6 +40,7 @@ class BuildRuntime(Enum): DENO_2_0 = "deno-2.0" DENO_2_5 = "deno-2.5" DENO_2_6 = "deno-2.6" + DENO_2_9 = "deno-2.9" DART_2_15 = "dart-2.15" DART_2_16 = "dart-2.16" DART_2_17 = "dart-2.17" @@ -86,7 +88,9 @@ class BuildRuntime(Enum): GO_1_24 = "go-1.24" GO_1_25 = "go-1.25" GO_1_26 = "go-1.26" + GO_1_27 = "go-1.27" RUST_1_83 = "rust-1.83" + RUST_1_99 = "rust-1.99" STATIC_1 = "static-1" FLUTTER_3_24 = "flutter-3.24" FLUTTER_3_27 = "flutter-3.27" diff --git a/appwrite/enums/firebase_migration_resource.py b/appwrite/enums/firebase_migration_resource.py new file mode 100644 index 00000000..cff8f24c --- /dev/null +++ b/appwrite/enums/firebase_migration_resource.py @@ -0,0 +1,14 @@ +from enum import Enum + + +class FirebaseMigrationResource(Enum): + USER = "user" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + BUCKET = "bucket" + FILE = "file" diff --git a/appwrite/enums/n_host_migration_resource.py b/appwrite/enums/n_host_migration_resource.py new file mode 100644 index 00000000..e0bdb4ce --- /dev/null +++ b/appwrite/enums/n_host_migration_resource.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class NHostMigrationResource(Enum): + USER = "user" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + INDEX = "index" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + BUCKET = "bucket" + FILE = "file" diff --git a/appwrite/enums/on_duplicate.py b/appwrite/enums/on_duplicate.py new file mode 100644 index 00000000..04557c19 --- /dev/null +++ b/appwrite/enums/on_duplicate.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class OnDuplicate(Enum): + FAIL = "fail" + SKIP = "skip" + OVERWRITE = "overwrite" diff --git a/appwrite/enums/project_policy_id.py b/appwrite/enums/project_policy_id.py index bc2ce903..5451fac3 100644 --- a/appwrite/enums/project_policy_id.py +++ b/appwrite/enums/project_policy_id.py @@ -19,4 +19,5 @@ class ProjectPolicyId(Enum): DENY_DISPOSABLE_EMAIL = "deny-disposable-email" DENY_FREE_EMAIL = "deny-free-email" DENY_CORPORATE_EMAIL = "deny-corporate-email" + DENY_REALTIME_GUESTS = "deny-realtime-guests" PASSKEY = "passkey" diff --git a/appwrite/enums/runtime.py b/appwrite/enums/runtime.py index c7f29dd5..c3a28c5f 100644 --- a/appwrite/enums/runtime.py +++ b/appwrite/enums/runtime.py @@ -31,6 +31,7 @@ class Runtime(Enum): PYTHON_3_12 = "python-3.12" PYTHON_3_13 = "python-3.13" PYTHON_3_14 = "python-3.14" + PYTHON_3_15 = "python-3.15" PYTHON_ML_3_11 = "python-ml-3.11" PYTHON_ML_3_12 = "python-ml-3.12" PYTHON_ML_3_13 = "python-ml-3.13" @@ -39,6 +40,7 @@ class Runtime(Enum): DENO_2_0 = "deno-2.0" DENO_2_5 = "deno-2.5" DENO_2_6 = "deno-2.6" + DENO_2_9 = "deno-2.9" DART_2_15 = "dart-2.15" DART_2_16 = "dart-2.16" DART_2_17 = "dart-2.17" @@ -86,7 +88,9 @@ class Runtime(Enum): GO_1_24 = "go-1.24" GO_1_25 = "go-1.25" GO_1_26 = "go-1.26" + GO_1_27 = "go-1.27" RUST_1_83 = "rust-1.83" + RUST_1_99 = "rust-1.99" STATIC_1 = "static-1" FLUTTER_3_24 = "flutter-3.24" FLUTTER_3_27 = "flutter-3.27" diff --git a/appwrite/enums/supabase_migration_resource.py b/appwrite/enums/supabase_migration_resource.py new file mode 100644 index 00000000..793a7caa --- /dev/null +++ b/appwrite/enums/supabase_migration_resource.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class SupabaseMigrationResource(Enum): + USER = "user" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + INDEX = "index" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + BUCKET = "bucket" + FILE = "file" diff --git a/appwrite/models/__init__.py b/appwrite/models/__init__.py index a38ce820..5926649d 100644 --- a/appwrite/models/__init__.py +++ b/appwrite/models/__init__.py @@ -41,6 +41,7 @@ from .subscriber_list import SubscriberList from .target_list import TargetList from .transaction_list import TransactionList +from .migration_list import MigrationList from .specification_list import SpecificationList from .vectorsdb_collection_list import VectorsdbCollectionList from .embedding_list import EmbeddingList @@ -198,6 +199,7 @@ from .policy_deny_disposable_email import PolicyDenyDisposableEmail from .policy_deny_free_email import PolicyDenyFreeEmail from .policy_deny_corporate_email import PolicyDenyCorporateEmail +from .policy_deny_realtime_guests import PolicyDenyRealtimeGuests from .platform_web import PlatformWeb from .platform_apple import PlatformApple from .platform_android import PlatformAndroid @@ -229,6 +231,8 @@ from .transaction import Transaction from .subscriber import Subscriber from .target import Target +from .migration import Migration +from .migration_report import MigrationReport from .insight import Insight from .insight_cta import InsightCTA from .report import Report @@ -365,6 +369,7 @@ 'SubscriberList', 'TargetList', 'TransactionList', + 'MigrationList', 'SpecificationList', 'VectorsdbCollectionList', 'EmbeddingList', @@ -522,6 +527,7 @@ 'PolicyDenyDisposableEmail', 'PolicyDenyFreeEmail', 'PolicyDenyCorporateEmail', + 'PolicyDenyRealtimeGuests', 'PlatformWeb', 'PlatformApple', 'PlatformAndroid', @@ -553,6 +559,8 @@ 'Transaction', 'Subscriber', 'Target', + 'Migration', + 'MigrationReport', 'Insight', 'InsightCTA', 'Report', diff --git a/appwrite/models/billing_plan.py b/appwrite/models/billing_plan.py index a5e65184..0ee63cae 100644 --- a/appwrite/models/billing_plan.py +++ b/appwrite/models/billing_plan.py @@ -73,6 +73,12 @@ class BillingPlan(AppwriteModel): Function executions executionsretentioncount : float Rolling max executions retained per function/site + deploymentretention : Optional[float] + Days non-active deployments are kept when a function or site sets no retention. + deploymentretentionmax : Optional[float] + Maximum deployment retention in days a function or site can set. 0 means unlimited. + deploymentsretentioncount : Optional[float] + Non-active deployments kept per function or site. 0 means unlimited. gbhours : float GB hours for functions realtime : float @@ -199,6 +205,9 @@ class BillingPlan(AppwriteModel): sites: float = Field(..., alias='sites') executions: float = Field(..., alias='executions') executionsretentioncount: float = Field(..., alias='executionsRetentionCount') + deploymentretention: Optional[float] = Field(default=None, alias='deploymentRetention') + deploymentretentionmax: Optional[float] = Field(default=None, alias='deploymentRetentionMax') + deploymentsretentioncount: Optional[float] = Field(default=None, alias='deploymentsRetentionCount') gbhours: float = Field(..., alias='GBHours') realtime: float = Field(..., alias='realtime') realtimemessages: float = Field(..., alias='realtimeMessages') diff --git a/appwrite/models/dns_record.py b/appwrite/models/dns_record.py index 612c896b..4c51923c 100644 --- a/appwrite/models/dns_record.py +++ b/appwrite/models/dns_record.py @@ -19,7 +19,7 @@ class DnsRecord(AppwriteModel): type : str DNS record type (e.g. A, CNAME, MX). name : str - Record name or subdomain. + Record name relative to the domain, or `@` for the domain itself. value : str Value of the record (IP address, domain, etc.). ttl : float diff --git a/appwrite/models/migration.py b/appwrite/models/migration.py new file mode 100644 index 00000000..42507b70 --- /dev/null +++ b/appwrite/models/migration.py @@ -0,0 +1,128 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr, model_validator + +from .base_model import AppwriteModel + + +class Migration(AppwriteModel): + """ + Migration + + Attributes + ---------- + id : str + Migration ID. + createdat : str + Migration creation date in ISO 8601 format. + updatedat : str + Variable creation date in ISO 8601 format. + status : str + Migration status ( pending, processing, failed, completed ) + stage : str + Migration stage ( init, processing, source-check, destination-check, migrating, finished ) + source : str + A string containing the type of source of the migration. + destination : str + A string containing the type of destination of the migration. + resources : List[Any] + Resources to migrate. + resourceid : str + ID of the resource being migrated. + resourceinternalid : str + Internal ID of the resource being migrated. + resourcetype : str + Type of the resource being migrated. + parentresourceid : str + ID of the parent resource that contains the migrated resource. + parentresourceinternalid : str + Internal ID of the parent resource that contains the migrated resource. + parentresourcetype : str + Type of the parent resource that contains the migrated resource. + destinationresourceid : str + ID of the destination resource created or overwritten by the migration. + destinationresourceinternalid : str + Internal ID of the destination resource created or overwritten by the migration. + destinationresourcetype : str + Type of the destination resource created or overwritten by the migration. + statuscounters : Dict[str, Any] + A group of counters that represent the total progress of the migration. + resourcedata : List[Any] + An array of objects containing the report data of the resources that were migrated. + errors : List[Any] + All errors that occurred during the migration process. + options : Dict[str, Any] + Migration options used during the migration process. + """ + + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + status: str = Field(..., alias='status') + stage: str = Field(..., alias='stage') + source: str = Field(..., alias='source') + destination: str = Field(..., alias='destination') + resources: List[Any] = Field(..., alias='resources') + resourceid: str = Field(..., alias='resourceId') + resourceinternalid: str = Field(..., alias='resourceInternalId') + resourcetype: str = Field(..., alias='resourceType') + parentresourceid: str = Field(..., alias='parentResourceId') + parentresourceinternalid: str = Field(..., alias='parentResourceInternalId') + parentresourcetype: str = Field(..., alias='parentResourceType') + destinationresourceid: str = Field(..., alias='destinationResourceId') + destinationresourceinternalid: str = Field(..., alias='destinationResourceInternalId') + destinationresourcetype: str = Field(..., alias='destinationResourceType') + statuscounters: Dict[str, Any] = Field(..., alias='statusCounters') + resourcedata: List[Any] = Field(..., alias='resourceData') + errors: List[Any] = Field(..., alias='errors') + options: Dict[str, Any] = Field(..., alias='options') + + @model_validator(mode='before') + @classmethod + def _apply_defaults(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + + data = dict(data) + if '$id' not in data and 'id' not in data: + data['$id'] = '' + if '$createdAt' not in data and 'createdat' not in data: + data['$createdAt'] = '' + if '$updatedAt' not in data and 'updatedat' not in data: + data['$updatedAt'] = '' + if 'status' not in data: + data['status'] = '' + if 'stage' not in data: + data['stage'] = '' + if 'source' not in data: + data['source'] = '' + if 'destination' not in data: + data['destination'] = 'Appwrite' + if 'resources' not in data: + data['resources'] = [] + if 'resourceId' not in data and 'resourceid' not in data: + data['resourceId'] = '' + if 'resourceInternalId' not in data and 'resourceinternalid' not in data: + data['resourceInternalId'] = '' + if 'resourceType' not in data and 'resourcetype' not in data: + data['resourceType'] = '' + if 'parentResourceId' not in data and 'parentresourceid' not in data: + data['parentResourceId'] = '' + if 'parentResourceInternalId' not in data and 'parentresourceinternalid' not in data: + data['parentResourceInternalId'] = '' + if 'parentResourceType' not in data and 'parentresourcetype' not in data: + data['parentResourceType'] = '' + if 'destinationResourceId' not in data and 'destinationresourceid' not in data: + data['destinationResourceId'] = '' + if 'destinationResourceInternalId' not in data and 'destinationresourceinternalid' not in data: + data['destinationResourceInternalId'] = '' + if 'destinationResourceType' not in data and 'destinationresourcetype' not in data: + data['destinationResourceType'] = '' + if 'statusCounters' not in data and 'statuscounters' not in data: + data['statusCounters'] = {} + if 'resourceData' not in data and 'resourcedata' not in data: + data['resourceData'] = [] + if 'errors' not in data: + data['errors'] = [] + if 'options' not in data: + data['options'] = {} + return data diff --git a/appwrite/models/migration_list.py b/appwrite/models/migration_list.py new file mode 100644 index 00000000..8e6a4493 --- /dev/null +++ b/appwrite/models/migration_list.py @@ -0,0 +1,34 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr, model_validator + +from .base_model import AppwriteModel +from .migration import Migration + + +class MigrationList(AppwriteModel): + """ + Migrations List + + Attributes + ---------- + total : float + Total number of migrations that matched your query. + migrations : List[Migration] + List of migrations. + """ + + total: float = Field(..., alias='total') + migrations: List[Migration] = Field(..., alias='migrations') + + @model_validator(mode='before') + @classmethod + def _apply_defaults(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + + data = dict(data) + if 'total' not in data: + data['total'] = 0 + if 'migrations' not in data: + data['migrations'] = [] + return data diff --git a/appwrite/models/migration_report.py b/appwrite/models/migration_report.py new file mode 100644 index 00000000..fee9774d --- /dev/null +++ b/appwrite/models/migration_report.py @@ -0,0 +1,168 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr, model_validator + +from .base_model import AppwriteModel + + +class MigrationReport(AppwriteModel): + """ + Migration Report + + Attributes + ---------- + user : float + Number of users to be migrated. + team : float + Number of teams to be migrated. + database : float + Number of databases to be migrated. + row : float + Number of rows to be migrated. + file : float + Number of files to be migrated. + bucket : float + Number of buckets to be migrated. + function : float + Number of functions to be migrated. + platform : float + Number of platforms to be migrated. + api_key : float + Number of API keys to be migrated. + project_variable : float + Number of project variables to be migrated. + webhook : float + Number of webhooks to be migrated. + auth_methods : float + Number of auth-method configs to be migrated (always 0 or 1 — the project-level flag bundle). + project_protocols : float + Number of protocol configs to be migrated (always 0 or 1 — the project-level REST/GraphQL/WebSocket flags). + project_labels : float + Number of label sets to be migrated (always 0 or 1 — the project-level RBAC label array). + project_services : float + Number of service configs to be migrated (always 0 or 1 — the project-level enable/disable flags for all 17 services). + policies : float + Number of policy bundles to be migrated (always 0 or 1 — the project-level security policies covering password rules, session behavior, user limits, and membership privacy). + smtp : float + Number of SMTP configurations to be migrated (always 0 or 1 — the project-level custom SMTP settings; password is not exposed by the source API). + rule : float + Number of custom-domain proxy rules to be migrated. Auto-generated `.appwrite.network` rules are skipped — they are recreated by parent Function/Site migration. + project_email_template : float + Number of custom email templates to be migrated (one per templateId × locale pair). + project_environment : float + Number of project environments to be migrated. Each is recreated under a new ID, empty: its own resources belong to a separate project. + site : float + Number of sites to be migrated. + provider : float + Number of providers to be migrated. + topic : float + Number of topics to be migrated. + subscriber : float + Number of subscribers to be migrated. + message : float + Number of messages to be migrated. + size : float + Size of files to be migrated in mb. + version : str + Version of the Appwrite instance to be migrated. + oauth2_provider : float + Number of OAuth2 provider configurations to be migrated. Secrets (clientSecret, p8File) are never migrated — destination admin must re-enter them per provider. + backup_policy : float + Number of backup policies to be migrated. + """ + + user: float = Field(..., alias='user') + team: float = Field(..., alias='team') + database: float = Field(..., alias='database') + row: float = Field(..., alias='row') + file: float = Field(..., alias='file') + bucket: float = Field(..., alias='bucket') + function: float = Field(..., alias='function') + platform: float = Field(..., alias='platform') + api_key: float = Field(..., alias='api-key') + project_variable: float = Field(..., alias='project-variable') + webhook: float = Field(..., alias='webhook') + auth_methods: float = Field(..., alias='auth-methods') + project_protocols: float = Field(..., alias='project-protocols') + project_labels: float = Field(..., alias='project-labels') + project_services: float = Field(..., alias='project-services') + policies: float = Field(..., alias='policies') + smtp: float = Field(..., alias='smtp') + rule: float = Field(..., alias='rule') + project_email_template: float = Field(..., alias='project-email-template') + project_environment: float = Field(..., alias='project-environment') + site: float = Field(..., alias='site') + provider: float = Field(..., alias='provider') + topic: float = Field(..., alias='topic') + subscriber: float = Field(..., alias='subscriber') + message: float = Field(..., alias='message') + size: float = Field(..., alias='size') + version: str = Field(..., alias='version') + oauth2_provider: float = Field(..., alias='oauth2-provider') + backup_policy: float = Field(..., alias='backup-policy') + + @model_validator(mode='before') + @classmethod + def _apply_defaults(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + + data = dict(data) + if 'user' not in data: + data['user'] = 0 + if 'team' not in data: + data['team'] = 0 + if 'database' not in data: + data['database'] = 0 + if 'row' not in data: + data['row'] = 0 + if 'file' not in data: + data['file'] = 0 + if 'bucket' not in data: + data['bucket'] = 0 + if 'function' not in data: + data['function'] = 0 + if 'platform' not in data: + data['platform'] = 0 + if 'api-key' not in data and 'api_key' not in data: + data['api-key'] = 0 + if 'project-variable' not in data and 'project_variable' not in data: + data['project-variable'] = 0 + if 'webhook' not in data: + data['webhook'] = 0 + if 'auth-methods' not in data and 'auth_methods' not in data: + data['auth-methods'] = 0 + if 'project-protocols' not in data and 'project_protocols' not in data: + data['project-protocols'] = 0 + if 'project-labels' not in data and 'project_labels' not in data: + data['project-labels'] = 0 + if 'project-services' not in data and 'project_services' not in data: + data['project-services'] = 0 + if 'policies' not in data: + data['policies'] = 0 + if 'smtp' not in data: + data['smtp'] = 0 + if 'rule' not in data: + data['rule'] = 0 + if 'project-email-template' not in data and 'project_email_template' not in data: + data['project-email-template'] = 0 + if 'project-environment' not in data and 'project_environment' not in data: + data['project-environment'] = 0 + if 'site' not in data: + data['site'] = 0 + if 'provider' not in data: + data['provider'] = 0 + if 'topic' not in data: + data['topic'] = 0 + if 'subscriber' not in data: + data['subscriber'] = 0 + if 'message' not in data: + data['message'] = 0 + if 'size' not in data: + data['size'] = 0 + if 'version' not in data: + data['version'] = '' + if 'oauth2-provider' not in data and 'oauth2_provider' not in data: + data['oauth2-provider'] = 0 + if 'backup-policy' not in data and 'backup_policy' not in data: + data['backup-policy'] = 0 + return data diff --git a/appwrite/models/policy_deny_realtime_guests.py b/appwrite/models/policy_deny_realtime_guests.py new file mode 100644 index 00000000..228b28c7 --- /dev/null +++ b/appwrite/models/policy_deny_realtime_guests.py @@ -0,0 +1,33 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr, model_validator + +from .base_model import AppwriteModel + + +class PolicyDenyRealtimeGuests(AppwriteModel): + """ + Policy Deny Realtime Guests + + Attributes + ---------- + id : str + Policy ID. + enabled : bool + Whether guests are stopped from subscribing to realtime channels. + """ + + id: str = Field(..., alias='$id') + enabled: bool = Field(..., alias='enabled') + + @model_validator(mode='before') + @classmethod + def _apply_defaults(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + + data = dict(data) + if '$id' not in data and 'id' not in data: + data['$id'] = '' + if 'enabled' not in data: + data['enabled'] = False + return data diff --git a/appwrite/models/policy_list.py b/appwrite/models/policy_list.py index af4063f2..0109c6ff 100644 --- a/appwrite/models/policy_list.py +++ b/appwrite/models/policy_list.py @@ -19,6 +19,7 @@ from .policy_deny_disposable_email import PolicyDenyDisposableEmail from .policy_deny_free_email import PolicyDenyFreeEmail from .policy_deny_corporate_email import PolicyDenyCorporateEmail +from .policy_deny_realtime_guests import PolicyDenyRealtimeGuests from .policy_passkey import PolicyPasskey @@ -30,7 +31,7 @@ class PolicyList(AppwriteModel): ---------- total : float Total number of policies in the given project. - policies : List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyPasskey]] + policies : List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyDenyRealtimeGuests, PolicyPasskey]] List of policies. """ @@ -54,6 +55,7 @@ class PolicyList(AppwriteModel): PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, + PolicyDenyRealtimeGuests, PolicyPasskey, ] ] = Field(..., alias='policies') diff --git a/appwrite/models/provider.py b/appwrite/models/provider.py index ebe30dfb..e045e09c 100644 --- a/appwrite/models/provider.py +++ b/appwrite/models/provider.py @@ -25,7 +25,7 @@ class Provider(AppwriteModel): type : str Type of provider. credentials : Dict[str, Any] - Provider credentials. + Provider credentials. Secret values are write-only and never returned. options : Optional[Dict[str, Any]] Provider options. """ diff --git a/appwrite/service.py b/appwrite/service.py index abc7cf4b..b2fd9bbe 100644 --- a/appwrite/service.py +++ b/appwrite/service.py @@ -1,5 +1,6 @@ from enum import Enum from typing import Any, Optional, Type, TypeVar +from urllib.parse import quote from pydantic import ValidationError @@ -29,6 +30,14 @@ def _normalize_value(self, value: Any) -> Any: return value + def _encode_path(self, value: Any) -> str: + # Everything but letters, digits and -_.!~*'() is escaped, as encodeURIComponent does, + # so a value fills exactly the one path segment it replaces. + segment = str(self._normalize_value(value)) + if segment in ('.', '..'): + raise AppwriteException('Path parameter cannot be "." or ".."') + return quote(segment, safe="!*'()") + def _parse_response(self, response: Any, model: Optional[Type[ModelType]] = None) -> Any: if model is None: return response diff --git a/appwrite/services/account.py b/appwrite/services/account.py index 9788e53c..c71d2fe0 100644 --- a/appwrite/services/account.py +++ b/appwrite/services/account.py @@ -205,7 +205,7 @@ def get_consent( api_params = {} if consent_id is None or consent_id == '': raise AppwriteException('Missing required parameter: "consent_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) response = self.client.call( 'get', @@ -245,7 +245,7 @@ def delete_consent( api_params = {} if consent_id is None or consent_id == '': raise AppwriteException('Missing required parameter: "consent_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) response = self.client.call( 'delete', @@ -292,7 +292,7 @@ def list_consent_tokens( api_params = {} if consent_id is None or consent_id == '': raise AppwriteException('Missing required parameter: "consent_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -341,8 +341,8 @@ def get_consent_token( raise AppwriteException('Missing required parameter: "consent_id"') if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'get', @@ -387,8 +387,8 @@ def delete_consent_token( raise AppwriteException('Missing required parameter: "consent_id"') if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'delete', @@ -525,7 +525,7 @@ def delete_identity( api_params = {} if identity_id is None or identity_id == '': raise AppwriteException('Missing required parameter: "identity_id"') - api_path = api_path.replace('{identityId}', str(self._normalize_value(identity_id))) + api_path = api_path.replace('{identityId}', self._encode_path(identity_id)) response = self.client.call( 'delete', @@ -655,7 +655,7 @@ def create_mfa_authenticator( api_params = {} if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{type}', self._encode_path(type)) response = self.client.call( 'post', @@ -705,7 +705,7 @@ def update_mfa_authenticator( raise AppwriteException('Missing required parameter: "type"') if otp is None: raise AppwriteException('Missing required parameter: "otp"') - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{type}', self._encode_path(type)) api_params['otp'] = self._normalize_value(otp) response = self.client.call( @@ -747,7 +747,7 @@ def delete_mfa_authenticator( api_params = {} if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{type}', self._encode_path(type)) response = self.client.call( 'delete', @@ -1136,7 +1136,7 @@ def get_passkey( api_params = {} if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'get', @@ -1181,7 +1181,7 @@ def update_passkey( raise AppwriteException('Missing required parameter: "passkey_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1223,7 +1223,7 @@ def delete_passkey( api_params = {} if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'delete', @@ -1269,7 +1269,7 @@ def update_passkey_verification( raise AppwriteException('Missing required parameter: "passkey_id"') if credential is None: raise AppwriteException('Missing required parameter: "credential"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) api_params['credential'] = self._normalize_value(credential) response = self.client.call( @@ -2091,7 +2091,7 @@ def get_session( api_params = {} if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'get', @@ -2131,7 +2131,7 @@ def update_session( api_params = {} if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'patch', @@ -2172,7 +2172,7 @@ def delete_session( api_params = {} if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'delete', @@ -2383,7 +2383,7 @@ def create_o_auth2_token( api_params = {} if provider is None: raise AppwriteException('Missing required parameter: "provider"') - api_path = api_path.replace('{provider}', str(self._normalize_value(provider))) + api_path = api_path.replace('{provider}', self._encode_path(provider)) if success is not None: api_params['success'] = self._normalize_value(success) if failure is not None: diff --git a/appwrite/services/activities.py b/appwrite/services/activities.py index 8556609f..56a932c9 100644 --- a/appwrite/services/activities.py +++ b/appwrite/services/activities.py @@ -82,7 +82,7 @@ def get_event( api_params = {} if event_id is None or event_id == '': raise AppwriteException('Missing required parameter: "event_id"') - api_path = api_path.replace('{eventId}', str(self._normalize_value(event_id))) + api_path = api_path.replace('{eventId}', self._encode_path(event_id)) response = self.client.call( 'get', diff --git a/appwrite/services/advisor.py b/appwrite/services/advisor.py index 194d0b7a..6e52e89d 100644 --- a/appwrite/services/advisor.py +++ b/appwrite/services/advisor.py @@ -84,7 +84,7 @@ def get_report( api_params = {} if report_id is None or report_id == '': raise AppwriteException('Missing required parameter: "report_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) response = self.client.call( 'get', @@ -124,7 +124,7 @@ def delete_report( api_params = {} if report_id is None or report_id == '': raise AppwriteException('Missing required parameter: "report_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) response = self.client.call( 'delete', @@ -171,7 +171,7 @@ def list_insights( api_params = {} if report_id is None or report_id == '': raise AppwriteException('Missing required parameter: "report_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -220,8 +220,8 @@ def get_insight( raise AppwriteException('Missing required parameter: "report_id"') if insight_id is None or insight_id == '': raise AppwriteException('Missing required parameter: "insight_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) - api_path = api_path.replace('{insightId}', str(self._normalize_value(insight_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) + api_path = api_path.replace('{insightId}', self._encode_path(insight_id)) response = self.client.call( 'get', diff --git a/appwrite/services/apps.py b/appwrite/services/apps.py index 6a3439a6..066469de 100644 --- a/appwrite/services/apps.py +++ b/appwrite/services/apps.py @@ -286,7 +286,7 @@ def get( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'get', @@ -385,7 +385,7 @@ def update( raise AppwriteException('Missing required parameter: "app_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) api_params['name'] = self._normalize_value(name) if description is not None: api_params['description'] = self._normalize_value(description) @@ -463,7 +463,7 @@ def delete( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'delete', @@ -510,7 +510,7 @@ def list_installations( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -559,8 +559,8 @@ def get_installation( raise AppwriteException('Missing required parameter: "app_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'get', @@ -605,8 +605,8 @@ def delete_installation( raise AppwriteException('Missing required parameter: "app_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'delete', @@ -652,8 +652,8 @@ def create_installation_token( raise AppwriteException('Missing required parameter: "app_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'post', @@ -700,7 +700,7 @@ def list_keys( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -744,7 +744,7 @@ def create_key( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'post', @@ -790,8 +790,8 @@ def get_key( raise AppwriteException('Missing required parameter: "app_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'get', @@ -836,8 +836,8 @@ def delete_key( raise AppwriteException('Missing required parameter: "app_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'delete', @@ -883,7 +883,7 @@ def update_labels( raise AppwriteException('Missing required parameter: "app_id"') if labels is None: raise AppwriteException('Missing required parameter: "labels"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) api_params['labels'] = self._normalize_value(labels) response = self.client.call( @@ -931,7 +931,7 @@ def list_secrets( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -975,7 +975,7 @@ def create_secret( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'post', @@ -1021,8 +1021,8 @@ def get_secret( raise AppwriteException('Missing required parameter: "app_id"') if secret_id is None or secret_id == '': raise AppwriteException('Missing required parameter: "secret_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{secretId}', str(self._normalize_value(secret_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{secretId}', self._encode_path(secret_id)) response = self.client.call( 'get', @@ -1067,8 +1067,8 @@ def delete_secret( raise AppwriteException('Missing required parameter: "app_id"') if secret_id is None or secret_id == '': raise AppwriteException('Missing required parameter: "secret_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{secretId}', str(self._normalize_value(secret_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{secretId}', self._encode_path(secret_id)) response = self.client.call( 'delete', @@ -1114,7 +1114,7 @@ def update_team( raise AppwriteException('Missing required parameter: "app_id"') if team_id is None: raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) api_params['teamId'] = self._normalize_value(team_id) response = self.client.call( @@ -1156,7 +1156,7 @@ def delete_tokens( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/avatars.py b/appwrite/services/avatars.py index a01b4baf..0f5429ed 100644 --- a/appwrite/services/avatars.py +++ b/appwrite/services/avatars.py @@ -58,7 +58,7 @@ def get_browser( api_params = {} if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{code}', str(self._normalize_value(code))) + api_path = api_path.replace('{code}', self._encode_path(code)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: @@ -115,7 +115,7 @@ def get_credit_card( api_params = {} if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{code}', str(self._normalize_value(code))) + api_path = api_path.replace('{code}', self._encode_path(code)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: @@ -214,7 +214,7 @@ def get_flag( api_params = {} if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{code}', str(self._normalize_value(code))) + api_path = api_path.replace('{code}', self._encode_path(code)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: diff --git a/appwrite/services/backups.py b/appwrite/services/backups.py index b33b009c..0f398922 100644 --- a/appwrite/services/backups.py +++ b/appwrite/services/backups.py @@ -133,7 +133,7 @@ def get_archive( api_params = {} if archive_id is None or archive_id == '': raise AppwriteException('Missing required parameter: "archive_id"') - api_path = api_path.replace('{archiveId}', str(self._normalize_value(archive_id))) + api_path = api_path.replace('{archiveId}', self._encode_path(archive_id)) response = self.client.call( 'get', @@ -173,7 +173,7 @@ def delete_archive( api_params = {} if archive_id is None or archive_id == '': raise AppwriteException('Missing required parameter: "archive_id"') - api_path = api_path.replace('{archiveId}', str(self._normalize_value(archive_id))) + api_path = api_path.replace('{archiveId}', self._encode_path(archive_id)) response = self.client.call( 'delete', @@ -332,7 +332,7 @@ def get_policy( api_params = {} if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -384,7 +384,7 @@ def update_policy( api_params = {} if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if retention is not None: @@ -433,7 +433,7 @@ def delete_policy( api_params = {} if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -583,7 +583,7 @@ def get_restoration( api_params = {} if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', diff --git a/appwrite/services/databases.py b/appwrite/services/databases.py index 179b6b20..7ae5a682 100644 --- a/appwrite/services/databases.py +++ b/appwrite/services/databases.py @@ -276,7 +276,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -325,7 +325,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -373,7 +373,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -420,7 +420,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -466,7 +466,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -518,7 +518,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -568,7 +568,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -621,7 +621,7 @@ def list_collections( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -698,7 +698,7 @@ def create_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['collectionId'] = self._normalize_value(collection_id) api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -761,8 +761,8 @@ def get_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'get', @@ -828,8 +828,8 @@ def update_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if name is not None: api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -890,8 +890,8 @@ def delete_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'delete', @@ -946,8 +946,8 @@ def list_attributes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1024,8 +1024,8 @@ def create_big_int_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -1111,9 +1111,9 @@ def update_big_int_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -1191,8 +1191,8 @@ def create_boolean_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1268,9 +1268,9 @@ def update_boolean_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1344,8 +1344,8 @@ def create_datetime_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1421,9 +1421,9 @@ def update_datetime_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1497,8 +1497,8 @@ def create_email_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1574,9 +1574,9 @@ def update_email_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1655,8 +1655,8 @@ def create_enum_attribute( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) @@ -1738,9 +1738,9 @@ def update_enum_attribute( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) @@ -1821,8 +1821,8 @@ def create_float_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -1908,9 +1908,9 @@ def update_float_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -1994,8 +1994,8 @@ def create_integer_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -2081,9 +2081,9 @@ def update_integer_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -2161,8 +2161,8 @@ def create_ip_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2238,9 +2238,9 @@ def update_ip_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2311,8 +2311,8 @@ def create_line_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2386,9 +2386,9 @@ def update_line_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -2466,8 +2466,8 @@ def create_longtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2545,9 +2545,9 @@ def update_longtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2624,8 +2624,8 @@ def create_mediumtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2703,9 +2703,9 @@ def update_mediumtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2776,8 +2776,8 @@ def create_point_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2851,9 +2851,9 @@ def update_point_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -2925,8 +2925,8 @@ def create_polygon_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3000,9 +3000,9 @@ def update_polygon_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -3083,8 +3083,8 @@ def create_relationship_attribute( raise AppwriteException('Missing required parameter: "related_collection_id"') if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['relatedCollectionId'] = self._normalize_value(related_collection_id) api_params['type'] = self._normalize_value(type) if two_way is not None: @@ -3159,9 +3159,9 @@ def update_relationship_attribute( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: @@ -3243,8 +3243,8 @@ def create_string_attribute( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -3326,9 +3326,9 @@ def update_string_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -3407,8 +3407,8 @@ def create_text_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3486,9 +3486,9 @@ def update_text_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3562,8 +3562,8 @@ def create_url_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3639,9 +3639,9 @@ def update_url_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3723,8 +3723,8 @@ def create_varchar_attribute( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -3806,9 +3806,9 @@ def update_varchar_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -3889,9 +3889,9 @@ def get_attribute( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -4000,9 +4000,9 @@ def delete_attribute( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -4069,8 +4069,8 @@ def list_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4146,8 +4146,8 @@ def create_document( raise AppwriteException('Missing required parameter: "document_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documentId'] = self._normalize_value(document_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4214,8 +4214,8 @@ def create_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4279,8 +4279,8 @@ def upsert_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4345,8 +4345,8 @@ def update_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -4411,8 +4411,8 @@ def delete_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4481,8 +4481,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4554,9 +4554,9 @@ def get_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4626,9 +4626,9 @@ def upsert_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4701,9 +4701,9 @@ def update_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4766,9 +4766,9 @@ def delete_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4842,10 +4842,10 @@ def decrement_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if min is not None: @@ -4923,10 +4923,10 @@ def increment_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if max is not None: @@ -4987,8 +4987,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -5065,8 +5065,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if attributes is None: raise AppwriteException('Missing required parameter: "attributes"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['attributes'] = self._normalize_value(attributes) @@ -5129,9 +5129,9 @@ def get_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -5184,9 +5184,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', diff --git a/appwrite/services/documents_db.py b/appwrite/services/documents_db.py index 234f2b66..cc8e8241 100644 --- a/appwrite/services/documents_db.py +++ b/appwrite/services/documents_db.py @@ -286,7 +286,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -332,7 +332,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -377,7 +377,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -421,7 +421,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -464,7 +464,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -524,7 +524,7 @@ def update( raise AppwriteException('Missing required parameter: "database_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) @@ -576,7 +576,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -626,7 +626,7 @@ def list_collections( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -700,7 +700,7 @@ def create_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['collectionId'] = self._normalize_value(collection_id) api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -760,8 +760,8 @@ def get_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'get', @@ -826,8 +826,8 @@ def update_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['name'] = self._normalize_value(name) if permissions is not None: api_params['permissions'] = self._normalize_value(permissions) @@ -884,8 +884,8 @@ def delete_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'delete', @@ -947,8 +947,8 @@ def list_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1021,8 +1021,8 @@ def create_document( raise AppwriteException('Missing required parameter: "document_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documentId'] = self._normalize_value(document_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1086,8 +1086,8 @@ def create_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1148,8 +1148,8 @@ def upsert_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1211,8 +1211,8 @@ def update_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -1274,8 +1274,8 @@ def delete_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1341,8 +1341,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1411,9 +1411,9 @@ def get_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1480,9 +1480,9 @@ def upsert_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1552,9 +1552,9 @@ def update_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1614,9 +1614,9 @@ def delete_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1687,10 +1687,10 @@ def decrement_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if min is not None: @@ -1765,10 +1765,10 @@ def increment_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if max is not None: @@ -1826,8 +1826,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1901,8 +1901,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if attributes is None: raise AppwriteException('Missing required parameter: "attributes"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['attributes'] = self._normalize_value(attributes) @@ -1962,9 +1962,9 @@ def get_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -2014,9 +2014,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -2060,7 +2060,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -2112,7 +2112,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -2158,7 +2158,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -2198,7 +2198,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', diff --git a/appwrite/services/domains.py b/appwrite/services/domains.py index bce1dc23..2d2d687b 100644 --- a/appwrite/services/domains.py +++ b/appwrite/services/domains.py @@ -243,7 +243,7 @@ def get( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -284,7 +284,7 @@ def delete( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'delete', @@ -329,7 +329,7 @@ def update_nameservers( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) if nameservers is not None: api_params['nameservers'] = self._normalize_value(nameservers) @@ -374,7 +374,7 @@ def verify_nameservers( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'patch', @@ -415,7 +415,7 @@ def get_preset_google_workspace( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -456,7 +456,7 @@ def create_preset_google_workspace( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -497,7 +497,7 @@ def get_preset_i_cloud( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -538,7 +538,7 @@ def create_preset_i_cloud( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -579,7 +579,7 @@ def get_preset_mailgun( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -620,7 +620,7 @@ def create_preset_mailgun( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -661,7 +661,7 @@ def get_preset_outlook( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -702,7 +702,7 @@ def create_preset_outlook( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -743,7 +743,7 @@ def get_preset_proton_mail( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -784,7 +784,7 @@ def create_preset_proton_mail( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -825,7 +825,7 @@ def get_preset_zoho( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -866,7 +866,7 @@ def create_preset_zoho( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -914,7 +914,7 @@ def list_records( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -949,7 +949,7 @@ def create_record_a( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv4 address for this A record. ttl : float @@ -977,7 +977,7 @@ def create_record_a( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1018,7 +1018,7 @@ def update_record_a( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv4 address for this A record. ttl : float @@ -1048,8 +1048,8 @@ def update_record_a( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1086,7 +1086,7 @@ def create_record_aaaa( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv6 address for this AAAA record. ttl : float @@ -1114,7 +1114,7 @@ def create_record_aaaa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1155,7 +1155,7 @@ def update_record_aaaa( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv6 address for this AAAA record. ttl : float @@ -1185,8 +1185,8 @@ def update_record_aaaa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1224,7 +1224,7 @@ def create_record_alias( domain_id : str Domain unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target domain for this ALIAS record. ttl : float @@ -1252,7 +1252,7 @@ def create_record_alias( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1295,7 +1295,7 @@ def update_record_alias( record_id : str DNS record unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target domain for this ALIAS record. ttl : float @@ -1325,8 +1325,8 @@ def update_record_alias( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1363,7 +1363,7 @@ def create_record_caa( domain_id : str Domain unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str CAA value (e.g. issuer domain). ttl : float @@ -1391,7 +1391,7 @@ def create_record_caa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1432,7 +1432,7 @@ def update_record_caa( record_id : str DNS record unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str CAA value (e.g. issuer domain). ttl : float @@ -1462,8 +1462,8 @@ def update_record_caa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1503,7 +1503,7 @@ def create_record_cname( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Canonical target for this CNAME record. ttl : float @@ -1531,7 +1531,7 @@ def create_record_cname( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1570,7 +1570,7 @@ def update_record_cname( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Canonical target for this CNAME record. ttl : float @@ -1600,8 +1600,8 @@ def update_record_cname( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1638,7 +1638,7 @@ def create_record_https( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target for the HTTPS record. ttl : float @@ -1666,7 +1666,7 @@ def create_record_https( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1707,7 +1707,7 @@ def update_record_https( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target for the HTTPS record. ttl : float @@ -1737,8 +1737,8 @@ def update_record_https( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1778,7 +1778,7 @@ def create_record_mx( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Mail server domain for this MX record. ttl : float @@ -1810,7 +1810,7 @@ def create_record_mx( raise AppwriteException('Missing required parameter: "ttl"') if priority is None: raise AppwriteException('Missing required parameter: "priority"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1851,7 +1851,7 @@ def update_record_mx( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Mail server domain for this MX record. ttl : float @@ -1885,8 +1885,8 @@ def update_record_mx( raise AppwriteException('Missing required parameter: "ttl"') if priority is None: raise AppwriteException('Missing required parameter: "priority"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1924,7 +1924,7 @@ def create_record_ns( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Nameserver target for this NS record. ttl : float @@ -1952,7 +1952,7 @@ def create_record_ns( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1994,7 +1994,7 @@ def update_record_ns( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Nameserver target for this NS record. ttl : float @@ -2024,8 +2024,8 @@ def update_record_ns( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2066,7 +2066,7 @@ def create_record_srv( domain_id : str Domain unique ID. name : str - Record name (service name). + Record name relative to the domain: the service and protocol, such as `_sip._tcp`. A full name ending in the domain is also accepted and stored relative to it. value : str Target hostname for this SRV record. ttl : float @@ -2106,7 +2106,7 @@ def create_record_srv( raise AppwriteException('Missing required parameter: "weight"') if port is None: raise AppwriteException('Missing required parameter: "port"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2164,7 +2164,7 @@ def update_record_srv( record_id : str DNS record unique ID. name : str - Record name (service name). + Record name relative to the domain: the service and protocol, such as `_sip._tcp`. A full name ending in the domain is also accepted and stored relative to it. value : str Target hostname for this SRV record. ttl : float @@ -2206,8 +2206,8 @@ def update_record_srv( raise AppwriteException('Missing required parameter: "weight"') if port is None: raise AppwriteException('Missing required parameter: "port"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2248,7 +2248,7 @@ def create_record_txt( domain_id : str Domain unique ID. name : str - Record name (subdomain) for the TXT record. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. ttl : float Time to live, in seconds. Must be between 1 and 2147483647. value : Optional[str] @@ -2274,7 +2274,7 @@ def create_record_txt( raise AppwriteException('Missing required parameter: "name"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) if value is not None: api_params['value'] = self._normalize_value(value) @@ -2317,7 +2317,7 @@ def update_record_txt( record_id : str DNS record unique ID. name : str - Record name (subdomain) for the TXT record. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str TXT record value. ttl : float @@ -2347,8 +2347,8 @@ def update_record_txt( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2403,8 +2403,8 @@ def get_record( raise AppwriteException('Missing required parameter: "domain_id"') if record_id is None or record_id == '': raise AppwriteException('Missing required parameter: "record_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) response = self.client.call( 'get', @@ -2450,8 +2450,8 @@ def delete_record( raise AppwriteException('Missing required parameter: "domain_id"') if record_id is None or record_id == '': raise AppwriteException('Missing required parameter: "record_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) response = self.client.call( 'delete', @@ -2501,7 +2501,7 @@ def update_team( raise AppwriteException('Missing required parameter: "domain_id"') if team_id is None: raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['teamId'] = self._normalize_value(team_id) response = self.client.call( @@ -2543,7 +2543,7 @@ def get_transfer_status( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -2584,7 +2584,7 @@ def get_zone( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -2606,7 +2606,7 @@ def update_zone( ) -> Domain: """ Update the DNS zone for the given domain using the provided zone file content. - All parsed records are imported and then the main domain document is returned. + All parsed records the domain does not already have are imported and then the main domain document is returned. Parameters ---------- @@ -2631,7 +2631,7 @@ def update_zone( raise AppwriteException('Missing required parameter: "domain_id"') if content is None: raise AppwriteException('Missing required parameter: "content"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['content'] = self._normalize_value(content) response = self.client.call( diff --git a/appwrite/services/functions.py b/appwrite/services/functions.py index fa109998..b20d8fa5 100644 --- a/appwrite/services/functions.py +++ b/appwrite/services/functions.py @@ -151,7 +151,7 @@ def create( runtime_specification : Optional[str] Runtime specification for the function executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. description : Optional[str] @@ -343,7 +343,7 @@ def get( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) response = self.client.call( 'get', @@ -433,7 +433,7 @@ def update( runtime_specification : Optional[str] Runtime specification for the function executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. When omitted, the current interval is kept unless schedule is set. description : Optional[str] @@ -457,7 +457,7 @@ def update( raise AppwriteException('Missing required parameter: "function_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['name'] = self._normalize_value(name) if runtime is not None: api_params['runtime'] = self._normalize_value(runtime) @@ -545,7 +545,7 @@ def delete( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) response = self.client.call( 'delete', @@ -591,7 +591,7 @@ def update_function_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) response = self.client.call( @@ -642,7 +642,7 @@ def list_deployments( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -712,7 +712,7 @@ def create_deployment( raise AppwriteException('Missing required parameter: "code"') if activate is None: raise AppwriteException('Missing required parameter: "activate"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if entrypoint is not None: api_params['entrypoint'] = self._normalize_value(entrypoint) if commands is not None: @@ -774,7 +774,7 @@ def create_duplicate_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) if build_id is not None: api_params['buildId'] = self._normalize_value(build_id) @@ -848,7 +848,7 @@ def create_template_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['repository'] = self._normalize_value(repository) api_params['owner'] = self._normalize_value(owner) api_params['rootDirectory'] = self._normalize_value(root_directory) @@ -911,7 +911,7 @@ def create_vcs_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['type'] = self._normalize_value(type) api_params['reference'] = self._normalize_value(reference) if activate is not None: @@ -961,8 +961,8 @@ def get_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'get', @@ -1007,8 +1007,8 @@ def delete_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'delete', @@ -1060,8 +1060,8 @@ def get_deployment_download( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) if type is not None: api_params['type'] = self._normalize_value(type) if token is not None: @@ -1110,8 +1110,8 @@ def update_deployment_status( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'patch', @@ -1158,7 +1158,7 @@ def list_executions( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1220,7 +1220,7 @@ def create_execution( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if body is not None: api_params['body'] = self._normalize_value(body) if xasync is not None: @@ -1278,8 +1278,8 @@ def get_execution( raise AppwriteException('Missing required parameter: "function_id"') if execution_id is None or execution_id == '': raise AppwriteException('Missing required parameter: "execution_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{executionId}', str(self._normalize_value(execution_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{executionId}', self._encode_path(execution_id)) response = self.client.call( 'get', @@ -1324,8 +1324,8 @@ def delete_execution( raise AppwriteException('Missing required parameter: "function_id"') if execution_id is None or execution_id == '': raise AppwriteException('Missing required parameter: "execution_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{executionId}', str(self._normalize_value(execution_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{executionId}', self._encode_path(execution_id)) response = self.client.call( 'delete', @@ -1372,7 +1372,7 @@ def list_variables( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1437,7 +1437,7 @@ def create_variable( raise AppwriteException('Missing required parameter: "key"') if value is None: raise AppwriteException('Missing required parameter: "value"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['variableId'] = self._normalize_value(variable_id) api_params['key'] = self._normalize_value(key) api_params['value'] = self._normalize_value(value) @@ -1490,8 +1490,8 @@ def get_variable( raise AppwriteException('Missing required parameter: "function_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'get', @@ -1548,8 +1548,8 @@ def update_variable( raise AppwriteException('Missing required parameter: "function_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) if key is not None: api_params['key'] = self._normalize_value(key) if value is not None: @@ -1603,8 +1603,8 @@ def delete_variable( raise AppwriteException('Missing required parameter: "function_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/messaging.py b/appwrite/services/messaging.py index df5083f7..cab499ba 100644 --- a/appwrite/services/messaging.py +++ b/appwrite/services/messaging.py @@ -251,7 +251,7 @@ def update_email( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if topics is not None: api_params['topics'] = self._normalize_value(topics) if users is not None: @@ -513,7 +513,7 @@ def update_push( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if topics is not None: api_params['topics'] = self._normalize_value(topics) if users is not None: @@ -682,7 +682,7 @@ def update_sms( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if topics is not None: api_params['topics'] = self._normalize_value(topics) if users is not None: @@ -735,7 +735,7 @@ def get_message( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) response = self.client.call( 'get', @@ -775,7 +775,7 @@ def delete( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) response = self.client.call( 'delete', @@ -822,7 +822,7 @@ def list_targets( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1013,7 +1013,7 @@ def update_apns_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1142,7 +1142,7 @@ def update_appwrite_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1257,7 +1257,7 @@ def update_fcm_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1418,7 +1418,7 @@ def update_mailgun_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if api_key is not None: @@ -1559,7 +1559,7 @@ def update_msg91_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1708,7 +1708,7 @@ def update_resend_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1861,7 +1861,7 @@ def update_sendgrid_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2030,7 +2030,7 @@ def update_ses_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2236,7 +2236,7 @@ def update_smtp_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if host is not None: @@ -2385,7 +2385,7 @@ def update_telesign_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2518,7 +2518,7 @@ def update_textmagic_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2651,7 +2651,7 @@ def update_twilio_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2784,7 +2784,7 @@ def update_vonage_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2835,7 +2835,7 @@ def get_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) response = self.client.call( 'get', @@ -2875,7 +2875,7 @@ def delete_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) response = self.client.call( 'delete', @@ -3032,7 +3032,7 @@ def get_topic( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) response = self.client.call( 'get', @@ -3087,7 +3087,7 @@ def update_topic( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) if name is not None: api_params['name'] = self._normalize_value(name) if subscribe is not None: @@ -3138,7 +3138,7 @@ def delete_topic( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) response = self.client.call( 'delete', @@ -3188,7 +3188,7 @@ def list_subscribers( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -3244,7 +3244,7 @@ def create_subscriber( raise AppwriteException('Missing required parameter: "subscriber_id"') if target_id is None: raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) api_params['subscriberId'] = self._normalize_value(subscriber_id) api_params['targetId'] = self._normalize_value(target_id) @@ -3292,8 +3292,8 @@ def get_subscriber( raise AppwriteException('Missing required parameter: "topic_id"') if subscriber_id is None or subscriber_id == '': raise AppwriteException('Missing required parameter: "subscriber_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) - api_path = api_path.replace('{subscriberId}', str(self._normalize_value(subscriber_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) + api_path = api_path.replace('{subscriberId}', self._encode_path(subscriber_id)) response = self.client.call( 'get', @@ -3338,8 +3338,8 @@ def delete_subscriber( raise AppwriteException('Missing required parameter: "topic_id"') if subscriber_id is None or subscriber_id == '': raise AppwriteException('Missing required parameter: "subscriber_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) - api_path = api_path.replace('{subscriberId}', str(self._normalize_value(subscriber_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) + api_path = api_path.replace('{subscriberId}', self._encode_path(subscriber_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/migrations.py b/appwrite/services/migrations.py new file mode 100644 index 00000000..cb67bc08 --- /dev/null +++ b/appwrite/services/migrations.py @@ -0,0 +1,955 @@ +from ..service import Service +from urllib.parse import quote +from typing import Any, Dict, List, Optional, Union +from ..exception import AppwriteException +from appwrite.utils.deprecated import deprecated +from ..models.migration_list import MigrationList +from ..enums.appwrite_migration_resource import AppwriteMigrationResource +from ..enums.on_duplicate import OnDuplicate +from ..models.migration import Migration +from ..enums.firebase_migration_resource import FirebaseMigrationResource +from ..models.migration_report import MigrationReport +from ..enums.n_host_migration_resource import NHostMigrationResource +from ..enums.supabase_migration_resource import SupabaseMigrationResource + + +class Migrations(Service): + + def __init__(self, client) -> None: + super(Migrations, self).__init__(client) + + def list( + self, + queries: Optional[List[str]] = None, + search: Optional[str] = None, + total: Optional[bool] = None, + ) -> MigrationList: + """ + List all migrations in the current project. This endpoint returns a list of all migrations including their status, progress, and any errors that occurred during the migration process. + + Parameters + ---------- + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, stage, source, destination, resources, resourceId, resourceInternalId, resourceType, parentResourceId, parentResourceInternalId, parentResourceType, destinationResourceId, destinationResourceInternalId, destinationResourceType, statusCounters, resourceData, errors + search : Optional[str] + Search term to filter your list results. Max length: 256 chars. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + Returns + ------- + MigrationList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations' + api_params = {} + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if search is not None: + api_params['search'] = self._normalize_value(search) + if total is not None: + api_params['total'] = self._normalize_value(total) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=MigrationList) + + def create_appwrite_migration( + self, + resources: List[AppwriteMigrationResource], + endpoint: str, + project_id: str, + api_key: str, + on_duplicate: Optional[OnDuplicate] = None, + ) -> Migration: + """ + Migrate data from another Appwrite project to your current project. This endpoint allows you to migrate resources like databases, collections, documents, users, and files from an existing Appwrite project. + + Parameters + ---------- + resources : List[AppwriteMigrationResource] + List of resources to migrate + endpoint : str + Source Appwrite endpoint + project_id : str + Source Project ID + api_key : str + Source API Key + on_duplicate : Optional[OnDuplicate] + Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/appwrite' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if endpoint is None: + raise AppwriteException('Missing required parameter: "endpoint"') + if project_id is None: + raise AppwriteException('Missing required parameter: "project_id"') + if api_key is None: + raise AppwriteException('Missing required parameter: "api_key"') + api_params['resources'] = self._normalize_value(resources) + api_params['endpoint'] = self._normalize_value(endpoint) + api_params['projectId'] = self._normalize_value(project_id) + api_params['apiKey'] = self._normalize_value(api_key) + if on_duplicate is not None: + api_params['onDuplicate'] = self._normalize_value(on_duplicate) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_csv_export( + self, + database_id: str, + collection_id: str, + filename: str, + columns: Optional[List[str]] = None, + queries: Optional[List[str]] = None, + delimiter: Optional[str] = None, + enclosure: Optional[str] = None, + escape: Optional[str] = None, + header: Optional[bool] = None, + notify: Optional[bool] = None, + ) -> Migration: + """ + Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete. + + Parameters + ---------- + database_id : str + Database ID containing the source collection. + collection_id : str + Collection ID to export documents from. + filename : str + The name of the file to be created for the export, excluding the .csv extension. + columns : Optional[List[str]] + List of attributes to export. If empty, all attributes will be exported. You can use the `*` wildcard to export all attributes from the collection. + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK to filter documents to export. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. + delimiter : Optional[str] + The character that separates each column value. Default is comma. + enclosure : Optional[str] + The character that encloses each column value. Default is double quotes. + escape : Optional[str] + The escape character for the enclosure character. Default is double quotes. + header : Optional[bool] + Whether to include the header row with column names. Default is true. + notify : Optional[bool] + Set to true to receive an email when the export is complete. Default is true. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/csv/exports' + api_params = {} + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + if filename is None: + raise AppwriteException('Missing required parameter: "filename"') + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + api_params['filename'] = self._normalize_value(filename) + if columns is not None: + api_params['columns'] = self._normalize_value(columns) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if delimiter is not None: + api_params['delimiter'] = self._normalize_value(delimiter) + if enclosure is not None: + api_params['enclosure'] = self._normalize_value(enclosure) + if escape is not None: + api_params['escape'] = self._normalize_value(escape) + if header is not None: + api_params['header'] = self._normalize_value(header) + if notify is not None: + api_params['notify'] = self._normalize_value(notify) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_csv_import( + self, + bucket_id: str, + file_id: str, + database_id: str, + collection_id: str, + internal_file: Optional[bool] = None, + on_duplicate: Optional[OnDuplicate] = None, + ) -> Migration: + """ + Import documents from a CSV file into your Appwrite database. This endpoint allows you to import documents from a CSV file uploaded to Appwrite Storage bucket. + + Parameters + ---------- + bucket_id : str + Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket). + file_id : str + File ID. + database_id : str + Database ID containing the target collection. + collection_id : str + Collection ID to import documents into. + internal_file : Optional[bool] + Is the file stored in an internal bucket? + on_duplicate : Optional[OnDuplicate] + Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/csv/imports' + api_params = {} + if bucket_id is None: + raise AppwriteException('Missing required parameter: "bucket_id"') + if file_id is None: + raise AppwriteException('Missing required parameter: "file_id"') + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + api_params['bucketId'] = self._normalize_value(bucket_id) + api_params['fileId'] = self._normalize_value(file_id) + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + if internal_file is not None: + api_params['internalFile'] = self._normalize_value(internal_file) + if on_duplicate is not None: + api_params['onDuplicate'] = self._normalize_value(on_duplicate) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_firebase_migration( + self, + resources: List[FirebaseMigrationResource], + service_account: str, + ) -> Migration: + """ + Migrate data from a Firebase project to your Appwrite project. This endpoint allows you to migrate resources like authentication and other supported services from a Firebase project. + + Parameters + ---------- + resources : List[FirebaseMigrationResource] + List of resources to migrate + service_account : str + JSON of the Firebase service account credentials + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/firebase' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if service_account is None: + raise AppwriteException('Missing required parameter: "service_account"') + api_params['resources'] = self._normalize_value(resources) + api_params['serviceAccount'] = self._normalize_value(service_account) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def get_firebase_report( + self, + resources: List[FirebaseMigrationResource], + service_account: str, + ) -> MigrationReport: + """ + Generate a report of the data in a Firebase project before migrating. This endpoint analyzes the source project and returns information about the resources that can be migrated. + + Parameters + ---------- + resources : List[FirebaseMigrationResource] + List of resources to migrate + service_account : str + JSON of the Firebase service account credentials + Returns + ------- + MigrationReport + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/firebase/report' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if service_account is None: + raise AppwriteException('Missing required parameter: "service_account"') + api_params['resources'] = self._normalize_value(resources) + api_params['serviceAccount'] = self._normalize_value(service_account) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=MigrationReport) + + def create_json_export( + self, + database_id: str, + collection_id: str, + filename: str, + columns: Optional[List[str]] = None, + queries: Optional[List[str]] = None, + notify: Optional[bool] = None, + ) -> Migration: + """ + Export documents to a JSON file from your Appwrite database. This endpoint allows you to export documents to a JSON file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete. + + Parameters + ---------- + database_id : str + Database ID containing the source collection. + collection_id : str + Collection ID to export documents from. + filename : str + The name of the file to be created for the export, excluding the .json extension. + columns : Optional[List[str]] + List of attributes to export. If empty, all attributes will be exported. You can use the `*` wildcard to export all attributes from the collection. + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK to filter documents to export. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. + notify : Optional[bool] + Set to true to receive an email when the export is complete. Default is true. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/json/exports' + api_params = {} + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + if filename is None: + raise AppwriteException('Missing required parameter: "filename"') + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + api_params['filename'] = self._normalize_value(filename) + if columns is not None: + api_params['columns'] = self._normalize_value(columns) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if notify is not None: + api_params['notify'] = self._normalize_value(notify) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_json_import( + self, + bucket_id: str, + file_id: str, + database_id: str, + collection_id: str, + internal_file: Optional[bool] = None, + on_duplicate: Optional[OnDuplicate] = None, + ) -> Migration: + """ + Import documents from a JSON file into your Appwrite database. This endpoint allows you to import documents from a JSON file uploaded to Appwrite Storage bucket. + + Parameters + ---------- + bucket_id : str + Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket). + file_id : str + File ID. + database_id : str + Database ID containing the target collection. + collection_id : str + Collection ID to import documents into. + internal_file : Optional[bool] + Is the file stored in an internal bucket? + on_duplicate : Optional[OnDuplicate] + Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/json/imports' + api_params = {} + if bucket_id is None: + raise AppwriteException('Missing required parameter: "bucket_id"') + if file_id is None: + raise AppwriteException('Missing required parameter: "file_id"') + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + api_params['bucketId'] = self._normalize_value(bucket_id) + api_params['fileId'] = self._normalize_value(file_id) + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + if internal_file is not None: + api_params['internalFile'] = self._normalize_value(internal_file) + if on_duplicate is not None: + api_params['onDuplicate'] = self._normalize_value(on_duplicate) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_n_host_migration( + self, + resources: List[NHostMigrationResource], + subdomain: str, + region: str, + admin_secret: str, + database: str, + username: str, + password: str, + port: Optional[float] = None, + ) -> Migration: + """ + Migrate data from an NHost project to your Appwrite project. This endpoint allows you to migrate resources like authentication, databases, and other supported services from an NHost project. + + Parameters + ---------- + resources : List[NHostMigrationResource] + List of resources to migrate + subdomain : str + Source's Subdomain + region : str + Source's Region + admin_secret : str + Source's Admin Secret + database : str + Source's Database Name + username : str + Source's Database Username + password : str + Source's Database Password + port : Optional[float] + Source's Database Port + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/nhost' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if subdomain is None: + raise AppwriteException('Missing required parameter: "subdomain"') + if region is None: + raise AppwriteException('Missing required parameter: "region"') + if admin_secret is None: + raise AppwriteException('Missing required parameter: "admin_secret"') + if database is None: + raise AppwriteException('Missing required parameter: "database"') + if username is None: + raise AppwriteException('Missing required parameter: "username"') + if password is None: + raise AppwriteException('Missing required parameter: "password"') + api_params['resources'] = self._normalize_value(resources) + api_params['subdomain'] = self._normalize_value(subdomain) + api_params['region'] = self._normalize_value(region) + api_params['adminSecret'] = self._normalize_value(admin_secret) + api_params['database'] = self._normalize_value(database) + api_params['username'] = self._normalize_value(username) + api_params['password'] = self._normalize_value(password) + if port is not None: + api_params['port'] = self._normalize_value(port) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def get_n_host_report( + self, + resources: List[NHostMigrationResource], + subdomain: str, + region: str, + admin_secret: str, + database: str, + username: str, + password: str, + port: Optional[float] = None, + ) -> MigrationReport: + """ + Generate a detailed report of the data in an NHost project before migrating. This endpoint analyzes the source project and returns information about the resources that can be migrated. + + Parameters + ---------- + resources : List[NHostMigrationResource] + List of resources to migrate. + subdomain : str + Source's Subdomain. + region : str + Source's Region. + admin_secret : str + Source's Admin Secret. + database : str + Source's Database Name. + username : str + Source's Database Username. + password : str + Source's Database Password. + port : Optional[float] + Source's Database Port. + Returns + ------- + MigrationReport + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/nhost/report' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if subdomain is None: + raise AppwriteException('Missing required parameter: "subdomain"') + if region is None: + raise AppwriteException('Missing required parameter: "region"') + if admin_secret is None: + raise AppwriteException('Missing required parameter: "admin_secret"') + if database is None: + raise AppwriteException('Missing required parameter: "database"') + if username is None: + raise AppwriteException('Missing required parameter: "username"') + if password is None: + raise AppwriteException('Missing required parameter: "password"') + api_params['resources'] = self._normalize_value(resources) + api_params['subdomain'] = self._normalize_value(subdomain) + api_params['region'] = self._normalize_value(region) + api_params['adminSecret'] = self._normalize_value(admin_secret) + api_params['database'] = self._normalize_value(database) + api_params['username'] = self._normalize_value(username) + api_params['password'] = self._normalize_value(password) + if port is not None: + api_params['port'] = self._normalize_value(port) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=MigrationReport) + + def create_supabase_migration( + self, + resources: List[SupabaseMigrationResource], + endpoint: str, + api_key: str, + database_host: str, + username: str, + password: str, + port: Optional[float] = None, + ) -> Migration: + """ + Migrate data from a Supabase project to your Appwrite project. This endpoint allows you to migrate resources like authentication, databases, and other supported services from a Supabase project. + + Parameters + ---------- + resources : List[SupabaseMigrationResource] + List of resources to migrate + endpoint : str + Source's Supabase Endpoint + api_key : str + Source's API Key + database_host : str + Source's Database Host + username : str + Source's Database Username + password : str + Source's Database Password + port : Optional[float] + Source's Database Port + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/supabase' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if endpoint is None: + raise AppwriteException('Missing required parameter: "endpoint"') + if api_key is None: + raise AppwriteException('Missing required parameter: "api_key"') + if database_host is None: + raise AppwriteException('Missing required parameter: "database_host"') + if username is None: + raise AppwriteException('Missing required parameter: "username"') + if password is None: + raise AppwriteException('Missing required parameter: "password"') + api_params['resources'] = self._normalize_value(resources) + api_params['endpoint'] = self._normalize_value(endpoint) + api_params['apiKey'] = self._normalize_value(api_key) + api_params['databaseHost'] = self._normalize_value(database_host) + api_params['username'] = self._normalize_value(username) + api_params['password'] = self._normalize_value(password) + if port is not None: + api_params['port'] = self._normalize_value(port) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def get_supabase_report( + self, + resources: List[SupabaseMigrationResource], + endpoint: str, + api_key: str, + database_host: str, + username: str, + password: str, + port: Optional[float] = None, + ) -> MigrationReport: + """ + Generate a report of the data in a Supabase project before migrating. This endpoint analyzes the source project and returns information about the resources that can be migrated. + + Parameters + ---------- + resources : List[SupabaseMigrationResource] + List of resources to migrate + endpoint : str + Source's Supabase Endpoint. + api_key : str + Source's API Key. + database_host : str + Source's Database Host. + username : str + Source's Database Username. + password : str + Source's Database Password. + port : Optional[float] + Source's Database Port. + Returns + ------- + MigrationReport + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/supabase/report' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if endpoint is None: + raise AppwriteException('Missing required parameter: "endpoint"') + if api_key is None: + raise AppwriteException('Missing required parameter: "api_key"') + if database_host is None: + raise AppwriteException('Missing required parameter: "database_host"') + if username is None: + raise AppwriteException('Missing required parameter: "username"') + if password is None: + raise AppwriteException('Missing required parameter: "password"') + api_params['resources'] = self._normalize_value(resources) + api_params['endpoint'] = self._normalize_value(endpoint) + api_params['apiKey'] = self._normalize_value(api_key) + api_params['databaseHost'] = self._normalize_value(database_host) + api_params['username'] = self._normalize_value(username) + api_params['password'] = self._normalize_value(password) + if port is not None: + api_params['port'] = self._normalize_value(port) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=MigrationReport) + + def get( + self, + migration_id: str, + ) -> Migration: + """ + Get a migration by its unique ID. This endpoint returns detailed information about a specific migration including its current status, progress, and any errors that occurred during the migration process. + + Parameters + ---------- + migration_id : str + Migration unique ID. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/{migrationId}' + api_params = {} + if migration_id is None or migration_id == '': + raise AppwriteException('Missing required parameter: "migration_id"') + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def retry( + self, + migration_id: str, + ) -> Migration: + """ + Retry a failed migration. This endpoint allows you to retry a migration that has previously failed. + + Parameters + ---------- + migration_id : str + Migration unique ID. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/{migrationId}' + api_params = {} + if migration_id is None or migration_id == '': + raise AppwriteException('Missing required parameter: "migration_id"') + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) + + response = self.client.call( + 'patch', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def delete( + self, + migration_id: str, + ) -> Dict[str, Any]: + """ + Delete a migration by its unique ID. This endpoint allows you to remove a migration from your project's migration history. + + Parameters + ---------- + migration_id : str + Migration ID. + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/{migrationId}' + api_params = {} + if migration_id is None or migration_id == '': + raise AppwriteException('Missing required parameter: "migration_id"') + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) + + response = self.client.call( + 'delete', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return response diff --git a/appwrite/services/mongo.py b/appwrite/services/mongo.py index f377fe6c..27bd10ba 100644 --- a/appwrite/services/mongo.py +++ b/appwrite/services/mongo.py @@ -236,7 +236,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -336,7 +336,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if status is not None: @@ -419,7 +419,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -466,7 +466,7 @@ def list_backups( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -513,7 +513,7 @@ def create_backup( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) @@ -562,7 +562,7 @@ def list_backup_policies( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -632,7 +632,7 @@ def create_backup_policy( raise AppwriteException('Missing required parameter: "schedule"') if retention is None: raise AppwriteException('Missing required parameter: "retention"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['policyId'] = self._normalize_value(policy_id) api_params['name'] = self._normalize_value(name) api_params['schedule'] = self._normalize_value(schedule) @@ -686,8 +686,8 @@ def get_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -744,8 +744,8 @@ def update_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if schedule is not None: @@ -799,8 +799,8 @@ def delete_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -870,7 +870,7 @@ def update_backup_storage( raise AppwriteException('Missing required parameter: "access_key"') if secret_key is None: raise AppwriteException('Missing required parameter: "secret_key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['provider'] = self._normalize_value(provider) api_params['bucket'] = self._normalize_value(bucket) if region is not None: @@ -926,8 +926,8 @@ def get_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'get', @@ -972,8 +972,8 @@ def delete_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'delete', @@ -1014,7 +1014,7 @@ def list_branches( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1060,7 +1060,7 @@ def create_branch( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if branch_id is not None: api_params['branchId'] = self._normalize_value(branch_id) if ttl is not None: @@ -1110,8 +1110,8 @@ def delete_branch( raise AppwriteException('Missing required parameter: "database_id"') if branch_id is None or branch_id == '': raise AppwriteException('Missing required parameter: "branch_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{branchId}', str(self._normalize_value(branch_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{branchId}', self._encode_path(branch_id)) response = self.client.call( 'delete', @@ -1152,7 +1152,7 @@ def update_credentials( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'patch', @@ -1196,7 +1196,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1249,7 +1249,7 @@ def update_maintenance( raise AppwriteException('Missing required parameter: "day"') if hour_utc is None: raise AppwriteException('Missing required parameter: "hour_utc"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['day'] = self._normalize_value(day) api_params['hourUtc'] = self._normalize_value(hour_utc) @@ -1300,7 +1300,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if target_type is None: raise AppwriteException('Missing required parameter: "target_type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetType'] = self._normalize_value(target_type) if specification is not None: api_params['specification'] = self._normalize_value(specification) @@ -1353,7 +1353,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1399,7 +1399,7 @@ def get_pitr( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1439,7 +1439,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1491,7 +1491,7 @@ def list_restorations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if type is not None: @@ -1562,7 +1562,7 @@ def create_restoration( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) if backup_id is not None: @@ -1622,8 +1622,8 @@ def get_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', @@ -1671,8 +1671,8 @@ def promote_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'post', @@ -1713,7 +1713,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1758,7 +1758,7 @@ def create_upgrade( raise AppwriteException('Missing required parameter: "database_id"') if target_version is None: raise AppwriteException('Missing required parameter: "target_version"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetVersion'] = self._normalize_value(target_version) response = self.client.call( diff --git a/appwrite/services/mysql.py b/appwrite/services/mysql.py index 0e402fe2..064693a6 100644 --- a/appwrite/services/mysql.py +++ b/appwrite/services/mysql.py @@ -238,7 +238,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -338,7 +338,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if status is not None: @@ -421,7 +421,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -468,7 +468,7 @@ def list_backups( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -515,7 +515,7 @@ def create_backup( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) @@ -564,7 +564,7 @@ def list_backup_policies( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -634,7 +634,7 @@ def create_backup_policy( raise AppwriteException('Missing required parameter: "schedule"') if retention is None: raise AppwriteException('Missing required parameter: "retention"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['policyId'] = self._normalize_value(policy_id) api_params['name'] = self._normalize_value(name) api_params['schedule'] = self._normalize_value(schedule) @@ -688,8 +688,8 @@ def get_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -746,8 +746,8 @@ def update_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if schedule is not None: @@ -801,8 +801,8 @@ def delete_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -872,7 +872,7 @@ def update_backup_storage( raise AppwriteException('Missing required parameter: "access_key"') if secret_key is None: raise AppwriteException('Missing required parameter: "secret_key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['provider'] = self._normalize_value(provider) api_params['bucket'] = self._normalize_value(bucket) if region is not None: @@ -928,8 +928,8 @@ def get_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'get', @@ -974,8 +974,8 @@ def delete_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'delete', @@ -1016,7 +1016,7 @@ def list_branches( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1062,7 +1062,7 @@ def create_branch( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if branch_id is not None: api_params['branchId'] = self._normalize_value(branch_id) if ttl is not None: @@ -1112,8 +1112,8 @@ def delete_branch( raise AppwriteException('Missing required parameter: "database_id"') if branch_id is None or branch_id == '': raise AppwriteException('Missing required parameter: "branch_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{branchId}', str(self._normalize_value(branch_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{branchId}', self._encode_path(branch_id)) response = self.client.call( 'delete', @@ -1154,7 +1154,7 @@ def update_credentials( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'patch', @@ -1206,7 +1206,7 @@ def create_execution( raise AppwriteException('Missing required parameter: "database_id"') if sql is None: raise AppwriteException('Missing required parameter: "sql"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['sql'] = self._normalize_value(sql) if bindings is not None: api_params['bindings'] = self._normalize_value(bindings) @@ -1255,7 +1255,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1308,7 +1308,7 @@ def update_maintenance( raise AppwriteException('Missing required parameter: "day"') if hour_utc is None: raise AppwriteException('Missing required parameter: "hour_utc"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['day'] = self._normalize_value(day) api_params['hourUtc'] = self._normalize_value(hour_utc) @@ -1359,7 +1359,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if target_type is None: raise AppwriteException('Missing required parameter: "target_type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetType'] = self._normalize_value(target_type) if specification is not None: api_params['specification'] = self._normalize_value(specification) @@ -1412,7 +1412,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1458,7 +1458,7 @@ def get_pitr( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1498,7 +1498,7 @@ def get_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1562,7 +1562,7 @@ def update_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if mode is not None: api_params['mode'] = self._normalize_value(mode) if max_connections is not None: @@ -1619,7 +1619,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1671,7 +1671,7 @@ def list_restorations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if type is not None: @@ -1742,7 +1742,7 @@ def create_restoration( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) if backup_id is not None: @@ -1802,8 +1802,8 @@ def get_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', @@ -1851,8 +1851,8 @@ def promote_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'post', @@ -1893,7 +1893,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1938,7 +1938,7 @@ def create_upgrade( raise AppwriteException('Missing required parameter: "database_id"') if target_version is None: raise AppwriteException('Missing required parameter: "target_version"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetVersion'] = self._normalize_value(target_version) response = self.client.call( diff --git a/appwrite/services/oauth2.py b/appwrite/services/oauth2.py index 774dcf2c..a4d6e729 100644 --- a/appwrite/services/oauth2.py +++ b/appwrite/services/oauth2.py @@ -52,7 +52,7 @@ def approve( api_params = {} if grant_id is None: raise AppwriteException('Missing required parameter: "grant_id"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['grant_id'] = self._normalize_value(grant_id) if authorization_details is not None: api_params['authorization_details'] = self._normalize_value(authorization_details) @@ -134,7 +134,7 @@ def authorize( api_path = '/oauth2/{project_id}/authorize' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if client_id is not None: api_params['client_id'] = self._normalize_value(client_id) if redirect_uri is not None: @@ -238,7 +238,7 @@ def authorize_post( api_path = '/oauth2/{project_id}/authorize' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if client_id is not None: api_params['client_id'] = self._normalize_value(client_id) if redirect_uri is not None: @@ -316,7 +316,7 @@ def create_device_authorization( api_path = '/oauth2/{project_id}/device_authorization' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if client_id is not None: api_params['client_id'] = self._normalize_value(client_id) if scope is not None: @@ -366,7 +366,7 @@ def create_grant( api_params = {} if user_code is None: raise AppwriteException('Missing required parameter: "user_code"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['user_code'] = self._normalize_value(user_code) response = self.client.call( @@ -407,8 +407,8 @@ def get_grant( api_params = {} if grant_id is None or grant_id == '': raise AppwriteException('Missing required parameter: "grant_id"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) - api_path = api_path.replace('{grant_id}', str(self._normalize_value(grant_id))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) + api_path = api_path.replace('{grant_id}', self._encode_path(grant_id)) response = self.client.call( 'get', @@ -456,7 +456,7 @@ def introspect( api_params = {} if token is None: raise AppwriteException('Missing required parameter: "token"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['token'] = self._normalize_value(token) if token_type_hint is not None: api_params['token_type_hint'] = self._normalize_value(token_type_hint) @@ -507,7 +507,7 @@ def list_organizations( api_path = '/oauth2/{project_id}/organizations' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if limit is not None: api_params['limit'] = self._normalize_value(limit) if offset is not None: @@ -592,7 +592,7 @@ def create_par( raise AppwriteException('Missing required parameter: "redirect_uri"') if response_type is None: raise AppwriteException('Missing required parameter: "response_type"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['client_id'] = self._normalize_value(client_id) api_params['redirect_uri'] = self._normalize_value(redirect_uri) api_params['response_type'] = self._normalize_value(response_type) @@ -659,7 +659,7 @@ def list_projects( api_path = '/oauth2/{project_id}/projects' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if limit is not None: api_params['limit'] = self._normalize_value(limit) if offset is not None: @@ -704,7 +704,7 @@ def reject( api_params = {} if grant_id is None: raise AppwriteException('Missing required parameter: "grant_id"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['grant_id'] = self._normalize_value(grant_id) response = self.client.call( @@ -754,7 +754,7 @@ def revoke( api_params = {} if token is None: raise AppwriteException('Missing required parameter: "token"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['token'] = self._normalize_value(token) if token_type_hint is not None: api_params['token_type_hint'] = self._normalize_value(token_type_hint) @@ -828,7 +828,7 @@ def create_token( api_params = {} if grant_type is None: raise AppwriteException('Missing required parameter: "grant_type"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['grant_type'] = self._normalize_value(grant_type) if code is not None: api_params['code'] = self._normalize_value(code) diff --git a/appwrite/services/organization.py b/appwrite/services/organization.py index ecb7efdb..7b2a9fcf 100644 --- a/appwrite/services/organization.py +++ b/appwrite/services/organization.py @@ -255,7 +255,7 @@ def get_installation( api_params = {} if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'get', @@ -298,7 +298,7 @@ def update_installation( api_params = {} if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) if authorization_details is not None: api_params['authorizationDetails'] = self._normalize_value(authorization_details) @@ -341,7 +341,7 @@ def delete_installation( api_params = {} if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'delete', @@ -497,7 +497,7 @@ def get_membership( api_params = {} if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'get', @@ -542,7 +542,7 @@ def update_membership( raise AppwriteException('Missing required parameter: "membership_id"') if roles is None: raise AppwriteException('Missing required parameter: "roles"') - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) api_params['roles'] = self._normalize_value(roles) response = self.client.call( @@ -584,7 +584,7 @@ def delete_membership( api_params = {} if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'delete', @@ -726,7 +726,7 @@ def get_project( api_params = {} if project_id is None or project_id == '': raise AppwriteException('Missing required parameter: "project_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) response = self.client.call( 'get', @@ -771,7 +771,7 @@ def update_project( raise AppwriteException('Missing required parameter: "project_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -813,7 +813,7 @@ def delete_project( api_params = {} if project_id is None or project_id == '': raise AppwriteException('Missing required parameter: "project_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) response = self.client.call( 'delete', @@ -860,7 +860,7 @@ def list_project_keys( api_params = {} if project_id is None or project_id == '': raise AppwriteException('Missing required parameter: "project_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -916,7 +916,7 @@ def create_ephemeral_project_key( raise AppwriteException('Missing required parameter: "scopes"') if duration is None: raise AppwriteException('Missing required parameter: "duration"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) api_params['scopes'] = self._normalize_value(scopes) api_params['duration'] = self._normalize_value(duration) @@ -964,8 +964,8 @@ def get_project_key( raise AppwriteException('Missing required parameter: "project_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'get', @@ -1026,8 +1026,8 @@ def update_project_key( raise AppwriteException('Missing required parameter: "name"') if scopes is None: raise AppwriteException('Missing required parameter: "scopes"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) if expire is not None: @@ -1079,8 +1079,8 @@ def delete_project_key( raise AppwriteException('Missing required parameter: "project_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/postgresql.py b/appwrite/services/postgresql.py index a5692e05..85d50041 100644 --- a/appwrite/services/postgresql.py +++ b/appwrite/services/postgresql.py @@ -239,7 +239,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -339,7 +339,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if status is not None: @@ -422,7 +422,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -469,7 +469,7 @@ def list_backups( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -516,7 +516,7 @@ def create_backup( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) @@ -565,7 +565,7 @@ def list_backup_policies( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -635,7 +635,7 @@ def create_backup_policy( raise AppwriteException('Missing required parameter: "schedule"') if retention is None: raise AppwriteException('Missing required parameter: "retention"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['policyId'] = self._normalize_value(policy_id) api_params['name'] = self._normalize_value(name) api_params['schedule'] = self._normalize_value(schedule) @@ -689,8 +689,8 @@ def get_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -747,8 +747,8 @@ def update_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if schedule is not None: @@ -802,8 +802,8 @@ def delete_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -873,7 +873,7 @@ def update_backup_storage( raise AppwriteException('Missing required parameter: "access_key"') if secret_key is None: raise AppwriteException('Missing required parameter: "secret_key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['provider'] = self._normalize_value(provider) api_params['bucket'] = self._normalize_value(bucket) if region is not None: @@ -929,8 +929,8 @@ def get_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'get', @@ -975,8 +975,8 @@ def delete_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'delete', @@ -1017,7 +1017,7 @@ def list_branches( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1063,7 +1063,7 @@ def create_branch( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if branch_id is not None: api_params['branchId'] = self._normalize_value(branch_id) if ttl is not None: @@ -1113,8 +1113,8 @@ def delete_branch( raise AppwriteException('Missing required parameter: "database_id"') if branch_id is None or branch_id == '': raise AppwriteException('Missing required parameter: "branch_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{branchId}', str(self._normalize_value(branch_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{branchId}', self._encode_path(branch_id)) response = self.client.call( 'delete', @@ -1155,7 +1155,7 @@ def update_credentials( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'patch', @@ -1207,7 +1207,7 @@ def create_execution( raise AppwriteException('Missing required parameter: "database_id"') if sql is None: raise AppwriteException('Missing required parameter: "sql"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['sql'] = self._normalize_value(sql) if bindings is not None: api_params['bindings'] = self._normalize_value(bindings) @@ -1253,7 +1253,7 @@ def list_extensions( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1298,7 +1298,7 @@ def create_extension( raise AppwriteException('Missing required parameter: "database_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1345,8 +1345,8 @@ def delete_extension( raise AppwriteException('Missing required parameter: "database_id"') if extension_name is None or extension_name == '': raise AppwriteException('Missing required parameter: "extension_name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{extensionName}', str(self._normalize_value(extension_name))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{extensionName}', self._encode_path(extension_name)) response = self.client.call( 'delete', @@ -1390,7 +1390,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1443,7 +1443,7 @@ def update_maintenance( raise AppwriteException('Missing required parameter: "day"') if hour_utc is None: raise AppwriteException('Missing required parameter: "hour_utc"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['day'] = self._normalize_value(day) api_params['hourUtc'] = self._normalize_value(hour_utc) @@ -1494,7 +1494,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if target_type is None: raise AppwriteException('Missing required parameter: "target_type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetType'] = self._normalize_value(target_type) if specification is not None: api_params['specification'] = self._normalize_value(specification) @@ -1547,7 +1547,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1593,7 +1593,7 @@ def get_pitr( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1633,7 +1633,7 @@ def get_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1697,7 +1697,7 @@ def update_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if mode is not None: api_params['mode'] = self._normalize_value(mode) if max_connections is not None: @@ -1754,7 +1754,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1806,7 +1806,7 @@ def list_restorations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if type is not None: @@ -1877,7 +1877,7 @@ def create_restoration( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) if backup_id is not None: @@ -1937,8 +1937,8 @@ def get_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', @@ -1986,8 +1986,8 @@ def promote_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'post', @@ -2028,7 +2028,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -2073,7 +2073,7 @@ def create_upgrade( raise AppwriteException('Missing required parameter: "database_id"') if target_version is None: raise AppwriteException('Missing required parameter: "target_version"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetVersion'] = self._normalize_value(target_version) response = self.client.call( diff --git a/appwrite/services/presences.py b/appwrite/services/presences.py index 7066914a..d64a3c54 100644 --- a/appwrite/services/presences.py +++ b/appwrite/services/presences.py @@ -87,7 +87,7 @@ def get( api_params = {} if presence_id is None or presence_id == '': raise AppwriteException('Missing required parameter: "presence_id"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) response = self.client.call( 'get', @@ -146,7 +146,7 @@ def upsert( raise AppwriteException('Missing required parameter: "user_id"') if status is None: raise AppwriteException('Missing required parameter: "status"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) api_params['userId'] = self._normalize_value(user_id) api_params['status'] = self._normalize_value(status) if permissions is not None: @@ -215,7 +215,7 @@ def update( raise AppwriteException('Missing required parameter: "presence_id"') if user_id is None: raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) api_params['userId'] = self._normalize_value(user_id) if status is not None: api_params['status'] = self._normalize_value(status) @@ -267,7 +267,7 @@ def delete( api_params = {} if presence_id is None or presence_id == '': raise AppwriteException('Missing required parameter: "presence_id"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/project.py b/appwrite/services/project.py index d1d6da2d..de74643b 100644 --- a/appwrite/services/project.py +++ b/appwrite/services/project.py @@ -96,6 +96,7 @@ from ..models.policy_deny_disposable_email import PolicyDenyDisposableEmail from ..models.policy_deny_free_email import PolicyDenyFreeEmail from ..models.policy_deny_corporate_email import PolicyDenyCorporateEmail +from ..models.policy_deny_realtime_guests import PolicyDenyRealtimeGuests from ..models.policy_passkey import PolicyPasskey from ..enums.project_protocol_id import ProjectProtocolId from ..enums.project_service_id import ProjectServiceId @@ -207,7 +208,7 @@ def update_auth_method( raise AppwriteException('Missing required parameter: "method_id"') if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') - api_path = api_path.replace('{methodId}', str(self._normalize_value(method_id))) + api_path = api_path.replace('{methodId}', self._encode_path(method_id)) api_params['enabled'] = self._normalize_value(enabled) response = self.client.call( @@ -340,7 +341,7 @@ def get_environment( api_params = {} if environment_id is None or environment_id == '': raise AppwriteException('Missing required parameter: "environment_id"') - api_path = api_path.replace('{environmentId}', str(self._normalize_value(environment_id))) + api_path = api_path.replace('{environmentId}', self._encode_path(environment_id)) response = self.client.call( 'get', @@ -385,7 +386,7 @@ def update_environment( raise AppwriteException('Missing required parameter: "environment_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{environmentId}', str(self._normalize_value(environment_id))) + api_path = api_path.replace('{environmentId}', self._encode_path(environment_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -427,7 +428,7 @@ def delete_environment( api_params = {} if environment_id is None or environment_id == '': raise AppwriteException('Missing required parameter: "environment_id"') - api_path = api_path.replace('{environmentId}', str(self._normalize_value(environment_id))) + api_path = api_path.replace('{environmentId}', self._encode_path(environment_id)) response = self.client.call( 'delete', @@ -561,7 +562,7 @@ def get_key( api_params = {} if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'get', @@ -617,7 +618,7 @@ def update_key( raise AppwriteException('Missing required parameter: "name"') if scopes is None: raise AppwriteException('Missing required parameter: "scopes"') - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) if expire is not None: @@ -664,7 +665,7 @@ def delete_key( api_params = {} if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'delete', @@ -837,7 +838,7 @@ def get_mock_phone( api_params = {} if number is None or number == '': raise AppwriteException('Missing required parameter: "number"') - api_path = api_path.replace('{number}', str(self._normalize_value(number))) + api_path = api_path.replace('{number}', self._encode_path(number)) response = self.client.call( 'get', @@ -882,7 +883,7 @@ def update_mock_phone( raise AppwriteException('Missing required parameter: "number"') if otp is None: raise AppwriteException('Missing required parameter: "otp"') - api_path = api_path.replace('{number}', str(self._normalize_value(number))) + api_path = api_path.replace('{number}', self._encode_path(number)) api_params['otp'] = self._normalize_value(otp) response = self.client.call( @@ -924,7 +925,7 @@ def delete_mock_phone( api_params = {} if number is None or number == '': raise AppwriteException('Missing required parameter: "number"') - api_path = api_path.replace('{number}', str(self._normalize_value(number))) + api_path = api_path.replace('{number}', self._encode_path(number)) response = self.client.call( 'delete', @@ -3773,7 +3774,7 @@ def get_o_auth2_provider( api_params = {} if provider_id is None: raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) response = self.client.call( 'get', @@ -4068,7 +4069,7 @@ def update_android_platform( raise AppwriteException('Missing required parameter: "name"') if application_id is None: raise AppwriteException('Missing required parameter: "application_id"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['applicationId'] = self._normalize_value(application_id) if fingerprints is not None: @@ -4176,7 +4177,7 @@ def update_apple_platform( raise AppwriteException('Missing required parameter: "name"') if bundle_identifier is None: raise AppwriteException('Missing required parameter: "bundle_identifier"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['bundleIdentifier'] = self._normalize_value(bundle_identifier) @@ -4282,7 +4283,7 @@ def update_linux_platform( raise AppwriteException('Missing required parameter: "name"') if package_name is None: raise AppwriteException('Missing required parameter: "package_name"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['packageName'] = self._normalize_value(package_name) @@ -4388,7 +4389,7 @@ def update_web_platform( raise AppwriteException('Missing required parameter: "name"') if hostname is None: raise AppwriteException('Missing required parameter: "hostname"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['hostname'] = self._normalize_value(hostname) @@ -4494,7 +4495,7 @@ def update_windows_platform( raise AppwriteException('Missing required parameter: "name"') if package_identifier_name is None: raise AppwriteException('Missing required parameter: "package_identifier_name"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['packageIdentifierName'] = self._normalize_value(package_identifier_name) @@ -4543,7 +4544,7 @@ def get_platform( api_params = {} if platform_id is None or platform_id == '': raise AppwriteException('Missing required parameter: "platform_id"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) response = self.client.call( 'get', @@ -4600,7 +4601,7 @@ def delete_platform( api_params = {} if platform_id is None or platform_id == '': raise AppwriteException('Missing required parameter: "platform_id"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) response = self.client.call( 'delete', @@ -4823,6 +4824,47 @@ def update_deny_free_email_policy( return self._parse_response(response, model=ProjectModel) + def update_deny_realtime_guests_policy( + self, + enabled: bool, + ) -> ProjectModel: + """ + Configures if guests, connections without a signed-in user, may subscribe to realtime channels. When enabled, a guest can still connect and sign in over the connection, but cannot subscribe until it does. + + Parameters + ---------- + enabled : bool + Set whether or not to stop guests from subscribing to realtime channels. + Returns + ------- + ProjectModel + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/project/policies/deny-realtime-guests' + api_params = {} + if enabled is None: + raise AppwriteException('Missing required parameter: "enabled"') + api_params['enabled'] = self._normalize_value(enabled) + + response = self.client.call( + 'patch', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=ProjectModel) + def update_membership_privacy_policy( self, user_id: Optional[bool] = None, @@ -5501,6 +5543,7 @@ def get_policy( PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, + PolicyDenyRealtimeGuests, PolicyPasskey, ]: """ @@ -5509,10 +5552,10 @@ def get_policy( Parameters ---------- policy_id : ProjectPolicyId - Policy ID. Can be one of: password-dictionary, password-history, password-strength, password-personal-data, password-pwned, password-rotation, session-alert, session-duration, session-invalidation, session-limit, user-limit, membership-privacy, mfa-factors, deny-aliased-email, deny-disposable-email, deny-free-email, deny-corporate-email, passkey. + Policy ID. Can be one of: password-dictionary, password-history, password-strength, password-personal-data, password-pwned, password-rotation, session-alert, session-duration, session-invalidation, session-limit, user-limit, membership-privacy, mfa-factors, deny-aliased-email, deny-disposable-email, deny-free-email, deny-corporate-email, deny-realtime-guests, passkey. Returns ------- - Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyPasskey] + Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyDenyRealtimeGuests, PolicyPasskey] API response as one of the typed response models Raises @@ -5525,7 +5568,7 @@ def get_policy( api_params = {} if policy_id is None: raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -5590,6 +5633,9 @@ def get_policy( if response.get('$id') == 'deny-corporate-email': return self._parse_response(response, model=PolicyDenyCorporateEmail) + if response.get('$id') == 'deny-realtime-guests': + return self._parse_response(response, model=PolicyDenyRealtimeGuests) + if response.get('$id') == 'passkey': return self._parse_response(response, model=PolicyPasskey) @@ -5626,7 +5672,7 @@ def update_protocol( raise AppwriteException('Missing required parameter: "protocol_id"') if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') - api_path = api_path.replace('{protocolId}', str(self._normalize_value(protocol_id))) + api_path = api_path.replace('{protocolId}', self._encode_path(protocol_id)) api_params['enabled'] = self._normalize_value(enabled) response = self.client.call( @@ -5673,7 +5719,7 @@ def update_service( raise AppwriteException('Missing required parameter: "service_id"') if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') - api_path = api_path.replace('{serviceId}', str(self._normalize_value(service_id))) + api_path = api_path.replace('{serviceId}', self._encode_path(service_id)) api_params['enabled'] = self._normalize_value(enabled) response = self.client.call( @@ -5689,6 +5735,38 @@ def update_service( return self._parse_response(response, model=ProjectModel) + def delete_sessions( + self, + ) -> Dict[str, Any]: + """ + Delete all sessions in the project. Any session created before this request will be invalidated immediately: users will be signed out of every active client (web, mobile, server SDKs, JWTs), and will be able to sign in again afterwards. API keys are not affected by this action. + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/project/sessions' + api_params = {} + + response = self.client.call( + 'delete', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return response + def update_smtp( self, host: Optional[str] = None, @@ -5964,7 +6042,7 @@ def get_email_template( api_params = {} if template_id is None: raise AppwriteException('Missing required parameter: "template_id"') - api_path = api_path.replace('{templateId}', str(self._normalize_value(template_id))) + api_path = api_path.replace('{templateId}', self._encode_path(template_id)) if locale is not None: api_params['locale'] = self._normalize_value(locale) @@ -6113,7 +6191,7 @@ def get_variable( api_params = {} if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'get', @@ -6165,7 +6243,7 @@ def update_variable( api_params = {} if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) if key is not None: api_params['key'] = self._normalize_value(key) if value is not None: @@ -6214,7 +6292,7 @@ def delete_variable( api_params = {} if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/proxy.py b/appwrite/services/proxy.py index 5f4e5c68..273857c5 100644 --- a/appwrite/services/proxy.py +++ b/appwrite/services/proxy.py @@ -412,7 +412,7 @@ def get_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'get', @@ -452,7 +452,7 @@ def delete_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'delete', @@ -493,7 +493,7 @@ def update_rule_status( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'patch', diff --git a/appwrite/services/sites.py b/appwrite/services/sites.py index f3320934..23bbad9a 100644 --- a/appwrite/services/sites.py +++ b/appwrite/services/sites.py @@ -155,7 +155,7 @@ def create( runtime_specification : Optional[str] Runtime specification for the SSR executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. description : Optional[str] @@ -350,7 +350,7 @@ def get( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) response = self.client.call( 'get', @@ -442,7 +442,7 @@ def update( runtime_specification : Optional[str] Runtime specification for the SSR executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. description : Optional[str] @@ -466,7 +466,7 @@ def update( raise AppwriteException('Missing required parameter: "name"') if framework is None: raise AppwriteException('Missing required parameter: "framework"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['name'] = self._normalize_value(name) api_params['framework'] = self._normalize_value(framework) if enabled is not None: @@ -553,7 +553,7 @@ def delete( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) response = self.client.call( 'delete', @@ -599,7 +599,7 @@ def update_site_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) response = self.client.call( @@ -650,7 +650,7 @@ def list_deployments( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -717,7 +717,7 @@ def create_deployment( raise AppwriteException('Missing required parameter: "site_id"') if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if install_command is not None: api_params['installCommand'] = self._normalize_value(install_command) if build_command is not None: @@ -781,7 +781,7 @@ def create_duplicate_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) response = self.client.call( @@ -853,7 +853,7 @@ def create_template_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['repository'] = self._normalize_value(repository) api_params['owner'] = self._normalize_value(owner) api_params['rootDirectory'] = self._normalize_value(root_directory) @@ -916,7 +916,7 @@ def create_vcs_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['type'] = self._normalize_value(type) api_params['reference'] = self._normalize_value(reference) if activate is not None: @@ -966,8 +966,8 @@ def get_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'get', @@ -1012,8 +1012,8 @@ def delete_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'delete', @@ -1065,8 +1065,8 @@ def get_deployment_download( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) if type is not None: api_params['type'] = self._normalize_value(type) if token is not None: @@ -1115,8 +1115,8 @@ def update_deployment_status( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'patch', @@ -1163,7 +1163,7 @@ def list_logs( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1212,8 +1212,8 @@ def get_log( raise AppwriteException('Missing required parameter: "site_id"') if log_id is None or log_id == '': raise AppwriteException('Missing required parameter: "log_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{logId}', str(self._normalize_value(log_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{logId}', self._encode_path(log_id)) response = self.client.call( 'get', @@ -1258,8 +1258,8 @@ def delete_log( raise AppwriteException('Missing required parameter: "site_id"') if log_id is None or log_id == '': raise AppwriteException('Missing required parameter: "log_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{logId}', str(self._normalize_value(log_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{logId}', self._encode_path(log_id)) response = self.client.call( 'delete', @@ -1306,7 +1306,7 @@ def list_variables( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1371,7 +1371,7 @@ def create_variable( raise AppwriteException('Missing required parameter: "key"') if value is None: raise AppwriteException('Missing required parameter: "value"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['variableId'] = self._normalize_value(variable_id) api_params['key'] = self._normalize_value(key) api_params['value'] = self._normalize_value(value) @@ -1424,8 +1424,8 @@ def get_variable( raise AppwriteException('Missing required parameter: "site_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'get', @@ -1482,8 +1482,8 @@ def update_variable( raise AppwriteException('Missing required parameter: "site_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) if key is not None: api_params['key'] = self._normalize_value(key) if value is not None: @@ -1537,8 +1537,8 @@ def delete_variable( raise AppwriteException('Missing required parameter: "site_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/storage.py b/appwrite/services/storage.py index 2981f526..cce353ca 100644 --- a/appwrite/services/storage.py +++ b/appwrite/services/storage.py @@ -195,7 +195,7 @@ def get_bucket( api_params = {} if bucket_id is None or bucket_id == '': raise AppwriteException('Missing required parameter: "bucket_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) response = self.client.call( 'get', @@ -273,7 +273,7 @@ def update_bucket( raise AppwriteException('Missing required parameter: "bucket_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) api_params['name'] = self._normalize_value(name) if permissions is not None: api_params['permissions'] = self._normalize_value(permissions) @@ -337,7 +337,7 @@ def delete_bucket( api_params = {} if bucket_id is None or bucket_id == '': raise AppwriteException('Missing required parameter: "bucket_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) response = self.client.call( 'delete', @@ -387,7 +387,7 @@ def list_files( api_params = {} if bucket_id is None or bucket_id == '': raise AppwriteException('Missing required parameter: "bucket_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -459,7 +459,7 @@ def create_file( raise AppwriteException('Missing required parameter: "file_id"') if file is None: raise AppwriteException('Missing required parameter: "file"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) api_params['fileId'] = self._normalize_value(file_id) api_params['file'] = self._normalize_value(file) if permissions is not None: @@ -519,8 +519,8 @@ def get_file( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) response = self.client.call( 'get', @@ -571,8 +571,8 @@ def update_file( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if name is not None: api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -622,8 +622,8 @@ def delete_file( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) response = self.client.call( 'delete', @@ -672,8 +672,8 @@ def get_file_download( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if token is not None: api_params['token'] = self._normalize_value(token) @@ -756,8 +756,8 @@ def get_file_preview( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: @@ -829,8 +829,8 @@ def get_file_view( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if token is not None: api_params['token'] = self._normalize_value(token) diff --git a/appwrite/services/tables_db.py b/appwrite/services/tables_db.py index 19f181a5..abeba563 100644 --- a/appwrite/services/tables_db.py +++ b/appwrite/services/tables_db.py @@ -314,7 +314,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -360,7 +360,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -405,7 +405,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -449,7 +449,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -492,7 +492,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -550,7 +550,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -603,7 +603,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -647,7 +647,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -690,7 +690,7 @@ def list_migrations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -738,7 +738,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if specification is None: raise AppwriteException('Missing required parameter: "specification"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['specification'] = self._normalize_value(specification) if auto_cutover is not None: api_params['autoCutover'] = self._normalize_value(auto_cutover) @@ -787,8 +787,8 @@ def get_migration( raise AppwriteException('Missing required parameter: "database_id"') if migration_id is None or migration_id == '': raise AppwriteException('Missing required parameter: "migration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{migrationId}', str(self._normalize_value(migration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) response = self.client.call( 'get', @@ -833,8 +833,8 @@ def delete_migration( raise AppwriteException('Missing required parameter: "database_id"') if migration_id is None or migration_id == '': raise AppwriteException('Missing required parameter: "migration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{migrationId}', str(self._normalize_value(migration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) response = self.client.call( 'delete', @@ -880,8 +880,8 @@ def create_cutover( raise AppwriteException('Missing required parameter: "database_id"') if migration_id is None or migration_id == '': raise AppwriteException('Missing required parameter: "migration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{migrationId}', str(self._normalize_value(migration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) response = self.client.call( 'post', @@ -931,7 +931,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -977,7 +977,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1017,7 +1017,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1066,7 +1066,7 @@ def list_tables( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -1140,7 +1140,7 @@ def create_table( raise AppwriteException('Missing required parameter: "table_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['tableId'] = self._normalize_value(table_id) api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -1200,8 +1200,8 @@ def get_table( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) response = self.client.call( 'get', @@ -1264,8 +1264,8 @@ def update_table( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if name is not None: api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -1323,8 +1323,8 @@ def delete_table( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) response = self.client.call( 'delete', @@ -1376,8 +1376,8 @@ def list_columns( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1451,8 +1451,8 @@ def create_big_int_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -1535,9 +1535,9 @@ def update_big_int_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -1612,8 +1612,8 @@ def create_boolean_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1686,9 +1686,9 @@ def update_boolean_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1759,8 +1759,8 @@ def create_datetime_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1833,9 +1833,9 @@ def update_datetime_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1906,8 +1906,8 @@ def create_email_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1980,9 +1980,9 @@ def update_email_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2058,8 +2058,8 @@ def create_enum_column( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) @@ -2138,9 +2138,9 @@ def update_enum_column( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) @@ -2218,8 +2218,8 @@ def create_float_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -2302,9 +2302,9 @@ def update_float_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -2385,8 +2385,8 @@ def create_integer_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -2469,9 +2469,9 @@ def update_integer_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -2546,8 +2546,8 @@ def create_ip_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2620,9 +2620,9 @@ def update_ip_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2690,8 +2690,8 @@ def create_line_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2762,9 +2762,9 @@ def update_line_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -2839,8 +2839,8 @@ def create_longtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2915,9 +2915,9 @@ def update_longtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2991,8 +2991,8 @@ def create_mediumtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3067,9 +3067,9 @@ def update_mediumtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3137,8 +3137,8 @@ def create_point_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3209,9 +3209,9 @@ def update_point_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -3280,8 +3280,8 @@ def create_polygon_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3352,9 +3352,9 @@ def update_polygon_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -3432,8 +3432,8 @@ def create_relationship_column( raise AppwriteException('Missing required parameter: "related_table_id"') if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['relatedTableId'] = self._normalize_value(related_table_id) api_params['type'] = self._normalize_value(type) if two_way is not None: @@ -3505,9 +3505,9 @@ def update_relationship_column( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: @@ -3589,8 +3589,8 @@ def create_string_column( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -3672,9 +3672,9 @@ def update_string_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -3750,8 +3750,8 @@ def create_text_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3826,9 +3826,9 @@ def update_text_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3899,8 +3899,8 @@ def create_url_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3973,9 +3973,9 @@ def update_url_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -4054,8 +4054,8 @@ def create_varchar_column( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -4134,9 +4134,9 @@ def update_varchar_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -4214,9 +4214,9 @@ def get_column( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -4322,9 +4322,9 @@ def delete_column( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -4376,8 +4376,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -4451,8 +4451,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if columns is None: raise AppwriteException('Missing required parameter: "columns"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['columns'] = self._normalize_value(columns) @@ -4512,9 +4512,9 @@ def get_index( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -4564,9 +4564,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -4630,8 +4630,8 @@ def list_rows( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4704,8 +4704,8 @@ def create_row( raise AppwriteException('Missing required parameter: "row_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['rowId'] = self._normalize_value(row_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4769,8 +4769,8 @@ def create_rows( raise AppwriteException('Missing required parameter: "table_id"') if rows is None: raise AppwriteException('Missing required parameter: "rows"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['rows'] = self._normalize_value(rows) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4831,8 +4831,8 @@ def upsert_rows( raise AppwriteException('Missing required parameter: "table_id"') if rows is None: raise AppwriteException('Missing required parameter: "rows"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['rows'] = self._normalize_value(rows) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4894,8 +4894,8 @@ def update_rows( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -4957,8 +4957,8 @@ def delete_rows( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -5024,8 +5024,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -5094,9 +5094,9 @@ def get_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -5163,9 +5163,9 @@ def upsert_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -5235,9 +5235,9 @@ def update_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -5297,9 +5297,9 @@ def delete_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -5370,10 +5370,10 @@ def decrement_row_column( raise AppwriteException('Missing required parameter: "row_id"') if column is None or column == '': raise AppwriteException('Missing required parameter: "column"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) - api_path = api_path.replace('{column}', str(self._normalize_value(column))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) + api_path = api_path.replace('{column}', self._encode_path(column)) if value is not None: api_params['value'] = self._normalize_value(value) if min is not None: @@ -5448,10 +5448,10 @@ def increment_row_column( raise AppwriteException('Missing required parameter: "row_id"') if column is None or column == '': raise AppwriteException('Missing required parameter: "column"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) - api_path = api_path.replace('{column}', str(self._normalize_value(column))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) + api_path = api_path.replace('{column}', self._encode_path(column)) if value is not None: api_params['value'] = self._normalize_value(value) if max is not None: diff --git a/appwrite/services/teams.py b/appwrite/services/teams.py index 00a695d3..1df920f0 100644 --- a/appwrite/services/teams.py +++ b/appwrite/services/teams.py @@ -158,7 +158,7 @@ def get( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) response = self.client.call( 'get', @@ -207,7 +207,7 @@ def update_name( raise AppwriteException('Missing required parameter: "team_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -249,7 +249,7 @@ def delete( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) response = self.client.call( 'delete', @@ -296,7 +296,7 @@ def list_installations( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -348,7 +348,7 @@ def create_installation( raise AppwriteException('Missing required parameter: "team_id"') if app_id is None: raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) api_params['appId'] = self._normalize_value(app_id) if authorization_details is not None: api_params['authorizationDetails'] = self._normalize_value(authorization_details) @@ -397,8 +397,8 @@ def get_installation( raise AppwriteException('Missing required parameter: "team_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'get', @@ -446,8 +446,8 @@ def update_installation( raise AppwriteException('Missing required parameter: "team_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) if authorization_details is not None: api_params['authorizationDetails'] = self._normalize_value(authorization_details) @@ -495,8 +495,8 @@ def delete_installation( raise AppwriteException('Missing required parameter: "team_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'delete', @@ -546,7 +546,7 @@ def list_memberships( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -621,7 +621,7 @@ def create_membership( raise AppwriteException('Missing required parameter: "team_id"') if roles is None: raise AppwriteException('Missing required parameter: "roles"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) if email is not None: api_params['email'] = self._normalize_value(email) if user_id is not None: @@ -680,8 +680,8 @@ def get_membership( raise AppwriteException('Missing required parameter: "team_id"') if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'get', @@ -731,8 +731,8 @@ def update_membership( raise AppwriteException('Missing required parameter: "membership_id"') if roles is None: raise AppwriteException('Missing required parameter: "roles"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) api_params['roles'] = self._normalize_value(roles) response = self.client.call( @@ -779,8 +779,8 @@ def delete_membership( raise AppwriteException('Missing required parameter: "team_id"') if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'delete', @@ -838,8 +838,8 @@ def update_membership_status( raise AppwriteException('Missing required parameter: "user_id"') if secret is None: raise AppwriteException('Missing required parameter: "secret"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) api_params['userId'] = self._normalize_value(user_id) api_params['secret'] = self._normalize_value(secret) @@ -886,7 +886,7 @@ def get_prefs( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) response = self.client.call( 'get', @@ -935,7 +935,7 @@ def update_prefs( raise AppwriteException('Missing required parameter: "team_id"') if prefs is None: raise AppwriteException('Missing required parameter: "prefs"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) api_params['prefs'] = self._normalize_value(prefs) response = self.client.call( diff --git a/appwrite/services/tokens.py b/appwrite/services/tokens.py index b3edf492..2edf9900 100644 --- a/appwrite/services/tokens.py +++ b/appwrite/services/tokens.py @@ -49,8 +49,8 @@ def list( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -102,8 +102,8 @@ def create_file_token( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if expire is not None: api_params['expire'] = self._normalize_value(expire) @@ -146,7 +146,7 @@ def get( api_params = {} if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'get', @@ -189,7 +189,7 @@ def update( api_params = {} if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) if expire is not None: api_params['expire'] = self._normalize_value(expire) @@ -232,7 +232,7 @@ def delete( api_params = {} if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/users.py b/appwrite/services/users.py index 6e89654b..5dba7e46 100644 --- a/appwrite/services/users.py +++ b/appwrite/services/users.py @@ -348,7 +348,7 @@ def delete_identity( api_params = {} if identity_id is None or identity_id == '': raise AppwriteException('Missing required parameter: "identity_id"') - api_path = api_path.replace('{identityId}', str(self._normalize_value(identity_id))) + api_path = api_path.replace('{identityId}', self._encode_path(identity_id)) response = self.client.call( 'delete', @@ -514,13 +514,13 @@ def create_scrypt_user( password_salt : str Optional salt used to hash password. password_cpu : float - Optional CPU cost used to hash password. + Optional CPU cost used to hash password. Must be a power of two, at most 65536. password_memory : float - Optional memory cost used to hash password. + Optional memory cost used to hash password. Maximum is 16. password_parallel : float - Optional parallelization cost used to hash password. + Optional parallelization cost used to hash password. Maximum is 4. password_length : float - Optional hash length used to hash password. + Optional hash length used to hash password. Must be between 16 and 128. name : Optional[str] User name. Max length: 128 chars. model_type : Type[T], optional @@ -756,7 +756,7 @@ def get( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -796,7 +796,7 @@ def delete( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'delete', @@ -846,7 +846,7 @@ def update_email( raise AppwriteException('Missing required parameter: "user_id"') if email is None: raise AppwriteException('Missing required parameter: "email"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['email'] = self._normalize_value(email) response = self.client.call( @@ -897,7 +897,7 @@ def update_impersonator( raise AppwriteException('Missing required parameter: "user_id"') if impersonator is None: raise AppwriteException('Missing required parameter: "impersonator"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['impersonator'] = self._normalize_value(impersonator) response = self.client.call( @@ -945,7 +945,7 @@ def create_jwt( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if session_id is not None: api_params['sessionId'] = self._normalize_value(session_id) if duration is not None: @@ -1001,7 +1001,7 @@ def update_labels( raise AppwriteException('Missing required parameter: "user_id"') if labels is None: raise AppwriteException('Missing required parameter: "labels"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['labels'] = self._normalize_value(labels) response = self.client.call( @@ -1052,7 +1052,7 @@ def list_memberships( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -1107,7 +1107,7 @@ def update_mfa( raise AppwriteException('Missing required parameter: "user_id"') if mfa is None: raise AppwriteException('Missing required parameter: "mfa"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['mfa'] = self._normalize_value(mfa) response = self.client.call( @@ -1154,8 +1154,8 @@ def delete_mfa_authenticator( raise AppwriteException('Missing required parameter: "user_id"') if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{type}', self._encode_path(type)) response = self.client.call( 'delete', @@ -1201,8 +1201,8 @@ def get_mfa_challenge( raise AppwriteException('Missing required parameter: "user_id"') if challenge_id is None or challenge_id == '': raise AppwriteException('Missing required parameter: "challenge_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{challengeId}', str(self._normalize_value(challenge_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{challengeId}', self._encode_path(challenge_id)) response = self.client.call( 'get', @@ -1242,7 +1242,7 @@ def list_mfa_factors( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -1282,7 +1282,7 @@ def get_mfa_recovery_codes( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -1322,7 +1322,7 @@ def update_mfa_recovery_codes( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'put', @@ -1363,7 +1363,7 @@ def create_mfa_recovery_codes( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'patch', @@ -1413,7 +1413,7 @@ def update_name( raise AppwriteException('Missing required parameter: "user_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1461,7 +1461,7 @@ def list_passkeys( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1510,8 +1510,8 @@ def get_passkey( raise AppwriteException('Missing required parameter: "user_id"') if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'get', @@ -1561,8 +1561,8 @@ def update_passkey( raise AppwriteException('Missing required parameter: "passkey_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1609,8 +1609,8 @@ def delete_passkey( raise AppwriteException('Missing required parameter: "user_id"') if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'delete', @@ -1660,7 +1660,7 @@ def update_password( raise AppwriteException('Missing required parameter: "user_id"') if password is None: raise AppwriteException('Missing required parameter: "password"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['password'] = self._normalize_value(password) response = self.client.call( @@ -1711,7 +1711,7 @@ def update_phone( raise AppwriteException('Missing required parameter: "user_id"') if number is None: raise AppwriteException('Missing required parameter: "number"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['number'] = self._normalize_value(number) response = self.client.call( @@ -1757,7 +1757,7 @@ def get_prefs( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -1806,7 +1806,7 @@ def update_prefs( raise AppwriteException('Missing required parameter: "user_id"') if prefs is None: raise AppwriteException('Missing required parameter: "prefs"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['prefs'] = self._normalize_value(prefs) response = self.client.call( @@ -1851,7 +1851,7 @@ def list_sessions( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if total is not None: api_params['total'] = self._normalize_value(total) @@ -1895,7 +1895,7 @@ def create_session( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'post', @@ -1936,7 +1936,7 @@ def delete_sessions( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'delete', @@ -1982,8 +1982,8 @@ def delete_session( raise AppwriteException('Missing required parameter: "user_id"') if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'delete', @@ -2033,7 +2033,7 @@ def update_status( raise AppwriteException('Missing required parameter: "user_id"') if status is None: raise AppwriteException('Missing required parameter: "status"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['status'] = self._normalize_value(status) response = self.client.call( @@ -2081,7 +2081,7 @@ def list_targets( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -2146,7 +2146,7 @@ def create_target( raise AppwriteException('Missing required parameter: "provider_type"') if identifier is None: raise AppwriteException('Missing required parameter: "identifier"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['targetId'] = self._normalize_value(target_id) api_params['providerType'] = self._normalize_value(provider_type) api_params['identifier'] = self._normalize_value(identifier) @@ -2199,8 +2199,8 @@ def get_target( raise AppwriteException('Missing required parameter: "user_id"') if target_id is None or target_id == '': raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{targetId}', str(self._normalize_value(target_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{targetId}', self._encode_path(target_id)) response = self.client.call( 'get', @@ -2254,8 +2254,8 @@ def update_target( raise AppwriteException('Missing required parameter: "user_id"') if target_id is None or target_id == '': raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{targetId}', str(self._normalize_value(target_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{targetId}', self._encode_path(target_id)) if identifier is not None: api_params['identifier'] = self._normalize_value(identifier) if provider_id is not None: @@ -2307,8 +2307,8 @@ def delete_target( raise AppwriteException('Missing required parameter: "user_id"') if target_id is None or target_id == '': raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{targetId}', str(self._normalize_value(target_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{targetId}', self._encode_path(target_id)) response = self.client.call( 'delete', @@ -2355,7 +2355,7 @@ def create_token( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if length is not None: api_params['length'] = self._normalize_value(length) if expire is not None: @@ -2409,7 +2409,7 @@ def update_email_verification( raise AppwriteException('Missing required parameter: "user_id"') if email_verification is None: raise AppwriteException('Missing required parameter: "email_verification"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['emailVerification'] = self._normalize_value(email_verification) response = self.client.call( @@ -2460,7 +2460,7 @@ def update_phone_verification( raise AppwriteException('Missing required parameter: "user_id"') if phone_verification is None: raise AppwriteException('Missing required parameter: "phone_verification"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['phoneVerification'] = self._normalize_value(phone_verification) response = self.client.call( diff --git a/appwrite/services/vectors_db.py b/appwrite/services/vectors_db.py index eda5f00d..dd304916 100644 --- a/appwrite/services/vectors_db.py +++ b/appwrite/services/vectors_db.py @@ -286,7 +286,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -332,7 +332,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -377,7 +377,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -421,7 +421,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -464,7 +464,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -524,7 +524,7 @@ def update( raise AppwriteException('Missing required parameter: "database_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) @@ -576,7 +576,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -626,7 +626,7 @@ def list_collections( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -699,7 +699,7 @@ def create_collection( raise AppwriteException('Missing required parameter: "name"') if dimension is None: raise AppwriteException('Missing required parameter: "dimension"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['collectionId'] = self._normalize_value(collection_id) api_params['name'] = self._normalize_value(name) api_params['dimension'] = self._normalize_value(dimension) @@ -756,8 +756,8 @@ def get_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'get', @@ -822,8 +822,8 @@ def update_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['name'] = self._normalize_value(name) if dimension is not None: api_params['dimension'] = self._normalize_value(dimension) @@ -880,8 +880,8 @@ def delete_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'delete', @@ -943,8 +943,8 @@ def list_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1017,8 +1017,8 @@ def create_document( raise AppwriteException('Missing required parameter: "document_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documentId'] = self._normalize_value(document_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1082,8 +1082,8 @@ def create_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1144,8 +1144,8 @@ def upsert_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1207,8 +1207,8 @@ def update_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -1270,8 +1270,8 @@ def delete_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1337,8 +1337,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1407,9 +1407,9 @@ def get_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1476,9 +1476,9 @@ def upsert_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1548,9 +1548,9 @@ def update_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1610,9 +1610,9 @@ def delete_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1666,8 +1666,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1741,8 +1741,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if attributes is None: raise AppwriteException('Missing required parameter: "attributes"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['attributes'] = self._normalize_value(attributes) @@ -1802,9 +1802,9 @@ def get_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -1854,9 +1854,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -1900,7 +1900,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1952,7 +1952,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1998,7 +1998,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -2038,7 +2038,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', diff --git a/appwrite/services/waf.py b/appwrite/services/waf.py index 8aaa3ebc..3d47de74 100644 --- a/appwrite/services/waf.py +++ b/appwrite/services/waf.py @@ -77,7 +77,7 @@ def create_bypass_rule( conditions: Optional[List[str]] = None, ) -> WafRuleBypass: """ - Create a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -155,7 +155,7 @@ def update_bypass_rule( conditions: Optional[List[str]] = None, ) -> WafRuleBypass: """ - Update a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -190,7 +190,7 @@ def update_bypass_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -234,7 +234,7 @@ def create_challenge_rule( ttl: Optional[float] = None, ) -> WafRuleChallenge: """ - Create a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -327,7 +327,7 @@ def update_challenge_rule( ttl: Optional[float] = None, ) -> WafRuleChallenge: """ - Update a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -368,7 +368,7 @@ def update_challenge_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -415,7 +415,7 @@ def create_deny_rule( conditions: Optional[List[str]] = None, ) -> WafRuleDeny: """ - Create a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. `sessionAge` and `mfaAge` hold the seconds since the request's session was created and since its last MFA challenge. Both are null for requests without a session, such as guests and API keys, while a request authenticated with the `x-appwrite-jwt` header carries the age of the session its token is bound to; on `functions` rules the session comes from the `x-appwrite-user-jwt` header, and `mfaAge` is null when the session never completed MFA. A deny matching on `mfaAge` returns `user_challenge_required` and one matching on `sessionAge` returns `user_reauthentication_required`, both with status 401, so the user can sign in again and retry. To also deny requests without a session, combine `isNull` and `greaterThan` in an `or` condition. Parameters ---------- @@ -493,7 +493,7 @@ def update_deny_rule( conditions: Optional[List[str]] = None, ) -> WafRuleDeny: """ - Update a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. `sessionAge` and `mfaAge` hold the seconds since the request's session was created and since its last MFA challenge. Both are null for requests without a session, such as guests and API keys, while a request authenticated with the `x-appwrite-jwt` header carries the age of the session its token is bound to; on `functions` rules the session comes from the `x-appwrite-user-jwt` header, and `mfaAge` is null when the session never completed MFA. A deny matching on `mfaAge` returns `user_challenge_required` and one matching on `sessionAge` returns `user_reauthentication_required`, both with status 401, so the user can sign in again and retry. To also deny requests without a session, combine `isNull` and `greaterThan` in an `or` condition. Parameters ---------- @@ -528,7 +528,7 @@ def update_deny_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -574,7 +574,7 @@ def create_rate_limit_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRateLimit: """ - Create a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -678,7 +678,7 @@ def update_rate_limit_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRateLimit: """ - Update a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -721,7 +721,7 @@ def update_rate_limit_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -772,7 +772,7 @@ def create_redirect_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRedirect: """ - Create a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -862,7 +862,7 @@ def update_redirect_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRedirect: """ - Update a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.`, `query.`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -901,7 +901,7 @@ def update_redirect_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -966,7 +966,7 @@ def get_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'get', @@ -1023,7 +1023,7 @@ def delete_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/webhooks.py b/appwrite/services/webhooks.py index 41f4ef49..3a4003f3 100644 --- a/appwrite/services/webhooks.py +++ b/appwrite/services/webhooks.py @@ -171,7 +171,7 @@ def get( api_params = {} if webhook_id is None or webhook_id == '': raise AppwriteException('Missing required parameter: "webhook_id"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) response = self.client.call( 'get', @@ -241,7 +241,7 @@ def update( raise AppwriteException('Missing required parameter: "url"') if events is None: raise AppwriteException('Missing required parameter: "events"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) api_params['name'] = self._normalize_value(name) api_params['url'] = self._normalize_value(url) api_params['events'] = self._normalize_value(events) @@ -295,7 +295,7 @@ def delete( api_params = {} if webhook_id is None or webhook_id == '': raise AppwriteException('Missing required parameter: "webhook_id"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) response = self.client.call( 'delete', @@ -339,7 +339,7 @@ def update_secret( api_params = {} if webhook_id is None or webhook_id == '': raise AppwriteException('Missing required parameter: "webhook_id"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) if secret is not None: api_params['secret'] = self._normalize_value(secret) diff --git a/docs/examples/backups/update-policy.md b/docs/examples/backups/update-policy.md index 74fbdeb3..f812cb3a 100644 --- a/docs/examples/backups/update-policy.md +++ b/docs/examples/backups/update-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = backups.update_policy( policy_id = '', name = '', # optional retention = 1, # optional - schedule = '', # optional + schedule = None, # optional enabled = False # optional ) diff --git a/docs/examples/migrations/create-appwrite-migration.md b/docs/examples/migrations/create-appwrite-migration.md new file mode 100644 index 00000000..9acaa15f --- /dev/null +++ b/docs/examples/migrations/create-appwrite-migration.md @@ -0,0 +1,24 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.appwrite_migration_resource import AppwriteMigrationResource +from appwrite.enums.on_duplicate import OnDuplicate + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_appwrite_migration( + resources = [AppwriteMigrationResource.USER], + endpoint = 'https://example.com', + project_id = '', + api_key = '', + on_duplicate = OnDuplicate.FAIL # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-csv-export.md b/docs/examples/migrations/create-csv-export.md new file mode 100644 index 00000000..e51ca7fe --- /dev/null +++ b/docs/examples/migrations/create-csv-export.md @@ -0,0 +1,27 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_csv_export( + database_id = '', + collection_id = '', + filename = '', + columns = [], # optional + queries = [], # optional + delimiter = '', # optional + enclosure = '', # optional + escape = '', # optional + header = False, # optional + notify = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-csv-import.md b/docs/examples/migrations/create-csv-import.md new file mode 100644 index 00000000..7546bade --- /dev/null +++ b/docs/examples/migrations/create-csv-import.md @@ -0,0 +1,24 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.on_duplicate import OnDuplicate + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_csv_import( + bucket_id = '', + file_id = '', + database_id = '', + collection_id = '', + internal_file = False, # optional + on_duplicate = OnDuplicate.FAIL # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-firebase-migration.md b/docs/examples/migrations/create-firebase-migration.md new file mode 100644 index 00000000..4f6da77f --- /dev/null +++ b/docs/examples/migrations/create-firebase-migration.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.firebase_migration_resource import FirebaseMigrationResource + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_firebase_migration( + resources = [FirebaseMigrationResource.USER], + service_account = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-json-export.md b/docs/examples/migrations/create-json-export.md new file mode 100644 index 00000000..abdce897 --- /dev/null +++ b/docs/examples/migrations/create-json-export.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_json_export( + database_id = '', + collection_id = '', + filename = '', + columns = [], # optional + queries = [], # optional + notify = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-json-import.md b/docs/examples/migrations/create-json-import.md new file mode 100644 index 00000000..cec5f7a3 --- /dev/null +++ b/docs/examples/migrations/create-json-import.md @@ -0,0 +1,24 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.on_duplicate import OnDuplicate + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_json_import( + bucket_id = '', + file_id = '', + database_id = '', + collection_id = '', + internal_file = False, # optional + on_duplicate = OnDuplicate.FAIL # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-n-host-migration.md b/docs/examples/migrations/create-n-host-migration.md new file mode 100644 index 00000000..75273344 --- /dev/null +++ b/docs/examples/migrations/create-n-host-migration.md @@ -0,0 +1,26 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.n_host_migration_resource import NHostMigrationResource + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_n_host_migration( + resources = [NHostMigrationResource.USER], + subdomain = '', + region = '', + admin_secret = '', + database = '', + username = '', + password = 'password', + port = 5432 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-supabase-migration.md b/docs/examples/migrations/create-supabase-migration.md new file mode 100644 index 00000000..9d1fd650 --- /dev/null +++ b/docs/examples/migrations/create-supabase-migration.md @@ -0,0 +1,25 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.supabase_migration_resource import SupabaseMigrationResource + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_supabase_migration( + resources = [SupabaseMigrationResource.USER], + endpoint = 'https://example.com', + api_key = '', + database_host = '', + username = '', + password = 'password', + port = 5432 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/delete.md b/docs/examples/migrations/delete.md new file mode 100644 index 00000000..80093504 --- /dev/null +++ b/docs/examples/migrations/delete.md @@ -0,0 +1,15 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result = migrations.delete( + migration_id = '' +) +``` diff --git a/docs/examples/migrations/get-firebase-report.md b/docs/examples/migrations/get-firebase-report.md new file mode 100644 index 00000000..8fa3491b --- /dev/null +++ b/docs/examples/migrations/get-firebase-report.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import MigrationReport +from appwrite.enums.firebase_migration_resource import FirebaseMigrationResource + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: MigrationReport = migrations.get_firebase_report( + resources = [FirebaseMigrationResource.USER], + service_account = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/get-n-host-report.md b/docs/examples/migrations/get-n-host-report.md new file mode 100644 index 00000000..57d4b091 --- /dev/null +++ b/docs/examples/migrations/get-n-host-report.md @@ -0,0 +1,26 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import MigrationReport +from appwrite.enums.n_host_migration_resource import NHostMigrationResource + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: MigrationReport = migrations.get_n_host_report( + resources = [NHostMigrationResource.USER], + subdomain = '', + region = '', + admin_secret = '', + database = '', + username = '', + password = 'password', + port = 5432 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/get-supabase-report.md b/docs/examples/migrations/get-supabase-report.md new file mode 100644 index 00000000..f667c202 --- /dev/null +++ b/docs/examples/migrations/get-supabase-report.md @@ -0,0 +1,25 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import MigrationReport +from appwrite.enums.supabase_migration_resource import SupabaseMigrationResource + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: MigrationReport = migrations.get_supabase_report( + resources = [SupabaseMigrationResource.USER], + endpoint = 'https://example.com', + api_key = '', + database_host = '', + username = '', + password = 'password', + port = 5432 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/get.md b/docs/examples/migrations/get.md new file mode 100644 index 00000000..4d676c1c --- /dev/null +++ b/docs/examples/migrations/get.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.get( + migration_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/list.md b/docs/examples/migrations/list.md new file mode 100644 index 00000000..fae89b16 --- /dev/null +++ b/docs/examples/migrations/list.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import MigrationList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: MigrationList = migrations.list( + queries = [], # optional + search = '', # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/retry.md b/docs/examples/migrations/retry.md new file mode 100644 index 00000000..15e148ea --- /dev/null +++ b/docs/examples/migrations/retry.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.retry( + migration_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/mongo/update-backup-policy.md b/docs/examples/mongo/update-backup-policy.md index 5a02ffe1..103f2221 100644 --- a/docs/examples/mongo/update-backup-policy.md +++ b/docs/examples/mongo/update-backup-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = mongo.update_backup_policy( database_id = '', policy_id = '', name = '', # optional - schedule = '', # optional + schedule = None, # optional retention = 1, # optional enabled = False # optional ) diff --git a/docs/examples/mongo/update-backup-storage.md b/docs/examples/mongo/update-backup-storage.md index 9a62d4fd..27e1a85d 100644 --- a/docs/examples/mongo/update-backup-storage.md +++ b/docs/examples/mongo/update-backup-storage.md @@ -18,7 +18,7 @@ result: DedicatedDatabaseBackupStorage = mongo.update_backup_storage( secret_key = '', region = '', # optional prefix = '', # optional - endpoint = '' # optional + endpoint = 'https://example.com' # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/update-backup-policy.md b/docs/examples/mysql/update-backup-policy.md index 65f9a9a3..b3951d9e 100644 --- a/docs/examples/mysql/update-backup-policy.md +++ b/docs/examples/mysql/update-backup-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = mysql.update_backup_policy( database_id = '', policy_id = '', name = '', # optional - schedule = '', # optional + schedule = None, # optional retention = 1, # optional enabled = False # optional ) diff --git a/docs/examples/mysql/update-backup-storage.md b/docs/examples/mysql/update-backup-storage.md index ecc41eb9..cc3b9d48 100644 --- a/docs/examples/mysql/update-backup-storage.md +++ b/docs/examples/mysql/update-backup-storage.md @@ -18,7 +18,7 @@ result: DedicatedDatabaseBackupStorage = mysql.update_backup_storage( secret_key = '', region = '', # optional prefix = '', # optional - endpoint = '' # optional + endpoint = 'https://example.com' # optional ) print(result.model_dump()) diff --git a/docs/examples/oauth2/authorize-post.md b/docs/examples/oauth2/authorize-post.md index 8cf274b7..f64d5c6f 100644 --- a/docs/examples/oauth2/authorize-post.md +++ b/docs/examples/oauth2/authorize-post.md @@ -13,7 +13,7 @@ oauth2 = Oauth2(client) result: Oauth2Authorize = oauth2.authorize_post( client_id = '', # optional redirect_uri = 'https://example.com', # optional - response_type = '', # optional + response_type = None, # optional scope = '', # optional state = '', # optional nonce = '', # optional diff --git a/docs/examples/oauth2/authorize.md b/docs/examples/oauth2/authorize.md index 32fbf4b2..4534915a 100644 --- a/docs/examples/oauth2/authorize.md +++ b/docs/examples/oauth2/authorize.md @@ -13,7 +13,7 @@ oauth2 = Oauth2(client) result: Oauth2Authorize = oauth2.authorize( client_id = '', # optional redirect_uri = 'https://example.com', # optional - response_type = '', # optional + response_type = None, # optional scope = '', # optional state = '', # optional nonce = '', # optional diff --git a/docs/examples/postgresql/update-backup-policy.md b/docs/examples/postgresql/update-backup-policy.md index 02fac735..ce3a7937 100644 --- a/docs/examples/postgresql/update-backup-policy.md +++ b/docs/examples/postgresql/update-backup-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = postgresql.update_backup_policy( database_id = '', policy_id = '', name = '', # optional - schedule = '', # optional + schedule = None, # optional retention = 1, # optional enabled = False # optional ) diff --git a/docs/examples/postgresql/update-backup-storage.md b/docs/examples/postgresql/update-backup-storage.md index d0431b4a..1fe8728a 100644 --- a/docs/examples/postgresql/update-backup-storage.md +++ b/docs/examples/postgresql/update-backup-storage.md @@ -18,7 +18,7 @@ result: DedicatedDatabaseBackupStorage = postgresql.update_backup_storage( secret_key = '', region = '', # optional prefix = '', # optional - endpoint = '' # optional + endpoint = 'https://example.com' # optional ) print(result.model_dump()) diff --git a/docs/examples/project/delete-sessions.md b/docs/examples/project/delete-sessions.md new file mode 100644 index 00000000..142267f4 --- /dev/null +++ b/docs/examples/project/delete-sessions.md @@ -0,0 +1,13 @@ +```python +from appwrite.client import Client +from appwrite.services.project import Project + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +project = Project(client) + +result = project.delete_sessions() +``` diff --git a/docs/examples/project/get-policy.md b/docs/examples/project/get-policy.md index 3e746d52..ec56a786 100644 --- a/docs/examples/project/get-policy.md +++ b/docs/examples/project/get-policy.md @@ -18,6 +18,7 @@ from appwrite.models import PolicyDenyAliasedEmail from appwrite.models import PolicyDenyDisposableEmail from appwrite.models import PolicyDenyFreeEmail from appwrite.models import PolicyDenyCorporateEmail +from appwrite.models import PolicyDenyRealtimeGuests from appwrite.models import PolicyPasskey from typing import Union from appwrite.enums.project_policy_id import ProjectPolicyId @@ -29,7 +30,7 @@ client.set_key('') # Your secret API key project = Project(client) -result: Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyPasskey] = project.get_policy( +result: Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyDenyRealtimeGuests, PolicyPasskey] = project.get_policy( policy_id = ProjectPolicyId.PASSWORD_DICTIONARY ) diff --git a/docs/examples/project/update-deny-realtime-guests-policy.md b/docs/examples/project/update-deny-realtime-guests-policy.md new file mode 100644 index 00000000..dfaeaae7 --- /dev/null +++ b/docs/examples/project/update-deny-realtime-guests-policy.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.project import Project +from appwrite.models import Project as ProjectModel + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +project = Project(client) + +result: ProjectModel = project.update_deny_realtime_guests_policy( + enabled = False +) + +print(result.model_dump()) +``` diff --git a/docs/examples/storage/update-file.md b/docs/examples/storage/update-file.md index b0fad8d4..a6d7659c 100644 --- a/docs/examples/storage/update-file.md +++ b/docs/examples/storage/update-file.md @@ -15,7 +15,7 @@ storage = Storage(client) result: File = storage.update_file( bucket_id = '', file_id = '', - name = '', # optional + name = None, # optional permissions = [Permission.read(Role.any())] # optional ) diff --git a/docs/examples/users/create-argon-2-user.md b/docs/examples/users/create-argon-2-user.md index cd3e2bc9..b059011a 100644 --- a/docs/examples/users/create-argon-2-user.md +++ b/docs/examples/users/create-argon-2-user.md @@ -13,7 +13,7 @@ users = Users(client) result: User = users.create_argon2_user( user_id = '', email = 'email@example.com', - password = 'password', + password = '', name = '' # optional ) diff --git a/docs/examples/users/create-bcrypt-user.md b/docs/examples/users/create-bcrypt-user.md index d3ab13e6..142b2bf6 100644 --- a/docs/examples/users/create-bcrypt-user.md +++ b/docs/examples/users/create-bcrypt-user.md @@ -13,7 +13,7 @@ users = Users(client) result: User = users.create_bcrypt_user( user_id = '', email = 'email@example.com', - password = 'password', + password = '', name = '' # optional ) diff --git a/docs/examples/users/create-ph-pass-user.md b/docs/examples/users/create-ph-pass-user.md index 456a04b0..74400981 100644 --- a/docs/examples/users/create-ph-pass-user.md +++ b/docs/examples/users/create-ph-pass-user.md @@ -13,7 +13,7 @@ users = Users(client) result: User = users.create_ph_pass_user( user_id = '', email = 'email@example.com', - password = 'password', + password = '', name = '' # optional ) diff --git a/docs/examples/users/create-scrypt-user.md b/docs/examples/users/create-scrypt-user.md index ecfa6029..042f3bcf 100644 --- a/docs/examples/users/create-scrypt-user.md +++ b/docs/examples/users/create-scrypt-user.md @@ -16,7 +16,7 @@ result: User = users.create_scrypt_user( password = 'password', password_salt = '', password_cpu = 8, - password_memory = 65536, + password_memory = 14, password_parallel = 1, password_length = 64, name = '' # optional diff --git a/pyproject.toml b/pyproject.toml index a3ca05da..910edbea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "appwrite" -version = "27.0.0" +version = "27.1.0" description = "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API" readme = "README.md" requires-python = ">=3.10" diff --git a/setup.py b/setup.py index 09438025..6d48fc4a 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='appwrite', packages=setuptools.find_packages(), - version='27.0.0', + version='27.1.0', license='BSD-3-Clause', description='Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API', long_description=long_description, @@ -18,7 +18,7 @@ maintainer='Appwrite Team', maintainer_email='team@appwrite.io', url='https://appwrite.io/support', - download_url='https://github.com/appwrite/sdk-for-python/archive/27.0.0.tar.gz', + download_url='https://github.com/appwrite/sdk-for-python/archive/27.1.0.tar.gz', install_requires=[ 'requests>=2.34.2,<3', 'urllib3>=2.8.0,<3', diff --git a/test/services/test_account.py b/test/services/test_account.py index e37d5b47..4e816a23 100644 --- a/test/services/test_account.py +++ b/test/services/test_account.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -36,12 +38,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -66,7 +76,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -75,6 +85,23 @@ def test_create(self, m): '', 'email@example.com', 'password', + name='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'email': 'email@example.com', + 'password': 'password', + 'name': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -86,12 +113,28 @@ def test_list_consents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_consents() + response = self.account.list_consents( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -110,7 +153,7 @@ def test_get_consent(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -118,6 +161,14 @@ def test_get_consent(self, m): response = self.account.get_consent( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -125,7 +176,7 @@ def test_delete_consent(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -133,6 +184,14 @@ def test_delete_consent(self, m): response = self.account.delete_consent( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -143,13 +202,28 @@ def test_list_consent_tokens(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.list_consent_tokens( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents//tokens', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -170,7 +244,7 @@ def test_get_consent_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -179,6 +253,14 @@ def test_get_consent_token(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents//tokens/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -186,7 +268,7 @@ def test_delete_consent_token(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -195,6 +277,14 @@ def test_delete_consent_token(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents//tokens/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -219,7 +309,7 @@ def test_update_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -228,6 +318,20 @@ def test_update_email(self, m): 'email@example.com', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -238,12 +342,28 @@ def test_list_identities(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_identities() + response = self.account.list_identities( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/identities', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -251,7 +371,7 @@ def test_delete_identity(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -259,6 +379,14 @@ def test_delete_identity(self, m): response = self.account.delete_identity( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/identities/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -269,12 +397,28 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_memberships() + response = self.account.list_memberships( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -299,13 +443,26 @@ def test_update_mfa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_mfa( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mfa': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -317,7 +474,7 @@ def test_create_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -325,6 +482,14 @@ def test_create_mfa_authenticator(self, m): response = self.account.create_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -335,7 +500,7 @@ def test_create_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -343,6 +508,14 @@ def test_create_mfa_authenticator(self, m): response = self.account.create_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -367,7 +540,7 @@ def test_update_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -376,6 +549,19 @@ def test_update_mfa_authenticator(self, m): 'totp', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'otp': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -400,7 +586,7 @@ def test_update_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -409,6 +595,19 @@ def test_update_mfa_authenticator(self, m): 'totp', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'otp': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -416,7 +615,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -424,6 +623,14 @@ def test_delete_mfa_authenticator(self, m): response = self.account.delete_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -431,7 +638,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -439,6 +646,14 @@ def test_delete_mfa_authenticator(self, m): response = self.account.delete_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -451,7 +666,7 @@ def test_create_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -459,6 +674,19 @@ def test_create_mfa_challenge(self, m): response = self.account.create_mfa_challenge( 'email', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'factor': 'email', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -471,7 +699,7 @@ def test_create_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -479,6 +707,19 @@ def test_create_mfa_challenge(self, m): response = self.account.create_mfa_challenge( 'email', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'factor': 'email', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -516,7 +757,7 @@ def test_update_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -525,6 +766,20 @@ def test_update_mfa_challenge(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'challengeId': '', + 'otp': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -562,7 +817,7 @@ def test_update_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -571,6 +826,20 @@ def test_update_mfa_challenge(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'challengeId': '', + 'otp': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -584,12 +853,20 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.list_mfa_factors() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -603,12 +880,20 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.list_mfa_factors() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -618,12 +903,20 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -633,12 +926,20 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -648,12 +949,20 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -663,12 +972,20 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -678,12 +995,20 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -693,12 +1018,20 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -723,7 +1056,7 @@ def test_update_name(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -731,6 +1064,19 @@ def test_update_name(self, m): response = self.account.update_name( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/name', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -741,12 +1087,28 @@ def test_list_passkeys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_passkeys() + response = self.account.list_passkeys( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -760,12 +1122,29 @@ def test_create_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.create_passkey() + response = self.account.create_passkey( + passkey_id='', + name='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'passkeyId': '', + 'name': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -779,7 +1158,7 @@ def test_get_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -787,6 +1166,14 @@ def test_get_passkey(self, m): response = self.account.get_passkey( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -800,7 +1187,7 @@ def test_update_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -809,6 +1196,19 @@ def test_update_passkey(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -816,7 +1216,7 @@ def test_delete_passkey(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -824,6 +1224,14 @@ def test_delete_passkey(self, m): response = self.account.delete_passkey( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -837,7 +1245,7 @@ def test_update_passkey_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -846,6 +1254,19 @@ def test_update_passkey_verification(self, m): '', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys//verification', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'credential': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -870,13 +1291,28 @@ def test_update_password(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_password( 'password', + old_password='password', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/password', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'password': 'password', + 'oldPassword': 'password', + }, ) self.assertEqual(response.to_dict(), data) @@ -902,7 +1338,7 @@ def test_update_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -911,6 +1347,20 @@ def test_update_phone(self, m): '+12065550100', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'phone': '+12065550100', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -918,12 +1368,20 @@ def test_get_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get_prefs() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/prefs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -948,7 +1406,7 @@ def test_update_prefs(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -956,6 +1414,19 @@ def test_update_prefs(self, m): response = self.account.update_prefs( {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/prefs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'prefs': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -970,7 +1441,7 @@ def test_create_recovery(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -979,6 +1450,20 @@ def test_create_recovery(self, m): 'email@example.com', 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'url': 'https://example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -993,7 +1478,7 @@ def test_update_recovery(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1003,6 +1488,21 @@ def test_update_recovery(self, m): '', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1017,13 +1517,28 @@ def test_create_recovery_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_recovery_otp( 'email@example.com', + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'phrase': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1039,7 +1554,7 @@ def test_update_recovery_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1049,6 +1564,21 @@ def test_update_recovery_otp(self, m): '', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1059,12 +1589,27 @@ def test_list_sessions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_sessions() + response = self.account.list_sessions( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1072,12 +1617,27 @@ def test_delete_sessions(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.delete_sessions() + response = self.account.delete_sessions( + current=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'current': False, + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1115,12 +1675,20 @@ def test_create_anonymous_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_anonymous_session() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/anonymous', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1158,7 +1726,7 @@ def test_create_email_password_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1166,6 +1734,22 @@ def test_create_email_password_session(self, m): response = self.account.create_email_password_session( 'email@example.com', 'password', + duration=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'password': 'password', + 'duration': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1204,7 +1788,7 @@ def test_create_id_token_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1212,6 +1796,28 @@ def test_create_id_token_session(self, m): response = self.account.create_id_token_session( 'apple', '', + nonce='', + access_token='', + access_token_expiry=1, + name='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/id-token', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 'apple', + 'idToken': '', + 'nonce': '', + 'accessToken': '', + 'accessTokenExpiry': 1, + 'name': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1250,7 +1856,7 @@ def test_update_magic_url_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1259,6 +1865,20 @@ def test_update_magic_url_session(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/magic-url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1296,7 +1916,7 @@ def test_update_phone_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1305,6 +1925,20 @@ def test_update_phone_session(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1342,7 +1976,7 @@ def test_create_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1351,6 +1985,20 @@ def test_create_session(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/token', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1388,7 +2036,7 @@ def test_get_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1396,6 +2044,14 @@ def test_get_session(self, m): response = self.account.get_session( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1433,7 +2089,7 @@ def test_update_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1441,6 +2097,14 @@ def test_update_session(self, m): response = self.account.update_session( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1448,7 +2112,7 @@ def test_delete_session(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1456,6 +2120,14 @@ def test_delete_session(self, m): response = self.account.delete_session( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1480,12 +2152,20 @@ def test_update_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_status() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1500,7 +2180,7 @@ def test_create_email_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1508,6 +2188,22 @@ def test_create_email_token(self, m): response = self.account.create_email_token( '', 'email@example.com', + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'email': 'email@example.com', + 'phrase': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1523,7 +2219,7 @@ def test_create_magic_url_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1531,6 +2227,24 @@ def test_create_magic_url_token(self, m): response = self.account.create_magic_url_token( '', 'email@example.com', + url='https://example.com', + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/magic-url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'email': 'email@example.com', + 'url': 'https://example.com', + 'phrase': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1539,13 +2253,32 @@ def test_create_o_auth2_token(self, m): data = None headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_o_auth2_token( 'amazon', + success='https://example.com', + failure='https://example.com', + scopes=[], + state='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/oauth2/amazon', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'success': ['https://example.com'], + 'failure': ['https://example.com'], + 'state': [''], + }, ) self.assertEqual(response, data) @@ -1560,12 +2293,20 @@ def test_create_passkey_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_passkey_token() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/passkey', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1580,7 +2321,7 @@ def test_update_passkey_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1589,6 +2330,20 @@ def test_update_passkey_token(self, m): '', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/passkey', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'challengeId': '', + 'credential': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1603,7 +2358,7 @@ def test_create_phone_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1612,6 +2367,20 @@ def test_create_phone_token(self, m): '', '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'phone': '+12065550100', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1626,7 +2395,7 @@ def test_create_email_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1634,6 +2403,19 @@ def test_create_email_verification(self, m): response = self.account.create_email_verification( 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'url': 'https://example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1648,7 +2430,7 @@ def test_create_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1656,6 +2438,19 @@ def test_create_verification(self, m): response = self.account.create_verification( 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'url': 'https://example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1670,7 +2465,7 @@ def test_update_email_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1679,6 +2474,20 @@ def test_update_email_verification(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1693,7 +2502,7 @@ def test_update_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1702,6 +2511,20 @@ def test_update_verification(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1716,12 +2539,27 @@ def test_create_email_verification_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.create_email_verification_otp() + response = self.account.create_email_verification_otp( + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'phrase': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1736,7 +2574,7 @@ def test_update_email_verification_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1745,6 +2583,20 @@ def test_update_email_verification_otp(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1759,12 +2611,20 @@ def test_create_phone_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_phone_verification() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1779,7 +2639,7 @@ def test_update_phone_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1788,4 +2648,18 @@ def test_update_phone_verification(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '', + 'secret': '', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_activities.py b/test/services/test_activities.py index d9d1e662..f6fe64d5 100644 --- a/test/services/test_activities.py +++ b/test/services/test_activities.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list_events(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.activities.list_events() + response = self.activities.list_events( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/activities/events', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -65,7 +83,7 @@ def test_get_event(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -73,4 +91,12 @@ def test_get_event(self, m): response = self.activities.get_event( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/activities/events/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_advisor.py b/test/services/test_advisor.py index 74d8eb1d..f2dacf9a 100644 --- a/test/services/test_advisor.py +++ b/test/services/test_advisor.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list_reports(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.advisor.list_reports() + response = self.advisor.list_reports( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -47,7 +65,7 @@ def test_get_report(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -55,6 +73,14 @@ def test_get_report(self, m): response = self.advisor.get_report( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -62,7 +88,7 @@ def test_delete_report(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -70,6 +96,14 @@ def test_delete_report(self, m): response = self.advisor.delete_report( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -80,13 +114,28 @@ def test_list_insights(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.advisor.list_insights( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports//insights', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -110,7 +159,7 @@ def test_get_insight(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -119,4 +168,12 @@ def test_get_insight(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports//insights/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_apps.py b/test/services/test_apps.py index 0e6c2628..6c71f4ae 100644 --- a/test/services/test_apps.py +++ b/test/services/test_apps.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.apps.list() + response = self.apps.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -62,7 +80,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -71,6 +89,53 @@ def test_create(self, m): '', '', [], + description='', + client_uri='https://example.com', + logo_uri='https://example.com', + privacy_policy_url='https://example.com', + terms_url='https://example.com', + contacts=[], + tagline='', + tags=[], + images=[], + support_url='https://example.com', + data_deletion_url='https://example.com', + post_logout_redirect_uris=[], + enabled=False, + type='public', + device_flow=False, + team_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '', + 'name': '', + 'description': '', + 'clientUri': 'https://example.com', + 'logoUri': 'https://example.com', + 'privacyPolicyUrl': 'https://example.com', + 'termsUrl': 'https://example.com', + 'contacts': [], + 'tagline': '', + 'tags': [], + 'images': [], + 'supportUrl': 'https://example.com', + 'dataDeletionUrl': 'https://example.com', + 'redirectUris': [], + 'postLogoutRedirectUris': [], + 'enabled': False, + 'type': 'public', + 'deviceFlow': False, + 'teamId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -82,12 +147,20 @@ def test_list_installation_scopes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_installation_scopes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/scopes/installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +171,20 @@ def test_list_o_auth2_scopes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_o_auth2_scopes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/scopes/oauth2', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -138,7 +219,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -146,6 +227,14 @@ def test_get(self, m): response = self.apps.get( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -180,7 +269,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -188,6 +277,55 @@ def test_update(self, m): response = self.apps.update( '', '', + description='', + client_uri='https://example.com', + logo_uri='https://example.com', + privacy_policy_url='https://example.com', + terms_url='https://example.com', + contacts=[], + tagline='', + tags=[], + images=[], + support_url='https://example.com', + data_deletion_url='https://example.com', + enabled=False, + redirect_uris=[], + post_logout_redirect_uris=[], + type='public', + device_flow=False, + installation_scopes=[], + installation_redirect_url='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + 'description': '', + 'clientUri': 'https://example.com', + 'logoUri': 'https://example.com', + 'privacyPolicyUrl': 'https://example.com', + 'termsUrl': 'https://example.com', + 'contacts': [], + 'tagline': '', + 'tags': [], + 'images': [], + 'supportUrl': 'https://example.com', + 'dataDeletionUrl': 'https://example.com', + 'enabled': False, + 'redirectUris': [], + 'postLogoutRedirectUris': [], + 'type': 'public', + 'deviceFlow': False, + 'installationScopes': [], + 'installationRedirectUrl': 'https://example.com', + }, ) self.assertEqual(response.to_dict(), data) @@ -196,7 +334,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -204,6 +342,14 @@ def test_delete(self, m): response = self.apps.delete( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -214,13 +360,28 @@ def test_list_installations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_installations( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -239,7 +400,7 @@ def test_get_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -248,6 +409,14 @@ def test_get_installation(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//installations/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -255,7 +424,7 @@ def test_delete_installation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -264,6 +433,14 @@ def test_delete_installation(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//installations/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -277,7 +454,7 @@ def test_create_installation_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -286,6 +463,14 @@ def test_create_installation_token(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//installations//tokens', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -296,13 +481,28 @@ def test_list_keys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_keys( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//keys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -320,7 +520,7 @@ def test_create_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -328,6 +528,14 @@ def test_create_key(self, m): response = self.apps.create_key( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//keys', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -344,7 +552,7 @@ def test_get_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -353,6 +561,14 @@ def test_get_key(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//keys/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -360,7 +576,7 @@ def test_delete_key(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -369,6 +585,14 @@ def test_delete_key(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//keys/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -403,7 +627,7 @@ def test_update_labels(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -412,6 +636,19 @@ def test_update_labels(self, m): '', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//labels', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'labels': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -422,13 +659,28 @@ def test_list_secrets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_secrets( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//secrets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -446,7 +698,7 @@ def test_create_secret(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -454,6 +706,14 @@ def test_create_secret(self, m): response = self.apps.create_secret( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//secrets', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -470,7 +730,7 @@ def test_get_secret(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -479,6 +739,14 @@ def test_get_secret(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//secrets/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -486,7 +754,7 @@ def test_delete_secret(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +763,14 @@ def test_delete_secret(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//secrets/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -529,7 +805,7 @@ def test_update_team(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -538,6 +814,19 @@ def test_update_team(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//team', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -545,7 +834,7 @@ def test_delete_tokens(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -553,4 +842,12 @@ def test_delete_tokens(self, m): response = self.apps.delete_tokens( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps//tokens', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_avatars.py b/test/services/test_avatars.py index c54fe183..96f0e5ba 100644 --- a/test/services/test_avatars.py +++ b/test/services/test_avatars.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -19,13 +21,31 @@ def test_get_browser(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_browser( 'aa', + width=1, + height=1, + quality=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/browsers/aa', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + }, ) self.assertEqual(response, data) @@ -34,13 +54,31 @@ def test_get_credit_card(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_credit_card( 'amex', + width=1, + height=1, + quality=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/credit-cards/amex', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + }, ) self.assertEqual(response, data) @@ -49,7 +87,7 @@ def test_get_favicon(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -57,6 +95,19 @@ def test_get_favicon(self, m): response = self.avatars.get_favicon( 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/favicon', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'url': ['https://example.com'], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -64,13 +115,31 @@ def test_get_flag(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_flag( 'af', + width=1, + height=1, + quality=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/flags/af', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + }, ) self.assertEqual(response, data) @@ -79,13 +148,30 @@ def test_get_image(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_image( 'https://example.com', + width=1, + height=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/image', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'url': ['https://example.com'], + 'width': ['1'], + 'height': ['1'], + }, ) self.assertEqual(response, data) @@ -94,12 +180,33 @@ def test_get_initials(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) - response = self.avatars.get_initials() + response = self.avatars.get_initials( + name='', + width=1, + height=1, + background='FFFFFF', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/initials', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'name': [''], + 'width': ['1'], + 'height': ['1'], + 'background': ['FFFFFF'], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -107,12 +214,41 @@ def test_get_photo(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) - response = self.avatars.get_photo() + response = self.avatars.get_photo( + width=1, + height=1, + quality=1, + output='png', + rating='g', + user_id='current()', + email_hash='', + name='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/photo', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + 'output': ['png'], + 'rating': ['g'], + 'userId': ['current()'], + 'emailHash': [''], + 'name': [''], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -137,7 +273,7 @@ def test_update_photo(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -145,6 +281,25 @@ def test_update_photo(self, m): response = self.avatars.update_photo( InputFile.from_bytes(bytearray(), "example.file"), ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/photo', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'file': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -152,12 +307,20 @@ def test_delete_photo(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.avatars.delete_photo() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/photo', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -165,13 +328,32 @@ def test_get_qr(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_qr( '', + size=1, + margin=1, + download=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/qr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'text': [''], + 'size': ['1'], + 'margin': ['1'], + 'download': ['false'], + }, ) self.assertEqual(response, data) @@ -180,12 +362,61 @@ def test_get_screenshot(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_screenshot( 'https://example.com', + headers={}, + viewport_width=1, + viewport_height=1, + scale=1.0, + theme='dark', + user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', + fullpage=False, + locale='en-US', + timezone='America/New_York', + latitude=1.0, + longitude=1.0, + accuracy=1.0, + touch=False, + permissions=[], + sleep=1, + width=1, + height=1, + quality=1, + output='jpeg', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/screenshots', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'url': ['https://example.com'], + 'viewportWidth': ['1'], + 'viewportHeight': ['1'], + 'scale': ['1.0'], + 'theme': ['dark'], + 'userAgent': ['Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15'], + 'fullpage': ['false'], + 'locale': ['en-US'], + 'timezone': ['America/New_York'], + 'latitude': ['1.0'], + 'longitude': ['1.0'], + 'accuracy': ['1.0'], + 'touch': ['false'], + 'sleep': ['1'], + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + 'output': ['jpeg'], + }, ) self.assertEqual(response, data) diff --git a/test/services/test_backups.py b/test/services/test_backups.py index 175b3515..2254017e 100644 --- a/test/services/test_backups.py +++ b/test/services/test_backups.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list_archives(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.backups.list_archives() + response = self.backups.list_archives( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -44,13 +62,28 @@ def test_create_archive(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.backups.create_archive( [], + resource_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'services': [], + 'resourceId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -68,7 +101,7 @@ def test_get_archive(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -76,6 +109,14 @@ def test_get_archive(self, m): response = self.backups.get_archive( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -83,7 +124,7 @@ def test_delete_archive(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -91,6 +132,14 @@ def test_delete_archive(self, m): response = self.backups.delete_archive( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -101,12 +150,28 @@ def test_list_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.backups.list_policies() + response = self.backups.list_policies( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -125,7 +190,7 @@ def test_create_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -135,6 +200,28 @@ def test_create_policy(self, m): [], 1, 'schedule', + name='', + resource_id='', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '', + 'name': '', + 'services': [], + 'resourceId': '', + 'enabled': False, + 'retention': 1, + 'schedule': 'schedule', + }, ) self.assertEqual(response.to_dict(), data) @@ -154,7 +241,7 @@ def test_get_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -162,6 +249,14 @@ def test_get_policy(self, m): response = self.backups.get_policy( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -180,13 +275,33 @@ def test_update_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.backups.update_policy( '', + name='', + retention=1, + schedule='schedule', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + 'retention': 1, + 'schedule': 'schedule', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -195,7 +310,7 @@ def test_delete_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -203,6 +318,14 @@ def test_delete_policy(self, m): response = self.backups.delete_policy( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -221,7 +344,7 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -229,6 +352,24 @@ def test_create_restoration(self, m): response = self.backups.create_restoration( '', [], + new_resource_id='', + new_resource_name='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/restoration', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'archiveId': '', + 'services': [], + 'newResourceId': '', + 'newResourceName': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -240,12 +381,28 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.backups.list_restorations() + response = self.backups.list_restorations( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -264,7 +421,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -272,4 +429,12 @@ def test_get_restoration(self, m): response = self.backups.get_restoration( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/restorations/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_databases.py b/test/services/test_databases.py index 14d81297..65de0627 100644 --- a/test/services/test_databases.py +++ b/test/services/test_databases.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.databases.list() + response = self.databases.list( + queries=[], + search='', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': [''], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +63,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +71,24 @@ def test_create(self, m): response = self.databases.create( '', '', + enabled=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '', + 'name': '', + 'enabled': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +100,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.databases.list_transactions() + response = self.databases.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -82,12 +136,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.databases.create_transaction() + response = self.databases.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -102,7 +171,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -110,6 +179,14 @@ def test_get_transaction(self, m): response = self.databases.get_transaction( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -124,13 +201,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.update_transaction( '', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -139,7 +232,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -147,6 +240,14 @@ def test_delete_transaction(self, m): response = self.databases.delete_transaction( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -161,13 +262,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.create_operations( '', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions//operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -184,7 +299,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -192,6 +307,14 @@ def test_get(self, m): response = self.databases.get( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -207,13 +330,31 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.update( '', + name='', + enabled=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + 'enabled': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -222,7 +363,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -230,6 +371,14 @@ def test_delete(self, m): response = self.databases.delete( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -240,13 +389,30 @@ def test_list_collections(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.list_collections( '', + queries=[], + search='', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': [''], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -269,7 +435,7 @@ def test_create_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -278,6 +444,32 @@ def test_create_collection(self, m): '', '', '', + permissions=[], + document_security=False, + enabled=False, + attributes=[], + indexes=[], + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'collectionId': '', + 'name': '', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'attributes': [], + 'indexes': [], + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -300,7 +492,7 @@ def test_get_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -309,6 +501,14 @@ def test_get_collection(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -330,7 +530,7 @@ def test_update_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -338,6 +538,30 @@ def test_update_collection(self, m): response = self.databases.update_collection( '', '', + name='', + permissions=[], + document_security=False, + enabled=False, + purge=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'purge': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -346,7 +570,7 @@ def test_delete_collection(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -355,6 +579,14 @@ def test_delete_collection(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -365,7 +597,7 @@ def test_list_attributes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -373,6 +605,21 @@ def test_list_attributes(self, m): response = self.databases.list_attributes( '', '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -390,7 +637,7 @@ def test_create_big_int_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -399,7 +646,31 @@ def test_create_big_int_attribute(self, m): '', '', '', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/bigint', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -417,7 +688,7 @@ def test_update_big_int_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -426,8 +697,30 @@ def test_update_big_int_attribute(self, m): '', '', '', - True, + False, 1, + min=1, + max=1, + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/bigint/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -445,7 +738,7 @@ def test_create_boolean_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -454,7 +747,27 @@ def test_create_boolean_attribute(self, m): '', '', '', - True, + False, + default=False, + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/boolean', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': False, + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -472,7 +785,7 @@ def test_update_boolean_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -481,8 +794,26 @@ def test_update_boolean_attribute(self, m): '', '', '', - True, - True, + False, + False, + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/boolean/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': False, + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -501,7 +832,7 @@ def test_create_datetime_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -510,7 +841,27 @@ def test_create_datetime_attribute(self, m): '', '', '', - True, + False, + default='2020-10-15T06:38:00.000+00:00', + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/datetime', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -529,7 +880,7 @@ def test_update_datetime_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -538,8 +889,26 @@ def test_update_datetime_attribute(self, m): '', '', '', - True, + False, '2020-10-15T06:38:00.000+00:00', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/datetime/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -558,7 +927,7 @@ def test_create_email_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -567,7 +936,27 @@ def test_create_email_attribute(self, m): '', '', '', - True, + False, + default='email@example.com', + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': 'email@example.com', + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -586,7 +975,7 @@ def test_update_email_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -595,8 +984,26 @@ def test_update_email_attribute(self, m): '', '', '', - True, + False, 'email@example.com', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/email/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'email@example.com', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -616,7 +1023,7 @@ def test_create_enum_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -626,7 +1033,28 @@ def test_create_enum_attribute(self, m): '', '', [], - True, + False, + default='active', + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/enum', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'elements': [], + 'required': False, + 'default': 'active', + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -646,7 +1074,7 @@ def test_update_enum_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -656,8 +1084,27 @@ def test_update_enum_attribute(self, m): '', '', [], - True, + False, 'active', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/enum/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'elements': [], + 'required': False, + 'default': 'active', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -675,7 +1122,7 @@ def test_create_float_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -684,7 +1131,31 @@ def test_create_float_attribute(self, m): '', '', '', - True, + False, + min=1.0, + max=1.0, + default=1.0, + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/float', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -702,7 +1173,7 @@ def test_update_float_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -711,8 +1182,30 @@ def test_update_float_attribute(self, m): '', '', '', - True, + False, 1.0, + min=1.0, + max=1.0, + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/float/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -730,7 +1223,7 @@ def test_create_integer_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -739,7 +1232,31 @@ def test_create_integer_attribute(self, m): '', '', '', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/integer', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -757,7 +1274,7 @@ def test_update_integer_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -766,8 +1283,30 @@ def test_update_integer_attribute(self, m): '', '', '', - True, + False, 1, + min=1, + max=1, + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/integer/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -786,7 +1325,7 @@ def test_create_ip_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -795,7 +1334,27 @@ def test_create_ip_attribute(self, m): '', '', '', - True, + False, + default='192.0.2.0', + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/ip', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': '192.0.2.0', + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -814,7 +1373,7 @@ def test_update_ip_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -823,8 +1382,26 @@ def test_update_ip_attribute(self, m): '', '', '', - True, + False, '192.0.2.0', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/ip/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '192.0.2.0', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -842,7 +1419,7 @@ def test_create_line_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -851,7 +1428,25 @@ def test_create_line_attribute(self, m): '', '', '', - True, + False, + default=[], + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/line', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': [], + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -869,7 +1464,7 @@ def test_update_line_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -878,7 +1473,26 @@ def test_update_line_attribute(self, m): '', '', '', - True, + False, + default=[], + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/line/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -896,7 +1510,7 @@ def test_create_longtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -905,7 +1519,29 @@ def test_create_longtext_attribute(self, m): '', '', '', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/longtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -923,7 +1559,7 @@ def test_update_longtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -932,8 +1568,26 @@ def test_update_longtext_attribute(self, m): '', '', '', - True, + False, 'Hello World', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/longtext/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -951,7 +1605,7 @@ def test_create_mediumtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -960,7 +1614,29 @@ def test_create_mediumtext_attribute(self, m): '', '', '', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/mediumtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -978,7 +1654,7 @@ def test_update_mediumtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -987,8 +1663,26 @@ def test_update_mediumtext_attribute(self, m): '', '', '', - True, + False, 'Hello World', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/mediumtext/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1006,7 +1700,7 @@ def test_create_point_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1015,7 +1709,25 @@ def test_create_point_attribute(self, m): '', '', '', - True, + False, + default=[], + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/point', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': [], + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1033,7 +1745,7 @@ def test_update_point_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1042,7 +1754,26 @@ def test_update_point_attribute(self, m): '', '', '', - True, + False, + default=[], + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/point/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1060,7 +1791,7 @@ def test_create_polygon_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1069,7 +1800,25 @@ def test_create_polygon_attribute(self, m): '', '', '', - True, + False, + default=[], + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/polygon', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': [], + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1087,7 +1836,7 @@ def test_update_polygon_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1096,7 +1845,26 @@ def test_update_polygon_attribute(self, m): '', '', '', - True, + False, + default=[], + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/polygon/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1120,7 +1888,7 @@ def test_create_relationship_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1130,6 +1898,30 @@ def test_create_relationship_attribute(self, m): '', '', 'oneToOne', + two_way=False, + key='', + two_way_key='', + on_delete='cascade', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/relationship', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'relatedCollectionId': '', + 'type': 'oneToOne', + 'twoWay': False, + 'key': '', + 'twoWayKey': '', + 'onDelete': 'cascade', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1153,7 +1945,7 @@ def test_update_relationship_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1162,6 +1954,24 @@ def test_update_relationship_attribute(self, m): '', '', '', + on_delete='cascade', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/relationship/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'onDelete': 'cascade', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1180,7 +1990,7 @@ def test_create_string_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1190,7 +2000,30 @@ def test_create_string_attribute(self, m): '', '', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/string', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1209,7 +2042,7 @@ def test_update_string_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1218,8 +2051,28 @@ def test_update_string_attribute(self, m): '', '', '', - True, + False, 'Hello World', + size=1, + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/string/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1237,7 +2090,7 @@ def test_create_text_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1246,7 +2099,29 @@ def test_create_text_attribute(self, m): '', '', '', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/text', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1264,7 +2139,7 @@ def test_update_text_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1273,8 +2148,26 @@ def test_update_text_attribute(self, m): '', '', '', - True, + False, 'Hello World', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/text/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1293,7 +2186,7 @@ def test_create_url_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1302,7 +2195,27 @@ def test_create_url_attribute(self, m): '', '', '', - True, + False, + default='https://example.com', + array=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'required': False, + 'default': 'https://example.com', + 'array': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1321,7 +2234,7 @@ def test_update_url_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1330,8 +2243,26 @@ def test_update_url_attribute(self, m): '', '', '', - True, + False, 'https://example.com', + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/url/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'https://example.com', + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1350,7 +2281,7 @@ def test_create_varchar_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1360,7 +2291,30 @@ def test_create_varchar_attribute(self, m): '', '', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/varchar', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1379,7 +2333,7 @@ def test_update_varchar_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1388,8 +2342,28 @@ def test_update_varchar_attribute(self, m): '', '', '', - True, + False, 'Hello World', + size=1, + new_key='', + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/varchar/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '', + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1407,7 +2381,7 @@ def test_get_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1417,6 +2391,14 @@ def test_get_attribute(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1424,7 +2406,7 @@ def test_delete_attribute(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1434,6 +2416,14 @@ def test_delete_attribute(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//attributes/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1444,7 +2434,7 @@ def test_list_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1452,6 +2442,25 @@ def test_list_documents(self, m): response = self.databases.list_documents( '', '', + queries=[], + transaction_id='', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': [''], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1468,7 +2477,7 @@ def test_create_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1478,6 +2487,24 @@ def test_create_document(self, m): '', '', {}, + permissions=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documentId': '', + 'data': {}, + 'permissions': [], + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1490,7 +2517,7 @@ def test_create_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1499,6 +2526,21 @@ def test_create_documents(self, m): '', '', [], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1510,7 +2552,7 @@ def test_upsert_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1519,6 +2561,21 @@ def test_upsert_documents(self, m): '', '', [], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1530,7 +2587,7 @@ def test_update_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1538,6 +2595,24 @@ def test_update_documents(self, m): response = self.databases.update_documents( '', '', + data={}, + queries=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1549,7 +2624,7 @@ def test_delete_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1557,6 +2632,22 @@ def test_delete_documents(self, m): response = self.databases.delete_documents( '', '', + queries=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1568,7 +2659,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1576,6 +2667,26 @@ def test_create_query(self, m): response = self.databases.create_query( '', '', + queries=[], + transaction_id='', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1592,7 +2703,7 @@ def test_get_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1601,6 +2712,21 @@ def test_get_document(self, m): '', '', '', + queries=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': [''], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1618,7 +2744,7 @@ def test_upsert_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1627,6 +2753,24 @@ def test_upsert_document(self, m): '', '', '', + data={}, + permissions=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1644,7 +2788,7 @@ def test_update_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1653,6 +2797,24 @@ def test_update_document(self, m): '', '', '', + data={}, + permissions=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1662,7 +2824,7 @@ def test_delete_document(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1671,6 +2833,20 @@ def test_delete_document(self, m): '', '', '', + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '', + }, ) self.assertEqual(response, data) @@ -1687,7 +2863,7 @@ def test_decrement_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1697,6 +2873,24 @@ def test_decrement_document_attribute(self, m): '', '', '', + value=1.0, + min=1.0, + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents///decrement', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'min': 1.0, + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1714,7 +2908,7 @@ def test_increment_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1724,6 +2918,24 @@ def test_increment_document_attribute(self, m): '', '', '', + value=1.0, + max=1.0, + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//documents///increment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'max': 1.0, + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1736,7 +2948,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1744,6 +2956,21 @@ def test_list_indexes(self, m): response = self.databases.list_indexes( '', '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1763,7 +2990,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1774,6 +3001,27 @@ def test_create_index(self, m): '', 'key', [], + orders=[], + lengths=[], + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'type': 'key', + 'attributes': [], + 'orders': [], + 'lengths': [], + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1793,7 +3041,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1803,6 +3051,14 @@ def test_get_index(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//indexes/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1810,7 +3066,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1820,4 +3076,12 @@ def test_delete_index(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases//collections//indexes/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_documents_db.py b/test/services/test_documents_db.py index c9840596..40ceecf5 100644 --- a/test/services/test_documents_db.py +++ b/test/services/test_documents_db.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.documents_db.list() + response = self.documents_db.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +61,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +69,30 @@ def test_create(self, m): response = self.documents_db.create( '', '', + enabled=False, + description='', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '', + 'name': '', + 'enabled': False, + 'description': '', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +104,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -78,12 +128,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.documents_db.list_transactions() + response = self.documents_db.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +164,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.documents_db.create_transaction() + response = self.documents_db.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -118,7 +199,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -126,6 +207,14 @@ def test_get_transaction(self, m): response = self.documents_db.get_transaction( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -140,13 +229,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.update_transaction( '', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -155,7 +260,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -163,6 +268,14 @@ def test_delete_transaction(self, m): response = self.documents_db.delete_transaction( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -177,13 +290,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.create_operations( '', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions//operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -200,7 +327,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -208,6 +335,14 @@ def test_get(self, m): response = self.documents_db.get( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -223,7 +358,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -231,6 +366,29 @@ def test_update(self, m): response = self.documents_db.update( '', '', + enabled=False, + description='', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + 'enabled': False, + 'description': '', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -239,7 +397,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -247,6 +405,14 @@ def test_delete(self, m): response = self.documents_db.delete( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -257,13 +423,30 @@ def test_list_collections(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.list_collections( '', + queries=[], + search='', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': [''], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -286,7 +469,7 @@ def test_create_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -295,6 +478,32 @@ def test_create_collection(self, m): '', '', '', + permissions=[], + document_security=False, + enabled=False, + attributes=[], + indexes=[], + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'collectionId': '', + 'name': '', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'attributes': [], + 'indexes': [], + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -317,7 +526,7 @@ def test_get_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -326,6 +535,14 @@ def test_get_collection(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -347,7 +564,7 @@ def test_update_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -356,6 +573,29 @@ def test_update_collection(self, m): '', '', '', + permissions=[], + document_security=False, + enabled=False, + purge=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'purge': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -364,7 +604,7 @@ def test_delete_collection(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -373,6 +613,14 @@ def test_delete_collection(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -383,7 +631,7 @@ def test_list_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -391,6 +639,25 @@ def test_list_documents(self, m): response = self.documents_db.list_documents( '', '', + queries=[], + transaction_id='', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': [''], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -407,7 +674,7 @@ def test_create_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -417,6 +684,24 @@ def test_create_document(self, m): '', '', {}, + permissions=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documentId': '', + 'data': {}, + 'permissions': [], + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -429,7 +714,7 @@ def test_create_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -438,6 +723,21 @@ def test_create_documents(self, m): '', '', [], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -449,7 +749,7 @@ def test_upsert_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -458,6 +758,21 @@ def test_upsert_documents(self, m): '', '', [], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -469,7 +784,7 @@ def test_update_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +792,24 @@ def test_update_documents(self, m): response = self.documents_db.update_documents( '', '', + data={}, + queries=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -488,7 +821,7 @@ def test_delete_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -496,6 +829,22 @@ def test_delete_documents(self, m): response = self.documents_db.delete_documents( '', '', + queries=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -507,7 +856,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -515,6 +864,26 @@ def test_create_query(self, m): response = self.documents_db.create_query( '', '', + queries=[], + transaction_id='', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -531,7 +900,7 @@ def test_get_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -540,6 +909,21 @@ def test_get_document(self, m): '', '', '', + queries=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': [''], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -557,7 +941,7 @@ def test_upsert_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -566,6 +950,24 @@ def test_upsert_document(self, m): '', '', '', + data={}, + permissions=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -583,7 +985,7 @@ def test_update_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -592,6 +994,24 @@ def test_update_document(self, m): '', '', '', + data={}, + permissions=[], + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -601,7 +1021,7 @@ def test_delete_document(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -610,6 +1030,20 @@ def test_delete_document(self, m): '', '', '', + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '', + }, ) self.assertEqual(response, data) @@ -626,7 +1060,7 @@ def test_decrement_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -636,6 +1070,24 @@ def test_decrement_document_attribute(self, m): '', '', '', + value=1.0, + min=1.0, + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents///decrement', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'min': 1.0, + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -653,7 +1105,7 @@ def test_increment_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -663,6 +1115,24 @@ def test_increment_document_attribute(self, m): '', '', '', + value=1.0, + max=1.0, + transaction_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//documents///increment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'max': 1.0, + 'transactionId': '', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -675,7 +1145,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -683,6 +1153,21 @@ def test_list_indexes(self, m): response = self.documents_db.list_indexes( '', '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -702,7 +1187,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -713,6 +1198,27 @@ def test_create_index(self, m): '', 'key', [], + orders=[], + lengths=[], + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'type': 'key', + 'attributes': [], + 'orders': [], + 'lengths': [], + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -732,7 +1238,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -742,6 +1248,14 @@ def test_get_index(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//indexes/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -749,7 +1263,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -759,6 +1273,14 @@ def test_delete_index(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//collections//indexes/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -812,13 +1334,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.create_failover( '', + target_replica_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -830,13 +1366,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.list_operations( '', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -852,7 +1406,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -860,6 +1414,14 @@ def test_get_replicas(self, m): response = self.documents_db.get_replicas( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -879,7 +1441,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -887,4 +1449,12 @@ def test_get_status(self, m): response = self.documents_db.get_status( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb//status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_domains.py b/test/services/test_domains.py index 8ae6d3c4..279d3402 100644 --- a/test/services/test_domains.py +++ b/test/services/test_domains.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.domains.list() + response = self.domains.list( + queries=[], + search='', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': [''], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -48,7 +68,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -57,6 +77,20 @@ def test_create(self, m): '', 'example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '', + 'domain': 'example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -71,13 +105,30 @@ def test_get_price(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.get_price( 'example.com', + period_years=1, + registration_type='new', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/price', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'domain': ['example.com'], + 'periodYears': ['1'], + 'registrationType': ['new'], + }, ) self.assertEqual(response.to_dict(), data) @@ -89,13 +140,29 @@ def test_list_prices(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.list_prices( [], + period_years=1, + registration_type='new', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/prices', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'periodYears': ['1'], + 'registrationType': ['new'], + }, ) self.assertEqual(response.to_dict(), data) @@ -117,7 +184,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -125,6 +192,14 @@ def test_get(self, m): response = self.domains.get( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -132,7 +207,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -140,6 +215,14 @@ def test_delete(self, m): response = self.domains.delete( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -160,13 +243,27 @@ def test_update_nameservers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.update_nameservers( '', + nameservers=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//nameservers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'nameservers': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -188,7 +285,7 @@ def test_verify_nameservers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -196,6 +293,14 @@ def test_verify_nameservers(self, m): response = self.domains.verify_nameservers( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//nameservers/verification', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -206,7 +311,7 @@ def test_get_preset_google_workspace(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -214,6 +319,14 @@ def test_get_preset_google_workspace(self, m): response = self.domains.get_preset_google_workspace( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/google-workspace', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -224,7 +337,7 @@ def test_create_preset_google_workspace(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -232,6 +345,14 @@ def test_create_preset_google_workspace(self, m): response = self.domains.create_preset_google_workspace( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/google-workspace', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -242,7 +363,7 @@ def test_get_preset_i_cloud(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -250,6 +371,14 @@ def test_get_preset_i_cloud(self, m): response = self.domains.get_preset_i_cloud( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/icloud', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -260,7 +389,7 @@ def test_create_preset_i_cloud(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -268,6 +397,14 @@ def test_create_preset_i_cloud(self, m): response = self.domains.create_preset_i_cloud( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/icloud', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -278,7 +415,7 @@ def test_get_preset_mailgun(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -286,6 +423,14 @@ def test_get_preset_mailgun(self, m): response = self.domains.get_preset_mailgun( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/mailgun', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -296,7 +441,7 @@ def test_create_preset_mailgun(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -304,6 +449,14 @@ def test_create_preset_mailgun(self, m): response = self.domains.create_preset_mailgun( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/mailgun', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -314,7 +467,7 @@ def test_get_preset_outlook(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -322,6 +475,14 @@ def test_get_preset_outlook(self, m): response = self.domains.get_preset_outlook( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/outlook', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -332,7 +493,7 @@ def test_create_preset_outlook(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -340,6 +501,14 @@ def test_create_preset_outlook(self, m): response = self.domains.create_preset_outlook( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/outlook', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -350,7 +519,7 @@ def test_get_preset_proton_mail(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -358,6 +527,14 @@ def test_get_preset_proton_mail(self, m): response = self.domains.get_preset_proton_mail( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/proton-mail', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -368,7 +545,7 @@ def test_create_preset_proton_mail(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -376,6 +553,14 @@ def test_create_preset_proton_mail(self, m): response = self.domains.create_preset_proton_mail( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/proton-mail', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -386,7 +571,7 @@ def test_get_preset_zoho(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -394,6 +579,14 @@ def test_get_preset_zoho(self, m): response = self.domains.get_preset_zoho( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/zoho', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -404,7 +597,7 @@ def test_create_preset_zoho(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -412,6 +605,14 @@ def test_create_preset_zoho(self, m): response = self.domains.create_preset_zoho( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//presets/zoho', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -422,13 +623,28 @@ def test_list_records(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.list_records( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -450,7 +666,7 @@ def test_create_record_a(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -460,6 +676,23 @@ def test_create_record_a(self, m): 'name', 'value', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/a', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -481,7 +714,7 @@ def test_update_record_a(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -492,6 +725,23 @@ def test_update_record_a(self, m): 'name', 'value', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/a/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -513,7 +763,7 @@ def test_create_record_aaaa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -523,6 +773,23 @@ def test_create_record_aaaa(self, m): 'name', 'value', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/aaaa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -544,7 +811,7 @@ def test_update_record_aaaa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -555,6 +822,23 @@ def test_update_record_aaaa(self, m): 'name', 'value', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/aaaa/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -576,7 +860,7 @@ def test_create_record_alias(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -586,6 +870,23 @@ def test_create_record_alias(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/alias', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -607,7 +908,7 @@ def test_update_record_alias(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +919,23 @@ def test_update_record_alias(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/alias/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -639,7 +957,7 @@ def test_create_record_caa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -649,6 +967,23 @@ def test_create_record_caa(self, m): 'name', 'value', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/caa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -670,7 +1005,7 @@ def test_update_record_caa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -681,6 +1016,23 @@ def test_update_record_caa(self, m): 'name', 'value', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/caa/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -702,7 +1054,7 @@ def test_create_record_cname(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -712,6 +1064,23 @@ def test_create_record_cname(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/cname', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -733,7 +1102,7 @@ def test_update_record_cname(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -744,6 +1113,23 @@ def test_update_record_cname(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/cname/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -765,7 +1151,7 @@ def test_create_record_https(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -775,6 +1161,23 @@ def test_create_record_https(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/https', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -796,7 +1199,7 @@ def test_update_record_https(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -807,6 +1210,23 @@ def test_update_record_https(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/https/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -828,7 +1248,7 @@ def test_create_record_mx(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -839,6 +1259,24 @@ def test_create_record_mx(self, m): '', 1, 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/mx', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'priority': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -860,7 +1298,7 @@ def test_update_record_mx(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -872,6 +1310,24 @@ def test_update_record_mx(self, m): '', 1, 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/mx/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'priority': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -893,7 +1349,7 @@ def test_create_record_ns(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -903,6 +1359,23 @@ def test_create_record_ns(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/ns', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -924,7 +1397,7 @@ def test_update_record_ns(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -935,6 +1408,23 @@ def test_update_record_ns(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/ns/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -956,7 +1446,7 @@ def test_create_record_srv(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -969,6 +1459,26 @@ def test_create_record_srv(self, m): 1, 1, 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/srv', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'priority': 1, + 'weight': 1, + 'port': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -990,7 +1500,7 @@ def test_update_record_srv(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1004,6 +1514,26 @@ def test_update_record_srv(self, m): 1, 1, 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/srv/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'priority': 1, + 'weight': 1, + 'port': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1025,7 +1555,7 @@ def test_create_record_txt(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1034,6 +1564,24 @@ def test_create_record_txt(self, m): '', 'name', 1, + value='', + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/txt', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1055,7 +1603,7 @@ def test_update_record_txt(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1066,6 +1614,23 @@ def test_update_record_txt(self, m): 'name', '', 1, + comment='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/txt/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '', + 'ttl': 1, + 'comment': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -1087,7 +1652,7 @@ def test_get_record(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1096,6 +1661,14 @@ def test_get_record(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1103,7 +1676,7 @@ def test_delete_record(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1112,6 +1685,14 @@ def test_delete_record(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//records/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1132,7 +1713,7 @@ def test_update_team(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1141,6 +1722,19 @@ def test_update_team(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//team', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1152,7 +1746,7 @@ def test_get_transfer_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1160,6 +1754,14 @@ def test_get_transfer_status(self, m): response = self.domains.get_transfer_status( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//transfers/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1167,7 +1769,7 @@ def test_get_zone(self, m): data = '; café zone\nwww 3600 IN A 192.0.2.1\n' headers = {'Content-Type': 'text/plain; charset=utf-8'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=data, headers=headers, @@ -1175,6 +1777,14 @@ def test_get_zone(self, m): response = self.domains.get_zone( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//zone', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1195,7 +1805,7 @@ def test_update_zone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1204,4 +1814,17 @@ def test_update_zone(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains//zone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'content': '', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_embeddings.py b/test/services/test_embeddings.py index 4c37d10d..fde2f29b 100644 --- a/test/services/test_embeddings.py +++ b/test/services/test_embeddings.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,27 @@ def test_create_text_embeddings(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.embeddings.create_text_embeddings( [], + model='nomic-embed-text', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/embeddings/text', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'texts': [], + 'model': 'nomic-embed-text', + }, ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_functions.py b/test/services/test_functions.py index 1f63e700..c8c0593e 100644 --- a/test/services/test_functions.py +++ b/test/services/test_functions.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.functions.list() + response = self.functions.list( + queries=[], + search='', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': [''], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -70,7 +90,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -79,6 +99,63 @@ def test_create(self, m): '', '', 'node-14.5', + execute=[], + events=[], + schedule='0 0 * * *', + timeout=1, + enabled=False, + logging=False, + entrypoint='', + commands='', + scopes=[], + installation_id='', + provider_repository_id='', + provider_branch='', + provider_silent_mode=False, + provider_root_directory='', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + interval=1, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'functionId': '', + 'name': '', + 'runtime': 'node-14.5', + 'execute': [], + 'events': [], + 'schedule': '0 0 * * *', + 'timeout': 1, + 'enabled': False, + 'logging': False, + 'entrypoint': '', + 'commands': '', + 'scopes': [], + 'installationId': '', + 'providerRepositoryId': '', + 'providerBranch': '', + 'providerSilentMode': False, + 'providerRootDirectory': '', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'interval': 1, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -90,12 +167,27 @@ def test_list_runtimes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.functions.list_runtimes() + response = self.functions.list_runtimes( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/runtimes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -106,12 +198,29 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.functions.list_specifications() + response = self.functions.list_specifications( + type='runtimes', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['runtimes'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -154,7 +263,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -162,6 +271,14 @@ def test_get(self, m): response = self.functions.get( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -204,7 +321,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -212,6 +329,65 @@ def test_update(self, m): response = self.functions.update( '', '', + runtime='node-14.5', + execute=[], + events=[], + schedule='0 0 * * *', + timeout=1, + enabled=False, + logging=False, + entrypoint='', + commands='', + scopes=[], + installation_id='', + provider_repository_id='', + provider_branch='', + provider_silent_mode=False, + provider_root_directory='', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + interval=1, + description='', + email_enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '', + 'runtime': 'node-14.5', + 'execute': [], + 'events': [], + 'schedule': '0 0 * * *', + 'timeout': 1, + 'enabled': False, + 'logging': False, + 'entrypoint': '', + 'commands': '', + 'scopes': [], + 'installationId': '', + 'providerRepositoryId': '', + 'providerBranch': '', + 'providerSilentMode': False, + 'providerRootDirectory': '', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'interval': 1, + 'description': '', + 'emailEnabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -220,7 +396,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -228,6 +404,14 @@ def test_delete(self, m): response = self.functions.delete( '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -270,7 +454,7 @@ def test_update_function_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -279,6 +463,19 @@ def test_update_function_deployment(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -289,13 +486,30 @@ def test_list_deployments(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.list_deployments( '', + queries=[], + search='', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': [''], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -332,7 +546,7 @@ def test_create_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -340,7 +554,31 @@ def test_create_deployment(self, m): response = self.functions.create_deployment( '', InputFile.from_bytes(bytearray(), "example.file"), - True, + False, + entrypoint='', + commands='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'entrypoint': '', + 'commands': '', + 'code': '', + 'activate': 'false', + }, ) self.assertEqual(response.to_dict(), data) @@ -377,7 +615,7 @@ def test_create_duplicate_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -385,6 +623,21 @@ def test_create_duplicate_deployment(self, m): response = self.functions.create_duplicate_deployment( '', '', + build_id='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments/duplicate', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '', + 'buildId': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -421,7 +674,7 @@ def test_create_template_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -433,6 +686,25 @@ def test_create_template_deployment(self, m): '', 'commit', '', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments/template', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'repository': '', + 'owner': '', + 'rootDirectory': '', + 'type': 'commit', + 'reference': '', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -469,7 +741,7 @@ def test_create_vcs_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -478,6 +750,22 @@ def test_create_vcs_deployment(self, m): '', 'branch', '', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments/vcs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'branch', + 'reference': '', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -514,7 +802,7 @@ def test_get_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -523,6 +811,14 @@ def test_get_deployment(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -530,7 +826,7 @@ def test_delete_deployment(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -539,6 +835,14 @@ def test_delete_deployment(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -546,7 +850,7 @@ def test_get_deployment_download(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -554,6 +858,22 @@ def test_get_deployment_download(self, m): response = self.functions.get_deployment_download( '', '', + type='source', + token='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments//download', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['source'], + 'token': [''], + }, ) self.assertEqual(response, data) @@ -590,7 +910,7 @@ def test_update_deployment_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -599,6 +919,14 @@ def test_update_deployment_status(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//deployments//status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -609,13 +937,28 @@ def test_list_executions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.list_executions( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//executions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -643,13 +986,37 @@ def test_create_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.create_execution( '', + body='', + xasync=False, + path='', + method='GET', + headers={}, + scheduled_at='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//executions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'body': '', + 'async': False, + 'path': '', + 'method': 'GET', + 'headers': {}, + 'scheduledAt': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -677,7 +1044,7 @@ def test_get_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -686,6 +1053,14 @@ def test_get_execution(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//executions/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -693,7 +1068,7 @@ def test_delete_execution(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -702,6 +1077,14 @@ def test_delete_execution(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//executions/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -712,13 +1095,28 @@ def test_list_variables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.list_variables( '', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//variables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -737,7 +1135,7 @@ def test_create_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -747,6 +1145,25 @@ def test_create_variable(self, m): '', '', '', + secret=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//variables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'variableId': '', + 'key': '', + 'value': '', + 'secret': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -765,7 +1182,7 @@ def test_get_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -774,6 +1191,14 @@ def test_get_variable(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//variables/', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -791,7 +1216,7 @@ def test_update_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -799,6 +1224,26 @@ def test_update_variable(self, m): response = self.functions.update_variable( '', '', + key='', + value='', + secret=False, + description='', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//variables/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '', + 'value': '', + 'secret': False, + 'description': '', + }, ) self.assertEqual(response.to_dict(), data) @@ -807,7 +1252,7 @@ def test_delete_variable(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -816,4 +1261,12 @@ def test_delete_variable(self, m): '', '', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions//variables/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_graphql.py b/test/services/test_graphql.py index d9203da8..a95db416 100644 --- a/test/services/test_graphql.py +++ b/test/services/test_graphql.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -19,7 +21,7 @@ def test_query(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -27,6 +29,19 @@ def test_query(self, m): response = self.graphql.query( {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/graphql', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'query': {}, + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -34,7 +49,7 @@ def test_mutation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -42,4 +57,17 @@ def test_mutation(self, m): response = self.graphql.mutation( {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/graphql/mutation', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'query': {}, + }, + ) self.assertEqual(response, data) diff --git a/test/services/test_locale.py b/test/services/test_locale.py index 1438edfb..15c90cb0 100644 --- a/test/services/test_locale.py +++ b/test/services/test_locale.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -27,12 +29,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.locale.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,12 +53,27 @@ def test_list_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_codes() + response = self.locale.list_codes( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -59,12 +84,27 @@ def test_list_continents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_continents() + response = self.locale.list_continents( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/continents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -75,12 +115,27 @@ def test_list_countries(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_countries() + response = self.locale.list_countries( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/countries', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -91,12 +146,27 @@ def test_list_countries_eu(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_countries_eu() + response = self.locale.list_countries_eu( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/countries/eu', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -107,12 +177,27 @@ def test_list_countries_phones(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_countries_phones() + response = self.locale.list_countries_phones( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/countries/phones', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -123,12 +208,27 @@ def test_list_currencies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_currencies() + response = self.locale.list_currencies( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/currencies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -139,10 +239,25 @@ def test_list_languages(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_languages() + response = self.locale.list_languages( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/languages', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_messaging.py b/test/services/test_messaging.py index d22f32d6..0c3cf54c 100644 --- a/test/services/test_messaging.py +++ b/test/services/test_messaging.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list_messages(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.messaging.list_messages() + response = self.messaging.list_messages( + queries=[], + search='', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': [''], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -46,7 +66,7 @@ def test_create_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -55,6 +75,45 @@ def test_create_email(self, m): '', '', '', + topics=[], + users=[], + targets=[], + cc=[], + bcc=[], + attachments=[], + draft=False, + html=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + reply_to_email='email@example.com', + reply_to_name='', + emails=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '', + 'subject': '', + 'content': '', + 'topics': [], + 'users': [], + 'targets': [], + 'cc': [], + 'bcc': [], + 'attachments': [], + 'draft': False, + 'html': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'replyToEmail': 'email@example.com', + 'replyToName': '', + 'emails': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -74,13 +133,53 @@ def test_update_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_email( '', + topics=[], + users=[], + targets=[], + subject='', + content='', + draft=False, + html=False, + cc=[], + bcc=[], + scheduled_at='2020-10-15T06:38:00.000+00:00', + attachments=[], + reply_to_email='email@example.com', + reply_to_name='', + emails=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/email/', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'subject': '', + 'content': '', + 'draft': False, + 'html': False, + 'cc': [], + 'bcc': [], + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'attachments': [], + 'replyToEmail': 'email@example.com', + 'replyToName': '', + 'emails': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -100,13 +199,64 @@ def test_create_push(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.create_push( '', + title='', + body='<BODY>', + topics=[], + users=[], + targets=[], + data={}, + action='<ACTION>', + image='<ID1:ID2>', + icon='<ICON>', + sound='<SOUND>', + color='<COLOR>', + tag='<TAG>', + badge=1, + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + content_available=False, + critical=False, + priority='normal', + channel_id='<CHANNEL_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/push', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '<MESSAGE_ID>', + 'title': '<TITLE>', + 'body': '<BODY>', + 'topics': [], + 'users': [], + 'targets': [], + 'data': {}, + 'action': '<ACTION>', + 'image': '<ID1:ID2>', + 'icon': '<ICON>', + 'sound': '<SOUND>', + 'color': '<COLOR>', + 'tag': '<TAG>', + 'badge': 1, + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'contentAvailable': False, + 'critical': False, + 'priority': 'normal', + 'channelId': '<CHANNEL_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -126,13 +276,63 @@ def test_update_push(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_push( '<MESSAGE_ID>', + topics=[], + users=[], + targets=[], + title='<TITLE>', + body='<BODY>', + data={}, + action='<ACTION>', + image='<ID1:ID2>', + icon='<ICON>', + sound='<SOUND>', + color='<COLOR>', + tag='<TAG>', + badge=1, + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + content_available=False, + critical=False, + priority='normal', + channel_id='<CHANNEL_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/push/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'title': '<TITLE>', + 'body': '<BODY>', + 'data': {}, + 'action': '<ACTION>', + 'image': '<ID1:ID2>', + 'icon': '<ICON>', + 'sound': '<SOUND>', + 'color': '<COLOR>', + 'tag': '<TAG>', + 'badge': 1, + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'contentAvailable': False, + 'critical': False, + 'priority': 'normal', + 'channelId': '<CHANNEL_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -152,7 +352,7 @@ def test_create_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -160,6 +360,30 @@ def test_create_sms(self, m): response = self.messaging.create_sms( '<MESSAGE_ID>', '<CONTENT>', + topics=[], + users=[], + targets=[], + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '<MESSAGE_ID>', + 'content': '<CONTENT>', + 'topics': [], + 'users': [], + 'targets': [], + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -179,7 +403,7 @@ def test_create_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -187,6 +411,30 @@ def test_create_sms(self, m): response = self.messaging.create_sms( '<MESSAGE_ID>', '<CONTENT>', + topics=[], + users=[], + targets=[], + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '<MESSAGE_ID>', + 'content': '<CONTENT>', + 'topics': [], + 'users': [], + 'targets': [], + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -206,13 +454,37 @@ def test_update_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_sms( '<MESSAGE_ID>', + topics=[], + users=[], + targets=[], + content='<CONTENT>', + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'content': '<CONTENT>', + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -232,13 +504,37 @@ def test_update_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_sms( '<MESSAGE_ID>', + topics=[], + users=[], + targets=[], + content='<CONTENT>', + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'content': '<CONTENT>', + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -258,7 +554,7 @@ def test_get_message(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -266,6 +562,14 @@ def test_get_message(self, m): response = self.messaging.get_message( '<MESSAGE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/<MESSAGE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -273,7 +577,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -281,6 +585,14 @@ def test_delete(self, m): response = self.messaging.delete( '<MESSAGE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -291,13 +603,28 @@ def test_list_targets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.list_targets( '<MESSAGE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/<MESSAGE_ID>/targets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -309,12 +636,30 @@ def test_list_providers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.messaging.list_providers() + response = self.messaging.list_providers( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -331,7 +676,7 @@ def test_create_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -339,6 +684,32 @@ def test_create_apns_provider(self, m): response = self.messaging.create_apns_provider( '<PROVIDER_ID>', '<NAME>', + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -356,7 +727,7 @@ def test_create_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +735,32 @@ def test_create_apns_provider(self, m): response = self.messaging.create_apns_provider( '<PROVIDER_ID>', '<NAME>', + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -381,13 +778,39 @@ def test_update_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_apns_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -405,13 +828,39 @@ def test_update_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_apns_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -429,7 +878,7 @@ def test_create_appwrite_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -437,6 +886,26 @@ def test_create_appwrite_provider(self, m): response = self.messaging.create_appwrite_provider( '<PROVIDER_ID>', '<NAME>', + enabled=False, + qos=1, + expiry=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/appwrite', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'enabled': False, + 'qos': 1, + 'expiry': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -454,13 +923,33 @@ def test_update_appwrite_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_appwrite_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + qos=1, + expiry=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/appwrite/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'qos': 1, + 'expiry': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -478,7 +967,7 @@ def test_create_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -486,6 +975,24 @@ def test_create_fcm_provider(self, m): response = self.messaging.create_fcm_provider( '<PROVIDER_ID>', '<NAME>', + service_account_json={}, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'serviceAccountJSON': {}, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -503,7 +1010,7 @@ def test_create_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -511,6 +1018,24 @@ def test_create_fcm_provider(self, m): response = self.messaging.create_fcm_provider( '<PROVIDER_ID>', '<NAME>', + service_account_json={}, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'serviceAccountJSON': {}, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -528,13 +1053,31 @@ def test_update_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_fcm_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + service_account_json={}, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'serviceAccountJSON': {}, + }, ) self.assertEqual(response.to_dict(), data) @@ -552,13 +1095,31 @@ def test_update_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_fcm_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + service_account_json={}, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'serviceAccountJSON': {}, + }, ) self.assertEqual(response.to_dict(), data) @@ -576,7 +1137,7 @@ def test_create_mailgun_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -584,6 +1145,36 @@ def test_create_mailgun_provider(self, m): response = self.messaging.create_mailgun_provider( '<PROVIDER_ID>', '<NAME>', + api_key='<API_KEY>', + domain='example.com', + is_eu_region=False, + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/mailgun', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'domain': 'example.com', + 'isEuRegion': False, + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -601,13 +1192,43 @@ def test_update_mailgun_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_mailgun_provider( '<PROVIDER_ID>', + name='<NAME>', + api_key='<API_KEY>', + domain='example.com', + is_eu_region=False, + enabled=False, + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/mailgun/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'domain': 'example.com', + 'isEuRegion': False, + 'enabled': False, + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -625,7 +1246,7 @@ def test_create_msg91_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -633,6 +1254,28 @@ def test_create_msg91_provider(self, m): response = self.messaging.create_msg91_provider( '<PROVIDER_ID>', '<NAME>', + template_id='<TEMPLATE_ID>', + sender_id='<SENDER_ID>', + auth_key='<AUTH_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/msg91', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'templateId': '<TEMPLATE_ID>', + 'senderId': '<SENDER_ID>', + 'authKey': '<AUTH_KEY>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -650,13 +1293,35 @@ def test_update_msg91_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_msg91_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + template_id='<TEMPLATE_ID>', + sender_id='<SENDER_ID>', + auth_key='<AUTH_KEY>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/msg91/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'templateId': '<TEMPLATE_ID>', + 'senderId': '<SENDER_ID>', + 'authKey': '<AUTH_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -674,7 +1339,7 @@ def test_create_resend_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -682,6 +1347,32 @@ def test_create_resend_provider(self, m): response = self.messaging.create_resend_provider( '<PROVIDER_ID>', '<NAME>', + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/resend', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -699,13 +1390,39 @@ def test_update_resend_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_resend_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/resend/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -723,7 +1440,7 @@ def test_create_sendgrid_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -731,6 +1448,32 @@ def test_create_sendgrid_provider(self, m): response = self.messaging.create_sendgrid_provider( '<PROVIDER_ID>', '<NAME>', + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/sendgrid', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -748,13 +1491,39 @@ def test_update_sendgrid_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_sendgrid_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/sendgrid/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -772,7 +1541,7 @@ def test_create_ses_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -780,6 +1549,36 @@ def test_create_ses_provider(self, m): response = self.messaging.create_ses_provider( '<PROVIDER_ID>', '<NAME>', + access_key='<ACCESS_KEY>', + secret_key='<SECRET_KEY>', + region='<REGION>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/ses', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + 'region': '<REGION>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -797,13 +1596,43 @@ def test_update_ses_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_ses_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + access_key='<ACCESS_KEY>', + secret_key='<SECRET_KEY>', + region='<REGION>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/ses/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + 'region': '<REGION>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -821,7 +1650,7 @@ def test_create_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -830,6 +1659,43 @@ def test_create_smtp_provider(self, m): '<PROVIDER_ID>', '<NAME>', '<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -847,7 +1713,7 @@ def test_create_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -856,6 +1722,43 @@ def test_create_smtp_provider(self, m): '<PROVIDER_ID>', '<NAME>', '<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -873,13 +1776,51 @@ def test_update_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_smtp_provider( '<PROVIDER_ID>', + name='<NAME>', + host='<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -897,13 +1838,51 @@ def test_update_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_smtp_provider( '<PROVIDER_ID>', + name='<NAME>', + host='<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -921,7 +1900,7 @@ def test_create_telesign_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -929,6 +1908,28 @@ def test_create_telesign_provider(self, m): response = self.messaging.create_telesign_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='+12065550100', + customer_id='<CUSTOMER_ID>', + api_key='<API_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/telesign', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '+12065550100', + 'customerId': '<CUSTOMER_ID>', + 'apiKey': '<API_KEY>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -946,13 +1947,35 @@ def test_update_telesign_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_telesign_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + customer_id='<CUSTOMER_ID>', + api_key='<API_KEY>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/telesign/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'customerId': '<CUSTOMER_ID>', + 'apiKey': '<API_KEY>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -970,7 +1993,7 @@ def test_create_textmagic_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -978,6 +2001,28 @@ def test_create_textmagic_provider(self, m): response = self.messaging.create_textmagic_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='+12065550100', + username='<USERNAME>', + api_key='<API_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/textmagic', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '+12065550100', + 'username': '<USERNAME>', + 'apiKey': '<API_KEY>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -995,13 +2040,35 @@ def test_update_textmagic_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_textmagic_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + username='<USERNAME>', + api_key='<API_KEY>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/textmagic/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'username': '<USERNAME>', + 'apiKey': '<API_KEY>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1019,7 +2086,7 @@ def test_create_twilio_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1027,6 +2094,28 @@ def test_create_twilio_provider(self, m): response = self.messaging.create_twilio_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='<FROM>', + account_sid='<ACCOUNT_SID>', + auth_token='<AUTH_TOKEN>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/twilio', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '<FROM>', + 'accountSid': '<ACCOUNT_SID>', + 'authToken': '<AUTH_TOKEN>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1044,13 +2133,35 @@ def test_update_twilio_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_twilio_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + account_sid='<ACCOUNT_SID>', + auth_token='<AUTH_TOKEN>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/twilio/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'accountSid': '<ACCOUNT_SID>', + 'authToken': '<AUTH_TOKEN>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1068,7 +2179,7 @@ def test_create_vonage_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1076,6 +2187,28 @@ def test_create_vonage_provider(self, m): response = self.messaging.create_vonage_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='+12065550100', + api_key='<API_KEY>', + api_secret='<API_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/vonage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '+12065550100', + 'apiKey': '<API_KEY>', + 'apiSecret': '<API_SECRET>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1093,13 +2226,35 @@ def test_update_vonage_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_vonage_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + api_key='<API_KEY>', + api_secret='<API_SECRET>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/vonage/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'apiKey': '<API_KEY>', + 'apiSecret': '<API_SECRET>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1117,7 +2272,7 @@ def test_get_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1125,6 +2280,14 @@ def test_get_provider(self, m): response = self.messaging.get_provider( '<PROVIDER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/<PROVIDER_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1132,7 +2295,7 @@ def test_delete_provider(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1140,6 +2303,14 @@ def test_delete_provider(self, m): response = self.messaging.delete_provider( '<PROVIDER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1150,12 +2321,30 @@ def test_list_topics(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.messaging.list_topics() + response = self.messaging.list_topics( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1173,7 +2362,7 @@ def test_create_topic(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1181,6 +2370,28 @@ def test_create_topic(self, m): response = self.messaging.create_topic( '<TOPIC_ID>', '<NAME>', + subscribe=[], + qos=1, + expiry=1, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topicId': '<TOPIC_ID>', + 'name': '<NAME>', + 'subscribe': [], + 'qos': 1, + 'expiry': 1, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1199,7 +2410,7 @@ def test_get_topic(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1207,6 +2418,14 @@ def test_get_topic(self, m): response = self.messaging.get_topic( '<TOPIC_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1224,13 +2443,35 @@ def test_update_topic(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_topic( '<TOPIC_ID>', + name='<NAME>', + subscribe=[], + qos=1, + expiry=1, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'subscribe': [], + 'qos': 1, + 'expiry': 1, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1239,7 +2480,7 @@ def test_delete_topic(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1247,6 +2488,14 @@ def test_delete_topic(self, m): response = self.messaging.delete_topic( '<TOPIC_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1257,13 +2506,30 @@ def test_list_subscribers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.list_subscribers( '<TOPIC_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1291,7 +2557,7 @@ def test_create_subscriber(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1301,6 +2567,20 @@ def test_create_subscriber(self, m): '<SUBSCRIBER_ID>', '<TARGET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'subscriberId': '<SUBSCRIBER_ID>', + 'targetId': '<TARGET_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1327,7 +2607,7 @@ def test_get_subscriber(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1336,6 +2616,14 @@ def test_get_subscriber(self, m): '<TOPIC_ID>', '<SUBSCRIBER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers/<SUBSCRIBER_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1343,7 +2631,7 @@ def test_delete_subscriber(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1352,4 +2640,12 @@ def test_delete_subscriber(self, m): '<TOPIC_ID>', '<SUBSCRIBER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers/<SUBSCRIBER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_migrations.py b/test/services/test_migrations.py new file mode 100644 index 00000000..07ff4adc --- /dev/null +++ b/test/services/test_migrations.py @@ -0,0 +1,847 @@ +import json +import re +import requests_mock +import unittest +from urllib.parse import parse_qs, unquote, urlsplit + +from appwrite.client import Client +from appwrite.input_file import InputFile +from appwrite.models import * +from appwrite.services.migrations import Migrations + + +class MigrationsServiceTest(unittest.TestCase): + + def setUp(self): + self.client = Client() + self.migrations = Migrations(self.client) + + @requests_mock.Mocker() + def test_list(self, m): + data = { + "total": 5.0, + "migrations": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + 'GET', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_appwrite_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_appwrite_migration( + [], + 'https://example.com', + '<PROJECT_ID>', + '<API_KEY>', + on_duplicate='fail', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/appwrite', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'endpoint': 'https://example.com', + 'projectId': '<PROJECT_ID>', + 'apiKey': '<API_KEY>', + 'onDuplicate': 'fail', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_csv_export(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_csv_export( + '<DATABASE_ID>', + '<COLLECTION_ID>', + '<FILENAME>', + columns=[], + queries=[], + delimiter='<DELIMITER>', + enclosure='<ENCLOSURE>', + escape='<ESCAPE>', + header=False, + notify=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/csv/exports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'filename': '<FILENAME>', + 'columns': [], + 'queries': [], + 'delimiter': '<DELIMITER>', + 'enclosure': '<ENCLOSURE>', + 'escape': '<ESCAPE>', + 'header': False, + 'notify': False, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_csv_import(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_csv_import( + '<BUCKET_ID>', + '<FILE_ID>', + '<DATABASE_ID>', + '<COLLECTION_ID>', + internal_file=False, + on_duplicate='fail', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/csv/imports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'bucketId': '<BUCKET_ID>', + 'fileId': '<FILE_ID>', + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'internalFile': False, + 'onDuplicate': 'fail', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_firebase_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_firebase_migration( + [], + '<SERVICE_ACCOUNT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/firebase', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'serviceAccount': '<SERVICE_ACCOUNT>', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_firebase_report(self, m): + data = { + "user": 20.0, + "team": 20.0, + "database": 20.0, + "row": 20.0, + "file": 20.0, + "bucket": 20.0, + "function": 20.0, + "platform": 5.0, + "api-key": 5.0, + "project-variable": 5.0, + "webhook": 5.0, + "auth-methods": 1.0, + "project-protocols": 1.0, + "project-labels": 1.0, + "project-services": 1.0, + "policies": 1.0, + "smtp": 1.0, + "rule": 5.0, + "project-email-template": 7.0, + "project-environment": 2.0, + "site": 5.0, + "provider": 5.0, + "topic": 10.0, + "subscriber": 100.0, + "message": 50.0, + "size": 30000.0, + "version": "1.4.0", + "oauth2-provider": 5.0, + "backup-policy": 5.0, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'GET', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.get_firebase_report( + [], + '<SERVICE_ACCOUNT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/firebase/report', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'serviceAccount': ['<SERVICE_ACCOUNT>'], + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_json_export(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_json_export( + '<DATABASE_ID>', + '<COLLECTION_ID>', + '<FILENAME>', + columns=[], + queries=[], + notify=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/json/exports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'filename': '<FILENAME>', + 'columns': [], + 'queries': [], + 'notify': False, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_json_import(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_json_import( + '<BUCKET_ID>', + '<FILE_ID>', + '<DATABASE_ID>', + '<COLLECTION_ID>', + internal_file=False, + on_duplicate='fail', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/json/imports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'bucketId': '<BUCKET_ID>', + 'fileId': '<FILE_ID>', + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'internalFile': False, + 'onDuplicate': 'fail', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_n_host_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_n_host_migration( + [], + '<SUBDOMAIN>', + '<REGION>', + '<ADMIN_SECRET>', + '<DATABASE>', + '<USERNAME>', + 'password', + port=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/nhost', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'subdomain': '<SUBDOMAIN>', + 'region': '<REGION>', + 'adminSecret': '<ADMIN_SECRET>', + 'database': '<DATABASE>', + 'username': '<USERNAME>', + 'password': 'password', + 'port': 1, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_n_host_report(self, m): + data = { + "user": 20.0, + "team": 20.0, + "database": 20.0, + "row": 20.0, + "file": 20.0, + "bucket": 20.0, + "function": 20.0, + "platform": 5.0, + "api-key": 5.0, + "project-variable": 5.0, + "webhook": 5.0, + "auth-methods": 1.0, + "project-protocols": 1.0, + "project-labels": 1.0, + "project-services": 1.0, + "policies": 1.0, + "smtp": 1.0, + "rule": 5.0, + "project-email-template": 7.0, + "project-environment": 2.0, + "site": 5.0, + "provider": 5.0, + "topic": 10.0, + "subscriber": 100.0, + "message": 50.0, + "size": 30000.0, + "version": "1.4.0", + "oauth2-provider": 5.0, + "backup-policy": 5.0, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'GET', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.get_n_host_report( + [], + '<SUBDOMAIN>', + '<REGION>', + '<ADMIN_SECRET>', + '<DATABASE>', + '<USERNAME>', + 'password', + port=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/nhost/report', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'subdomain': ['<SUBDOMAIN>'], + 'region': ['<REGION>'], + 'adminSecret': ['<ADMIN_SECRET>'], + 'database': ['<DATABASE>'], + 'username': ['<USERNAME>'], + 'password': ['password'], + 'port': ['1'], + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_supabase_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_supabase_migration( + [], + 'https://example.com', + '<API_KEY>', + '<DATABASE_HOST>', + '<USERNAME>', + 'password', + port=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/supabase', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'endpoint': 'https://example.com', + 'apiKey': '<API_KEY>', + 'databaseHost': '<DATABASE_HOST>', + 'username': '<USERNAME>', + 'password': 'password', + 'port': 1, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_supabase_report(self, m): + data = { + "user": 20.0, + "team": 20.0, + "database": 20.0, + "row": 20.0, + "file": 20.0, + "bucket": 20.0, + "function": 20.0, + "platform": 5.0, + "api-key": 5.0, + "project-variable": 5.0, + "webhook": 5.0, + "auth-methods": 1.0, + "project-protocols": 1.0, + "project-labels": 1.0, + "project-services": 1.0, + "policies": 1.0, + "smtp": 1.0, + "rule": 5.0, + "project-email-template": 7.0, + "project-environment": 2.0, + "site": 5.0, + "provider": 5.0, + "topic": 10.0, + "subscriber": 100.0, + "message": 50.0, + "size": 30000.0, + "version": "1.4.0", + "oauth2-provider": 5.0, + "backup-policy": 5.0, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'GET', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.get_supabase_report( + [], + 'https://example.com', + '<API_KEY>', + '<DATABASE_HOST>', + '<USERNAME>', + 'password', + port=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/supabase/report', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'endpoint': ['https://example.com'], + 'apiKey': ['<API_KEY>'], + 'databaseHost': ['<DATABASE_HOST>'], + 'username': ['<USERNAME>'], + 'password': ['password'], + 'port': ['1'], + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'GET', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.get( + '<MIGRATION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/<MIGRATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_retry(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'PATCH', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.retry( + '<MIGRATION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/<MIGRATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request( + 'DELETE', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.delete( + '<MIGRATION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/<MIGRATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) + self.assertEqual(response, data) diff --git a/test/services/test_mongo.py b/test/services/test_mongo.py index 75b37bfa..5b9f8696 100644 --- a/test/services/test_mongo.py +++ b/test/services/test_mongo.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.mongo.list() + response = self.mongo.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -81,7 +99,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -89,6 +107,44 @@ def test_create(self, m): response = self.mongo.create( '<DATABASE_ID>', '<NAME>', + version='17', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'version': '17', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -100,12 +156,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +223,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +231,14 @@ def test_get(self, m): response = self.mongo.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -220,13 +292,65 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.update( '<DATABASE_ID>', + name='<NAME>', + status='ready', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + metrics_trace_sample_rate=1.0, + metrics_slow_query_log_threshold_ms=1, + sql_api_enabled=False, + sql_api_allowed_statements=[], + sql_api_max_rows=1, + sql_api_max_bytes=1, + sql_api_timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'status': 'ready', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + 'metricsTraceSampleRate': 1.0, + 'metricsSlowQueryLogThresholdMs': 1, + 'sqlApiEnabled': False, + 'sqlApiAllowedStatements': [], + 'sqlApiMaxRows': 1, + 'sqlApiMaxBytes': 1, + 'sqlApiTimeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -235,7 +359,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -243,6 +367,14 @@ def test_delete(self, m): response = self.mongo.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -253,13 +385,28 @@ def test_list_backups(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_backups( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -281,13 +428,27 @@ def test_create_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_backup( '<DATABASE_ID>', + type='full', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'full', + }, ) self.assertEqual(response.to_dict(), data) @@ -299,13 +460,28 @@ def test_list_backup_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_backup_policies( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -325,7 +501,7 @@ def test_create_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +512,26 @@ def test_create_backup_policy(self, m): '<NAME>', 'schedule', 1, + type='full', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '<POLICY_ID>', + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'type': 'full', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -355,7 +551,7 @@ def test_get_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +560,14 @@ def test_get_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -382,7 +586,7 @@ def test_update_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -390,6 +594,26 @@ def test_update_backup_policy(self, m): response = self.mongo.update_backup_policy( '<DATABASE_ID>', '<POLICY_ID>', + name='<NAME>', + schedule='schedule', + retention=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -398,7 +622,7 @@ def test_delete_backup_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,6 +631,14 @@ def test_delete_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -420,7 +652,7 @@ def test_update_backup_storage(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -431,6 +663,28 @@ def test_update_backup_storage(self, m): '<BUCKET>', '<ACCESS_KEY>', '<SECRET_KEY>', + region='<REGION>', + prefix='<PREFIX>', + endpoint='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/storage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 's3', + 'bucket': '<BUCKET>', + 'region': '<REGION>', + 'prefix': '<PREFIX>', + 'endpoint': 'https://example.com', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -452,7 +706,7 @@ def test_get_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -461,6 +715,14 @@ def test_get_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -468,7 +730,7 @@ def test_delete_backup(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +739,14 @@ def test_delete_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -487,7 +757,7 @@ def test_list_branches(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +765,14 @@ def test_list_branches(self, m): response = self.mongo.list_branches( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/branches', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -548,13 +826,29 @@ def test_create_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_branch( '<DATABASE_ID>', + branch_id='<BRANCH_ID>', + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/branches', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'branchId': '<BRANCH_ID>', + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -609,7 +903,7 @@ def test_delete_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +912,14 @@ def test_delete_branch(self, m): '<DATABASE_ID>', '<BRANCH_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/branches/<BRANCH_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -634,7 +936,7 @@ def test_update_credentials(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -642,6 +944,14 @@ def test_update_credentials(self, m): response = self.mongo.update_credentials( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/credentials', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -695,13 +1005,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -756,7 +1080,7 @@ def test_update_maintenance(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -766,6 +1090,20 @@ def test_update_maintenance(self, m): 'sun', 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/maintenance', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'day': 'sun', + 'hourUtc': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -819,7 +1157,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -827,6 +1165,21 @@ def test_create_migration(self, m): response = self.mongo.create_migration( '<DATABASE_ID>', 'shared', + specification='<SPECIFICATION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetType': 'shared', + 'specification': '<SPECIFICATION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -838,13 +1191,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -856,7 +1227,7 @@ def test_get_pitr(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -864,6 +1235,14 @@ def test_get_pitr(self, m): response = self.mongo.get_pitr( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/pitr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -878,7 +1257,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -886,6 +1265,14 @@ def test_get_replicas(self, m): response = self.mongo.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -896,13 +1283,33 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_restorations( '<DATABASE_ID>', + status='pending', + type='backup', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['pending'], + 'type': ['backup'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -920,13 +1327,39 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_restoration( '<DATABASE_ID>', + type='backup', + backup_id='<BACKUP_ID>', + target_database_id='<TARGET_DATABASE_ID>', + target_time='2020-10-15T06:38:00.000+00:00', + restore_mode='in_place', + target_name='<TARGET_NAME>', + auto_promote=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'backup', + 'backupId': '<BACKUP_ID>', + 'targetDatabaseId': '<TARGET_DATABASE_ID>', + 'targetTime': '2020-10-15T06:38:00.000+00:00', + 'restoreMode': 'in_place', + 'targetName': '<TARGET_NAME>', + 'autoPromote': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -944,7 +1377,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -953,6 +1386,14 @@ def test_get_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations/<RESTORATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -969,7 +1410,7 @@ def test_promote_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -978,6 +1419,14 @@ def test_promote_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations/<RESTORATION_ID>/promotions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -997,7 +1446,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1005,6 +1454,14 @@ def test_get_status(self, m): response = self.mongo.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1058,7 +1515,7 @@ def test_create_upgrade(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1067,4 +1524,17 @@ def test_create_upgrade(self, m): '<DATABASE_ID>', '<TARGET_VERSION>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/upgrades', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetVersion': '<TARGET_VERSION>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_mysql.py b/test/services/test_mysql.py index 3ea4f7d3..9bed7ea8 100644 --- a/test/services/test_mysql.py +++ b/test/services/test_mysql.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.mysql.list() + response = self.mysql.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -81,7 +99,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -89,6 +107,44 @@ def test_create(self, m): response = self.mysql.create( '<DATABASE_ID>', '<NAME>', + version='17', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'version': '17', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -100,12 +156,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +223,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +231,14 @@ def test_get(self, m): response = self.mysql.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -220,13 +292,65 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.update( '<DATABASE_ID>', + name='<NAME>', + status='ready', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + metrics_trace_sample_rate=1.0, + metrics_slow_query_log_threshold_ms=1, + sql_api_enabled=False, + sql_api_allowed_statements=[], + sql_api_max_rows=1, + sql_api_max_bytes=1, + sql_api_timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'status': 'ready', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + 'metricsTraceSampleRate': 1.0, + 'metricsSlowQueryLogThresholdMs': 1, + 'sqlApiEnabled': False, + 'sqlApiAllowedStatements': [], + 'sqlApiMaxRows': 1, + 'sqlApiMaxBytes': 1, + 'sqlApiTimeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -235,7 +359,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -243,6 +367,14 @@ def test_delete(self, m): response = self.mysql.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -253,13 +385,28 @@ def test_list_backups(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_backups( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -281,13 +428,27 @@ def test_create_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_backup( '<DATABASE_ID>', + type='full', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'full', + }, ) self.assertEqual(response.to_dict(), data) @@ -299,13 +460,28 @@ def test_list_backup_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_backup_policies( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -325,7 +501,7 @@ def test_create_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +512,26 @@ def test_create_backup_policy(self, m): '<NAME>', 'schedule', 1, + type='full', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '<POLICY_ID>', + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'type': 'full', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -355,7 +551,7 @@ def test_get_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +560,14 @@ def test_get_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -382,7 +586,7 @@ def test_update_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -390,6 +594,26 @@ def test_update_backup_policy(self, m): response = self.mysql.update_backup_policy( '<DATABASE_ID>', '<POLICY_ID>', + name='<NAME>', + schedule='schedule', + retention=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -398,7 +622,7 @@ def test_delete_backup_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,6 +631,14 @@ def test_delete_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -420,7 +652,7 @@ def test_update_backup_storage(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -431,6 +663,28 @@ def test_update_backup_storage(self, m): '<BUCKET>', '<ACCESS_KEY>', '<SECRET_KEY>', + region='<REGION>', + prefix='<PREFIX>', + endpoint='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/storage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 's3', + 'bucket': '<BUCKET>', + 'region': '<REGION>', + 'prefix': '<PREFIX>', + 'endpoint': 'https://example.com', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -452,7 +706,7 @@ def test_get_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -461,6 +715,14 @@ def test_get_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -468,7 +730,7 @@ def test_delete_backup(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +739,14 @@ def test_delete_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -487,7 +757,7 @@ def test_list_branches(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +765,14 @@ def test_list_branches(self, m): response = self.mysql.list_branches( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/branches', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -548,13 +826,29 @@ def test_create_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_branch( '<DATABASE_ID>', + branch_id='<BRANCH_ID>', + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/branches', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'branchId': '<BRANCH_ID>', + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -609,7 +903,7 @@ def test_delete_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +912,14 @@ def test_delete_branch(self, m): '<DATABASE_ID>', '<BRANCH_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/branches/<BRANCH_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -634,7 +936,7 @@ def test_update_credentials(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -642,6 +944,14 @@ def test_update_credentials(self, m): response = self.mysql.update_credentials( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/credentials', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -656,7 +966,7 @@ def test_create_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -664,6 +974,23 @@ def test_create_execution(self, m): response = self.mysql.create_execution( '<DATABASE_ID>', '<SQL>', + bindings={}, + timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/executions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'sql': '<SQL>', + 'bindings': {}, + 'timeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -718,13 +1045,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -779,7 +1120,7 @@ def test_update_maintenance(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -789,6 +1130,20 @@ def test_update_maintenance(self, m): 'sun', 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/maintenance', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'day': 'sun', + 'hourUtc': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -842,7 +1197,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -850,6 +1205,21 @@ def test_create_migration(self, m): response = self.mysql.create_migration( '<DATABASE_ID>', 'shared', + specification='<SPECIFICATION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetType': 'shared', + 'specification': '<SPECIFICATION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -861,13 +1231,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -879,7 +1267,7 @@ def test_get_pitr(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -887,6 +1275,14 @@ def test_get_pitr(self, m): response = self.mysql.get_pitr( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/pitr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -905,7 +1301,7 @@ def test_get_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -913,6 +1309,14 @@ def test_get_pooler(self, m): response = self.mysql.get_pooler( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/pooler', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -931,13 +1335,41 @@ def test_update_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.update_pooler( '<DATABASE_ID>', + mode='transaction', + max_connections=1, + default_pool_size=1, + read_write_splitting=False, + pooler_cpu_request='<POOLER_CPU_REQUEST>', + pooler_cpu_limit='<POOLER_CPU_LIMIT>', + pooler_memory_request='<POOLER_MEMORY_REQUEST>', + pooler_memory_limit='<POOLER_MEMORY_LIMIT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/pooler', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mode': 'transaction', + 'maxConnections': 1, + 'defaultPoolSize': 1, + 'readWriteSplitting': False, + 'poolerCpuRequest': '<POOLER_CPU_REQUEST>', + 'poolerCpuLimit': '<POOLER_CPU_LIMIT>', + 'poolerMemoryRequest': '<POOLER_MEMORY_REQUEST>', + 'poolerMemoryLimit': '<POOLER_MEMORY_LIMIT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -953,7 +1385,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -961,6 +1393,14 @@ def test_get_replicas(self, m): response = self.mysql.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -971,13 +1411,33 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_restorations( '<DATABASE_ID>', + status='pending', + type='backup', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['pending'], + 'type': ['backup'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -995,13 +1455,39 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_restoration( '<DATABASE_ID>', + type='backup', + backup_id='<BACKUP_ID>', + target_database_id='<TARGET_DATABASE_ID>', + target_time='2020-10-15T06:38:00.000+00:00', + restore_mode='in_place', + target_name='<TARGET_NAME>', + auto_promote=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'backup', + 'backupId': '<BACKUP_ID>', + 'targetDatabaseId': '<TARGET_DATABASE_ID>', + 'targetTime': '2020-10-15T06:38:00.000+00:00', + 'restoreMode': 'in_place', + 'targetName': '<TARGET_NAME>', + 'autoPromote': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1019,7 +1505,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1028,6 +1514,14 @@ def test_get_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations/<RESTORATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1044,7 +1538,7 @@ def test_promote_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1053,6 +1547,14 @@ def test_promote_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations/<RESTORATION_ID>/promotions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1072,7 +1574,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1080,6 +1582,14 @@ def test_get_status(self, m): response = self.mysql.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1133,7 +1643,7 @@ def test_create_upgrade(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1142,4 +1652,17 @@ def test_create_upgrade(self, m): '<DATABASE_ID>', '<TARGET_VERSION>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/upgrades', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetVersion': '<TARGET_VERSION>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_oauth2.py b/test/services/test_oauth2.py index da2520ee..fa7cf188 100644 --- a/test/services/test_oauth2.py +++ b/test/services/test_oauth2.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -21,13 +23,30 @@ def test_approve(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.approve( '<GRANT_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + scope='<SCOPE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//approve', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'grant_id': '<GRANT_ID>', + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'scope': '<SCOPE>', + }, ) self.assertEqual(response.to_dict(), data) @@ -39,12 +58,52 @@ def test_authorize(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.authorize() + response = self.oauth2.authorize( + client_id='<CLIENT_ID>', + redirect_uri='https://example.com', + response_type='response_type', + scope='<SCOPE>', + state='<STATE>', + nonce='<NONCE>', + code_challenge='<CODE_CHALLENGE>', + code_challenge_method='s256', + prompt='<PROMPT>', + max_age=1, + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + request_uri='<REQUEST_URI>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//authorize', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'client_id': ['<CLIENT_ID>'], + 'redirect_uri': ['https://example.com'], + 'response_type': ['response_type'], + 'scope': ['<SCOPE>'], + 'state': ['<STATE>'], + 'nonce': ['<NONCE>'], + 'code_challenge': ['<CODE_CHALLENGE>'], + 'code_challenge_method': ['s256'], + 'prompt': ['<PROMPT>'], + 'max_age': ['1'], + 'authorization_details': ['<AUTHORIZATION_DETAILS>'], + 'audience': ['<AUDIENCE>'], + 'request_uri': ['<REQUEST_URI>'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -55,12 +114,53 @@ def test_authorize_post(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.authorize_post() + response = self.oauth2.authorize_post( + client_id='<CLIENT_ID>', + redirect_uri='https://example.com', + response_type='response_type', + scope='<SCOPE>', + state='<STATE>', + nonce='<NONCE>', + code_challenge='<CODE_CHALLENGE>', + code_challenge_method='s256', + prompt='<PROMPT>', + max_age=1, + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + request_uri='<REQUEST_URI>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//authorize', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'client_id': '<CLIENT_ID>', + 'redirect_uri': 'https://example.com', + 'response_type': 'response_type', + 'scope': '<SCOPE>', + 'state': '<STATE>', + 'nonce': '<NONCE>', + 'code_challenge': '<CODE_CHALLENGE>', + 'code_challenge_method': 's256', + 'prompt': '<PROMPT>', + 'max_age': 1, + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'resource': [], + 'audience': '<AUDIENCE>', + 'request_uri': '<REQUEST_URI>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -75,12 +175,35 @@ def test_create_device_authorization(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.create_device_authorization() + response = self.oauth2.create_device_authorization( + client_id='<CLIENT_ID>', + scope='<SCOPE>', + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//device_authorization', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'client_id': '<CLIENT_ID>', + 'scope': '<SCOPE>', + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'resource': [], + 'audience': '<AUDIENCE>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -101,7 +224,7 @@ def test_create_grant(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -109,6 +232,19 @@ def test_create_grant(self, m): response = self.oauth2.create_grant( '<USER_CODE>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//grants', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'user_code': '<USER_CODE>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -129,7 +265,7 @@ def test_get_grant(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -137,6 +273,14 @@ def test_get_grant(self, m): response = self.oauth2.get_grant( '<GRANT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//grants/<GRANT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -146,13 +290,32 @@ def test_introspect(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.introspect( '<TOKEN>', + token_type_hint='access_token', + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//introspect', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'token': '<TOKEN>', + 'token_type_hint': 'access_token', + 'client_id': '<CLIENT_ID>', + 'client_secret': '<CLIENT_SECRET>', + }, ) self.assertEqual(response.to_dict(), data) @@ -164,12 +327,31 @@ def test_list_organizations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.list_organizations() + response = self.oauth2.list_organizations( + limit=1, + offset=1, + search='<SEARCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//organizations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'limit': ['1'], + 'offset': ['1'], + 'search': ['<SEARCH>'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -180,7 +362,7 @@ def test_create_par(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -189,6 +371,41 @@ def test_create_par(self, m): '<CLIENT_ID>', 'https://example.com', 'code', + scope='<SCOPE>', + state='<STATE>', + nonce='<NONCE>', + code_challenge='<CODE_CHALLENGE>', + code_challenge_method='s256', + prompt='<PROMPT>', + max_age=1, + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//par', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'client_id': '<CLIENT_ID>', + 'redirect_uri': 'https://example.com', + 'response_type': 'code', + 'scope': '<SCOPE>', + 'state': '<STATE>', + 'nonce': '<NONCE>', + 'code_challenge': '<CODE_CHALLENGE>', + 'code_challenge_method': 's256', + 'prompt': '<PROMPT>', + 'max_age': 1, + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'resource': [], + 'audience': '<AUDIENCE>', + }, ) self.assertEqual(response.to_dict(), data) @@ -200,12 +417,31 @@ def test_list_projects(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.list_projects() + response = self.oauth2.list_projects( + limit=1, + offset=1, + search='<SEARCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//projects', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'limit': ['1'], + 'offset': ['1'], + 'search': ['<SEARCH>'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -215,7 +451,7 @@ def test_reject(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -223,6 +459,19 @@ def test_reject(self, m): response = self.oauth2.reject( '<GRANT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//reject', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'grant_id': '<GRANT_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -230,13 +479,32 @@ def test_revoke(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.revoke( '<TOKEN>', + token_type_hint='access_token', + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//revoke', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'token': '<TOKEN>', + 'token_type_hint': 'access_token', + 'client_id': '<CLIENT_ID>', + 'client_secret': '<CLIENT_SECRET>', + }, ) self.assertEqual(response, data) @@ -251,12 +519,43 @@ def test_create_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.create_token( '<GRANT_TYPE>', + code='<CODE>', + refresh_token='<REFRESH_TOKEN>', + device_code='<DEVICE_CODE>', + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + code_verifier='<CODE_VERIFIER>', + redirect_uri='https://example.com', + resource=[], + audience='<AUDIENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//token', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'grant_type': '<GRANT_TYPE>', + 'code': '<CODE>', + 'refresh_token': '<REFRESH_TOKEN>', + 'device_code': '<DEVICE_CODE>', + 'client_id': '<CLIENT_ID>', + 'client_secret': '<CLIENT_SECRET>', + 'code_verifier': '<CODE_VERIFIER>', + 'redirect_uri': 'https://example.com', + 'resource': [], + 'audience': '<AUDIENCE>', + }, ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_organization.py b/test/services/test_organization.py index 41206a71..6f7eee50 100644 --- a/test/services/test_organization.py +++ b/test/services/test_organization.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -105,12 +107,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -204,7 +214,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -212,6 +222,19 @@ def test_update(self, m): response = self.organization.update( '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -219,12 +242,20 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.delete() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -235,12 +266,28 @@ def test_list_installations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.organization.list_installations() + response = self.organization.list_installations( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -258,13 +305,28 @@ def test_create_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.create_installation( '<APP_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '<APP_ID>', + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -283,7 +345,7 @@ def test_get_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -291,6 +353,14 @@ def test_get_installation(self, m): response = self.organization.get_installation( '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations/<INSTALLATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -308,13 +378,27 @@ def test_update_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.update_installation( '<INSTALLATION_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -323,7 +407,7 @@ def test_delete_installation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -331,6 +415,14 @@ def test_delete_installation(self, m): response = self.organization.delete_installation( '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -341,12 +433,30 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.organization.list_memberships() + response = self.organization.list_memberships( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -370,13 +480,36 @@ def test_create_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.create_membership( [], + email='email@example.com', + user_id='<USER_ID>', + phone='+12065550100', + url='https://example.com', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'userId': '<USER_ID>', + 'phone': '+12065550100', + 'roles': [], + 'url': 'https://example.com', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -401,7 +534,7 @@ def test_get_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -409,6 +542,14 @@ def test_get_membership(self, m): response = self.organization.get_membership( '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships/<MEMBERSHIP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -432,7 +573,7 @@ def test_update_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -441,6 +582,19 @@ def test_update_membership(self, m): '<MEMBERSHIP_ID>', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'roles': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -448,7 +602,7 @@ def test_delete_membership(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -456,6 +610,14 @@ def test_delete_membership(self, m): response = self.organization.delete_membership( '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -466,12 +628,30 @@ def test_list_projects(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.organization.list_projects() + response = self.organization.list_projects( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -506,7 +686,7 @@ def test_create_project(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -514,6 +694,22 @@ def test_create_project(self, m): response = self.organization.create_project( '<PROJECT_ID>', '<NAME>', + region='fra', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'projectId': '<PROJECT_ID>', + 'name': '<NAME>', + 'region': 'fra', + }, ) self.assertEqual(response.to_dict(), data) @@ -549,7 +745,7 @@ def test_get_project(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -557,6 +753,14 @@ def test_get_project(self, m): response = self.organization.get_project( '<PROJECT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -591,7 +795,7 @@ def test_update_project(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -600,6 +804,19 @@ def test_update_project(self, m): '<PROJECT_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -607,7 +824,7 @@ def test_delete_project(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -615,6 +832,14 @@ def test_delete_project(self, m): response = self.organization.delete_project( '<PROJECT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -625,13 +850,28 @@ def test_list_project_keys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.list_project_keys( '<PROJECT_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -651,7 +891,7 @@ def test_create_ephemeral_project_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -661,6 +901,20 @@ def test_create_ephemeral_project_key(self, m): [], 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/ephemeral', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'scopes': [], + 'duration': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -679,7 +933,7 @@ def test_get_project_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -688,6 +942,14 @@ def test_get_project_key(self, m): '<PROJECT_ID>', '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/<KEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -706,7 +968,7 @@ def test_update_project_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -716,6 +978,24 @@ def test_update_project_key(self, m): '<KEY_ID>', '<NAME>', [], + expire='2020-10-15T06:38:00.000+00:00', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'scopes': [], + 'expire': '2020-10-15T06:38:00.000+00:00', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -724,7 +1004,7 @@ def test_delete_project_key(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -733,4 +1013,12 @@ def test_delete_project_key(self, m): '<PROJECT_ID>', '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_postgresql.py b/test/services/test_postgresql.py index 756a1305..62bb5214 100644 --- a/test/services/test_postgresql.py +++ b/test/services/test_postgresql.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.postgresql.list() + response = self.postgresql.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -81,7 +99,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -89,6 +107,44 @@ def test_create(self, m): response = self.postgresql.create( '<DATABASE_ID>', '<NAME>', + version='17', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'version': '17', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -100,12 +156,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +223,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +231,14 @@ def test_get(self, m): response = self.postgresql.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -220,13 +292,65 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.update( '<DATABASE_ID>', + name='<NAME>', + status='ready', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + metrics_trace_sample_rate=1.0, + metrics_slow_query_log_threshold_ms=1, + sql_api_enabled=False, + sql_api_allowed_statements=[], + sql_api_max_rows=1, + sql_api_max_bytes=1, + sql_api_timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'status': 'ready', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + 'metricsTraceSampleRate': 1.0, + 'metricsSlowQueryLogThresholdMs': 1, + 'sqlApiEnabled': False, + 'sqlApiAllowedStatements': [], + 'sqlApiMaxRows': 1, + 'sqlApiMaxBytes': 1, + 'sqlApiTimeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -235,7 +359,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -243,6 +367,14 @@ def test_delete(self, m): response = self.postgresql.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -253,13 +385,28 @@ def test_list_backups(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_backups( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -281,13 +428,27 @@ def test_create_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_backup( '<DATABASE_ID>', + type='full', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'full', + }, ) self.assertEqual(response.to_dict(), data) @@ -299,13 +460,28 @@ def test_list_backup_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_backup_policies( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -325,7 +501,7 @@ def test_create_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +512,26 @@ def test_create_backup_policy(self, m): '<NAME>', 'schedule', 1, + type='full', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '<POLICY_ID>', + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'type': 'full', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -355,7 +551,7 @@ def test_get_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +560,14 @@ def test_get_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -382,7 +586,7 @@ def test_update_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -390,6 +594,26 @@ def test_update_backup_policy(self, m): response = self.postgresql.update_backup_policy( '<DATABASE_ID>', '<POLICY_ID>', + name='<NAME>', + schedule='schedule', + retention=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -398,7 +622,7 @@ def test_delete_backup_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,6 +631,14 @@ def test_delete_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -420,7 +652,7 @@ def test_update_backup_storage(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -431,6 +663,28 @@ def test_update_backup_storage(self, m): '<BUCKET>', '<ACCESS_KEY>', '<SECRET_KEY>', + region='<REGION>', + prefix='<PREFIX>', + endpoint='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/storage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 's3', + 'bucket': '<BUCKET>', + 'region': '<REGION>', + 'prefix': '<PREFIX>', + 'endpoint': 'https://example.com', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -452,7 +706,7 @@ def test_get_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -461,6 +715,14 @@ def test_get_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -468,7 +730,7 @@ def test_delete_backup(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +739,14 @@ def test_delete_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -487,7 +757,7 @@ def test_list_branches(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +765,14 @@ def test_list_branches(self, m): response = self.postgresql.list_branches( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/branches', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -548,13 +826,29 @@ def test_create_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_branch( '<DATABASE_ID>', + branch_id='<BRANCH_ID>', + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/branches', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'branchId': '<BRANCH_ID>', + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -609,7 +903,7 @@ def test_delete_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +912,14 @@ def test_delete_branch(self, m): '<DATABASE_ID>', '<BRANCH_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/branches/<BRANCH_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -634,7 +936,7 @@ def test_update_credentials(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -642,6 +944,14 @@ def test_update_credentials(self, m): response = self.postgresql.update_credentials( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/credentials', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -656,7 +966,7 @@ def test_create_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -664,6 +974,23 @@ def test_create_execution(self, m): response = self.postgresql.create_execution( '<DATABASE_ID>', '<SQL>', + bindings={}, + timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/executions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'sql': '<SQL>', + 'bindings': {}, + 'timeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -676,7 +1003,7 @@ def test_list_extensions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -684,6 +1011,14 @@ def test_list_extensions(self, m): response = self.postgresql.list_extensions( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/extensions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -737,7 +1072,7 @@ def test_create_extension(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -746,6 +1081,19 @@ def test_create_extension(self, m): '<DATABASE_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/extensions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -799,7 +1147,7 @@ def test_delete_extension(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -808,6 +1156,14 @@ def test_delete_extension(self, m): '<DATABASE_ID>', '<EXTENSION_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/extensions/<EXTENSION_NAME>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -861,13 +1217,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -922,7 +1292,7 @@ def test_update_maintenance(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -932,6 +1302,20 @@ def test_update_maintenance(self, m): 'sun', 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/maintenance', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'day': 'sun', + 'hourUtc': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -985,7 +1369,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -993,6 +1377,21 @@ def test_create_migration(self, m): response = self.postgresql.create_migration( '<DATABASE_ID>', 'shared', + specification='<SPECIFICATION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetType': 'shared', + 'specification': '<SPECIFICATION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1004,13 +1403,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1022,7 +1439,7 @@ def test_get_pitr(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1030,6 +1447,14 @@ def test_get_pitr(self, m): response = self.postgresql.get_pitr( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/pitr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1048,7 +1473,7 @@ def test_get_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1056,6 +1481,14 @@ def test_get_pooler(self, m): response = self.postgresql.get_pooler( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/pooler', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1074,13 +1507,41 @@ def test_update_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.update_pooler( '<DATABASE_ID>', + mode='transaction', + max_connections=1, + default_pool_size=1, + read_write_splitting=False, + pooler_cpu_request='<POOLER_CPU_REQUEST>', + pooler_cpu_limit='<POOLER_CPU_LIMIT>', + pooler_memory_request='<POOLER_MEMORY_REQUEST>', + pooler_memory_limit='<POOLER_MEMORY_LIMIT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/pooler', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mode': 'transaction', + 'maxConnections': 1, + 'defaultPoolSize': 1, + 'readWriteSplitting': False, + 'poolerCpuRequest': '<POOLER_CPU_REQUEST>', + 'poolerCpuLimit': '<POOLER_CPU_LIMIT>', + 'poolerMemoryRequest': '<POOLER_MEMORY_REQUEST>', + 'poolerMemoryLimit': '<POOLER_MEMORY_LIMIT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1096,7 +1557,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1104,6 +1565,14 @@ def test_get_replicas(self, m): response = self.postgresql.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1114,13 +1583,33 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_restorations( '<DATABASE_ID>', + status='pending', + type='backup', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['pending'], + 'type': ['backup'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1138,13 +1627,39 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_restoration( '<DATABASE_ID>', + type='backup', + backup_id='<BACKUP_ID>', + target_database_id='<TARGET_DATABASE_ID>', + target_time='2020-10-15T06:38:00.000+00:00', + restore_mode='in_place', + target_name='<TARGET_NAME>', + auto_promote=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'backup', + 'backupId': '<BACKUP_ID>', + 'targetDatabaseId': '<TARGET_DATABASE_ID>', + 'targetTime': '2020-10-15T06:38:00.000+00:00', + 'restoreMode': 'in_place', + 'targetName': '<TARGET_NAME>', + 'autoPromote': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1162,7 +1677,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1171,6 +1686,14 @@ def test_get_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations/<RESTORATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1187,7 +1710,7 @@ def test_promote_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1196,6 +1719,14 @@ def test_promote_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations/<RESTORATION_ID>/promotions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1215,7 +1746,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1223,6 +1754,14 @@ def test_get_status(self, m): response = self.postgresql.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1276,7 +1815,7 @@ def test_create_upgrade(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1285,4 +1824,17 @@ def test_create_upgrade(self, m): '<DATABASE_ID>', '<TARGET_VERSION>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/upgrades', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetVersion': '<TARGET_VERSION>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_presences.py b/test/services/test_presences.py index 709ebb15..acd09a40 100644 --- a/test/services/test_presences.py +++ b/test/services/test_presences.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.presences.list() + response = self.presences.list( + queries=[], + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + 'ttl': ['1'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -42,7 +62,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -50,6 +70,14 @@ def test_get(self, m): response = self.presences.get( '<PRESENCE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -64,7 +92,7 @@ def test_upsert(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -73,6 +101,26 @@ def test_upsert(self, m): '<PRESENCE_ID>', '<USER_ID>', '<STATUS>', + permissions=[], + expires_at='2020-10-15T06:38:00.000+00:00', + metadata={}, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'status': '<STATUS>', + 'permissions': [], + 'expiresAt': '2020-10-15T06:38:00.000+00:00', + 'metadata': {}, + }, ) self.assertEqual(response.to_dict(), data) @@ -88,7 +136,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -96,6 +144,29 @@ def test_update(self, m): response = self.presences.update( '<PRESENCE_ID>', '<USER_ID>', + status='<STATUS>', + expires_at='2020-10-15T06:38:00.000+00:00', + metadata={}, + permissions=[], + purge=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'status': '<STATUS>', + 'expiresAt': '2020-10-15T06:38:00.000+00:00', + 'metadata': {}, + 'permissions': [], + 'purge': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -104,7 +175,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -112,4 +183,12 @@ def test_delete(self, m): response = self.presences.delete( '<PRESENCE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_project.py b/test/services/test_project.py index d0c94701..b32608fb 100644 --- a/test/services/test_project.py +++ b/test/services/test_project.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -46,12 +48,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -59,12 +69,20 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.delete() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -99,14 +117,27 @@ def test_update_auth_method(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_auth_method( 'email-password', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/auth-methods/email-password', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -118,12 +149,28 @@ def test_list_environments(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_environments() + response = self.project.list_environments( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -158,7 +205,7 @@ def test_create_environment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +214,20 @@ def test_create_environment(self, m): '<ENVIRONMENT_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'environmentId': '<ENVIRONMENT_ID>', + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -201,7 +262,7 @@ def test_get_environment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -209,6 +270,14 @@ def test_get_environment(self, m): response = self.project.get_environment( '<ENVIRONMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments/<ENVIRONMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -243,7 +312,7 @@ def test_update_environment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -252,6 +321,19 @@ def test_update_environment(self, m): '<ENVIRONMENT_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments/<ENVIRONMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -259,7 +341,7 @@ def test_delete_environment(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -267,6 +349,14 @@ def test_delete_environment(self, m): response = self.project.delete_environment( '<ENVIRONMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments/<ENVIRONMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -277,12 +367,28 @@ def test_list_keys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_keys() + response = self.project.list_keys( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -301,7 +407,7 @@ def test_create_ephemeral_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -310,6 +416,20 @@ def test_create_ephemeral_key(self, m): [], 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/ephemeral', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'scopes': [], + 'duration': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -328,7 +448,7 @@ def test_get_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +456,14 @@ def test_get_key(self, m): response = self.project.get_key( '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/<KEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -354,7 +482,7 @@ def test_update_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -363,6 +491,24 @@ def test_update_key(self, m): '<KEY_ID>', '<NAME>', [], + expire='2020-10-15T06:38:00.000+00:00', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'scopes': [], + 'expire': '2020-10-15T06:38:00.000+00:00', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -371,7 +517,7 @@ def test_delete_key(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -379,6 +525,14 @@ def test_delete_key(self, m): response = self.project.delete_key( '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -413,7 +567,7 @@ def test_update_labels(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -421,6 +575,19 @@ def test_update_labels(self, m): response = self.project.update_labels( [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/labels', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'labels': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -431,12 +598,28 @@ def test_list_mock_phones(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_mock_phones() + response = self.project.list_mock_phones( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -449,7 +632,7 @@ def test_create_mock_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -458,6 +641,20 @@ def test_create_mock_phone(self, m): '+12065550100', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'number': '+12065550100', + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -470,7 +667,7 @@ def test_get_mock_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -478,6 +675,14 @@ def test_get_mock_phone(self, m): response = self.project.get_mock_phone( '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones/+12065550100', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -490,7 +695,7 @@ def test_update_mock_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -499,6 +704,19 @@ def test_update_mock_phone(self, m): '+12065550100', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones/+12065550100', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -506,7 +724,7 @@ def test_delete_mock_phone(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -514,6 +732,14 @@ def test_delete_mock_phone(self, m): response = self.project.delete_mock_phone( '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones/+12065550100', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -524,12 +750,28 @@ def test_list_o_auth2_providers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_o_auth2_providers() + response = self.project.list_o_auth2_providers( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -564,14 +806,56 @@ def test_update_o_auth2_server(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_o_auth2_server( - True, + False, 'https://example.com', + scopes=[], + authorization_details_types=[], + access_token_duration=1, + refresh_token_duration=1, + public_access_token_duration=1, + public_refresh_token_duration=1, + installation_access_token_duration=1, + confidential_pkce=False, + verification_url='https://example.com', + user_code_length=1, + user_code_format='numeric', + device_code_duration=1, + default_scopes=[], + installation_scopes=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2-server', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'authorizationUrl': 'https://example.com', + 'scopes': [], + 'authorizationDetailsTypes': [], + 'accessTokenDuration': 1, + 'refreshTokenDuration': 1, + 'publicAccessTokenDuration': 1, + 'publicRefreshTokenDuration': 1, + 'installationAccessTokenDuration': 1, + 'confidentialPkce': False, + 'verificationUrl': 'https://example.com', + 'userCodeLength': 1, + 'userCodeFormat': 'numeric', + 'deviceCodeDuration': 1, + 'defaultScopes': [], + 'installationScopes': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -585,12 +869,31 @@ def test_update_o_auth2_amazon(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_amazon() + response = self.project.update_o_auth2_amazon( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/amazon', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -607,12 +910,39 @@ def test_update_o_auth2_apple(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_apple() + response = self.project.update_o_auth2_apple( + service_id='<SERVICE_ID>', + key_id='<KEY_ID>', + team_id='<TEAM_ID>', + p8_file='<P8_FILE>', + enabled=False, + native_client_ids=[], + native_enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/apple', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'serviceId': '<SERVICE_ID>', + 'keyId': '<KEY_ID>', + 'teamId': '<TEAM_ID>', + 'p8File': '<P8_FILE>', + 'enabled': False, + 'nativeClientIds': [], + 'nativeEnabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -625,12 +955,31 @@ def test_update_o_auth2_appwrite(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_appwrite() + response = self.project.update_o_auth2_appwrite( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/appwrite', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -645,12 +994,35 @@ def test_update_o_auth2_auth0(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_auth0() + response = self.project.update_o_auth2_auth0( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/auth0', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -664,12 +1036,33 @@ def test_update_o_auth2_authentik(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_authentik() + response = self.project.update_o_auth2_authentik( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/authentik', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -682,12 +1075,31 @@ def test_update_o_auth2_autodesk(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_autodesk() + response = self.project.update_o_auth2_autodesk( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/autodesk', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -700,12 +1112,31 @@ def test_update_o_auth2_bitbucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_bitbucket() + response = self.project.update_o_auth2_bitbucket( + key='<KEY>', + secret='<SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/bitbucket', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'secret': '<SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -718,12 +1149,31 @@ def test_update_o_auth2_bitly(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_bitly() + response = self.project.update_o_auth2_bitly( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/bitly', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -736,12 +1186,31 @@ def test_update_o_auth2_box(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_box() + response = self.project.update_o_auth2_box( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/box', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -754,12 +1223,31 @@ def test_update_o_auth2_cloudflare(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_cloudflare() + response = self.project.update_o_auth2_cloudflare( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/cloudflare', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -772,12 +1260,31 @@ def test_update_o_auth2_dailymotion(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_dailymotion() + response = self.project.update_o_auth2_dailymotion( + api_key='<API_KEY>', + api_secret='<API_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/dailymotion', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'apiKey': '<API_KEY>', + 'apiSecret': '<API_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -791,12 +1298,33 @@ def test_update_o_auth2_discord(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_discord() + response = self.project.update_o_auth2_discord( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/discord', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -809,12 +1337,31 @@ def test_update_o_auth2_disqus(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_disqus() + response = self.project.update_o_auth2_disqus( + public_key='<PUBLIC_KEY>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/disqus', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'publicKey': '<PUBLIC_KEY>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -827,12 +1374,31 @@ def test_update_o_auth2_dropbox(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_dropbox() + response = self.project.update_o_auth2_dropbox( + app_key='<APP_KEY>', + app_secret='<APP_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/dropbox', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appKey': '<APP_KEY>', + 'appSecret': '<APP_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -845,12 +1411,31 @@ def test_update_o_auth2_etsy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_etsy() + response = self.project.update_o_auth2_etsy( + key_string='<KEY_STRING>', + shared_secret='<SHARED_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/etsy', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'keyString': '<KEY_STRING>', + 'sharedSecret': '<SHARED_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -863,12 +1448,31 @@ def test_update_o_auth2_facebook(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_facebook() + response = self.project.update_o_auth2_facebook( + app_id='<APP_ID>', + app_secret='<APP_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/facebook', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '<APP_ID>', + 'appSecret': '<APP_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -881,12 +1485,31 @@ def test_update_o_auth2_figma(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_figma() + response = self.project.update_o_auth2_figma( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/figma', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -900,12 +1523,33 @@ def test_update_o_auth2_fusion_auth(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_fusion_auth() + response = self.project.update_o_auth2_fusion_auth( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/fusionauth', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -919,12 +1563,33 @@ def test_update_o_auth2_git_hub(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_git_hub() + response = self.project.update_o_auth2_git_hub( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/github', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -938,12 +1603,33 @@ def test_update_o_auth2_gitlab(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_gitlab() + response = self.project.update_o_auth2_gitlab( + application_id='<APPLICATION_ID>', + secret='<SECRET>', + endpoint='https://example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/gitlab', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'applicationId': '<APPLICATION_ID>', + 'secret': '<SECRET>', + 'endpoint': 'https://example.com', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -959,12 +1645,37 @@ def test_update_o_auth2_google(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_google() + response = self.project.update_o_auth2_google( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + prompt=[], + enabled=False, + native_client_ids=[], + native_enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/google', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'prompt': [], + 'enabled': False, + 'nativeClientIds': [], + 'nativeEnabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -977,12 +1688,31 @@ def test_update_o_auth2_hugging_face(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_hugging_face() + response = self.project.update_o_auth2_hugging_face( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/huggingface', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -996,12 +1726,33 @@ def test_update_o_auth2_kakao(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_kakao() + response = self.project.update_o_auth2_kakao( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/kakao', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1016,12 +1767,35 @@ def test_update_o_auth2_keycloak(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_keycloak() + response = self.project.update_o_auth2_keycloak( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + realm_name='<REALM_NAME>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/keycloak', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'realmName': '<REALM_NAME>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1034,12 +1808,31 @@ def test_update_o_auth2_kick(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_kick() + response = self.project.update_o_auth2_kick( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/kick', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1052,12 +1845,31 @@ def test_update_o_auth2_linkedin(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_linkedin() + response = self.project.update_o_auth2_linkedin( + client_id='<CLIENT_ID>', + primary_client_secret='<PRIMARY_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/linkedin', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'primaryClientSecret': '<PRIMARY_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1072,12 +1884,35 @@ def test_update_o_auth2_microsoft(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_microsoft() + response = self.project.update_o_auth2_microsoft( + application_id='<APPLICATION_ID>', + application_secret='<APPLICATION_SECRET>', + tenant='<TENANT>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/microsoft', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'applicationId': '<APPLICATION_ID>', + 'applicationSecret': '<APPLICATION_SECRET>', + 'tenant': '<TENANT>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1090,12 +1925,31 @@ def test_update_o_auth2_notion(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_notion() + response = self.project.update_o_auth2_notion( + oauth_client_id='<OAUTH_CLIENT_ID>', + oauth_client_secret='<OAUTH_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/notion', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'oauthClientId': '<OAUTH_CLIENT_ID>', + 'oauthClientSecret': '<OAUTH_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1113,12 +1967,43 @@ def test_update_o_auth2_oidc(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_oidc() + response = self.project.update_o_auth2_oidc( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + well_known_url='https://example.com', + authorization_url='https://example.com', + token_url='https://example.com', + user_info_url='https://example.com', + prompt=[], + max_age=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/oidc', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'wellKnownURL': 'https://example.com', + 'authorizationURL': 'https://example.com', + 'tokenURL': 'https://example.com', + 'userInfoURL': 'https://example.com', + 'prompt': [], + 'maxAge': 1, + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1134,12 +2019,37 @@ def test_update_o_auth2_okta(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_okta() + response = self.project.update_o_auth2_okta( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + domain='example.com', + authorization_server_id='<AUTHORIZATION_SERVER_ID>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/okta', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'domain': 'example.com', + 'authorizationServerId': '<AUTHORIZATION_SERVER_ID>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1152,12 +2062,31 @@ def test_update_o_auth2_paypal(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_paypal() + response = self.project.update_o_auth2_paypal( + client_id='<CLIENT_ID>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/paypal', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1170,12 +2099,31 @@ def test_update_o_auth2_paypal_sandbox(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_paypal_sandbox() + response = self.project.update_o_auth2_paypal_sandbox( + client_id='<CLIENT_ID>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/paypalSandbox', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1188,12 +2136,31 @@ def test_update_o_auth2_podio(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_podio() + response = self.project.update_o_auth2_podio( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/podio', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1206,12 +2173,31 @@ def test_update_o_auth2_resend(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_resend() + response = self.project.update_o_auth2_resend( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/resend', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1225,12 +2211,33 @@ def test_update_o_auth2_salesforce(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_salesforce() + response = self.project.update_o_auth2_salesforce( + customer_key='<CUSTOMER_KEY>', + customer_secret='<CUSTOMER_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/salesforce', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'customerKey': '<CUSTOMER_KEY>', + 'customerSecret': '<CUSTOMER_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1243,12 +2250,31 @@ def test_update_o_auth2_slack(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_slack() + response = self.project.update_o_auth2_slack( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/slack', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1261,12 +2287,31 @@ def test_update_o_auth2_spotify(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_spotify() + response = self.project.update_o_auth2_spotify( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/spotify', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1279,12 +2324,31 @@ def test_update_o_auth2_stripe(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_stripe() + response = self.project.update_o_auth2_stripe( + client_id='<CLIENT_ID>', + api_secret_key='<API_SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/stripe', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'apiSecretKey': '<API_SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1297,12 +2361,31 @@ def test_update_o_auth2_tik_tok(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_tik_tok() + response = self.project.update_o_auth2_tik_tok( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/tiktok', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1315,12 +2398,31 @@ def test_update_o_auth2_tradeshift(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_tradeshift() + response = self.project.update_o_auth2_tradeshift( + oauth2_client_id='<OAUTH2_CLIENT_ID>', + oauth2_client_secret='<OAUTH2_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/tradeshift', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'oauth2ClientId': '<OAUTH2_CLIENT_ID>', + 'oauth2ClientSecret': '<OAUTH2_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1333,12 +2435,31 @@ def test_update_o_auth2_tradeshift_sandbox(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_tradeshift_sandbox() + response = self.project.update_o_auth2_tradeshift_sandbox( + oauth2_client_id='<OAUTH2_CLIENT_ID>', + oauth2_client_secret='<OAUTH2_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/tradeshiftBox', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'oauth2ClientId': '<OAUTH2_CLIENT_ID>', + 'oauth2ClientSecret': '<OAUTH2_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1351,12 +2472,31 @@ def test_update_o_auth2_twitch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_twitch() + response = self.project.update_o_auth2_twitch( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/twitch', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1369,12 +2509,31 @@ def test_update_o_auth2_webflow(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_webflow() + response = self.project.update_o_auth2_webflow( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/webflow', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1387,12 +2546,31 @@ def test_update_o_auth2_word_press(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_word_press() + response = self.project.update_o_auth2_word_press( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/wordpress', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1405,12 +2583,31 @@ def test_update_o_auth2_x(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_x() + response = self.project.update_o_auth2_x( + customer_key='<CUSTOMER_KEY>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/x', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'customerKey': '<CUSTOMER_KEY>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1423,12 +2620,31 @@ def test_update_o_auth2_yahoo(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_yahoo() + response = self.project.update_o_auth2_yahoo( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/yahoo', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1441,12 +2657,31 @@ def test_update_o_auth2_yandex(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_yandex() + response = self.project.update_o_auth2_yandex( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/yandex', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1460,12 +2695,33 @@ def test_update_o_auth2_zoho(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_zoho() + response = self.project.update_o_auth2_zoho( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/zoho', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1478,12 +2734,31 @@ def test_update_o_auth2_zoom(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_zoom() + response = self.project.update_o_auth2_zoom( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/zoom', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1497,7 +2772,7 @@ def test_get_o_auth2_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1505,6 +2780,14 @@ def test_get_o_auth2_provider(self, m): response = self.project.get_o_auth2_provider( 'amazon', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/amazon', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1515,12 +2798,28 @@ def test_list_platforms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_platforms() + response = self.project.list_platforms( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1536,7 +2835,7 @@ def test_create_android_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1545,6 +2844,23 @@ def test_create_android_platform(self, m): '<PLATFORM_ID>', '<NAME>', '<APPLICATION_ID>', + fingerprints=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/android', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'applicationId': '<APPLICATION_ID>', + 'fingerprints': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -1561,7 +2877,7 @@ def test_update_android_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1570,6 +2886,22 @@ def test_update_android_platform(self, m): '<PLATFORM_ID>', '<NAME>', '<APPLICATION_ID>', + fingerprints=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/android/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'applicationId': '<APPLICATION_ID>', + 'fingerprints': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -1585,7 +2917,7 @@ def test_create_apple_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1595,6 +2927,21 @@ def test_create_apple_platform(self, m): '<NAME>', '<BUNDLE_IDENTIFIER>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/apple', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'bundleIdentifier': '<BUNDLE_IDENTIFIER>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1609,7 +2956,7 @@ def test_update_apple_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1619,6 +2966,20 @@ def test_update_apple_platform(self, m): '<NAME>', '<BUNDLE_IDENTIFIER>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/apple/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'bundleIdentifier': '<BUNDLE_IDENTIFIER>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1633,7 +2994,7 @@ def test_create_linux_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1643,6 +3004,21 @@ def test_create_linux_platform(self, m): '<NAME>', '<PACKAGE_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/linux', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'packageName': '<PACKAGE_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1657,7 +3033,7 @@ def test_update_linux_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1667,6 +3043,20 @@ def test_update_linux_platform(self, m): '<NAME>', '<PACKAGE_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/linux/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'packageName': '<PACKAGE_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1681,7 +3071,7 @@ def test_create_web_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1691,6 +3081,21 @@ def test_create_web_platform(self, m): '<NAME>', 'app.example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/web', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'hostname': 'app.example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1705,7 +3110,7 @@ def test_update_web_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1715,6 +3120,20 @@ def test_update_web_platform(self, m): '<NAME>', 'app.example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/web/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'hostname': 'app.example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1729,7 +3148,7 @@ def test_create_windows_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1739,6 +3158,21 @@ def test_create_windows_platform(self, m): '<NAME>', '<PACKAGE_IDENTIFIER_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/windows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'packageIdentifierName': '<PACKAGE_IDENTIFIER_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1753,7 +3187,7 @@ def test_update_windows_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1763,6 +3197,20 @@ def test_update_windows_platform(self, m): '<NAME>', '<PACKAGE_IDENTIFIER_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/windows/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'packageIdentifierName': '<PACKAGE_IDENTIFIER_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1777,7 +3225,7 @@ def test_get_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1785,6 +3233,14 @@ def test_get_platform(self, m): response = self.project.get_platform( '<PLATFORM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/<PLATFORM_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1792,7 +3248,7 @@ def test_delete_platform(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1800,6 +3256,14 @@ def test_delete_platform(self, m): response = self.project.delete_platform( '<PLATFORM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1810,12 +3274,28 @@ def test_list_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_policies() + response = self.project.list_policies( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1850,13 +3330,26 @@ def test_update_deny_aliased_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_aliased_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-aliased-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1892,13 +3385,26 @@ def test_update_deny_corporate_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_corporate_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-corporate-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1934,13 +3440,26 @@ def test_update_deny_disposable_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_disposable_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-disposable-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1976,18 +3495,31 @@ def test_update_deny_free_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_free_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-free-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() - def test_update_membership_privacy_policy(self, m): + def test_update_deny_realtime_guests_policy(self, m): data = { "$id": "5e5ea5c16897e", "$createdAt": "2020-10-15T06:38:00.000+00:00", @@ -2018,12 +3550,92 @@ def test_update_membership_privacy_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( + 'PATCH', requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.project.update_deny_realtime_guests_policy( + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-realtime-guests', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_update_membership_privacy_policy(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "name": "New Project", + "teamId": "1592981250", + "region": "fra", + "smtpEnabled": True, + "smtpSenderName": "John Appwrite", + "smtpSenderEmail": "john@appwrite.io", + "smtpReplyToName": "Support Team", + "smtpReplyToEmail": "support@appwrite.io", + "smtpHost": "mail.appwrite.io", + "smtpPort": 25.0, + "smtpUsername": "emailuser", + "smtpPassword": "smtp-password", + "smtpSecure": "tls", + "pingCount": 1.0, + "pingedAt": "2020-10-15T06:38:00.000+00:00", + "labels": [], + "status": "active", + "onboarding": {}, + "authMethods": [], + "services": [], + "protocols": [], + "blocks": [], + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + } + headers = {'Content-Type': 'application/json'} + m.request( + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_membership_privacy_policy() + response = self.project.update_membership_privacy_policy( + user_id=False, + user_email=False, + user_phone=False, + user_name=False, + user_mfa=False, + user_accessed_at=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/membership-privacy', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': False, + 'userEmail': False, + 'userPhone': False, + 'userName': False, + 'userMFA': False, + 'userAccessedAt': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2058,12 +3670,33 @@ def test_update_mfa_factors_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_mfa_factors_policy() + response = self.project.update_mfa_factors_policy( + totp=False, + email=False, + phone=False, + custom=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/mfa-factors', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'totp': False, + 'email': False, + 'phone': False, + 'custom': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2098,7 +3731,7 @@ def test_update_passkey_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2106,6 +3739,19 @@ def test_update_passkey_policy(self, m): response = self.project.update_passkey_policy( '<RP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/passkey', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rpId': '<RP_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2140,13 +3786,28 @@ def test_update_password_dictionary_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_password_dictionary_policy( - True, + False, + deny_list=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-dictionary', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'denyList': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -2182,7 +3843,7 @@ def test_update_password_history_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2190,6 +3851,19 @@ def test_update_password_history_policy(self, m): response = self.project.update_password_history_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-history', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'total': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2224,13 +3898,28 @@ def test_update_password_personal_data_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_password_personal_data_policy( - True, + False, + deny_list=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-personal-data', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'denyList': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -2266,12 +3955,31 @@ def test_update_password_pwned_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_password_pwned_policy() + response = self.project.update_password_pwned_policy( + enabled=False, + sessions=False, + users=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-pwned', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'sessions': False, + 'users': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2306,12 +4014,29 @@ def test_update_password_rotation_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_password_rotation_policy() + response = self.project.update_password_rotation_policy( + enabled=False, + max_age=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-rotation', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'maxAge': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2326,12 +4051,35 @@ def test_update_password_strength_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_password_strength_policy() + response = self.project.update_password_strength_policy( + min=1, + uppercase=False, + lowercase=False, + number=False, + symbols=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-strength', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'min': 1, + 'uppercase': False, + 'lowercase': False, + 'number': False, + 'symbols': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2366,13 +4114,26 @@ def test_update_session_alert_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_session_alert_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-alert', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -2408,7 +4169,7 @@ def test_update_session_duration_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2416,6 +4177,19 @@ def test_update_session_duration_policy(self, m): response = self.project.update_session_duration_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-duration', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'duration': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2450,13 +4224,26 @@ def test_update_session_invalidation_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_session_invalidation_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-invalidation', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -2492,7 +4279,7 @@ def test_update_session_limit_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2500,6 +4287,19 @@ def test_update_session_limit_policy(self, m): response = self.project.update_session_limit_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-limit', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'total': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2534,7 +4334,7 @@ def test_update_user_limit_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2542,6 +4342,19 @@ def test_update_user_limit_policy(self, m): response = self.project.update_user_limit_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/user-limit', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'total': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2553,7 +4366,7 @@ def test_get_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2561,6 +4374,14 @@ def test_get_policy(self, m): response = self.project.get_policy( 'password-dictionary', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-dictionary', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2595,14 +4416,27 @@ def test_update_protocol(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_protocol( 'rest', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/protocols/rest', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -2638,17 +4472,51 @@ def test_update_service(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_service( 'account', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/services/account', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_delete_sessions(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request( + 'DELETE', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.project.delete_sessions() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) + self.assertEqual(response, data) + @requests_mock.Mocker() def test_update_smtp(self, m): data = { @@ -2681,12 +4549,45 @@ def test_update_smtp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_smtp() + response = self.project.update_smtp( + host='example.com', + port=1, + username='<USERNAME>', + password='password', + sender_email='email@example.com', + sender_name='<SENDER_NAME>', + reply_to_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + secure='tls', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'host': 'example.com', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'senderEmail': 'email@example.com', + 'senderName': '<SENDER_NAME>', + 'replyToEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'secure': 'tls', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2694,7 +4595,7 @@ def test_create_smtp_test(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2702,6 +4603,19 @@ def test_create_smtp_test(self, m): response = self.project.create_smtp_test( [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/smtp/tests', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'emails': [], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -2712,12 +4626,28 @@ def test_list_email_templates(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_email_templates() + response = self.project.list_email_templates( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/templates/email', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2734,13 +4664,40 @@ def test_update_email_template(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_email_template( 'verification', + locale='af', + subject='<SUBJECT>', + message='<MESSAGE>', + sender_name='<SENDER_NAME>', + sender_email='email@example.com', + reply_to_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/templates/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'templateId': 'verification', + 'locale': 'af', + 'subject': '<SUBJECT>', + 'message': '<MESSAGE>', + 'senderName': '<SENDER_NAME>', + 'senderEmail': 'email@example.com', + 'replyToEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -2758,13 +4715,27 @@ def test_get_email_template(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.get_email_template( 'verification', + locale='af', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/templates/email/verification', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'locale': ['af'], + }, ) self.assertEqual(response.to_dict(), data) @@ -2776,12 +4747,28 @@ def test_list_variables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_variables() + response = self.project.list_variables( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2799,7 +4786,7 @@ def test_create_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2808,6 +4795,25 @@ def test_create_variable(self, m): '<VARIABLE_ID>', '<KEY>', '<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'variableId': '<VARIABLE_ID>', + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -2826,7 +4832,7 @@ def test_get_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2834,6 +4840,14 @@ def test_get_variable(self, m): response = self.project.get_variable( '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables/<VARIABLE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2851,13 +4865,33 @@ def test_update_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_variable( '<VARIABLE_ID>', + key='<KEY>', + value='<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -2866,7 +4900,7 @@ def test_delete_variable(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2874,4 +4908,12 @@ def test_delete_variable(self, m): response = self.project.delete_variable( '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_proxy.py b/test/services/test_proxy.py index c12c5271..f20f3d13 100644 --- a/test/services/test_proxy.py +++ b/test/services/test_proxy.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -24,7 +26,7 @@ def test_create_invalidation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -32,6 +34,22 @@ def test_create_invalidation(self, m): response = self.proxy.create_invalidation( 'example.com', 'tag', + reference='<REFERENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/invalidations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'type': 'tag', + 'reference': '<REFERENCE>', + }, ) self.assertEqual(response.to_dict(), data) @@ -43,12 +61,28 @@ def test_list_rules(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.proxy.list_rules() + response = self.proxy.list_rules( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -72,7 +106,7 @@ def test_create_api_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -80,6 +114,19 @@ def test_create_api_rule(self, m): response = self.proxy.create_api_rule( 'example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/api', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -103,7 +150,7 @@ def test_create_function_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -111,6 +158,22 @@ def test_create_function_rule(self, m): response = self.proxy.create_function_rule( 'example.com', '<FUNCTION_ID>', + branch='<BRANCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/function', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'functionId': '<FUNCTION_ID>', + 'branch': '<BRANCH>', + }, ) self.assertEqual(response.to_dict(), data) @@ -135,7 +198,7 @@ def test_create_redirect_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -147,6 +210,23 @@ def test_create_redirect_rule(self, m): '<RESOURCE_ID>', 'site', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/redirect', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'url': 'https://example.com', + 'statusCode': '301', + 'resourceId': '<RESOURCE_ID>', + 'resourceType': 'site', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -170,7 +250,7 @@ def test_create_site_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -178,6 +258,22 @@ def test_create_site_rule(self, m): response = self.proxy.create_site_rule( 'example.com', '<SITE_ID>', + branch='<BRANCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/site', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'siteId': '<SITE_ID>', + 'branch': '<BRANCH>', + }, ) self.assertEqual(response.to_dict(), data) @@ -202,7 +298,7 @@ def test_create_smtp_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -210,6 +306,22 @@ def test_create_smtp_rule(self, m): response = self.proxy.create_smtp_rule( 'example.com', '<FUNCTION_ID>', + branch='<BRANCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'functionId': '<FUNCTION_ID>', + 'branch': '<BRANCH>', + }, ) self.assertEqual(response.to_dict(), data) @@ -234,7 +346,7 @@ def test_get_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -242,6 +354,14 @@ def test_get_rule(self, m): response = self.proxy.get_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/<RULE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -249,7 +369,7 @@ def test_delete_rule(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -257,6 +377,14 @@ def test_delete_rule(self, m): response = self.proxy.delete_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -280,7 +408,7 @@ def test_update_rule_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -288,4 +416,12 @@ def test_update_rule_status(self, m): response = self.proxy.update_rule_status( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/<RULE_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_sites.py b/test/services/test_sites.py index a1e4354e..9fedd1c3 100644 --- a/test/services/test_sites.py +++ b/test/services/test_sites.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.sites.list() + response = self.sites.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -72,7 +92,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -82,6 +102,64 @@ def test_create(self, m): '<NAME>', 'analog', 'node-14.5', + enabled=False, + logging=False, + timeout=1, + install_command='<INSTALL_COMMAND>', + build_command='<BUILD_COMMAND>', + start_command='<START_COMMAND>', + output_directory='<OUTPUT_DIRECTORY>', + adapter='static', + installation_id='<INSTALLATION_ID>', + fallback_file='<FALLBACK_FILE>', + provider_repository_id='<PROVIDER_REPOSITORY_ID>', + provider_branch='<PROVIDER_BRANCH>', + provider_silent_mode=False, + provider_root_directory='<PROVIDER_ROOT_DIRECTORY>', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + scopes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'siteId': '<SITE_ID>', + 'name': '<NAME>', + 'framework': 'analog', + 'enabled': False, + 'logging': False, + 'timeout': 1, + 'installCommand': '<INSTALL_COMMAND>', + 'buildCommand': '<BUILD_COMMAND>', + 'startCommand': '<START_COMMAND>', + 'outputDirectory': '<OUTPUT_DIRECTORY>', + 'buildRuntime': 'node-14.5', + 'adapter': 'static', + 'installationId': '<INSTALLATION_ID>', + 'fallbackFile': '<FALLBACK_FILE>', + 'providerRepositoryId': '<PROVIDER_REPOSITORY_ID>', + 'providerBranch': '<PROVIDER_BRANCH>', + 'providerSilentMode': False, + 'providerRootDirectory': '<PROVIDER_ROOT_DIRECTORY>', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'scopes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -93,12 +171,27 @@ def test_list_frameworks(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.sites.list_frameworks() + response = self.sites.list_frameworks( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/frameworks', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -109,12 +202,29 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.sites.list_specifications() + response = self.sites.list_specifications( + type='runtimes', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['runtimes'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +269,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +277,14 @@ def test_get(self, m): response = self.sites.get( '<SITE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -211,7 +329,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -220,6 +338,64 @@ def test_update(self, m): '<SITE_ID>', '<NAME>', 'analog', + enabled=False, + logging=False, + timeout=1, + install_command='<INSTALL_COMMAND>', + build_command='<BUILD_COMMAND>', + start_command='<START_COMMAND>', + output_directory='<OUTPUT_DIRECTORY>', + build_runtime='node-14.5', + adapter='static', + fallback_file='<FALLBACK_FILE>', + installation_id='<INSTALLATION_ID>', + provider_repository_id='<PROVIDER_REPOSITORY_ID>', + provider_branch='<PROVIDER_BRANCH>', + provider_silent_mode=False, + provider_root_directory='<PROVIDER_ROOT_DIRECTORY>', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + scopes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'framework': 'analog', + 'enabled': False, + 'logging': False, + 'timeout': 1, + 'installCommand': '<INSTALL_COMMAND>', + 'buildCommand': '<BUILD_COMMAND>', + 'startCommand': '<START_COMMAND>', + 'outputDirectory': '<OUTPUT_DIRECTORY>', + 'buildRuntime': 'node-14.5', + 'adapter': 'static', + 'fallbackFile': '<FALLBACK_FILE>', + 'installationId': '<INSTALLATION_ID>', + 'providerRepositoryId': '<PROVIDER_REPOSITORY_ID>', + 'providerBranch': '<PROVIDER_BRANCH>', + 'providerSilentMode': False, + 'providerRootDirectory': '<PROVIDER_ROOT_DIRECTORY>', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'scopes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -228,7 +404,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -236,6 +412,14 @@ def test_delete(self, m): response = self.sites.delete( '<SITE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -280,7 +464,7 @@ def test_update_site_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -289,6 +473,19 @@ def test_update_site_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '<DEPLOYMENT_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -299,13 +496,30 @@ def test_list_deployments(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.sites.list_deployments( '<SITE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -342,7 +556,7 @@ def test_create_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -350,6 +564,33 @@ def test_create_deployment(self, m): response = self.sites.create_deployment( '<SITE_ID>', InputFile.from_bytes(bytearray(), "example.file"), + install_command='<INSTALL_COMMAND>', + build_command='<BUILD_COMMAND>', + output_directory='<OUTPUT_DIRECTORY>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'installCommand': '<INSTALL_COMMAND>', + 'buildCommand': '<BUILD_COMMAND>', + 'outputDirectory': '<OUTPUT_DIRECTORY>', + 'code': '', + 'activate': 'false', + }, ) self.assertEqual(response.to_dict(), data) @@ -386,7 +627,7 @@ def test_create_duplicate_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -395,6 +636,19 @@ def test_create_duplicate_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/duplicate', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '<DEPLOYMENT_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -430,7 +684,7 @@ def test_create_template_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -442,6 +696,25 @@ def test_create_template_deployment(self, m): '<ROOT_DIRECTORY>', 'branch', '<REFERENCE>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/template', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'repository': '<REPOSITORY>', + 'owner': '<OWNER>', + 'rootDirectory': '<ROOT_DIRECTORY>', + 'type': 'branch', + 'reference': '<REFERENCE>', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -478,7 +751,7 @@ def test_create_vcs_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -487,6 +760,22 @@ def test_create_vcs_deployment(self, m): '<SITE_ID>', 'branch', '<REFERENCE>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/vcs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'branch', + 'reference': '<REFERENCE>', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -523,7 +812,7 @@ def test_get_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -532,6 +821,14 @@ def test_get_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -539,7 +836,7 @@ def test_delete_deployment(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -548,6 +845,14 @@ def test_delete_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -555,7 +860,7 @@ def test_get_deployment_download(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -563,6 +868,22 @@ def test_get_deployment_download(self, m): response = self.sites.get_deployment_download( '<SITE_ID>', '<DEPLOYMENT_ID>', + type='source', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>/download', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['source'], + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) @@ -599,7 +920,7 @@ def test_update_deployment_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -608,6 +929,14 @@ def test_update_deployment_status(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -618,13 +947,28 @@ def test_list_logs(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.sites.list_logs( '<SITE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/logs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -652,7 +996,7 @@ def test_get_log(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -661,6 +1005,14 @@ def test_get_log(self, m): '<SITE_ID>', '<LOG_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/logs/<LOG_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -668,7 +1020,7 @@ def test_delete_log(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -677,6 +1029,14 @@ def test_delete_log(self, m): '<SITE_ID>', '<LOG_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/logs/<LOG_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -687,13 +1047,28 @@ def test_list_variables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.sites.list_variables( '<SITE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -712,7 +1087,7 @@ def test_create_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -722,6 +1097,25 @@ def test_create_variable(self, m): '<VARIABLE_ID>', '<KEY>', '<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'variableId': '<VARIABLE_ID>', + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -740,7 +1134,7 @@ def test_get_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -749,6 +1143,14 @@ def test_get_variable(self, m): '<SITE_ID>', '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables/<VARIABLE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -766,7 +1168,7 @@ def test_update_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -774,6 +1176,26 @@ def test_update_variable(self, m): response = self.sites.update_variable( '<SITE_ID>', '<VARIABLE_ID>', + key='<KEY>', + value='<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -782,7 +1204,7 @@ def test_delete_variable(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -791,4 +1213,12 @@ def test_delete_variable(self, m): '<SITE_ID>', '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_storage.py b/test/services/test_storage.py index 15c0da0a..bf69311f 100644 --- a/test/services/test_storage.py +++ b/test/services/test_storage.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list_buckets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.storage.list_buckets() + response = self.storage.list_buckets( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -52,7 +72,7 @@ def test_create_bucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -60,6 +80,42 @@ def test_create_bucket(self, m): response = self.storage.create_bucket( '<BUCKET_ID>', '<NAME>', + permissions=[], + file_security=False, + enabled=False, + maximum_file_size=1, + allowed_file_extensions=[], + compression='none', + encryption=False, + antivirus=False, + transformations=False, + description='<DESCRIPTION>', + strip_metadata=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'bucketId': '<BUCKET_ID>', + 'name': '<NAME>', + 'permissions': [], + 'fileSecurity': False, + 'enabled': False, + 'maximumFileSize': 1, + 'allowedFileExtensions': [], + 'compression': 'none', + 'encryption': False, + 'antivirus': False, + 'transformations': False, + 'description': '<DESCRIPTION>', + 'stripMetadata': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -85,7 +141,7 @@ def test_get_bucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -93,6 +149,14 @@ def test_get_bucket(self, m): response = self.storage.get_bucket( '<BUCKET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -117,7 +181,7 @@ def test_update_bucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -125,6 +189,41 @@ def test_update_bucket(self, m): response = self.storage.update_bucket( '<BUCKET_ID>', '<NAME>', + permissions=[], + file_security=False, + enabled=False, + maximum_file_size=1, + allowed_file_extensions=[], + compression='none', + encryption=False, + antivirus=False, + transformations=False, + description='<DESCRIPTION>', + strip_metadata=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'permissions': [], + 'fileSecurity': False, + 'enabled': False, + 'maximumFileSize': 1, + 'allowedFileExtensions': [], + 'compression': 'none', + 'encryption': False, + 'antivirus': False, + 'transformations': False, + 'description': '<DESCRIPTION>', + 'stripMetadata': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -133,7 +232,7 @@ def test_delete_bucket(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -141,6 +240,14 @@ def test_delete_bucket(self, m): response = self.storage.delete_bucket( '<BUCKET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -151,13 +258,30 @@ def test_list_files(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.storage.list_files( '<BUCKET_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -183,7 +307,7 @@ def test_create_file(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -192,6 +316,29 @@ def test_create_file(self, m): '<BUCKET_ID>', '<FILE_ID>', InputFile.from_bytes(bytearray(), "example.file"), + permissions=[], + folder='photos/2026', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'fileId': '<FILE_ID>', + 'file': '', + 'folder': 'photos/2026', + }, ) self.assertEqual(response.to_dict(), data) @@ -217,7 +364,7 @@ def test_get_file(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -226,6 +373,14 @@ def test_get_file(self, m): '<BUCKET_ID>', '<FILE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -250,7 +405,7 @@ def test_update_file(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -258,6 +413,22 @@ def test_update_file(self, m): response = self.storage.update_file( '<BUCKET_ID>', '<FILE_ID>', + name='name', + permissions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'permissions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -266,7 +437,7 @@ def test_delete_file(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -275,6 +446,14 @@ def test_delete_file(self, m): '<BUCKET_ID>', '<FILE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -282,7 +461,7 @@ def test_get_file_download(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -290,6 +469,20 @@ def test_get_file_download(self, m): response = self.storage.get_file_download( '<BUCKET_ID>', '<FILE_ID>', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>/download', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) @@ -298,7 +491,7 @@ def test_get_file_preview(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -306,6 +499,42 @@ def test_get_file_preview(self, m): response = self.storage.get_file_preview( '<BUCKET_ID>', '<FILE_ID>', + width=1, + height=1, + gravity='auto', + quality=1, + border_width=1, + border_color='FFFFFF', + border_radius=1, + opacity=1.0, + rotation=1, + background='FFFFFF', + output='jpg', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>/preview', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'gravity': ['auto'], + 'quality': ['1'], + 'borderWidth': ['1'], + 'borderColor': ['FFFFFF'], + 'borderRadius': ['1'], + 'opacity': ['1.0'], + 'rotation': ['1'], + 'background': ['FFFFFF'], + 'output': ['jpg'], + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) @@ -314,7 +543,7 @@ def test_get_file_view(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -322,5 +551,19 @@ def test_get_file_view(self, m): response = self.storage.get_file_view( '<BUCKET_ID>', '<FILE_ID>', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>/view', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) diff --git a/test/services/test_tables_db.py b/test/services/test_tables_db.py index 31151822..e2300156 100644 --- a/test/services/test_tables_db.py +++ b/test/services/test_tables_db.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.tables_db.list() + response = self.tables_db.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +63,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +71,30 @@ def test_create(self, m): response = self.tables_db.create( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +106,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -78,12 +130,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.tables_db.list_transactions() + response = self.tables_db.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +166,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.tables_db.create_transaction() + response = self.tables_db.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -118,7 +201,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -126,6 +209,14 @@ def test_get_transaction(self, m): response = self.tables_db.get_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -140,13 +231,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.update_transaction( '<TRANSACTION_ID>', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -155,7 +262,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -163,6 +270,14 @@ def test_delete_transaction(self, m): response = self.tables_db.delete_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -177,13 +292,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.create_operations( '<TRANSACTION_ID>', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>/operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -200,7 +329,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -208,6 +337,14 @@ def test_get(self, m): response = self.tables_db.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -223,13 +360,37 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.update( '<DATABASE_ID>', + name='<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -238,7 +399,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -246,6 +407,14 @@ def test_delete(self, m): response = self.tables_db.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -299,13 +468,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -317,7 +500,7 @@ def test_list_migrations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -325,6 +508,14 @@ def test_list_migrations(self, m): response = self.tables_db.list_migrations( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -350,7 +541,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -358,6 +549,21 @@ def test_create_migration(self, m): response = self.tables_db.create_migration( '<DATABASE_ID>', 's-1vcpu-1gb', + auto_cutover=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'specification': 's-1vcpu-1gb', + 'autoCutover': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -384,7 +590,7 @@ def test_get_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -393,6 +599,14 @@ def test_get_migration(self, m): '<DATABASE_ID>', '<MIGRATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations/<MIGRATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -400,7 +614,7 @@ def test_delete_migration(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -409,6 +623,14 @@ def test_delete_migration(self, m): '<DATABASE_ID>', '<MIGRATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations/<MIGRATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -434,7 +656,7 @@ def test_create_cutover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -443,6 +665,14 @@ def test_create_cutover(self, m): '<DATABASE_ID>', '<MIGRATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations/<MIGRATION_ID>/cutovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -453,13 +683,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -475,7 +723,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -483,6 +731,14 @@ def test_get_replicas(self, m): response = self.tables_db.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -502,7 +758,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -510,6 +766,14 @@ def test_get_status(self, m): response = self.tables_db.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -520,13 +784,30 @@ def test_list_tables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.list_tables( '<DATABASE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -549,7 +830,7 @@ def test_create_table(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -558,6 +839,32 @@ def test_create_table(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<NAME>', + permissions=[], + row_security=False, + enabled=False, + columns=[], + indexes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'tableId': '<TABLE_ID>', + 'name': '<NAME>', + 'permissions': [], + 'rowSecurity': False, + 'enabled': False, + 'columns': [], + 'indexes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -580,7 +887,7 @@ def test_get_table(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -589,6 +896,14 @@ def test_get_table(self, m): '<DATABASE_ID>', '<TABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -610,7 +925,7 @@ def test_update_table(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +933,30 @@ def test_update_table(self, m): response = self.tables_db.update_table( '<DATABASE_ID>', '<TABLE_ID>', + name='<NAME>', + permissions=[], + row_security=False, + enabled=False, + purge=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'permissions': [], + 'rowSecurity': False, + 'enabled': False, + 'purge': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -626,7 +965,7 @@ def test_delete_table(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -635,6 +974,14 @@ def test_delete_table(self, m): '<DATABASE_ID>', '<TABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -645,7 +992,7 @@ def test_list_columns(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -653,6 +1000,21 @@ def test_list_columns(self, m): response = self.tables_db.list_columns( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -670,7 +1032,7 @@ def test_create_big_int_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -679,7 +1041,31 @@ def test_create_big_int_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/bigint', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -697,7 +1083,7 @@ def test_update_big_int_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -706,8 +1092,30 @@ def test_update_big_int_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 1, + min=1, + max=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/bigint/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -725,7 +1133,7 @@ def test_create_boolean_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -734,7 +1142,27 @@ def test_create_boolean_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=False, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/boolean', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': False, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -752,7 +1180,7 @@ def test_update_boolean_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -761,8 +1189,26 @@ def test_update_boolean_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, - True, + False, + False, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/boolean/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': False, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -781,7 +1227,7 @@ def test_create_datetime_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -790,7 +1236,27 @@ def test_create_datetime_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='2020-10-15T06:38:00.000+00:00', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/datetime', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -809,7 +1275,7 @@ def test_update_datetime_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -818,8 +1284,26 @@ def test_update_datetime_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, '2020-10-15T06:38:00.000+00:00', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/datetime/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -838,7 +1322,7 @@ def test_create_email_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -847,7 +1331,27 @@ def test_create_email_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='email@example.com', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'email@example.com', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -866,7 +1370,7 @@ def test_update_email_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -875,8 +1379,26 @@ def test_update_email_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'email@example.com', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/email/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'email@example.com', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -896,7 +1418,7 @@ def test_create_enum_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -906,7 +1428,28 @@ def test_create_enum_column(self, m): '<TABLE_ID>', '<KEY>', [], - True, + False, + default='active', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/enum', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'elements': [], + 'required': False, + 'default': 'active', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -926,7 +1469,7 @@ def test_update_enum_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -936,8 +1479,27 @@ def test_update_enum_column(self, m): '<TABLE_ID>', '<KEY>', [], - True, + False, 'active', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/enum/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'elements': [], + 'required': False, + 'default': 'active', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -955,7 +1517,7 @@ def test_create_float_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -964,7 +1526,31 @@ def test_create_float_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + min=1.0, + max=1.0, + default=1.0, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/float', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -982,7 +1568,7 @@ def test_update_float_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -991,8 +1577,30 @@ def test_update_float_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 1.0, + min=1.0, + max=1.0, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/float/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1010,7 +1618,7 @@ def test_create_integer_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1019,7 +1627,31 @@ def test_create_integer_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/integer', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1037,7 +1669,7 @@ def test_update_integer_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1046,8 +1678,30 @@ def test_update_integer_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 1, + min=1, + max=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/integer/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1066,7 +1720,7 @@ def test_create_ip_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1075,7 +1729,27 @@ def test_create_ip_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='192.0.2.0', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/ip', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': '192.0.2.0', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1094,7 +1768,7 @@ def test_update_ip_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1103,8 +1777,26 @@ def test_update_ip_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, '192.0.2.0', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/ip/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '192.0.2.0', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1122,7 +1814,7 @@ def test_create_line_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1131,7 +1823,25 @@ def test_create_line_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/line', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1149,7 +1859,7 @@ def test_update_line_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1158,7 +1868,26 @@ def test_update_line_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/line/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1176,7 +1905,7 @@ def test_create_longtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1185,7 +1914,29 @@ def test_create_longtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/longtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1203,7 +1954,7 @@ def test_update_longtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1212,8 +1963,26 @@ def test_update_longtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/longtext/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1231,7 +2000,7 @@ def test_create_mediumtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1240,7 +2009,29 @@ def test_create_mediumtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/mediumtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1258,7 +2049,7 @@ def test_update_mediumtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1267,8 +2058,26 @@ def test_update_mediumtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/mediumtext/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1286,7 +2095,7 @@ def test_create_point_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1295,7 +2104,25 @@ def test_create_point_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/point', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1313,7 +2140,7 @@ def test_update_point_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1322,7 +2149,26 @@ def test_update_point_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/point/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1340,7 +2186,7 @@ def test_create_polygon_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1349,7 +2195,25 @@ def test_create_polygon_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/polygon', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1367,7 +2231,7 @@ def test_update_polygon_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1376,7 +2240,26 @@ def test_update_polygon_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/polygon/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1400,7 +2283,7 @@ def test_create_relationship_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1410,6 +2293,30 @@ def test_create_relationship_column(self, m): '<TABLE_ID>', '<RELATED_TABLE_ID>', 'oneToOne', + two_way=False, + key='<KEY>', + two_way_key='<TWO_WAY_KEY>', + on_delete='cascade', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/relationship', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'relatedTableId': '<RELATED_TABLE_ID>', + 'type': 'oneToOne', + 'twoWay': False, + 'key': '<KEY>', + 'twoWayKey': '<TWO_WAY_KEY>', + 'onDelete': 'cascade', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1433,7 +2340,7 @@ def test_update_relationship_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1442,6 +2349,24 @@ def test_update_relationship_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', + on_delete='cascade', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/relationship/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'onDelete': 'cascade', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1460,7 +2385,7 @@ def test_create_string_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1470,7 +2395,30 @@ def test_create_string_column(self, m): '<TABLE_ID>', '<KEY>', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/string', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1489,7 +2437,7 @@ def test_update_string_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1498,8 +2446,28 @@ def test_update_string_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + size=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/string/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1517,7 +2485,7 @@ def test_create_text_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1526,7 +2494,29 @@ def test_create_text_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/text', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1544,7 +2534,7 @@ def test_update_text_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1553,8 +2543,26 @@ def test_update_text_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/text/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1573,7 +2581,7 @@ def test_create_url_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1582,7 +2590,27 @@ def test_create_url_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='https://example.com', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'https://example.com', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1601,7 +2629,7 @@ def test_update_url_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1610,8 +2638,26 @@ def test_update_url_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'https://example.com', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/url/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'https://example.com', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1630,7 +2676,7 @@ def test_create_varchar_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1640,7 +2686,30 @@ def test_create_varchar_column(self, m): '<TABLE_ID>', '<KEY>', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/varchar', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1659,7 +2728,7 @@ def test_update_varchar_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1668,8 +2737,28 @@ def test_update_varchar_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + size=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/varchar/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1687,7 +2776,7 @@ def test_get_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1697,6 +2786,14 @@ def test_get_column(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1704,7 +2801,7 @@ def test_delete_column(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1714,6 +2811,14 @@ def test_delete_column(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1724,7 +2829,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1732,6 +2837,21 @@ def test_list_indexes(self, m): response = self.tables_db.list_indexes( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1751,7 +2871,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1762,6 +2882,27 @@ def test_create_index(self, m): '<KEY>', 'key', [], + orders=[], + lengths=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'type': 'key', + 'columns': [], + 'orders': [], + 'lengths': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1781,7 +2922,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1791,6 +2932,14 @@ def test_get_index(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1798,7 +2947,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1808,6 +2957,14 @@ def test_delete_index(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1818,7 +2975,7 @@ def test_list_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1826,6 +2983,25 @@ def test_list_rows(self, m): response = self.tables_db.list_rows( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1842,7 +3018,7 @@ def test_create_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1852,6 +3028,24 @@ def test_create_row(self, m): '<TABLE_ID>', '<ROW_ID>', {}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rowId': '<ROW_ID>', + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1864,7 +3058,7 @@ def test_create_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1873,6 +3067,21 @@ def test_create_rows(self, m): '<DATABASE_ID>', '<TABLE_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rows': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1884,7 +3093,7 @@ def test_upsert_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1893,6 +3102,21 @@ def test_upsert_rows(self, m): '<DATABASE_ID>', '<TABLE_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rows': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1904,7 +3128,7 @@ def test_update_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1912,6 +3136,24 @@ def test_update_rows(self, m): response = self.tables_db.update_rows( '<DATABASE_ID>', '<TABLE_ID>', + data={}, + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1923,7 +3165,7 @@ def test_delete_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1931,6 +3173,22 @@ def test_delete_rows(self, m): response = self.tables_db.delete_rows( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1942,7 +3200,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1950,6 +3208,26 @@ def test_create_query(self, m): response = self.tables_db.create_query( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1966,7 +3244,7 @@ def test_get_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1975,6 +3253,21 @@ def test_get_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1992,7 +3285,7 @@ def test_upsert_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2001,6 +3294,24 @@ def test_upsert_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -2018,7 +3329,7 @@ def test_update_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2027,6 +3338,24 @@ def test_update_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -2036,7 +3365,7 @@ def test_delete_row(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2045,6 +3374,20 @@ def test_delete_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response, data) @@ -2061,7 +3404,7 @@ def test_decrement_row_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2071,6 +3414,24 @@ def test_decrement_row_column(self, m): '<TABLE_ID>', '<ROW_ID>', '<COLUMN>', + value=1.0, + min=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>/<COLUMN>/decrement', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'min': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -2088,7 +3449,7 @@ def test_increment_row_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2098,6 +3459,24 @@ def test_increment_row_column(self, m): '<TABLE_ID>', '<ROW_ID>', '<COLUMN>', + value=1.0, + max=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>/<COLUMN>/increment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'max': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_teams.py b/test/services/test_teams.py index 24f7dbb5..20116807 100644 --- a/test/services/test_teams.py +++ b/test/services/test_teams.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.teams.list() + response = self.teams.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -42,7 +62,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -50,6 +70,22 @@ def test_create(self, m): response = self.teams.create( '<TEAM_ID>', '<NAME>', + roles=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '<TEAM_ID>', + 'name': '<NAME>', + 'roles': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -65,7 +101,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -73,6 +109,14 @@ def test_get(self, m): response = self.teams.get( '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -87,7 +131,7 @@ def test_update_name(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -96,6 +140,19 @@ def test_update_name(self, m): '<TEAM_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -103,7 +160,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -111,6 +168,14 @@ def test_delete(self, m): response = self.teams.delete( '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -121,13 +186,28 @@ def test_list_installations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.teams.list_installations( '<TEAM_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -146,7 +226,7 @@ def test_create_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -154,6 +234,21 @@ def test_create_installation(self, m): response = self.teams.create_installation( '<TEAM_ID>', '<APP_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '<APP_ID>', + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -172,7 +267,7 @@ def test_get_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -181,6 +276,14 @@ def test_get_installation(self, m): '<TEAM_ID>', '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations/<INSTALLATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -198,7 +301,7 @@ def test_update_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -206,6 +309,20 @@ def test_update_installation(self, m): response = self.teams.update_installation( '<TEAM_ID>', '<INSTALLATION_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -214,7 +331,7 @@ def test_delete_installation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -223,6 +340,14 @@ def test_delete_installation(self, m): '<TEAM_ID>', '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -233,13 +358,30 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.teams.list_memberships( '<TEAM_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -264,7 +406,7 @@ def test_create_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -272,6 +414,31 @@ def test_create_membership(self, m): response = self.teams.create_membership( '<TEAM_ID>', [], + email='email@example.com', + user_id='<USER_ID>', + phone='+12065550100', + url='https://example.com', + name='<NAME>', + membership_id='<MEMBERSHIP_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'userId': '<USER_ID>', + 'phone': '+12065550100', + 'roles': [], + 'url': 'https://example.com', + 'name': '<NAME>', + 'membershipId': '<MEMBERSHIP_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -296,7 +463,7 @@ def test_get_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -305,6 +472,14 @@ def test_get_membership(self, m): '<TEAM_ID>', '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -328,7 +503,7 @@ def test_update_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -338,6 +513,19 @@ def test_update_membership(self, m): '<MEMBERSHIP_ID>', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'roles': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -345,7 +533,7 @@ def test_delete_membership(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -354,6 +542,14 @@ def test_delete_membership(self, m): '<TEAM_ID>', '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -377,7 +573,7 @@ def test_update_membership_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -388,6 +584,20 @@ def test_update_membership_status(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -395,7 +605,7 @@ def test_get_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -403,6 +613,14 @@ def test_get_prefs(self, m): response = self.teams.get_prefs( '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/prefs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -410,7 +628,7 @@ def test_update_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -419,4 +637,17 @@ def test_update_prefs(self, m): '<TEAM_ID>', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/prefs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'prefs': {}, + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_tokens.py b/test/services/test_tokens.py index de5f3f87..3883d77c 100644 --- a/test/services/test_tokens.py +++ b/test/services/test_tokens.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,7 +24,7 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -30,6 +32,21 @@ def test_list(self, m): response = self.tokens.list( '<BUCKET_ID>', '<FILE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -46,7 +63,7 @@ def test_create_file_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -54,6 +71,20 @@ def test_create_file_token(self, m): response = self.tokens.create_file_token( '<BUCKET_ID>', '<FILE_ID>', + expire='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'expire': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -70,7 +101,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -78,6 +109,14 @@ def test_get(self, m): response = self.tokens.get( '<TOKEN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/<TOKEN_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -93,13 +132,27 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tokens.update( '<TOKEN_ID>', + expire='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/<TOKEN_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'expire': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -108,7 +161,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -116,4 +169,12 @@ def test_delete(self, m): response = self.tokens.delete( '<TOKEN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/<TOKEN_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_users.py b/test/services/test_users.py index 4dad6e4c..323b7ed4 100644 --- a/test/services/test_users.py +++ b/test/services/test_users.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.users.list() + response = self.users.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -52,13 +72,34 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.create( '<USER_ID>', + email='email@example.com', + phone='+12065550100', + password='password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'phone': '+12065550100', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -84,7 +125,7 @@ def test_create_argon2_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -93,6 +134,23 @@ def test_create_argon2_user(self, m): '<USER_ID>', 'email@example.com', 'password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/argon2', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -118,7 +176,7 @@ def test_create_bcrypt_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -127,6 +185,23 @@ def test_create_bcrypt_user(self, m): '<USER_ID>', 'email@example.com', 'password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/bcrypt', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -138,12 +213,30 @@ def test_list_identities(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.users.list_identities() + response = self.users.list_identities( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/identities', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -151,7 +244,7 @@ def test_delete_identity(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -159,6 +252,14 @@ def test_delete_identity(self, m): response = self.users.delete_identity( '<IDENTITY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/identities/<IDENTITY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -183,7 +284,7 @@ def test_create_md5_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -192,6 +293,23 @@ def test_create_md5_user(self, m): '<USER_ID>', 'email@example.com', 'password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/md5', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -217,7 +335,7 @@ def test_create_ph_pass_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -226,6 +344,23 @@ def test_create_ph_pass_user(self, m): '<USER_ID>', 'email@example.com', 'password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/phpass', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -251,7 +386,7 @@ def test_create_scrypt_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -265,6 +400,28 @@ def test_create_scrypt_user(self, m): 1, 1, 1, + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/scrypt', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'passwordSalt': '<PASSWORD_SALT>', + 'passwordCpu': 1, + 'passwordMemory': 1, + 'passwordParallel': 1, + 'passwordLength': 1, + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -290,7 +447,7 @@ def test_create_scrypt_modified_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -302,6 +459,26 @@ def test_create_scrypt_modified_user(self, m): '<PASSWORD_SALT>', '<PASSWORD_SALT_SEPARATOR>', '<PASSWORD_SIGNER_KEY>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/scrypt-modified', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'passwordSalt': '<PASSWORD_SALT>', + 'passwordSaltSeparator': '<PASSWORD_SALT_SEPARATOR>', + 'passwordSignerKey': '<PASSWORD_SIGNER_KEY>', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -327,7 +504,7 @@ def test_create_sha_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +513,25 @@ def test_create_sha_user(self, m): '<USER_ID>', 'email@example.com', 'password', + password_version='sha1', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/sha', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'passwordVersion': 'sha1', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -361,7 +557,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -369,6 +565,14 @@ def test_get(self, m): response = self.users.get( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -376,7 +580,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -384,6 +588,14 @@ def test_delete(self, m): response = self.users.delete( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -408,7 +620,7 @@ def test_update_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -417,6 +629,19 @@ def test_update_email(self, m): '<USER_ID>', 'email@example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -441,14 +666,27 @@ def test_update_impersonator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_impersonator( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/impersonator', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'impersonator': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -459,13 +697,29 @@ def test_create_jwt(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.create_jwt( '<USER_ID>', + session_id='recent()', + duration=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/jwts', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'sessionId': 'recent()', + 'duration': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -491,7 +745,7 @@ def test_update_labels(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -500,6 +754,19 @@ def test_update_labels(self, m): '<USER_ID>', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/labels', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'labels': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -510,13 +777,30 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_memberships( '<USER_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -542,14 +826,27 @@ def test_update_mfa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_mfa( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mfa': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -575,14 +872,27 @@ def test_update_mfa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_mfa( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mfa': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -591,7 +901,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -600,6 +910,14 @@ def test_delete_mfa_authenticator(self, m): '<USER_ID>', 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -607,7 +925,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -616,6 +934,14 @@ def test_delete_mfa_authenticator(self, m): '<USER_ID>', 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -629,7 +955,7 @@ def test_get_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -638,6 +964,14 @@ def test_get_mfa_challenge(self, m): '<USER_ID>', '<CHALLENGE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/challenges/<CHALLENGE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -651,7 +985,7 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -659,6 +993,14 @@ def test_list_mfa_factors(self, m): response = self.users.list_mfa_factors( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -672,7 +1014,7 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -680,6 +1022,14 @@ def test_list_mfa_factors(self, m): response = self.users.list_mfa_factors( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -689,7 +1039,7 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -697,6 +1047,14 @@ def test_get_mfa_recovery_codes(self, m): response = self.users.get_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -706,7 +1064,7 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -714,6 +1072,14 @@ def test_get_mfa_recovery_codes(self, m): response = self.users.get_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -723,7 +1089,7 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -731,6 +1097,14 @@ def test_update_mfa_recovery_codes(self, m): response = self.users.update_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -740,7 +1114,7 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -748,6 +1122,14 @@ def test_update_mfa_recovery_codes(self, m): response = self.users.update_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -757,7 +1139,7 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -765,6 +1147,14 @@ def test_create_mfa_recovery_codes(self, m): response = self.users.create_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -774,7 +1164,7 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -782,6 +1172,14 @@ def test_create_mfa_recovery_codes(self, m): response = self.users.create_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -806,7 +1204,7 @@ def test_update_name(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -815,6 +1213,19 @@ def test_update_name(self, m): '<USER_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/name', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -825,13 +1236,28 @@ def test_list_passkeys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_passkeys( '<USER_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -846,7 +1272,7 @@ def test_get_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -855,6 +1281,14 @@ def test_get_passkey(self, m): '<USER_ID>', '<PASSKEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys/<PASSKEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -868,7 +1302,7 @@ def test_update_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -878,6 +1312,19 @@ def test_update_passkey(self, m): '<PASSKEY_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys/<PASSKEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -885,7 +1332,7 @@ def test_delete_passkey(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -894,6 +1341,14 @@ def test_delete_passkey(self, m): '<USER_ID>', '<PASSKEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys/<PASSKEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -918,7 +1373,7 @@ def test_update_password(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -927,6 +1382,19 @@ def test_update_password(self, m): '<USER_ID>', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/password', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -951,7 +1419,7 @@ def test_update_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -960,6 +1428,19 @@ def test_update_phone(self, m): '<USER_ID>', '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'number': '+12065550100', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -967,7 +1448,7 @@ def test_get_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -975,6 +1456,14 @@ def test_get_prefs(self, m): response = self.users.get_prefs( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/prefs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -982,7 +1471,7 @@ def test_update_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -991,6 +1480,19 @@ def test_update_prefs(self, m): '<USER_ID>', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/prefs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'prefs': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1001,13 +1503,27 @@ def test_list_sessions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_sessions( '<USER_ID>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1046,7 +1562,7 @@ def test_create_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1054,6 +1570,14 @@ def test_create_session(self, m): response = self.users.create_session( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1061,7 +1585,7 @@ def test_delete_sessions(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1069,6 +1593,14 @@ def test_delete_sessions(self, m): response = self.users.delete_sessions( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1076,7 +1608,7 @@ def test_delete_session(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1085,6 +1617,14 @@ def test_delete_session(self, m): '<USER_ID>', '<SESSION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions/<SESSION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1109,14 +1649,27 @@ def test_update_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_status( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'status': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1128,13 +1681,28 @@ def test_list_targets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_targets( '<USER_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1152,7 +1720,7 @@ def test_create_target(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1162,6 +1730,25 @@ def test_create_target(self, m): '<TARGET_ID>', 'email', '<IDENTIFIER>', + provider_id='<PROVIDER_ID>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetId': '<TARGET_ID>', + 'providerType': 'email', + 'identifier': '<IDENTIFIER>', + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1179,7 +1766,7 @@ def test_get_target(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1188,6 +1775,14 @@ def test_get_target(self, m): '<USER_ID>', '<TARGET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets/<TARGET_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1204,7 +1799,7 @@ def test_update_target(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1212,6 +1807,24 @@ def test_update_target(self, m): response = self.users.update_target( '<USER_ID>', '<TARGET_ID>', + identifier='<IDENTIFIER>', + provider_id='<PROVIDER_ID>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets/<TARGET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'identifier': '<IDENTIFIER>', + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1220,7 +1833,7 @@ def test_delete_target(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1229,6 +1842,14 @@ def test_delete_target(self, m): '<USER_ID>', '<TARGET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets/<TARGET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1243,13 +1864,29 @@ def test_create_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.create_token( '<USER_ID>', + length=1, + expire=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/tokens', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'length': 1, + 'expire': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1275,14 +1912,27 @@ def test_update_email_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_email_verification( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/verification', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'emailVerification': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1308,13 +1958,26 @@ def test_update_phone_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_phone_verification( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/verification/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'phoneVerification': False, + }, ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_vectors_db.py b/test/services/test_vectors_db.py index 2c6f2240..42b73f4b 100644 --- a/test/services/test_vectors_db.py +++ b/test/services/test_vectors_db.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.vectors_db.list() + response = self.vectors_db.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +61,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +69,30 @@ def test_create(self, m): response = self.vectors_db.create( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +104,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -78,12 +128,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.vectors_db.list_transactions() + response = self.vectors_db.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +164,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.vectors_db.create_transaction() + response = self.vectors_db.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -118,7 +199,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -126,6 +207,14 @@ def test_get_transaction(self, m): response = self.vectors_db.get_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -140,13 +229,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.update_transaction( '<TRANSACTION_ID>', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -155,7 +260,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -163,6 +268,14 @@ def test_delete_transaction(self, m): response = self.vectors_db.delete_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -177,13 +290,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.create_operations( '<TRANSACTION_ID>', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>/operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -200,7 +327,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -208,6 +335,14 @@ def test_get(self, m): response = self.vectors_db.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -223,7 +358,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -231,6 +366,29 @@ def test_update(self, m): response = self.vectors_db.update( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -239,7 +397,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -247,6 +405,14 @@ def test_delete(self, m): response = self.vectors_db.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -257,13 +423,30 @@ def test_list_collections(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.list_collections( '<DATABASE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -287,7 +470,7 @@ def test_create_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -297,6 +480,29 @@ def test_create_collection(self, m): '<COLLECTION_ID>', '<NAME>', 1, + permissions=[], + document_security=False, + enabled=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'collectionId': '<COLLECTION_ID>', + 'name': '<NAME>', + 'dimension': 1, + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -320,7 +526,7 @@ def test_get_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -329,6 +535,14 @@ def test_get_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -351,7 +565,7 @@ def test_update_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -360,6 +574,29 @@ def test_update_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<NAME>', + dimension=1, + permissions=[], + document_security=False, + enabled=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'dimension': 1, + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -368,7 +605,7 @@ def test_delete_collection(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -377,6 +614,14 @@ def test_delete_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -387,7 +632,7 @@ def test_list_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -395,6 +640,25 @@ def test_list_documents(self, m): response = self.vectors_db.list_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -411,7 +675,7 @@ def test_create_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -421,6 +685,24 @@ def test_create_document(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', {}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documentId': '<DOCUMENT_ID>', + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -433,7 +715,7 @@ def test_create_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -442,6 +724,21 @@ def test_create_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -453,7 +750,7 @@ def test_upsert_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -462,6 +759,21 @@ def test_upsert_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -473,7 +785,7 @@ def test_update_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -481,6 +793,24 @@ def test_update_documents(self, m): response = self.vectors_db.update_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + data={}, + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -492,7 +822,7 @@ def test_delete_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -500,6 +830,22 @@ def test_delete_documents(self, m): response = self.vectors_db.delete_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -511,7 +857,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -519,6 +865,26 @@ def test_create_query(self, m): response = self.vectors_db.create_query( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -535,7 +901,7 @@ def test_get_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -544,6 +910,21 @@ def test_get_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -561,7 +942,7 @@ def test_upsert_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -570,6 +951,24 @@ def test_upsert_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -587,7 +986,7 @@ def test_update_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -596,6 +995,24 @@ def test_update_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -605,7 +1022,7 @@ def test_delete_document(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -614,6 +1031,20 @@ def test_delete_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response, data) @@ -625,7 +1056,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -633,6 +1064,21 @@ def test_list_indexes(self, m): response = self.vectors_db.list_indexes( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -652,7 +1098,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -663,6 +1109,27 @@ def test_create_index(self, m): '<KEY>', 'hnsw_euclidean', [], + orders=[], + lengths=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'type': 'hnsw_euclidean', + 'attributes': [], + 'orders': [], + 'lengths': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -682,7 +1149,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -692,6 +1159,14 @@ def test_get_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -699,7 +1174,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -709,6 +1184,14 @@ def test_delete_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -762,13 +1245,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -780,13 +1277,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -802,7 +1317,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -810,6 +1325,14 @@ def test_get_replicas(self, m): response = self.vectors_db.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -829,7 +1352,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -837,4 +1360,12 @@ def test_get_status(self, m): response = self.vectors_db.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_waf.py b/test/services/test_waf.py index 8c9e8bec..db44f9f5 100644 --- a/test/services/test_waf.py +++ b/test/services/test_waf.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list_rules(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.waf.list_rules() + response = self.waf.list_rules( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -50,7 +70,7 @@ def test_create_bypass_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -59,6 +79,31 @@ def test_create_bypass_rule(self, m): '<RULE_ID>', 'api', '<NAME>', + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/bypass', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -82,13 +127,39 @@ def test_update_bypass_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_bypass_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/bypass/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -115,7 +186,7 @@ def test_create_challenge_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -124,6 +195,37 @@ def test_create_challenge_rule(self, m): '<RULE_ID>', 'api', '<NAME>', + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + challenge_type='compute', + priority=1, + enabled=False, + conditions=[], + difficulty=1, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/challenge', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'challengeType': 'compute', + 'priority': 1, + 'enabled': False, + 'conditions': [], + 'difficulty': 1, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -150,13 +252,45 @@ def test_update_challenge_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_challenge_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + challenge_type='compute', + priority=1, + enabled=False, + conditions=[], + difficulty=1, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/challenge/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'challengeType': 'compute', + 'priority': 1, + 'enabled': False, + 'conditions': [], + 'difficulty': 1, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -180,7 +314,7 @@ def test_create_deny_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -189,6 +323,31 @@ def test_create_deny_rule(self, m): '<RULE_ID>', 'api', '<NAME>', + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/deny', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -212,13 +371,39 @@ def test_update_deny_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_deny_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/deny/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -247,7 +432,7 @@ def test_create_rate_limit_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -258,6 +443,39 @@ def test_create_rate_limit_rule(self, m): '<NAME>', 1, 1, + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + key='ip', + strategy='fixedWindow', + max_bucket_size=1, + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/rate-limit', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'limit': 1, + 'interval': 1, + 'key': 'ip', + 'strategy': 'fixedWindow', + 'maxBucketSize': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -286,13 +504,47 @@ def test_update_rate_limit_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_rate_limit_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + limit=1, + interval=1, + key='ip', + max_bucket_size=1, + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/rate-limit/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'limit': 1, + 'interval': 1, + 'key': 'ip', + 'maxBucketSize': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -318,7 +570,7 @@ def test_create_redirect_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -329,6 +581,33 @@ def test_create_redirect_rule(self, m): '<NAME>', '<LOCATION>', 1, + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/redirect', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'location': '<LOCATION>', + 'statusCode': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -354,13 +633,43 @@ def test_update_redirect_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_redirect_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + location='<LOCATION>', + status_code=1, + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/redirect/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'location': '<LOCATION>', + 'statusCode': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -384,7 +693,7 @@ def test_get_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -392,6 +701,14 @@ def test_get_rule(self, m): response = self.waf.get_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/<RULE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -399,7 +716,7 @@ def test_delete_rule(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,4 +724,12 @@ def test_delete_rule(self, m): response = self.waf.delete_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_webhooks.py b/test/services/test_webhooks.py index 0f65c6b2..50e6ee3d 100644 --- a/test/services/test_webhooks.py +++ b/test/services/test_webhooks.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.webhooks.list() + response = self.webhooks.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -50,7 +68,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -60,6 +78,34 @@ def test_create(self, m): 'https://example.com/webhook', '<NAME>', [], + enabled=False, + tls=False, + auth_username='<AUTH_USERNAME>', + auth_password='password', + secret='<SECRET>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'webhookId': '<WEBHOOK_ID>', + 'url': 'https://example.com/webhook', + 'name': '<NAME>', + 'events': [], + 'enabled': False, + 'tls': False, + 'authUsername': '<AUTH_USERNAME>', + 'authPassword': 'password', + 'secret': '<SECRET>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -83,7 +129,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -91,6 +137,14 @@ def test_get(self, m): response = self.webhooks.get( '<WEBHOOK_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -113,7 +167,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -123,6 +177,31 @@ def test_update(self, m): '<NAME>', 'https://example.com/webhook', [], + enabled=False, + tls=False, + auth_username='<AUTH_USERNAME>', + auth_password='password', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'url': 'https://example.com/webhook', + 'events': [], + 'enabled': False, + 'tls': False, + 'authUsername': '<AUTH_USERNAME>', + 'authPassword': 'password', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -131,7 +210,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -139,6 +218,14 @@ def test_delete(self, m): response = self.webhooks.delete( '<WEBHOOK_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -161,12 +248,26 @@ def test_update_secret(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.webhooks.update_secret( '<WEBHOOK_ID>', + secret='<SECRET>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>/secret', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'secret': '<SECRET>', + }, ) self.assertEqual(response.to_dict(), data) From 773765bb96c0a07207dc381f5b5a6dbc4d2494b6 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal <chiragaggarwal5k@gmail.com> Date: Tue, 27 Oct 2026 19:21:23 +0000 Subject: [PATCH 4/4] chore: update Python SDK to 27.1.0 --- CHANGELOG.md | 13 + README.md | 14 +- appwrite/client.py | 22 +- appwrite/encoders/value_class_encoder.py | 24 + appwrite/enums/appwrite_migration_resource.py | 44 + appwrite/enums/build_runtime.py | 4 + appwrite/enums/environment_resource.py | 38 + appwrite/enums/firebase_migration_resource.py | 14 + appwrite/enums/n_host_migration_resource.py | 15 + appwrite/enums/on_duplicate.py | 7 + appwrite/enums/project_policy_id.py | 1 + appwrite/enums/runtime.py | 4 + appwrite/enums/supabase_migration_resource.py | 15 + appwrite/models/__init__.py | 6 + appwrite/models/billing_plan.py | 9 + appwrite/models/dns_record.py | 2 +- appwrite/models/migration.py | 128 + appwrite/models/migration_list.py | 34 + .../models/policy_deny_realtime_guests.py | 33 + appwrite/models/policy_list.py | 4 +- appwrite/models/provider.py | 2 +- appwrite/service.py | 9 + appwrite/services/account.py | 38 +- appwrite/services/activities.py | 2 +- appwrite/services/advisor.py | 10 +- appwrite/services/apps.py | 50 +- appwrite/services/avatars.py | 6 +- appwrite/services/backups.py | 12 +- appwrite/services/databases.py | 314 +-- appwrite/services/documents_db.py | 126 +- appwrite/services/domains.py | 152 +- appwrite/services/functions.py | 66 +- appwrite/services/messaging.py | 60 +- appwrite/services/migrations.py | 752 +++++ appwrite/services/mongo.py | 74 +- appwrite/services/mysql.py | 80 +- appwrite/services/oauth2.py | 28 +- appwrite/services/organization.py | 34 +- appwrite/services/postgresql.py | 88 +- appwrite/services/presences.py | 8 +- appwrite/services/project.py | 140 +- appwrite/services/proxy.py | 6 +- appwrite/services/sites.py | 64 +- appwrite/services/storage.py | 34 +- appwrite/services/tables_db.py | 338 +-- appwrite/services/teams.py | 46 +- appwrite/services/tokens.py | 14 +- appwrite/services/users.py | 100 +- appwrite/services/vectors_db.py | 110 +- appwrite/services/waf.py | 34 +- appwrite/services/webhooks.py | 8 +- docs/examples/backups/update-policy.md | 2 +- .../migrations/create-appwrite-migration.md | 24 + docs/examples/migrations/create-csv-export.md | 27 + docs/examples/migrations/create-csv-import.md | 24 + .../migrations/create-firebase-migration.md | 20 + .../examples/migrations/create-json-export.md | 23 + .../examples/migrations/create-json-import.md | 24 + .../migrations/create-n-host-migration.md | 26 + .../migrations/create-supabase-migration.md | 25 + docs/examples/migrations/delete.md | 15 + docs/examples/migrations/get.md | 18 + docs/examples/migrations/list.md | 20 + docs/examples/migrations/retry.md | 18 + docs/examples/mongo/update-backup-policy.md | 2 +- docs/examples/mongo/update-backup-storage.md | 2 +- docs/examples/mysql/update-backup-policy.md | 2 +- docs/examples/mysql/update-backup-storage.md | 2 +- docs/examples/oauth2/authorize-post.md | 2 +- docs/examples/oauth2/authorize.md | 2 +- .../postgresql/update-backup-policy.md | 2 +- .../postgresql/update-backup-storage.md | 2 +- docs/examples/project/create-environment.md | 4 +- docs/examples/project/delete-sessions.md | 13 + docs/examples/project/get-policy.md | 3 +- .../update-deny-realtime-guests-policy.md | 18 + docs/examples/storage/update-file.md | 2 +- docs/examples/users/create-argon-2-user.md | 2 +- docs/examples/users/create-bcrypt-user.md | 2 +- docs/examples/users/create-ph-pass-user.md | 2 +- docs/examples/users/create-scrypt-user.md | 2 +- pyproject.toml | 2 +- setup.py | 4 +- test/services/test_account.py | 1036 ++++++- test/services/test_activities.py | 32 +- test/services/test_advisor.py | 69 +- test/services/test_apps.py | 343 ++- test/services/test_avatars.py | 257 +- test/services/test_backups.py | 195 +- test/services/test_databases.py | 1484 +++++++++- test/services/test_documents_db.py | 652 ++++- test/services/test_domains.py | 719 ++++- test/services/test_embeddings.py | 19 +- test/services/test_functions.py | 513 +++- test/services/test_graphql.py | 32 +- test/services/test_locale.py | 145 +- test/services/test_messaging.py | 1414 +++++++++- test/services/test_migrations.py | 648 +++++ test/services/test_mongo.py | 536 +++- test/services/test_mysql.py | 595 +++- test/services/test_oauth2.py | 335 ++- test/services/test_organization.py | 340 ++- test/services/test_postgresql.py | 630 ++++- test/services/test_presences.py | 91 +- test/services/test_project.py | 2426 +++++++++++++++-- test/services/test_proxy.py | 158 +- test/services/test_sites.py | 486 +++- test/services/test_storage.py | 271 +- test/services/test_tables_db.py | 1619 ++++++++++- test/services/test_teams.py | 269 +- test/services/test_tokens.py | 71 +- test/services/test_users.py | 791 +++++- test/services/test_vectors_db.py | 609 ++++- test/services/test_waf.py | 353 ++- test/services/test_webhooks.py | 115 +- 115 files changed, 18701 insertions(+), 2124 deletions(-) create mode 100644 appwrite/enums/appwrite_migration_resource.py create mode 100644 appwrite/enums/environment_resource.py create mode 100644 appwrite/enums/firebase_migration_resource.py create mode 100644 appwrite/enums/n_host_migration_resource.py create mode 100644 appwrite/enums/on_duplicate.py create mode 100644 appwrite/enums/supabase_migration_resource.py create mode 100644 appwrite/models/migration.py create mode 100644 appwrite/models/migration_list.py create mode 100644 appwrite/models/policy_deny_realtime_guests.py create mode 100644 appwrite/services/migrations.py create mode 100644 docs/examples/migrations/create-appwrite-migration.md create mode 100644 docs/examples/migrations/create-csv-export.md create mode 100644 docs/examples/migrations/create-csv-import.md create mode 100644 docs/examples/migrations/create-firebase-migration.md create mode 100644 docs/examples/migrations/create-json-export.md create mode 100644 docs/examples/migrations/create-json-import.md create mode 100644 docs/examples/migrations/create-n-host-migration.md create mode 100644 docs/examples/migrations/create-supabase-migration.md create mode 100644 docs/examples/migrations/delete.md create mode 100644 docs/examples/migrations/get.md create mode 100644 docs/examples/migrations/list.md create mode 100644 docs/examples/migrations/retry.md create mode 100644 docs/examples/project/delete-sessions.md create mode 100644 docs/examples/project/update-deny-realtime-guests-policy.md create mode 100644 test/services/test_migrations.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e69013cf..64f780c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## 27.1.0 + +* Added: `migrations` service to run Appwrite, Firebase, NHost, Supabase, CSV and JSON migrations, plus retries and listing +* Added: `Migration`, `MigrationList` and `MigrationReport` models and the `AppwriteMigrationResource`, `FirebaseMigrationResource`, `NHostMigrationResource`, `SupabaseMigrationResource` and `OnDuplicate` enums +* Added: `project.update_deny_realtime_guests_policy`, the `PolicyDenyRealtimeGuests` model and `ProjectPolicyId.DENY_REALTIME_GUESTS` +* Added: `project.delete_sessions` to sign out every user in the project +* Added: `BillingPlan.deploymentretention`, `deploymentretentionmax` and `deploymentsretentioncount` +* Added: `python-3.15`, `deno-2.9`, `go-1.27` and `rust-1.99` to `Runtime` and `BuildRuntime` +* Updated: requests send `X-Appwrite-Response-Format: 2.6.2`; README targets Appwrite 2.6.x +* Updated: redirects are no longer followed; a 3xx raises `AppwriteException` naming the `Location` +* Fixed: path parameters are percent-encoded as one segment; `.` and `..` raise `AppwriteException` +* Fixed: the chunked-upload resume lookup encodes the file ID + ## 27.0.0 This release updates the HTTP dependency requirements and raises the minimum supported Python version. diff --git a/README.md b/README.md index b23533eb..91ab58a5 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Appwrite Python SDK ![License](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-2.5.0-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-2.6.2-blue.svg?style=flat-square) [![X Account](https://img.shields.io/badge/follow-%40appwrite-000000?logo=x&style=flat-square)](https://x.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -**This SDK targets Appwrite server version 2.5.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-python/releases) when APIs differ. +**This SDK targets Appwrite server version 2.6.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-python/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 Python 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) @@ -27,6 +27,16 @@ uv add appwrite ## Getting started +### Requirements and installation + +Use Python 3.10 or later. Create and activate a virtual environment with that interpreter, then install the SDK: + +```sh +python -m pip install --upgrade appwrite +``` + +The SDK requires `requests>=2.34.2,<3` and `urllib3>=2.8.0,<3`. Update application pins or constraints that exclude these versions. + ### Initialize the client Create a client with your Appwrite endpoint, project ID and an [API key](https://appwrite.io/docs/partners/project/api-keys). Find the endpoint and project ID in your project's **Settings** page, and create an API key with only the scopes you need under **API keys** in the project sidebar. diff --git a/appwrite/client.py b/appwrite/client.py index 47f8757d..e187ea9b 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -6,6 +6,7 @@ import requests from concurrent.futures import ThreadPoolExecutor, as_completed from threading import Lock +from urllib.parse import quote from .input_file import InputFile from .exception import AppwriteException from .encoders.value_class_encoder import ValueClassEncoder @@ -19,12 +20,12 @@ def __init__(self): self._endpoint = 'https://cloud.appwrite.io/v1' self._global_headers = { 'content-type': '', - 'user-agent': f'AppwritePythonSDK/27.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', + 'user-agent': f'AppwritePythonSDK/27.1.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', 'x-sdk-name': 'Python', 'x-sdk-platform': 'server', 'x-sdk-language': 'python', - 'x-sdk-version': '27.0.0', - 'X-Appwrite-Response-Format': '2.5.0', + 'x-sdk-version': '27.1.0', + 'X-Appwrite-Response-Format': '2.6.2', } self._config = {} @@ -180,11 +181,18 @@ def call(self, method, path='', headers=None, params=None, response_type='json') files=files, headers=headers, verify=(not self._self_signed), - allow_redirects=False if response_type == 'location' else True, + # A redirect would carry the API key to wherever it points, possibly another host. + allow_redirects=False, ) response.raise_for_status() + if response_type != 'location' and 300 <= response.status_code < 400: + location = response.headers.get('Location', '') + raise AppwriteException( + f'Redirect to {location} not followed', response.status_code, None, response.text + ) + warnings = response.headers.get('x-appwrite-warning') if warnings: for warning in warnings.split(';'): @@ -202,6 +210,8 @@ def call(self, method, path='', headers=None, params=None, response_type='json') return response.json() return response._content + except AppwriteException: + raise except Exception as e: if response != None: content_type = response.headers['Content-Type'] @@ -249,8 +259,10 @@ def chunked_upload( counter = 0 if upload_id: + if upload_id in ('.', '..'): + raise AppwriteException('Path parameter cannot be "." or ".."') try: - result = self.call('get', path + '/' + upload_id, headers) + result = self.call('get', path + '/' + quote(upload_id, safe="!*'()"), headers) counter = result['chunksUploaded'] except: pass diff --git a/appwrite/encoders/value_class_encoder.py b/appwrite/encoders/value_class_encoder.py index f157b094..8f3adfef 100644 --- a/appwrite/encoders/value_class_encoder.py +++ b/appwrite/encoders/value_class_encoder.py @@ -27,8 +27,14 @@ from ..enums.execution_method import ExecutionMethod from ..enums.message_priority import MessagePriority from ..enums.smtp_encryption import SmtpEncryption +from ..enums.appwrite_migration_resource import AppwriteMigrationResource +from ..enums.on_duplicate import OnDuplicate +from ..enums.firebase_migration_resource import FirebaseMigrationResource +from ..enums.n_host_migration_resource import NHostMigrationResource +from ..enums.supabase_migration_resource import SupabaseMigrationResource from ..enums.region import Region from ..enums.project_auth_method_id import ProjectAuthMethodId +from ..enums.environment_resource import EnvironmentResource from ..enums.project_o_auth2_auth0_prompt import ProjectOAuth2Auth0Prompt from ..enums.project_o_auth2_discord_prompt import ProjectOAuth2DiscordPrompt from ..enums.project_o_auth2_git_hub_prompt import ProjectOAuth2GitHubPrompt @@ -173,12 +179,30 @@ def default(self, o): if isinstance(o, SmtpEncryption): return o.value + if isinstance(o, AppwriteMigrationResource): + return o.value + + if isinstance(o, OnDuplicate): + return o.value + + if isinstance(o, FirebaseMigrationResource): + return o.value + + if isinstance(o, NHostMigrationResource): + return o.value + + if isinstance(o, SupabaseMigrationResource): + return o.value + if isinstance(o, Region): return o.value if isinstance(o, ProjectAuthMethodId): return o.value + if isinstance(o, EnvironmentResource): + return o.value + if isinstance(o, ProjectOAuth2Auth0Prompt): return o.value diff --git a/appwrite/enums/appwrite_migration_resource.py b/appwrite/enums/appwrite_migration_resource.py new file mode 100644 index 00000000..c9cecc84 --- /dev/null +++ b/appwrite/enums/appwrite_migration_resource.py @@ -0,0 +1,44 @@ +from enum import Enum + + +class AppwriteMigrationResource(Enum): + USER = "user" + TEAM = "team" + MEMBERSHIP = "membership" + AUTH_METHODS = "auth-methods" + POLICIES = "policies" + OAUTH2_PROVIDER = "oauth2-provider" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + INDEX = "index" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + DOCUMENTSDB = "documentsdb" + VECTORSDB = "vectorsdb" + BUCKET = "bucket" + FILE = "file" + FUNCTION = "function" + DEPLOYMENT = "deployment" + ENVIRONMENT_VARIABLE = "environment-variable" + PROVIDER = "provider" + TOPIC = "topic" + SUBSCRIBER = "subscriber" + MESSAGE = "message" + SITE = "site" + SITE_DEPLOYMENT = "site-deployment" + SITE_VARIABLE = "site-variable" + PLATFORM = "platform" + API_KEY = "api-key" + WEBHOOK = "webhook" + SMTP = "smtp" + BACKUP_POLICY = "backup-policy" + PROJECT_VARIABLE = "project-variable" + PROJECT_PROTOCOLS = "project-protocols" + PROJECT_LABELS = "project-labels" + PROJECT_SERVICES = "project-services" + PROJECT_EMAIL_TEMPLATE = "project-email-template" + PROJECT_ENVIRONMENT = "project-environment" + RULE = "rule" diff --git a/appwrite/enums/build_runtime.py b/appwrite/enums/build_runtime.py index 4bf67543..8a5cf305 100644 --- a/appwrite/enums/build_runtime.py +++ b/appwrite/enums/build_runtime.py @@ -31,6 +31,7 @@ class BuildRuntime(Enum): PYTHON_3_12 = "python-3.12" PYTHON_3_13 = "python-3.13" PYTHON_3_14 = "python-3.14" + PYTHON_3_15 = "python-3.15" PYTHON_ML_3_11 = "python-ml-3.11" PYTHON_ML_3_12 = "python-ml-3.12" PYTHON_ML_3_13 = "python-ml-3.13" @@ -39,6 +40,7 @@ class BuildRuntime(Enum): DENO_2_0 = "deno-2.0" DENO_2_5 = "deno-2.5" DENO_2_6 = "deno-2.6" + DENO_2_9 = "deno-2.9" DART_2_15 = "dart-2.15" DART_2_16 = "dart-2.16" DART_2_17 = "dart-2.17" @@ -86,7 +88,9 @@ class BuildRuntime(Enum): GO_1_24 = "go-1.24" GO_1_25 = "go-1.25" GO_1_26 = "go-1.26" + GO_1_27 = "go-1.27" RUST_1_83 = "rust-1.83" + RUST_1_99 = "rust-1.99" STATIC_1 = "static-1" FLUTTER_3_24 = "flutter-3.24" FLUTTER_3_27 = "flutter-3.27" diff --git a/appwrite/enums/environment_resource.py b/appwrite/enums/environment_resource.py new file mode 100644 index 00000000..8a1af03c --- /dev/null +++ b/appwrite/enums/environment_resource.py @@ -0,0 +1,38 @@ +from enum import Enum + + +class EnvironmentResource(Enum): + USER = "user" + TEAM = "team" + MEMBERSHIP = "membership" + AUTH_METHODS = "auth-methods" + POLICIES = "policies" + OAUTH2_PROVIDER = "oauth2-provider" + DATABASE = "database" + DOCUMENTSDB = "documentsdb" + VECTORSDB = "vectorsdb" + TABLE = "table" + COLLECTION = "collection" + COLUMN = "column" + ATTRIBUTE = "attribute" + INDEX = "index" + ROW = "row" + DOCUMENT = "document" + BUCKET = "bucket" + FILE = "file" + FUNCTION = "function" + ENVIRONMENT_VARIABLE = "environment-variable" + DEPLOYMENT = "deployment" + SITE = "site" + SITE_VARIABLE = "site-variable" + SITE_DEPLOYMENT = "site-deployment" + PROVIDER = "provider" + TOPIC = "topic" + SUBSCRIBER = "subscriber" + PLATFORM = "platform" + SMTP = "smtp" + PROJECT_VARIABLE = "project-variable" + PROJECT_PROTOCOLS = "project-protocols" + PROJECT_LABELS = "project-labels" + PROJECT_SERVICES = "project-services" + PROJECT_EMAIL_TEMPLATE = "project-email-template" diff --git a/appwrite/enums/firebase_migration_resource.py b/appwrite/enums/firebase_migration_resource.py new file mode 100644 index 00000000..cff8f24c --- /dev/null +++ b/appwrite/enums/firebase_migration_resource.py @@ -0,0 +1,14 @@ +from enum import Enum + + +class FirebaseMigrationResource(Enum): + USER = "user" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + BUCKET = "bucket" + FILE = "file" diff --git a/appwrite/enums/n_host_migration_resource.py b/appwrite/enums/n_host_migration_resource.py new file mode 100644 index 00000000..e0bdb4ce --- /dev/null +++ b/appwrite/enums/n_host_migration_resource.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class NHostMigrationResource(Enum): + USER = "user" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + INDEX = "index" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + BUCKET = "bucket" + FILE = "file" diff --git a/appwrite/enums/on_duplicate.py b/appwrite/enums/on_duplicate.py new file mode 100644 index 00000000..04557c19 --- /dev/null +++ b/appwrite/enums/on_duplicate.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class OnDuplicate(Enum): + FAIL = "fail" + SKIP = "skip" + OVERWRITE = "overwrite" diff --git a/appwrite/enums/project_policy_id.py b/appwrite/enums/project_policy_id.py index bc2ce903..5451fac3 100644 --- a/appwrite/enums/project_policy_id.py +++ b/appwrite/enums/project_policy_id.py @@ -19,4 +19,5 @@ class ProjectPolicyId(Enum): DENY_DISPOSABLE_EMAIL = "deny-disposable-email" DENY_FREE_EMAIL = "deny-free-email" DENY_CORPORATE_EMAIL = "deny-corporate-email" + DENY_REALTIME_GUESTS = "deny-realtime-guests" PASSKEY = "passkey" diff --git a/appwrite/enums/runtime.py b/appwrite/enums/runtime.py index c7f29dd5..c3a28c5f 100644 --- a/appwrite/enums/runtime.py +++ b/appwrite/enums/runtime.py @@ -31,6 +31,7 @@ class Runtime(Enum): PYTHON_3_12 = "python-3.12" PYTHON_3_13 = "python-3.13" PYTHON_3_14 = "python-3.14" + PYTHON_3_15 = "python-3.15" PYTHON_ML_3_11 = "python-ml-3.11" PYTHON_ML_3_12 = "python-ml-3.12" PYTHON_ML_3_13 = "python-ml-3.13" @@ -39,6 +40,7 @@ class Runtime(Enum): DENO_2_0 = "deno-2.0" DENO_2_5 = "deno-2.5" DENO_2_6 = "deno-2.6" + DENO_2_9 = "deno-2.9" DART_2_15 = "dart-2.15" DART_2_16 = "dart-2.16" DART_2_17 = "dart-2.17" @@ -86,7 +88,9 @@ class Runtime(Enum): GO_1_24 = "go-1.24" GO_1_25 = "go-1.25" GO_1_26 = "go-1.26" + GO_1_27 = "go-1.27" RUST_1_83 = "rust-1.83" + RUST_1_99 = "rust-1.99" STATIC_1 = "static-1" FLUTTER_3_24 = "flutter-3.24" FLUTTER_3_27 = "flutter-3.27" diff --git a/appwrite/enums/supabase_migration_resource.py b/appwrite/enums/supabase_migration_resource.py new file mode 100644 index 00000000..793a7caa --- /dev/null +++ b/appwrite/enums/supabase_migration_resource.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class SupabaseMigrationResource(Enum): + USER = "user" + DATABASE = "database" + TABLE = "table" + COLUMN = "column" + INDEX = "index" + ROW = "row" + DOCUMENT = "document" + ATTRIBUTE = "attribute" + COLLECTION = "collection" + BUCKET = "bucket" + FILE = "file" diff --git a/appwrite/models/__init__.py b/appwrite/models/__init__.py index a38ce820..1599e33e 100644 --- a/appwrite/models/__init__.py +++ b/appwrite/models/__init__.py @@ -41,6 +41,7 @@ from .subscriber_list import SubscriberList from .target_list import TargetList from .transaction_list import TransactionList +from .migration_list import MigrationList from .specification_list import SpecificationList from .vectorsdb_collection_list import VectorsdbCollectionList from .embedding_list import EmbeddingList @@ -198,6 +199,7 @@ from .policy_deny_disposable_email import PolicyDenyDisposableEmail from .policy_deny_free_email import PolicyDenyFreeEmail from .policy_deny_corporate_email import PolicyDenyCorporateEmail +from .policy_deny_realtime_guests import PolicyDenyRealtimeGuests from .platform_web import PlatformWeb from .platform_apple import PlatformApple from .platform_android import PlatformAndroid @@ -229,6 +231,7 @@ from .transaction import Transaction from .subscriber import Subscriber from .target import Target +from .migration import Migration from .insight import Insight from .insight_cta import InsightCTA from .report import Report @@ -365,6 +368,7 @@ 'SubscriberList', 'TargetList', 'TransactionList', + 'MigrationList', 'SpecificationList', 'VectorsdbCollectionList', 'EmbeddingList', @@ -522,6 +526,7 @@ 'PolicyDenyDisposableEmail', 'PolicyDenyFreeEmail', 'PolicyDenyCorporateEmail', + 'PolicyDenyRealtimeGuests', 'PlatformWeb', 'PlatformApple', 'PlatformAndroid', @@ -553,6 +558,7 @@ 'Transaction', 'Subscriber', 'Target', + 'Migration', 'Insight', 'InsightCTA', 'Report', diff --git a/appwrite/models/billing_plan.py b/appwrite/models/billing_plan.py index a5e65184..0ee63cae 100644 --- a/appwrite/models/billing_plan.py +++ b/appwrite/models/billing_plan.py @@ -73,6 +73,12 @@ class BillingPlan(AppwriteModel): Function executions executionsretentioncount : float Rolling max executions retained per function/site + deploymentretention : Optional[float] + Days non-active deployments are kept when a function or site sets no retention. + deploymentretentionmax : Optional[float] + Maximum deployment retention in days a function or site can set. 0 means unlimited. + deploymentsretentioncount : Optional[float] + Non-active deployments kept per function or site. 0 means unlimited. gbhours : float GB hours for functions realtime : float @@ -199,6 +205,9 @@ class BillingPlan(AppwriteModel): sites: float = Field(..., alias='sites') executions: float = Field(..., alias='executions') executionsretentioncount: float = Field(..., alias='executionsRetentionCount') + deploymentretention: Optional[float] = Field(default=None, alias='deploymentRetention') + deploymentretentionmax: Optional[float] = Field(default=None, alias='deploymentRetentionMax') + deploymentsretentioncount: Optional[float] = Field(default=None, alias='deploymentsRetentionCount') gbhours: float = Field(..., alias='GBHours') realtime: float = Field(..., alias='realtime') realtimemessages: float = Field(..., alias='realtimeMessages') diff --git a/appwrite/models/dns_record.py b/appwrite/models/dns_record.py index 612c896b..4c51923c 100644 --- a/appwrite/models/dns_record.py +++ b/appwrite/models/dns_record.py @@ -19,7 +19,7 @@ class DnsRecord(AppwriteModel): type : str DNS record type (e.g. A, CNAME, MX). name : str - Record name or subdomain. + Record name relative to the domain, or `@` for the domain itself. value : str Value of the record (IP address, domain, etc.). ttl : float diff --git a/appwrite/models/migration.py b/appwrite/models/migration.py new file mode 100644 index 00000000..42507b70 --- /dev/null +++ b/appwrite/models/migration.py @@ -0,0 +1,128 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr, model_validator + +from .base_model import AppwriteModel + + +class Migration(AppwriteModel): + """ + Migration + + Attributes + ---------- + id : str + Migration ID. + createdat : str + Migration creation date in ISO 8601 format. + updatedat : str + Variable creation date in ISO 8601 format. + status : str + Migration status ( pending, processing, failed, completed ) + stage : str + Migration stage ( init, processing, source-check, destination-check, migrating, finished ) + source : str + A string containing the type of source of the migration. + destination : str + A string containing the type of destination of the migration. + resources : List[Any] + Resources to migrate. + resourceid : str + ID of the resource being migrated. + resourceinternalid : str + Internal ID of the resource being migrated. + resourcetype : str + Type of the resource being migrated. + parentresourceid : str + ID of the parent resource that contains the migrated resource. + parentresourceinternalid : str + Internal ID of the parent resource that contains the migrated resource. + parentresourcetype : str + Type of the parent resource that contains the migrated resource. + destinationresourceid : str + ID of the destination resource created or overwritten by the migration. + destinationresourceinternalid : str + Internal ID of the destination resource created or overwritten by the migration. + destinationresourcetype : str + Type of the destination resource created or overwritten by the migration. + statuscounters : Dict[str, Any] + A group of counters that represent the total progress of the migration. + resourcedata : List[Any] + An array of objects containing the report data of the resources that were migrated. + errors : List[Any] + All errors that occurred during the migration process. + options : Dict[str, Any] + Migration options used during the migration process. + """ + + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + status: str = Field(..., alias='status') + stage: str = Field(..., alias='stage') + source: str = Field(..., alias='source') + destination: str = Field(..., alias='destination') + resources: List[Any] = Field(..., alias='resources') + resourceid: str = Field(..., alias='resourceId') + resourceinternalid: str = Field(..., alias='resourceInternalId') + resourcetype: str = Field(..., alias='resourceType') + parentresourceid: str = Field(..., alias='parentResourceId') + parentresourceinternalid: str = Field(..., alias='parentResourceInternalId') + parentresourcetype: str = Field(..., alias='parentResourceType') + destinationresourceid: str = Field(..., alias='destinationResourceId') + destinationresourceinternalid: str = Field(..., alias='destinationResourceInternalId') + destinationresourcetype: str = Field(..., alias='destinationResourceType') + statuscounters: Dict[str, Any] = Field(..., alias='statusCounters') + resourcedata: List[Any] = Field(..., alias='resourceData') + errors: List[Any] = Field(..., alias='errors') + options: Dict[str, Any] = Field(..., alias='options') + + @model_validator(mode='before') + @classmethod + def _apply_defaults(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + + data = dict(data) + if '$id' not in data and 'id' not in data: + data['$id'] = '' + if '$createdAt' not in data and 'createdat' not in data: + data['$createdAt'] = '' + if '$updatedAt' not in data and 'updatedat' not in data: + data['$updatedAt'] = '' + if 'status' not in data: + data['status'] = '' + if 'stage' not in data: + data['stage'] = '' + if 'source' not in data: + data['source'] = '' + if 'destination' not in data: + data['destination'] = 'Appwrite' + if 'resources' not in data: + data['resources'] = [] + if 'resourceId' not in data and 'resourceid' not in data: + data['resourceId'] = '' + if 'resourceInternalId' not in data and 'resourceinternalid' not in data: + data['resourceInternalId'] = '' + if 'resourceType' not in data and 'resourcetype' not in data: + data['resourceType'] = '' + if 'parentResourceId' not in data and 'parentresourceid' not in data: + data['parentResourceId'] = '' + if 'parentResourceInternalId' not in data and 'parentresourceinternalid' not in data: + data['parentResourceInternalId'] = '' + if 'parentResourceType' not in data and 'parentresourcetype' not in data: + data['parentResourceType'] = '' + if 'destinationResourceId' not in data and 'destinationresourceid' not in data: + data['destinationResourceId'] = '' + if 'destinationResourceInternalId' not in data and 'destinationresourceinternalid' not in data: + data['destinationResourceInternalId'] = '' + if 'destinationResourceType' not in data and 'destinationresourcetype' not in data: + data['destinationResourceType'] = '' + if 'statusCounters' not in data and 'statuscounters' not in data: + data['statusCounters'] = {} + if 'resourceData' not in data and 'resourcedata' not in data: + data['resourceData'] = [] + if 'errors' not in data: + data['errors'] = [] + if 'options' not in data: + data['options'] = {} + return data diff --git a/appwrite/models/migration_list.py b/appwrite/models/migration_list.py new file mode 100644 index 00000000..8e6a4493 --- /dev/null +++ b/appwrite/models/migration_list.py @@ -0,0 +1,34 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr, model_validator + +from .base_model import AppwriteModel +from .migration import Migration + + +class MigrationList(AppwriteModel): + """ + Migrations List + + Attributes + ---------- + total : float + Total number of migrations that matched your query. + migrations : List[Migration] + List of migrations. + """ + + total: float = Field(..., alias='total') + migrations: List[Migration] = Field(..., alias='migrations') + + @model_validator(mode='before') + @classmethod + def _apply_defaults(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + + data = dict(data) + if 'total' not in data: + data['total'] = 0 + if 'migrations' not in data: + data['migrations'] = [] + return data diff --git a/appwrite/models/policy_deny_realtime_guests.py b/appwrite/models/policy_deny_realtime_guests.py new file mode 100644 index 00000000..228b28c7 --- /dev/null +++ b/appwrite/models/policy_deny_realtime_guests.py @@ -0,0 +1,33 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr, model_validator + +from .base_model import AppwriteModel + + +class PolicyDenyRealtimeGuests(AppwriteModel): + """ + Policy Deny Realtime Guests + + Attributes + ---------- + id : str + Policy ID. + enabled : bool + Whether guests are stopped from subscribing to realtime channels. + """ + + id: str = Field(..., alias='$id') + enabled: bool = Field(..., alias='enabled') + + @model_validator(mode='before') + @classmethod + def _apply_defaults(cls, data: Any) -> Any: + if not isinstance(data, dict): + return data + + data = dict(data) + if '$id' not in data and 'id' not in data: + data['$id'] = '' + if 'enabled' not in data: + data['enabled'] = False + return data diff --git a/appwrite/models/policy_list.py b/appwrite/models/policy_list.py index af4063f2..0109c6ff 100644 --- a/appwrite/models/policy_list.py +++ b/appwrite/models/policy_list.py @@ -19,6 +19,7 @@ from .policy_deny_disposable_email import PolicyDenyDisposableEmail from .policy_deny_free_email import PolicyDenyFreeEmail from .policy_deny_corporate_email import PolicyDenyCorporateEmail +from .policy_deny_realtime_guests import PolicyDenyRealtimeGuests from .policy_passkey import PolicyPasskey @@ -30,7 +31,7 @@ class PolicyList(AppwriteModel): ---------- total : float Total number of policies in the given project. - policies : List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyPasskey]] + policies : List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyDenyRealtimeGuests, PolicyPasskey]] List of policies. """ @@ -54,6 +55,7 @@ class PolicyList(AppwriteModel): PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, + PolicyDenyRealtimeGuests, PolicyPasskey, ] ] = Field(..., alias='policies') diff --git a/appwrite/models/provider.py b/appwrite/models/provider.py index ebe30dfb..e045e09c 100644 --- a/appwrite/models/provider.py +++ b/appwrite/models/provider.py @@ -25,7 +25,7 @@ class Provider(AppwriteModel): type : str Type of provider. credentials : Dict[str, Any] - Provider credentials. + Provider credentials. Secret values are write-only and never returned. options : Optional[Dict[str, Any]] Provider options. """ diff --git a/appwrite/service.py b/appwrite/service.py index abc7cf4b..b2fd9bbe 100644 --- a/appwrite/service.py +++ b/appwrite/service.py @@ -1,5 +1,6 @@ from enum import Enum from typing import Any, Optional, Type, TypeVar +from urllib.parse import quote from pydantic import ValidationError @@ -29,6 +30,14 @@ def _normalize_value(self, value: Any) -> Any: return value + def _encode_path(self, value: Any) -> str: + # Everything but letters, digits and -_.!~*'() is escaped, as encodeURIComponent does, + # so a value fills exactly the one path segment it replaces. + segment = str(self._normalize_value(value)) + if segment in ('.', '..'): + raise AppwriteException('Path parameter cannot be "." or ".."') + return quote(segment, safe="!*'()") + def _parse_response(self, response: Any, model: Optional[Type[ModelType]] = None) -> Any: if model is None: return response diff --git a/appwrite/services/account.py b/appwrite/services/account.py index 9788e53c..c71d2fe0 100644 --- a/appwrite/services/account.py +++ b/appwrite/services/account.py @@ -205,7 +205,7 @@ def get_consent( api_params = {} if consent_id is None or consent_id == '': raise AppwriteException('Missing required parameter: "consent_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) response = self.client.call( 'get', @@ -245,7 +245,7 @@ def delete_consent( api_params = {} if consent_id is None or consent_id == '': raise AppwriteException('Missing required parameter: "consent_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) response = self.client.call( 'delete', @@ -292,7 +292,7 @@ def list_consent_tokens( api_params = {} if consent_id is None or consent_id == '': raise AppwriteException('Missing required parameter: "consent_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -341,8 +341,8 @@ def get_consent_token( raise AppwriteException('Missing required parameter: "consent_id"') if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'get', @@ -387,8 +387,8 @@ def delete_consent_token( raise AppwriteException('Missing required parameter: "consent_id"') if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_id))) - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{consentId}', self._encode_path(consent_id)) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'delete', @@ -525,7 +525,7 @@ def delete_identity( api_params = {} if identity_id is None or identity_id == '': raise AppwriteException('Missing required parameter: "identity_id"') - api_path = api_path.replace('{identityId}', str(self._normalize_value(identity_id))) + api_path = api_path.replace('{identityId}', self._encode_path(identity_id)) response = self.client.call( 'delete', @@ -655,7 +655,7 @@ def create_mfa_authenticator( api_params = {} if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{type}', self._encode_path(type)) response = self.client.call( 'post', @@ -705,7 +705,7 @@ def update_mfa_authenticator( raise AppwriteException('Missing required parameter: "type"') if otp is None: raise AppwriteException('Missing required parameter: "otp"') - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{type}', self._encode_path(type)) api_params['otp'] = self._normalize_value(otp) response = self.client.call( @@ -747,7 +747,7 @@ def delete_mfa_authenticator( api_params = {} if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{type}', self._encode_path(type)) response = self.client.call( 'delete', @@ -1136,7 +1136,7 @@ def get_passkey( api_params = {} if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'get', @@ -1181,7 +1181,7 @@ def update_passkey( raise AppwriteException('Missing required parameter: "passkey_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1223,7 +1223,7 @@ def delete_passkey( api_params = {} if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'delete', @@ -1269,7 +1269,7 @@ def update_passkey_verification( raise AppwriteException('Missing required parameter: "passkey_id"') if credential is None: raise AppwriteException('Missing required parameter: "credential"') - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) api_params['credential'] = self._normalize_value(credential) response = self.client.call( @@ -2091,7 +2091,7 @@ def get_session( api_params = {} if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'get', @@ -2131,7 +2131,7 @@ def update_session( api_params = {} if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'patch', @@ -2172,7 +2172,7 @@ def delete_session( api_params = {} if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'delete', @@ -2383,7 +2383,7 @@ def create_o_auth2_token( api_params = {} if provider is None: raise AppwriteException('Missing required parameter: "provider"') - api_path = api_path.replace('{provider}', str(self._normalize_value(provider))) + api_path = api_path.replace('{provider}', self._encode_path(provider)) if success is not None: api_params['success'] = self._normalize_value(success) if failure is not None: diff --git a/appwrite/services/activities.py b/appwrite/services/activities.py index 8556609f..56a932c9 100644 --- a/appwrite/services/activities.py +++ b/appwrite/services/activities.py @@ -82,7 +82,7 @@ def get_event( api_params = {} if event_id is None or event_id == '': raise AppwriteException('Missing required parameter: "event_id"') - api_path = api_path.replace('{eventId}', str(self._normalize_value(event_id))) + api_path = api_path.replace('{eventId}', self._encode_path(event_id)) response = self.client.call( 'get', diff --git a/appwrite/services/advisor.py b/appwrite/services/advisor.py index 194d0b7a..6e52e89d 100644 --- a/appwrite/services/advisor.py +++ b/appwrite/services/advisor.py @@ -84,7 +84,7 @@ def get_report( api_params = {} if report_id is None or report_id == '': raise AppwriteException('Missing required parameter: "report_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) response = self.client.call( 'get', @@ -124,7 +124,7 @@ def delete_report( api_params = {} if report_id is None or report_id == '': raise AppwriteException('Missing required parameter: "report_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) response = self.client.call( 'delete', @@ -171,7 +171,7 @@ def list_insights( api_params = {} if report_id is None or report_id == '': raise AppwriteException('Missing required parameter: "report_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -220,8 +220,8 @@ def get_insight( raise AppwriteException('Missing required parameter: "report_id"') if insight_id is None or insight_id == '': raise AppwriteException('Missing required parameter: "insight_id"') - api_path = api_path.replace('{reportId}', str(self._normalize_value(report_id))) - api_path = api_path.replace('{insightId}', str(self._normalize_value(insight_id))) + api_path = api_path.replace('{reportId}', self._encode_path(report_id)) + api_path = api_path.replace('{insightId}', self._encode_path(insight_id)) response = self.client.call( 'get', diff --git a/appwrite/services/apps.py b/appwrite/services/apps.py index 6a3439a6..066469de 100644 --- a/appwrite/services/apps.py +++ b/appwrite/services/apps.py @@ -286,7 +286,7 @@ def get( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'get', @@ -385,7 +385,7 @@ def update( raise AppwriteException('Missing required parameter: "app_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) api_params['name'] = self._normalize_value(name) if description is not None: api_params['description'] = self._normalize_value(description) @@ -463,7 +463,7 @@ def delete( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'delete', @@ -510,7 +510,7 @@ def list_installations( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -559,8 +559,8 @@ def get_installation( raise AppwriteException('Missing required parameter: "app_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'get', @@ -605,8 +605,8 @@ def delete_installation( raise AppwriteException('Missing required parameter: "app_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'delete', @@ -652,8 +652,8 @@ def create_installation_token( raise AppwriteException('Missing required parameter: "app_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'post', @@ -700,7 +700,7 @@ def list_keys( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -744,7 +744,7 @@ def create_key( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'post', @@ -790,8 +790,8 @@ def get_key( raise AppwriteException('Missing required parameter: "app_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'get', @@ -836,8 +836,8 @@ def delete_key( raise AppwriteException('Missing required parameter: "app_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'delete', @@ -883,7 +883,7 @@ def update_labels( raise AppwriteException('Missing required parameter: "app_id"') if labels is None: raise AppwriteException('Missing required parameter: "labels"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) api_params['labels'] = self._normalize_value(labels) response = self.client.call( @@ -931,7 +931,7 @@ def list_secrets( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -975,7 +975,7 @@ def create_secret( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'post', @@ -1021,8 +1021,8 @@ def get_secret( raise AppwriteException('Missing required parameter: "app_id"') if secret_id is None or secret_id == '': raise AppwriteException('Missing required parameter: "secret_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{secretId}', str(self._normalize_value(secret_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{secretId}', self._encode_path(secret_id)) response = self.client.call( 'get', @@ -1067,8 +1067,8 @@ def delete_secret( raise AppwriteException('Missing required parameter: "app_id"') if secret_id is None or secret_id == '': raise AppwriteException('Missing required parameter: "secret_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) - api_path = api_path.replace('{secretId}', str(self._normalize_value(secret_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) + api_path = api_path.replace('{secretId}', self._encode_path(secret_id)) response = self.client.call( 'delete', @@ -1114,7 +1114,7 @@ def update_team( raise AppwriteException('Missing required parameter: "app_id"') if team_id is None: raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) api_params['teamId'] = self._normalize_value(team_id) response = self.client.call( @@ -1156,7 +1156,7 @@ def delete_tokens( api_params = {} if app_id is None or app_id == '': raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + api_path = api_path.replace('{appId}', self._encode_path(app_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/avatars.py b/appwrite/services/avatars.py index a01b4baf..0f5429ed 100644 --- a/appwrite/services/avatars.py +++ b/appwrite/services/avatars.py @@ -58,7 +58,7 @@ def get_browser( api_params = {} if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{code}', str(self._normalize_value(code))) + api_path = api_path.replace('{code}', self._encode_path(code)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: @@ -115,7 +115,7 @@ def get_credit_card( api_params = {} if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{code}', str(self._normalize_value(code))) + api_path = api_path.replace('{code}', self._encode_path(code)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: @@ -214,7 +214,7 @@ def get_flag( api_params = {} if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{code}', str(self._normalize_value(code))) + api_path = api_path.replace('{code}', self._encode_path(code)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: diff --git a/appwrite/services/backups.py b/appwrite/services/backups.py index b33b009c..0f398922 100644 --- a/appwrite/services/backups.py +++ b/appwrite/services/backups.py @@ -133,7 +133,7 @@ def get_archive( api_params = {} if archive_id is None or archive_id == '': raise AppwriteException('Missing required parameter: "archive_id"') - api_path = api_path.replace('{archiveId}', str(self._normalize_value(archive_id))) + api_path = api_path.replace('{archiveId}', self._encode_path(archive_id)) response = self.client.call( 'get', @@ -173,7 +173,7 @@ def delete_archive( api_params = {} if archive_id is None or archive_id == '': raise AppwriteException('Missing required parameter: "archive_id"') - api_path = api_path.replace('{archiveId}', str(self._normalize_value(archive_id))) + api_path = api_path.replace('{archiveId}', self._encode_path(archive_id)) response = self.client.call( 'delete', @@ -332,7 +332,7 @@ def get_policy( api_params = {} if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -384,7 +384,7 @@ def update_policy( api_params = {} if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if retention is not None: @@ -433,7 +433,7 @@ def delete_policy( api_params = {} if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -583,7 +583,7 @@ def get_restoration( api_params = {} if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', diff --git a/appwrite/services/databases.py b/appwrite/services/databases.py index 179b6b20..7ae5a682 100644 --- a/appwrite/services/databases.py +++ b/appwrite/services/databases.py @@ -276,7 +276,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -325,7 +325,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -373,7 +373,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -420,7 +420,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -466,7 +466,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -518,7 +518,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -568,7 +568,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -621,7 +621,7 @@ def list_collections( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -698,7 +698,7 @@ def create_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['collectionId'] = self._normalize_value(collection_id) api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -761,8 +761,8 @@ def get_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'get', @@ -828,8 +828,8 @@ def update_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if name is not None: api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -890,8 +890,8 @@ def delete_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'delete', @@ -946,8 +946,8 @@ def list_attributes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1024,8 +1024,8 @@ def create_big_int_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -1111,9 +1111,9 @@ def update_big_int_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -1191,8 +1191,8 @@ def create_boolean_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1268,9 +1268,9 @@ def update_boolean_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1344,8 +1344,8 @@ def create_datetime_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1421,9 +1421,9 @@ def update_datetime_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1497,8 +1497,8 @@ def create_email_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1574,9 +1574,9 @@ def update_email_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1655,8 +1655,8 @@ def create_enum_attribute( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) @@ -1738,9 +1738,9 @@ def update_enum_attribute( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) @@ -1821,8 +1821,8 @@ def create_float_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -1908,9 +1908,9 @@ def update_float_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -1994,8 +1994,8 @@ def create_integer_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -2081,9 +2081,9 @@ def update_integer_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -2161,8 +2161,8 @@ def create_ip_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2238,9 +2238,9 @@ def update_ip_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2311,8 +2311,8 @@ def create_line_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2386,9 +2386,9 @@ def update_line_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -2466,8 +2466,8 @@ def create_longtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2545,9 +2545,9 @@ def update_longtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2624,8 +2624,8 @@ def create_mediumtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2703,9 +2703,9 @@ def update_mediumtext_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2776,8 +2776,8 @@ def create_point_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2851,9 +2851,9 @@ def update_point_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -2925,8 +2925,8 @@ def create_polygon_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3000,9 +3000,9 @@ def update_polygon_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -3083,8 +3083,8 @@ def create_relationship_attribute( raise AppwriteException('Missing required parameter: "related_collection_id"') if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['relatedCollectionId'] = self._normalize_value(related_collection_id) api_params['type'] = self._normalize_value(type) if two_way is not None: @@ -3159,9 +3159,9 @@ def update_relationship_attribute( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: @@ -3243,8 +3243,8 @@ def create_string_attribute( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -3326,9 +3326,9 @@ def update_string_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -3407,8 +3407,8 @@ def create_text_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3486,9 +3486,9 @@ def update_text_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3562,8 +3562,8 @@ def create_url_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3639,9 +3639,9 @@ def update_url_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3723,8 +3723,8 @@ def create_varchar_attribute( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -3806,9 +3806,9 @@ def update_varchar_attribute( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -3889,9 +3889,9 @@ def get_attribute( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -4000,9 +4000,9 @@ def delete_attribute( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -4069,8 +4069,8 @@ def list_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4146,8 +4146,8 @@ def create_document( raise AppwriteException('Missing required parameter: "document_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documentId'] = self._normalize_value(document_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4214,8 +4214,8 @@ def create_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4279,8 +4279,8 @@ def upsert_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4345,8 +4345,8 @@ def update_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -4411,8 +4411,8 @@ def delete_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4481,8 +4481,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4554,9 +4554,9 @@ def get_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4626,9 +4626,9 @@ def upsert_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4701,9 +4701,9 @@ def update_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4766,9 +4766,9 @@ def delete_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4842,10 +4842,10 @@ def decrement_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if min is not None: @@ -4923,10 +4923,10 @@ def increment_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if max is not None: @@ -4987,8 +4987,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -5065,8 +5065,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if attributes is None: raise AppwriteException('Missing required parameter: "attributes"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['attributes'] = self._normalize_value(attributes) @@ -5129,9 +5129,9 @@ def get_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -5184,9 +5184,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', diff --git a/appwrite/services/documents_db.py b/appwrite/services/documents_db.py index 234f2b66..cc8e8241 100644 --- a/appwrite/services/documents_db.py +++ b/appwrite/services/documents_db.py @@ -286,7 +286,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -332,7 +332,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -377,7 +377,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -421,7 +421,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -464,7 +464,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -524,7 +524,7 @@ def update( raise AppwriteException('Missing required parameter: "database_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) @@ -576,7 +576,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -626,7 +626,7 @@ def list_collections( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -700,7 +700,7 @@ def create_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['collectionId'] = self._normalize_value(collection_id) api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -760,8 +760,8 @@ def get_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'get', @@ -826,8 +826,8 @@ def update_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['name'] = self._normalize_value(name) if permissions is not None: api_params['permissions'] = self._normalize_value(permissions) @@ -884,8 +884,8 @@ def delete_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'delete', @@ -947,8 +947,8 @@ def list_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1021,8 +1021,8 @@ def create_document( raise AppwriteException('Missing required parameter: "document_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documentId'] = self._normalize_value(document_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1086,8 +1086,8 @@ def create_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1148,8 +1148,8 @@ def upsert_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1211,8 +1211,8 @@ def update_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -1274,8 +1274,8 @@ def delete_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1341,8 +1341,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1411,9 +1411,9 @@ def get_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1480,9 +1480,9 @@ def upsert_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1552,9 +1552,9 @@ def update_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1614,9 +1614,9 @@ def delete_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1687,10 +1687,10 @@ def decrement_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if min is not None: @@ -1765,10 +1765,10 @@ def increment_document_attribute( raise AppwriteException('Missing required parameter: "document_id"') if attribute is None or attribute == '': raise AppwriteException('Missing required parameter: "attribute"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) - api_path = api_path.replace('{attribute}', str(self._normalize_value(attribute))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) + api_path = api_path.replace('{attribute}', self._encode_path(attribute)) if value is not None: api_params['value'] = self._normalize_value(value) if max is not None: @@ -1826,8 +1826,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1901,8 +1901,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if attributes is None: raise AppwriteException('Missing required parameter: "attributes"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['attributes'] = self._normalize_value(attributes) @@ -1962,9 +1962,9 @@ def get_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -2014,9 +2014,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -2060,7 +2060,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -2112,7 +2112,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -2158,7 +2158,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -2198,7 +2198,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', diff --git a/appwrite/services/domains.py b/appwrite/services/domains.py index bce1dc23..2d2d687b 100644 --- a/appwrite/services/domains.py +++ b/appwrite/services/domains.py @@ -243,7 +243,7 @@ def get( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -284,7 +284,7 @@ def delete( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'delete', @@ -329,7 +329,7 @@ def update_nameservers( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) if nameservers is not None: api_params['nameservers'] = self._normalize_value(nameservers) @@ -374,7 +374,7 @@ def verify_nameservers( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'patch', @@ -415,7 +415,7 @@ def get_preset_google_workspace( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -456,7 +456,7 @@ def create_preset_google_workspace( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -497,7 +497,7 @@ def get_preset_i_cloud( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -538,7 +538,7 @@ def create_preset_i_cloud( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -579,7 +579,7 @@ def get_preset_mailgun( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -620,7 +620,7 @@ def create_preset_mailgun( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -661,7 +661,7 @@ def get_preset_outlook( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -702,7 +702,7 @@ def create_preset_outlook( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -743,7 +743,7 @@ def get_preset_proton_mail( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -784,7 +784,7 @@ def create_preset_proton_mail( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -825,7 +825,7 @@ def get_preset_zoho( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -866,7 +866,7 @@ def create_preset_zoho( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'post', @@ -914,7 +914,7 @@ def list_records( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -949,7 +949,7 @@ def create_record_a( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv4 address for this A record. ttl : float @@ -977,7 +977,7 @@ def create_record_a( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1018,7 +1018,7 @@ def update_record_a( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv4 address for this A record. ttl : float @@ -1048,8 +1048,8 @@ def update_record_a( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1086,7 +1086,7 @@ def create_record_aaaa( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv6 address for this AAAA record. ttl : float @@ -1114,7 +1114,7 @@ def create_record_aaaa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1155,7 +1155,7 @@ def update_record_aaaa( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str IPv6 address for this AAAA record. ttl : float @@ -1185,8 +1185,8 @@ def update_record_aaaa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1224,7 +1224,7 @@ def create_record_alias( domain_id : str Domain unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target domain for this ALIAS record. ttl : float @@ -1252,7 +1252,7 @@ def create_record_alias( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1295,7 +1295,7 @@ def update_record_alias( record_id : str DNS record unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target domain for this ALIAS record. ttl : float @@ -1325,8 +1325,8 @@ def update_record_alias( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1363,7 +1363,7 @@ def create_record_caa( domain_id : str Domain unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str CAA value (e.g. issuer domain). ttl : float @@ -1391,7 +1391,7 @@ def create_record_caa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1432,7 +1432,7 @@ def update_record_caa( record_id : str DNS record unique ID. name : str - Record name. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str CAA value (e.g. issuer domain). ttl : float @@ -1462,8 +1462,8 @@ def update_record_caa( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1503,7 +1503,7 @@ def create_record_cname( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Canonical target for this CNAME record. ttl : float @@ -1531,7 +1531,7 @@ def create_record_cname( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1570,7 +1570,7 @@ def update_record_cname( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Canonical target for this CNAME record. ttl : float @@ -1600,8 +1600,8 @@ def update_record_cname( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1638,7 +1638,7 @@ def create_record_https( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target for the HTTPS record. ttl : float @@ -1666,7 +1666,7 @@ def create_record_https( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1707,7 +1707,7 @@ def update_record_https( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Target for the HTTPS record. ttl : float @@ -1737,8 +1737,8 @@ def update_record_https( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1778,7 +1778,7 @@ def create_record_mx( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Mail server domain for this MX record. ttl : float @@ -1810,7 +1810,7 @@ def create_record_mx( raise AppwriteException('Missing required parameter: "ttl"') if priority is None: raise AppwriteException('Missing required parameter: "priority"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1851,7 +1851,7 @@ def update_record_mx( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Mail server domain for this MX record. ttl : float @@ -1885,8 +1885,8 @@ def update_record_mx( raise AppwriteException('Missing required parameter: "ttl"') if priority is None: raise AppwriteException('Missing required parameter: "priority"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1924,7 +1924,7 @@ def create_record_ns( domain_id : str Domain unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Nameserver target for this NS record. ttl : float @@ -1952,7 +1952,7 @@ def create_record_ns( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -1994,7 +1994,7 @@ def update_record_ns( record_id : str DNS record unique ID. name : str - Record name (subdomain). + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str Nameserver target for this NS record. ttl : float @@ -2024,8 +2024,8 @@ def update_record_ns( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2066,7 +2066,7 @@ def create_record_srv( domain_id : str Domain unique ID. name : str - Record name (service name). + Record name relative to the domain: the service and protocol, such as `_sip._tcp`. A full name ending in the domain is also accepted and stored relative to it. value : str Target hostname for this SRV record. ttl : float @@ -2106,7 +2106,7 @@ def create_record_srv( raise AppwriteException('Missing required parameter: "weight"') if port is None: raise AppwriteException('Missing required parameter: "port"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2164,7 +2164,7 @@ def update_record_srv( record_id : str DNS record unique ID. name : str - Record name (service name). + Record name relative to the domain: the service and protocol, such as `_sip._tcp`. A full name ending in the domain is also accepted and stored relative to it. value : str Target hostname for this SRV record. ttl : float @@ -2206,8 +2206,8 @@ def update_record_srv( raise AppwriteException('Missing required parameter: "weight"') if port is None: raise AppwriteException('Missing required parameter: "port"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2248,7 +2248,7 @@ def create_record_txt( domain_id : str Domain unique ID. name : str - Record name (subdomain) for the TXT record. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. ttl : float Time to live, in seconds. Must be between 1 and 2147483647. value : Optional[str] @@ -2274,7 +2274,7 @@ def create_record_txt( raise AppwriteException('Missing required parameter: "name"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['name'] = self._normalize_value(name) if value is not None: api_params['value'] = self._normalize_value(value) @@ -2317,7 +2317,7 @@ def update_record_txt( record_id : str DNS record unique ID. name : str - Record name (subdomain) for the TXT record. + Record name relative to the domain, such as `www`, or `@` for the domain itself. A full name ending in the domain is also accepted and stored relative to it. value : str TXT record value. ttl : float @@ -2347,8 +2347,8 @@ def update_record_txt( raise AppwriteException('Missing required parameter: "value"') if ttl is None: raise AppwriteException('Missing required parameter: "ttl"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) api_params['name'] = self._normalize_value(name) api_params['value'] = self._normalize_value(value) api_params['ttl'] = self._normalize_value(ttl) @@ -2403,8 +2403,8 @@ def get_record( raise AppwriteException('Missing required parameter: "domain_id"') if record_id is None or record_id == '': raise AppwriteException('Missing required parameter: "record_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) response = self.client.call( 'get', @@ -2450,8 +2450,8 @@ def delete_record( raise AppwriteException('Missing required parameter: "domain_id"') if record_id is None or record_id == '': raise AppwriteException('Missing required parameter: "record_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) - api_path = api_path.replace('{recordId}', str(self._normalize_value(record_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) + api_path = api_path.replace('{recordId}', self._encode_path(record_id)) response = self.client.call( 'delete', @@ -2501,7 +2501,7 @@ def update_team( raise AppwriteException('Missing required parameter: "domain_id"') if team_id is None: raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['teamId'] = self._normalize_value(team_id) response = self.client.call( @@ -2543,7 +2543,7 @@ def get_transfer_status( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -2584,7 +2584,7 @@ def get_zone( api_params = {} if domain_id is None or domain_id == '': raise AppwriteException('Missing required parameter: "domain_id"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) response = self.client.call( 'get', @@ -2606,7 +2606,7 @@ def update_zone( ) -> Domain: """ Update the DNS zone for the given domain using the provided zone file content. - All parsed records are imported and then the main domain document is returned. + All parsed records the domain does not already have are imported and then the main domain document is returned. Parameters ---------- @@ -2631,7 +2631,7 @@ def update_zone( raise AppwriteException('Missing required parameter: "domain_id"') if content is None: raise AppwriteException('Missing required parameter: "content"') - api_path = api_path.replace('{domainId}', str(self._normalize_value(domain_id))) + api_path = api_path.replace('{domainId}', self._encode_path(domain_id)) api_params['content'] = self._normalize_value(content) response = self.client.call( diff --git a/appwrite/services/functions.py b/appwrite/services/functions.py index fa109998..b20d8fa5 100644 --- a/appwrite/services/functions.py +++ b/appwrite/services/functions.py @@ -151,7 +151,7 @@ def create( runtime_specification : Optional[str] Runtime specification for the function executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. description : Optional[str] @@ -343,7 +343,7 @@ def get( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) response = self.client.call( 'get', @@ -433,7 +433,7 @@ def update( runtime_specification : Optional[str] Runtime specification for the function executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. interval : Optional[float] Minutes between scheduled executions. Appwrite picks when within each interval the function runs. Use 0 to disable. Cannot be combined with schedule. When omitted, the current interval is kept unless schedule is set. description : Optional[str] @@ -457,7 +457,7 @@ def update( raise AppwriteException('Missing required parameter: "function_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['name'] = self._normalize_value(name) if runtime is not None: api_params['runtime'] = self._normalize_value(runtime) @@ -545,7 +545,7 @@ def delete( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) response = self.client.call( 'delete', @@ -591,7 +591,7 @@ def update_function_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) response = self.client.call( @@ -642,7 +642,7 @@ def list_deployments( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -712,7 +712,7 @@ def create_deployment( raise AppwriteException('Missing required parameter: "code"') if activate is None: raise AppwriteException('Missing required parameter: "activate"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if entrypoint is not None: api_params['entrypoint'] = self._normalize_value(entrypoint) if commands is not None: @@ -774,7 +774,7 @@ def create_duplicate_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) if build_id is not None: api_params['buildId'] = self._normalize_value(build_id) @@ -848,7 +848,7 @@ def create_template_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['repository'] = self._normalize_value(repository) api_params['owner'] = self._normalize_value(owner) api_params['rootDirectory'] = self._normalize_value(root_directory) @@ -911,7 +911,7 @@ def create_vcs_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['type'] = self._normalize_value(type) api_params['reference'] = self._normalize_value(reference) if activate is not None: @@ -961,8 +961,8 @@ def get_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'get', @@ -1007,8 +1007,8 @@ def delete_deployment( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'delete', @@ -1060,8 +1060,8 @@ def get_deployment_download( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) if type is not None: api_params['type'] = self._normalize_value(type) if token is not None: @@ -1110,8 +1110,8 @@ def update_deployment_status( raise AppwriteException('Missing required parameter: "function_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'patch', @@ -1158,7 +1158,7 @@ def list_executions( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1220,7 +1220,7 @@ def create_execution( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if body is not None: api_params['body'] = self._normalize_value(body) if xasync is not None: @@ -1278,8 +1278,8 @@ def get_execution( raise AppwriteException('Missing required parameter: "function_id"') if execution_id is None or execution_id == '': raise AppwriteException('Missing required parameter: "execution_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{executionId}', str(self._normalize_value(execution_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{executionId}', self._encode_path(execution_id)) response = self.client.call( 'get', @@ -1324,8 +1324,8 @@ def delete_execution( raise AppwriteException('Missing required parameter: "function_id"') if execution_id is None or execution_id == '': raise AppwriteException('Missing required parameter: "execution_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{executionId}', str(self._normalize_value(execution_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{executionId}', self._encode_path(execution_id)) response = self.client.call( 'delete', @@ -1372,7 +1372,7 @@ def list_variables( api_params = {} if function_id is None or function_id == '': raise AppwriteException('Missing required parameter: "function_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1437,7 +1437,7 @@ def create_variable( raise AppwriteException('Missing required parameter: "key"') if value is None: raise AppwriteException('Missing required parameter: "value"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) api_params['variableId'] = self._normalize_value(variable_id) api_params['key'] = self._normalize_value(key) api_params['value'] = self._normalize_value(value) @@ -1490,8 +1490,8 @@ def get_variable( raise AppwriteException('Missing required parameter: "function_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'get', @@ -1548,8 +1548,8 @@ def update_variable( raise AppwriteException('Missing required parameter: "function_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) if key is not None: api_params['key'] = self._normalize_value(key) if value is not None: @@ -1603,8 +1603,8 @@ def delete_variable( raise AppwriteException('Missing required parameter: "function_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{functionId}', str(self._normalize_value(function_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{functionId}', self._encode_path(function_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/messaging.py b/appwrite/services/messaging.py index df5083f7..cab499ba 100644 --- a/appwrite/services/messaging.py +++ b/appwrite/services/messaging.py @@ -251,7 +251,7 @@ def update_email( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if topics is not None: api_params['topics'] = self._normalize_value(topics) if users is not None: @@ -513,7 +513,7 @@ def update_push( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if topics is not None: api_params['topics'] = self._normalize_value(topics) if users is not None: @@ -682,7 +682,7 @@ def update_sms( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if topics is not None: api_params['topics'] = self._normalize_value(topics) if users is not None: @@ -735,7 +735,7 @@ def get_message( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) response = self.client.call( 'get', @@ -775,7 +775,7 @@ def delete( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) response = self.client.call( 'delete', @@ -822,7 +822,7 @@ def list_targets( api_params = {} if message_id is None or message_id == '': raise AppwriteException('Missing required parameter: "message_id"') - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) + api_path = api_path.replace('{messageId}', self._encode_path(message_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1013,7 +1013,7 @@ def update_apns_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1142,7 +1142,7 @@ def update_appwrite_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1257,7 +1257,7 @@ def update_fcm_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1418,7 +1418,7 @@ def update_mailgun_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if api_key is not None: @@ -1559,7 +1559,7 @@ def update_msg91_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1708,7 +1708,7 @@ def update_resend_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -1861,7 +1861,7 @@ def update_sendgrid_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2030,7 +2030,7 @@ def update_ses_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2236,7 +2236,7 @@ def update_smtp_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if host is not None: @@ -2385,7 +2385,7 @@ def update_telesign_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2518,7 +2518,7 @@ def update_textmagic_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2651,7 +2651,7 @@ def update_twilio_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2784,7 +2784,7 @@ def update_vonage_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -2835,7 +2835,7 @@ def get_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) response = self.client.call( 'get', @@ -2875,7 +2875,7 @@ def delete_provider( api_params = {} if provider_id is None or provider_id == '': raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) response = self.client.call( 'delete', @@ -3032,7 +3032,7 @@ def get_topic( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) response = self.client.call( 'get', @@ -3087,7 +3087,7 @@ def update_topic( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) if name is not None: api_params['name'] = self._normalize_value(name) if subscribe is not None: @@ -3138,7 +3138,7 @@ def delete_topic( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) response = self.client.call( 'delete', @@ -3188,7 +3188,7 @@ def list_subscribers( api_params = {} if topic_id is None or topic_id == '': raise AppwriteException('Missing required parameter: "topic_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -3244,7 +3244,7 @@ def create_subscriber( raise AppwriteException('Missing required parameter: "subscriber_id"') if target_id is None: raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) api_params['subscriberId'] = self._normalize_value(subscriber_id) api_params['targetId'] = self._normalize_value(target_id) @@ -3292,8 +3292,8 @@ def get_subscriber( raise AppwriteException('Missing required parameter: "topic_id"') if subscriber_id is None or subscriber_id == '': raise AppwriteException('Missing required parameter: "subscriber_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) - api_path = api_path.replace('{subscriberId}', str(self._normalize_value(subscriber_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) + api_path = api_path.replace('{subscriberId}', self._encode_path(subscriber_id)) response = self.client.call( 'get', @@ -3338,8 +3338,8 @@ def delete_subscriber( raise AppwriteException('Missing required parameter: "topic_id"') if subscriber_id is None or subscriber_id == '': raise AppwriteException('Missing required parameter: "subscriber_id"') - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) - api_path = api_path.replace('{subscriberId}', str(self._normalize_value(subscriber_id))) + api_path = api_path.replace('{topicId}', self._encode_path(topic_id)) + api_path = api_path.replace('{subscriberId}', self._encode_path(subscriber_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/migrations.py b/appwrite/services/migrations.py new file mode 100644 index 00000000..a0328233 --- /dev/null +++ b/appwrite/services/migrations.py @@ -0,0 +1,752 @@ +from ..service import Service +from urllib.parse import quote +from typing import Any, Dict, List, Optional, Union +from ..exception import AppwriteException +from appwrite.utils.deprecated import deprecated +from ..models.migration_list import MigrationList +from ..enums.appwrite_migration_resource import AppwriteMigrationResource +from ..enums.on_duplicate import OnDuplicate +from ..models.migration import Migration +from ..enums.firebase_migration_resource import FirebaseMigrationResource +from ..enums.n_host_migration_resource import NHostMigrationResource +from ..enums.supabase_migration_resource import SupabaseMigrationResource + + +class Migrations(Service): + + def __init__(self, client) -> None: + super(Migrations, self).__init__(client) + + def list( + self, + queries: Optional[List[str]] = None, + search: Optional[str] = None, + total: Optional[bool] = None, + ) -> MigrationList: + """ + List all migrations in the current project. This endpoint returns a list of all migrations including their status, progress, and any errors that occurred during the migration process. + + Parameters + ---------- + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: status, stage, source, destination, resources, resourceId, resourceInternalId, resourceType, parentResourceId, parentResourceInternalId, parentResourceType, destinationResourceId, destinationResourceInternalId, destinationResourceType, statusCounters, resourceData, errors + search : Optional[str] + Search term to filter your list results. Max length: 256 chars. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + Returns + ------- + MigrationList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations' + api_params = {} + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if search is not None: + api_params['search'] = self._normalize_value(search) + if total is not None: + api_params['total'] = self._normalize_value(total) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=MigrationList) + + def create_appwrite_migration( + self, + resources: List[AppwriteMigrationResource], + endpoint: str, + project_id: str, + api_key: str, + on_duplicate: Optional[OnDuplicate] = None, + ) -> Migration: + """ + Migrate data from another Appwrite project to your current project. This endpoint allows you to migrate resources like databases, collections, documents, users, and files from an existing Appwrite project. + + Parameters + ---------- + resources : List[AppwriteMigrationResource] + List of resources to migrate + endpoint : str + Source Appwrite endpoint + project_id : str + Source Project ID + api_key : str + Source API Key + on_duplicate : Optional[OnDuplicate] + Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/appwrite' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if endpoint is None: + raise AppwriteException('Missing required parameter: "endpoint"') + if project_id is None: + raise AppwriteException('Missing required parameter: "project_id"') + if api_key is None: + raise AppwriteException('Missing required parameter: "api_key"') + api_params['resources'] = self._normalize_value(resources) + api_params['endpoint'] = self._normalize_value(endpoint) + api_params['projectId'] = self._normalize_value(project_id) + api_params['apiKey'] = self._normalize_value(api_key) + if on_duplicate is not None: + api_params['onDuplicate'] = self._normalize_value(on_duplicate) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_csv_export( + self, + database_id: str, + collection_id: str, + filename: str, + columns: Optional[List[str]] = None, + queries: Optional[List[str]] = None, + delimiter: Optional[str] = None, + enclosure: Optional[str] = None, + escape: Optional[str] = None, + header: Optional[bool] = None, + notify: Optional[bool] = None, + ) -> Migration: + """ + Export documents to a CSV file from your Appwrite database. This endpoint allows you to export documents to a CSV file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete. + + Parameters + ---------- + database_id : str + Database ID containing the source collection. + collection_id : str + Collection ID to export documents from. + filename : str + The name of the file to be created for the export, excluding the .csv extension. + columns : Optional[List[str]] + List of attributes to export. If empty, all attributes will be exported. You can use the `*` wildcard to export all attributes from the collection. + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK to filter documents to export. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. + delimiter : Optional[str] + The character that separates each column value. Default is comma. + enclosure : Optional[str] + The character that encloses each column value. Default is double quotes. + escape : Optional[str] + The escape character for the enclosure character. Default is double quotes. + header : Optional[bool] + Whether to include the header row with column names. Default is true. + notify : Optional[bool] + Set to true to receive an email when the export is complete. Default is true. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/csv/exports' + api_params = {} + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + if filename is None: + raise AppwriteException('Missing required parameter: "filename"') + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + api_params['filename'] = self._normalize_value(filename) + if columns is not None: + api_params['columns'] = self._normalize_value(columns) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if delimiter is not None: + api_params['delimiter'] = self._normalize_value(delimiter) + if enclosure is not None: + api_params['enclosure'] = self._normalize_value(enclosure) + if escape is not None: + api_params['escape'] = self._normalize_value(escape) + if header is not None: + api_params['header'] = self._normalize_value(header) + if notify is not None: + api_params['notify'] = self._normalize_value(notify) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_csv_import( + self, + bucket_id: str, + file_id: str, + database_id: str, + collection_id: str, + internal_file: Optional[bool] = None, + on_duplicate: Optional[OnDuplicate] = None, + ) -> Migration: + """ + Import documents from a CSV file into your Appwrite database. This endpoint allows you to import documents from a CSV file uploaded to Appwrite Storage bucket. + + Parameters + ---------- + bucket_id : str + Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket). + file_id : str + File ID. + database_id : str + Database ID containing the target collection. + collection_id : str + Collection ID to import documents into. + internal_file : Optional[bool] + Is the file stored in an internal bucket? + on_duplicate : Optional[OnDuplicate] + Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/csv/imports' + api_params = {} + if bucket_id is None: + raise AppwriteException('Missing required parameter: "bucket_id"') + if file_id is None: + raise AppwriteException('Missing required parameter: "file_id"') + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + api_params['bucketId'] = self._normalize_value(bucket_id) + api_params['fileId'] = self._normalize_value(file_id) + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + if internal_file is not None: + api_params['internalFile'] = self._normalize_value(internal_file) + if on_duplicate is not None: + api_params['onDuplicate'] = self._normalize_value(on_duplicate) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_firebase_migration( + self, + resources: List[FirebaseMigrationResource], + service_account: str, + ) -> Migration: + """ + Migrate data from a Firebase project to your Appwrite project. This endpoint allows you to migrate resources like authentication and other supported services from a Firebase project. + + Parameters + ---------- + resources : List[FirebaseMigrationResource] + List of resources to migrate + service_account : str + JSON of the Firebase service account credentials + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/firebase' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if service_account is None: + raise AppwriteException('Missing required parameter: "service_account"') + api_params['resources'] = self._normalize_value(resources) + api_params['serviceAccount'] = self._normalize_value(service_account) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_json_export( + self, + database_id: str, + collection_id: str, + filename: str, + columns: Optional[List[str]] = None, + queries: Optional[List[str]] = None, + notify: Optional[bool] = None, + ) -> Migration: + """ + Export documents to a JSON file from your Appwrite database. This endpoint allows you to export documents to a JSON file stored in a secure internal bucket. You'll receive an email with a download link when the export is complete. + + Parameters + ---------- + database_id : str + Database ID containing the source collection. + collection_id : str + Collection ID to export documents from. + filename : str + The name of the file to be created for the export, excluding the .json extension. + columns : Optional[List[str]] + List of attributes to export. If empty, all attributes will be exported. You can use the `*` wildcard to export all attributes from the collection. + queries : Optional[List[str]] + Array of query strings generated using the Query class provided by the SDK to filter documents to export. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of 100 queries are allowed, each 4096 characters long. + notify : Optional[bool] + Set to true to receive an email when the export is complete. Default is true. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/json/exports' + api_params = {} + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + if filename is None: + raise AppwriteException('Missing required parameter: "filename"') + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + api_params['filename'] = self._normalize_value(filename) + if columns is not None: + api_params['columns'] = self._normalize_value(columns) + if queries is not None: + api_params['queries'] = self._normalize_value(queries) + if notify is not None: + api_params['notify'] = self._normalize_value(notify) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_json_import( + self, + bucket_id: str, + file_id: str, + database_id: str, + collection_id: str, + internal_file: Optional[bool] = None, + on_duplicate: Optional[OnDuplicate] = None, + ) -> Migration: + """ + Import documents from a JSON file into your Appwrite database. This endpoint allows you to import documents from a JSON file uploaded to Appwrite Storage bucket. + + Parameters + ---------- + bucket_id : str + Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket). + file_id : str + File ID. + database_id : str + Database ID containing the target collection. + collection_id : str + Collection ID to import documents into. + internal_file : Optional[bool] + Is the file stored in an internal bucket? + on_duplicate : Optional[OnDuplicate] + Behavior when a row with an existing $id is encountered. "fail" (default): abort on first conflict. "skip": silently ignore. "overwrite": replace existing row. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/json/imports' + api_params = {} + if bucket_id is None: + raise AppwriteException('Missing required parameter: "bucket_id"') + if file_id is None: + raise AppwriteException('Missing required parameter: "file_id"') + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + if collection_id is None: + raise AppwriteException('Missing required parameter: "collection_id"') + api_params['bucketId'] = self._normalize_value(bucket_id) + api_params['fileId'] = self._normalize_value(file_id) + api_params['databaseId'] = self._normalize_value(database_id) + api_params['collectionId'] = self._normalize_value(collection_id) + if internal_file is not None: + api_params['internalFile'] = self._normalize_value(internal_file) + if on_duplicate is not None: + api_params['onDuplicate'] = self._normalize_value(on_duplicate) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_n_host_migration( + self, + resources: List[NHostMigrationResource], + subdomain: str, + region: str, + admin_secret: str, + database: str, + username: str, + password: str, + port: Optional[float] = None, + ) -> Migration: + """ + Migrate data from an NHost project to your Appwrite project. This endpoint allows you to migrate resources like authentication, databases, and other supported services from an NHost project. + + Parameters + ---------- + resources : List[NHostMigrationResource] + List of resources to migrate + subdomain : str + Source's Subdomain + region : str + Source's Region + admin_secret : str + Source's Admin Secret + database : str + Source's Database Name + username : str + Source's Database Username + password : str + Source's Database Password + port : Optional[float] + Source's Database Port + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/nhost' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if subdomain is None: + raise AppwriteException('Missing required parameter: "subdomain"') + if region is None: + raise AppwriteException('Missing required parameter: "region"') + if admin_secret is None: + raise AppwriteException('Missing required parameter: "admin_secret"') + if database is None: + raise AppwriteException('Missing required parameter: "database"') + if username is None: + raise AppwriteException('Missing required parameter: "username"') + if password is None: + raise AppwriteException('Missing required parameter: "password"') + api_params['resources'] = self._normalize_value(resources) + api_params['subdomain'] = self._normalize_value(subdomain) + api_params['region'] = self._normalize_value(region) + api_params['adminSecret'] = self._normalize_value(admin_secret) + api_params['database'] = self._normalize_value(database) + api_params['username'] = self._normalize_value(username) + api_params['password'] = self._normalize_value(password) + if port is not None: + api_params['port'] = self._normalize_value(port) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def create_supabase_migration( + self, + resources: List[SupabaseMigrationResource], + endpoint: str, + api_key: str, + database_host: str, + username: str, + password: str, + port: Optional[float] = None, + ) -> Migration: + """ + Migrate data from a Supabase project to your Appwrite project. This endpoint allows you to migrate resources like authentication, databases, and other supported services from a Supabase project. + + Parameters + ---------- + resources : List[SupabaseMigrationResource] + List of resources to migrate + endpoint : str + Source's Supabase Endpoint + api_key : str + Source's API Key + database_host : str + Source's Database Host + username : str + Source's Database Username + password : str + Source's Database Password + port : Optional[float] + Source's Database Port + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/supabase' + api_params = {} + if resources is None: + raise AppwriteException('Missing required parameter: "resources"') + if endpoint is None: + raise AppwriteException('Missing required parameter: "endpoint"') + if api_key is None: + raise AppwriteException('Missing required parameter: "api_key"') + if database_host is None: + raise AppwriteException('Missing required parameter: "database_host"') + if username is None: + raise AppwriteException('Missing required parameter: "username"') + if password is None: + raise AppwriteException('Missing required parameter: "password"') + api_params['resources'] = self._normalize_value(resources) + api_params['endpoint'] = self._normalize_value(endpoint) + api_params['apiKey'] = self._normalize_value(api_key) + api_params['databaseHost'] = self._normalize_value(database_host) + api_params['username'] = self._normalize_value(username) + api_params['password'] = self._normalize_value(password) + if port is not None: + api_params['port'] = self._normalize_value(port) + + response = self.client.call( + 'post', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def get( + self, + migration_id: str, + ) -> Migration: + """ + Get a migration by its unique ID. This endpoint returns detailed information about a specific migration including its current status, progress, and any errors that occurred during the migration process. + + Parameters + ---------- + migration_id : str + Migration unique ID. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/{migrationId}' + api_params = {} + if migration_id is None or migration_id == '': + raise AppwriteException('Missing required parameter: "migration_id"') + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) + + response = self.client.call( + 'get', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def retry( + self, + migration_id: str, + ) -> Migration: + """ + Retry a failed migration. This endpoint allows you to retry a migration that has previously failed. + + Parameters + ---------- + migration_id : str + Migration unique ID. + Returns + ------- + Migration + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/{migrationId}' + api_params = {} + if migration_id is None or migration_id == '': + raise AppwriteException('Missing required parameter: "migration_id"') + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) + + response = self.client.call( + 'patch', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=Migration) + + def delete( + self, + migration_id: str, + ) -> Dict[str, Any]: + """ + Delete a migration by its unique ID. This endpoint allows you to remove a migration from your project's migration history. + + Parameters + ---------- + migration_id : str + Migration ID. + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/migrations/{migrationId}' + api_params = {} + if migration_id is None or migration_id == '': + raise AppwriteException('Missing required parameter: "migration_id"') + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) + + response = self.client.call( + 'delete', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return response diff --git a/appwrite/services/mongo.py b/appwrite/services/mongo.py index f377fe6c..27bd10ba 100644 --- a/appwrite/services/mongo.py +++ b/appwrite/services/mongo.py @@ -236,7 +236,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -336,7 +336,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if status is not None: @@ -419,7 +419,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -466,7 +466,7 @@ def list_backups( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -513,7 +513,7 @@ def create_backup( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) @@ -562,7 +562,7 @@ def list_backup_policies( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -632,7 +632,7 @@ def create_backup_policy( raise AppwriteException('Missing required parameter: "schedule"') if retention is None: raise AppwriteException('Missing required parameter: "retention"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['policyId'] = self._normalize_value(policy_id) api_params['name'] = self._normalize_value(name) api_params['schedule'] = self._normalize_value(schedule) @@ -686,8 +686,8 @@ def get_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -744,8 +744,8 @@ def update_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if schedule is not None: @@ -799,8 +799,8 @@ def delete_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -870,7 +870,7 @@ def update_backup_storage( raise AppwriteException('Missing required parameter: "access_key"') if secret_key is None: raise AppwriteException('Missing required parameter: "secret_key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['provider'] = self._normalize_value(provider) api_params['bucket'] = self._normalize_value(bucket) if region is not None: @@ -926,8 +926,8 @@ def get_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'get', @@ -972,8 +972,8 @@ def delete_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'delete', @@ -1014,7 +1014,7 @@ def list_branches( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1060,7 +1060,7 @@ def create_branch( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if branch_id is not None: api_params['branchId'] = self._normalize_value(branch_id) if ttl is not None: @@ -1110,8 +1110,8 @@ def delete_branch( raise AppwriteException('Missing required parameter: "database_id"') if branch_id is None or branch_id == '': raise AppwriteException('Missing required parameter: "branch_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{branchId}', str(self._normalize_value(branch_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{branchId}', self._encode_path(branch_id)) response = self.client.call( 'delete', @@ -1152,7 +1152,7 @@ def update_credentials( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'patch', @@ -1196,7 +1196,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1249,7 +1249,7 @@ def update_maintenance( raise AppwriteException('Missing required parameter: "day"') if hour_utc is None: raise AppwriteException('Missing required parameter: "hour_utc"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['day'] = self._normalize_value(day) api_params['hourUtc'] = self._normalize_value(hour_utc) @@ -1300,7 +1300,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if target_type is None: raise AppwriteException('Missing required parameter: "target_type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetType'] = self._normalize_value(target_type) if specification is not None: api_params['specification'] = self._normalize_value(specification) @@ -1353,7 +1353,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1399,7 +1399,7 @@ def get_pitr( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1439,7 +1439,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1491,7 +1491,7 @@ def list_restorations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if type is not None: @@ -1562,7 +1562,7 @@ def create_restoration( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) if backup_id is not None: @@ -1622,8 +1622,8 @@ def get_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', @@ -1671,8 +1671,8 @@ def promote_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'post', @@ -1713,7 +1713,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1758,7 +1758,7 @@ def create_upgrade( raise AppwriteException('Missing required parameter: "database_id"') if target_version is None: raise AppwriteException('Missing required parameter: "target_version"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetVersion'] = self._normalize_value(target_version) response = self.client.call( diff --git a/appwrite/services/mysql.py b/appwrite/services/mysql.py index 0e402fe2..064693a6 100644 --- a/appwrite/services/mysql.py +++ b/appwrite/services/mysql.py @@ -238,7 +238,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -338,7 +338,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if status is not None: @@ -421,7 +421,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -468,7 +468,7 @@ def list_backups( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -515,7 +515,7 @@ def create_backup( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) @@ -564,7 +564,7 @@ def list_backup_policies( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -634,7 +634,7 @@ def create_backup_policy( raise AppwriteException('Missing required parameter: "schedule"') if retention is None: raise AppwriteException('Missing required parameter: "retention"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['policyId'] = self._normalize_value(policy_id) api_params['name'] = self._normalize_value(name) api_params['schedule'] = self._normalize_value(schedule) @@ -688,8 +688,8 @@ def get_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -746,8 +746,8 @@ def update_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if schedule is not None: @@ -801,8 +801,8 @@ def delete_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -872,7 +872,7 @@ def update_backup_storage( raise AppwriteException('Missing required parameter: "access_key"') if secret_key is None: raise AppwriteException('Missing required parameter: "secret_key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['provider'] = self._normalize_value(provider) api_params['bucket'] = self._normalize_value(bucket) if region is not None: @@ -928,8 +928,8 @@ def get_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'get', @@ -974,8 +974,8 @@ def delete_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'delete', @@ -1016,7 +1016,7 @@ def list_branches( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1062,7 +1062,7 @@ def create_branch( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if branch_id is not None: api_params['branchId'] = self._normalize_value(branch_id) if ttl is not None: @@ -1112,8 +1112,8 @@ def delete_branch( raise AppwriteException('Missing required parameter: "database_id"') if branch_id is None or branch_id == '': raise AppwriteException('Missing required parameter: "branch_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{branchId}', str(self._normalize_value(branch_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{branchId}', self._encode_path(branch_id)) response = self.client.call( 'delete', @@ -1154,7 +1154,7 @@ def update_credentials( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'patch', @@ -1206,7 +1206,7 @@ def create_execution( raise AppwriteException('Missing required parameter: "database_id"') if sql is None: raise AppwriteException('Missing required parameter: "sql"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['sql'] = self._normalize_value(sql) if bindings is not None: api_params['bindings'] = self._normalize_value(bindings) @@ -1255,7 +1255,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1308,7 +1308,7 @@ def update_maintenance( raise AppwriteException('Missing required parameter: "day"') if hour_utc is None: raise AppwriteException('Missing required parameter: "hour_utc"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['day'] = self._normalize_value(day) api_params['hourUtc'] = self._normalize_value(hour_utc) @@ -1359,7 +1359,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if target_type is None: raise AppwriteException('Missing required parameter: "target_type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetType'] = self._normalize_value(target_type) if specification is not None: api_params['specification'] = self._normalize_value(specification) @@ -1412,7 +1412,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1458,7 +1458,7 @@ def get_pitr( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1498,7 +1498,7 @@ def get_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1562,7 +1562,7 @@ def update_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if mode is not None: api_params['mode'] = self._normalize_value(mode) if max_connections is not None: @@ -1619,7 +1619,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1671,7 +1671,7 @@ def list_restorations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if type is not None: @@ -1742,7 +1742,7 @@ def create_restoration( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) if backup_id is not None: @@ -1802,8 +1802,8 @@ def get_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', @@ -1851,8 +1851,8 @@ def promote_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'post', @@ -1893,7 +1893,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1938,7 +1938,7 @@ def create_upgrade( raise AppwriteException('Missing required parameter: "database_id"') if target_version is None: raise AppwriteException('Missing required parameter: "target_version"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetVersion'] = self._normalize_value(target_version) response = self.client.call( diff --git a/appwrite/services/oauth2.py b/appwrite/services/oauth2.py index 774dcf2c..a4d6e729 100644 --- a/appwrite/services/oauth2.py +++ b/appwrite/services/oauth2.py @@ -52,7 +52,7 @@ def approve( api_params = {} if grant_id is None: raise AppwriteException('Missing required parameter: "grant_id"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['grant_id'] = self._normalize_value(grant_id) if authorization_details is not None: api_params['authorization_details'] = self._normalize_value(authorization_details) @@ -134,7 +134,7 @@ def authorize( api_path = '/oauth2/{project_id}/authorize' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if client_id is not None: api_params['client_id'] = self._normalize_value(client_id) if redirect_uri is not None: @@ -238,7 +238,7 @@ def authorize_post( api_path = '/oauth2/{project_id}/authorize' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if client_id is not None: api_params['client_id'] = self._normalize_value(client_id) if redirect_uri is not None: @@ -316,7 +316,7 @@ def create_device_authorization( api_path = '/oauth2/{project_id}/device_authorization' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if client_id is not None: api_params['client_id'] = self._normalize_value(client_id) if scope is not None: @@ -366,7 +366,7 @@ def create_grant( api_params = {} if user_code is None: raise AppwriteException('Missing required parameter: "user_code"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['user_code'] = self._normalize_value(user_code) response = self.client.call( @@ -407,8 +407,8 @@ def get_grant( api_params = {} if grant_id is None or grant_id == '': raise AppwriteException('Missing required parameter: "grant_id"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) - api_path = api_path.replace('{grant_id}', str(self._normalize_value(grant_id))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) + api_path = api_path.replace('{grant_id}', self._encode_path(grant_id)) response = self.client.call( 'get', @@ -456,7 +456,7 @@ def introspect( api_params = {} if token is None: raise AppwriteException('Missing required parameter: "token"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['token'] = self._normalize_value(token) if token_type_hint is not None: api_params['token_type_hint'] = self._normalize_value(token_type_hint) @@ -507,7 +507,7 @@ def list_organizations( api_path = '/oauth2/{project_id}/organizations' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if limit is not None: api_params['limit'] = self._normalize_value(limit) if offset is not None: @@ -592,7 +592,7 @@ def create_par( raise AppwriteException('Missing required parameter: "redirect_uri"') if response_type is None: raise AppwriteException('Missing required parameter: "response_type"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['client_id'] = self._normalize_value(client_id) api_params['redirect_uri'] = self._normalize_value(redirect_uri) api_params['response_type'] = self._normalize_value(response_type) @@ -659,7 +659,7 @@ def list_projects( api_path = '/oauth2/{project_id}/projects' api_params = {} - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) if limit is not None: api_params['limit'] = self._normalize_value(limit) if offset is not None: @@ -704,7 +704,7 @@ def reject( api_params = {} if grant_id is None: raise AppwriteException('Missing required parameter: "grant_id"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['grant_id'] = self._normalize_value(grant_id) response = self.client.call( @@ -754,7 +754,7 @@ def revoke( api_params = {} if token is None: raise AppwriteException('Missing required parameter: "token"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['token'] = self._normalize_value(token) if token_type_hint is not None: api_params['token_type_hint'] = self._normalize_value(token_type_hint) @@ -828,7 +828,7 @@ def create_token( api_params = {} if grant_type is None: raise AppwriteException('Missing required parameter: "grant_type"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + api_path = api_path.replace('{project_id}', self._encode_path(self.client.get_config('project'))) api_params['grant_type'] = self._normalize_value(grant_type) if code is not None: api_params['code'] = self._normalize_value(code) diff --git a/appwrite/services/organization.py b/appwrite/services/organization.py index ecb7efdb..7b2a9fcf 100644 --- a/appwrite/services/organization.py +++ b/appwrite/services/organization.py @@ -255,7 +255,7 @@ def get_installation( api_params = {} if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'get', @@ -298,7 +298,7 @@ def update_installation( api_params = {} if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) if authorization_details is not None: api_params['authorizationDetails'] = self._normalize_value(authorization_details) @@ -341,7 +341,7 @@ def delete_installation( api_params = {} if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'delete', @@ -497,7 +497,7 @@ def get_membership( api_params = {} if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'get', @@ -542,7 +542,7 @@ def update_membership( raise AppwriteException('Missing required parameter: "membership_id"') if roles is None: raise AppwriteException('Missing required parameter: "roles"') - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) api_params['roles'] = self._normalize_value(roles) response = self.client.call( @@ -584,7 +584,7 @@ def delete_membership( api_params = {} if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'delete', @@ -726,7 +726,7 @@ def get_project( api_params = {} if project_id is None or project_id == '': raise AppwriteException('Missing required parameter: "project_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) response = self.client.call( 'get', @@ -771,7 +771,7 @@ def update_project( raise AppwriteException('Missing required parameter: "project_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -813,7 +813,7 @@ def delete_project( api_params = {} if project_id is None or project_id == '': raise AppwriteException('Missing required parameter: "project_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) response = self.client.call( 'delete', @@ -860,7 +860,7 @@ def list_project_keys( api_params = {} if project_id is None or project_id == '': raise AppwriteException('Missing required parameter: "project_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -916,7 +916,7 @@ def create_ephemeral_project_key( raise AppwriteException('Missing required parameter: "scopes"') if duration is None: raise AppwriteException('Missing required parameter: "duration"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) api_params['scopes'] = self._normalize_value(scopes) api_params['duration'] = self._normalize_value(duration) @@ -964,8 +964,8 @@ def get_project_key( raise AppwriteException('Missing required parameter: "project_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'get', @@ -1026,8 +1026,8 @@ def update_project_key( raise AppwriteException('Missing required parameter: "name"') if scopes is None: raise AppwriteException('Missing required parameter: "scopes"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) if expire is not None: @@ -1079,8 +1079,8 @@ def delete_project_key( raise AppwriteException('Missing required parameter: "project_id"') if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{projectId}', str(self._normalize_value(project_id))) - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{projectId}', self._encode_path(project_id)) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/postgresql.py b/appwrite/services/postgresql.py index a5692e05..85d50041 100644 --- a/appwrite/services/postgresql.py +++ b/appwrite/services/postgresql.py @@ -239,7 +239,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -339,7 +339,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if status is not None: @@ -422,7 +422,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -469,7 +469,7 @@ def list_backups( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -516,7 +516,7 @@ def create_backup( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) @@ -565,7 +565,7 @@ def list_backup_policies( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -635,7 +635,7 @@ def create_backup_policy( raise AppwriteException('Missing required parameter: "schedule"') if retention is None: raise AppwriteException('Missing required parameter: "retention"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['policyId'] = self._normalize_value(policy_id) api_params['name'] = self._normalize_value(name) api_params['schedule'] = self._normalize_value(schedule) @@ -689,8 +689,8 @@ def get_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -747,8 +747,8 @@ def update_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) if name is not None: api_params['name'] = self._normalize_value(name) if schedule is not None: @@ -802,8 +802,8 @@ def delete_backup_policy( raise AppwriteException('Missing required parameter: "database_id"') if policy_id is None or policy_id == '': raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'delete', @@ -873,7 +873,7 @@ def update_backup_storage( raise AppwriteException('Missing required parameter: "access_key"') if secret_key is None: raise AppwriteException('Missing required parameter: "secret_key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['provider'] = self._normalize_value(provider) api_params['bucket'] = self._normalize_value(bucket) if region is not None: @@ -929,8 +929,8 @@ def get_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'get', @@ -975,8 +975,8 @@ def delete_backup( raise AppwriteException('Missing required parameter: "database_id"') if backup_id is None or backup_id == '': raise AppwriteException('Missing required parameter: "backup_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{backupId}', str(self._normalize_value(backup_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{backupId}', self._encode_path(backup_id)) response = self.client.call( 'delete', @@ -1017,7 +1017,7 @@ def list_branches( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1063,7 +1063,7 @@ def create_branch( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if branch_id is not None: api_params['branchId'] = self._normalize_value(branch_id) if ttl is not None: @@ -1113,8 +1113,8 @@ def delete_branch( raise AppwriteException('Missing required parameter: "database_id"') if branch_id is None or branch_id == '': raise AppwriteException('Missing required parameter: "branch_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{branchId}', str(self._normalize_value(branch_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{branchId}', self._encode_path(branch_id)) response = self.client.call( 'delete', @@ -1155,7 +1155,7 @@ def update_credentials( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'patch', @@ -1207,7 +1207,7 @@ def create_execution( raise AppwriteException('Missing required parameter: "database_id"') if sql is None: raise AppwriteException('Missing required parameter: "sql"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['sql'] = self._normalize_value(sql) if bindings is not None: api_params['bindings'] = self._normalize_value(bindings) @@ -1253,7 +1253,7 @@ def list_extensions( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1298,7 +1298,7 @@ def create_extension( raise AppwriteException('Missing required parameter: "database_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1345,8 +1345,8 @@ def delete_extension( raise AppwriteException('Missing required parameter: "database_id"') if extension_name is None or extension_name == '': raise AppwriteException('Missing required parameter: "extension_name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{extensionName}', str(self._normalize_value(extension_name))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{extensionName}', self._encode_path(extension_name)) response = self.client.call( 'delete', @@ -1390,7 +1390,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1443,7 +1443,7 @@ def update_maintenance( raise AppwriteException('Missing required parameter: "day"') if hour_utc is None: raise AppwriteException('Missing required parameter: "hour_utc"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['day'] = self._normalize_value(day) api_params['hourUtc'] = self._normalize_value(hour_utc) @@ -1494,7 +1494,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if target_type is None: raise AppwriteException('Missing required parameter: "target_type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetType'] = self._normalize_value(target_type) if specification is not None: api_params['specification'] = self._normalize_value(specification) @@ -1547,7 +1547,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1593,7 +1593,7 @@ def get_pitr( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1633,7 +1633,7 @@ def get_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1697,7 +1697,7 @@ def update_pooler( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if mode is not None: api_params['mode'] = self._normalize_value(mode) if max_connections is not None: @@ -1754,7 +1754,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1806,7 +1806,7 @@ def list_restorations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if type is not None: @@ -1877,7 +1877,7 @@ def create_restoration( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if type is not None: api_params['type'] = self._normalize_value(type) if backup_id is not None: @@ -1937,8 +1937,8 @@ def get_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'get', @@ -1986,8 +1986,8 @@ def promote_restoration( raise AppwriteException('Missing required parameter: "database_id"') if restoration_id is None or restoration_id == '': raise AppwriteException('Missing required parameter: "restoration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{restorationId}', str(self._normalize_value(restoration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{restorationId}', self._encode_path(restoration_id)) response = self.client.call( 'post', @@ -2028,7 +2028,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -2073,7 +2073,7 @@ def create_upgrade( raise AppwriteException('Missing required parameter: "database_id"') if target_version is None: raise AppwriteException('Missing required parameter: "target_version"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['targetVersion'] = self._normalize_value(target_version) response = self.client.call( diff --git a/appwrite/services/presences.py b/appwrite/services/presences.py index 7066914a..d64a3c54 100644 --- a/appwrite/services/presences.py +++ b/appwrite/services/presences.py @@ -87,7 +87,7 @@ def get( api_params = {} if presence_id is None or presence_id == '': raise AppwriteException('Missing required parameter: "presence_id"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) response = self.client.call( 'get', @@ -146,7 +146,7 @@ def upsert( raise AppwriteException('Missing required parameter: "user_id"') if status is None: raise AppwriteException('Missing required parameter: "status"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) api_params['userId'] = self._normalize_value(user_id) api_params['status'] = self._normalize_value(status) if permissions is not None: @@ -215,7 +215,7 @@ def update( raise AppwriteException('Missing required parameter: "presence_id"') if user_id is None: raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) api_params['userId'] = self._normalize_value(user_id) if status is not None: api_params['status'] = self._normalize_value(status) @@ -267,7 +267,7 @@ def delete( api_params = {} if presence_id is None or presence_id == '': raise AppwriteException('Missing required parameter: "presence_id"') - api_path = api_path.replace('{presenceId}', str(self._normalize_value(presence_id))) + api_path = api_path.replace('{presenceId}', self._encode_path(presence_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/project.py b/appwrite/services/project.py index d1d6da2d..fb980b51 100644 --- a/appwrite/services/project.py +++ b/appwrite/services/project.py @@ -6,6 +6,7 @@ from ..models.project import Project as ProjectModel from ..enums.project_auth_method_id import ProjectAuthMethodId from ..models.project_list import ProjectList +from ..enums.environment_resource import EnvironmentResource from ..models.key_list import KeyList from ..enums.project_key_scopes import ProjectKeyScopes from ..models.ephemeral_key import EphemeralKey @@ -96,6 +97,7 @@ from ..models.policy_deny_disposable_email import PolicyDenyDisposableEmail from ..models.policy_deny_free_email import PolicyDenyFreeEmail from ..models.policy_deny_corporate_email import PolicyDenyCorporateEmail +from ..models.policy_deny_realtime_guests import PolicyDenyRealtimeGuests from ..models.policy_passkey import PolicyPasskey from ..enums.project_protocol_id import ProjectProtocolId from ..enums.project_service_id import ProjectServiceId @@ -207,7 +209,7 @@ def update_auth_method( raise AppwriteException('Missing required parameter: "method_id"') if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') - api_path = api_path.replace('{methodId}', str(self._normalize_value(method_id))) + api_path = api_path.replace('{methodId}', self._encode_path(method_id)) api_params['enabled'] = self._normalize_value(enabled) response = self.client.call( @@ -271,16 +273,21 @@ def create_environment( self, environment_id: str, name: str, + resources: Optional[List[EnvironmentResource]] = None, ) -> ProjectModel: """ Create a new environment of the project. An environment is a separate project in the same organization and region, with its own resources and users. Its usage is billed under the project it belongs to. + Pass `resources` to copy the project into the environment. The environment is returned at once and the copy runs as a migration of the environment, listed by its migrations routes and announced on its `migrations.*` realtime events; a resource whose ID can be chosen keeps it. Users are copied newest first and at most 90 of them, so the environment keeps room under its limit of 100 users for test accounts, and the memberships of users left behind are not copied. + Parameters ---------- environment_id : str Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can't start with a special char. Max length is 36 chars. name : str Environment name. Max length: 128 chars. + resources : Optional[List[EnvironmentResource]] + Resources to copy from the project into the environment, by type. A structural type such as `database`, `table`, `column`, `index`, `bucket`, `team`, `function` or `site` copies the structure and its settings; `row`, `file`, `user`, `membership`, `deployment` and `site-deployment` copy what it holds. A function or site is copied with its active deployment; add `deployment` or `site-deployment` to copy every deployment. Users are copied newest first and at most 90 of them, leaving room under the limit of 100 users of an environment for test accounts; the memberships of users left behind are not copied. Leave empty for an empty environment. Maximum of 100 types. Returns ------- ProjectModel @@ -300,6 +307,8 @@ def create_environment( raise AppwriteException('Missing required parameter: "name"') api_params['environmentId'] = self._normalize_value(environment_id) api_params['name'] = self._normalize_value(name) + if resources is not None: + api_params['resources'] = self._normalize_value(resources) response = self.client.call( 'post', @@ -340,7 +349,7 @@ def get_environment( api_params = {} if environment_id is None or environment_id == '': raise AppwriteException('Missing required parameter: "environment_id"') - api_path = api_path.replace('{environmentId}', str(self._normalize_value(environment_id))) + api_path = api_path.replace('{environmentId}', self._encode_path(environment_id)) response = self.client.call( 'get', @@ -385,7 +394,7 @@ def update_environment( raise AppwriteException('Missing required parameter: "environment_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{environmentId}', str(self._normalize_value(environment_id))) + api_path = api_path.replace('{environmentId}', self._encode_path(environment_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -427,7 +436,7 @@ def delete_environment( api_params = {} if environment_id is None or environment_id == '': raise AppwriteException('Missing required parameter: "environment_id"') - api_path = api_path.replace('{environmentId}', str(self._normalize_value(environment_id))) + api_path = api_path.replace('{environmentId}', self._encode_path(environment_id)) response = self.client.call( 'delete', @@ -561,7 +570,7 @@ def get_key( api_params = {} if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'get', @@ -617,7 +626,7 @@ def update_key( raise AppwriteException('Missing required parameter: "name"') if scopes is None: raise AppwriteException('Missing required parameter: "scopes"') - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) if expire is not None: @@ -664,7 +673,7 @@ def delete_key( api_params = {} if key_id is None or key_id == '': raise AppwriteException('Missing required parameter: "key_id"') - api_path = api_path.replace('{keyId}', str(self._normalize_value(key_id))) + api_path = api_path.replace('{keyId}', self._encode_path(key_id)) response = self.client.call( 'delete', @@ -837,7 +846,7 @@ def get_mock_phone( api_params = {} if number is None or number == '': raise AppwriteException('Missing required parameter: "number"') - api_path = api_path.replace('{number}', str(self._normalize_value(number))) + api_path = api_path.replace('{number}', self._encode_path(number)) response = self.client.call( 'get', @@ -882,7 +891,7 @@ def update_mock_phone( raise AppwriteException('Missing required parameter: "number"') if otp is None: raise AppwriteException('Missing required parameter: "otp"') - api_path = api_path.replace('{number}', str(self._normalize_value(number))) + api_path = api_path.replace('{number}', self._encode_path(number)) api_params['otp'] = self._normalize_value(otp) response = self.client.call( @@ -924,7 +933,7 @@ def delete_mock_phone( api_params = {} if number is None or number == '': raise AppwriteException('Missing required parameter: "number"') - api_path = api_path.replace('{number}', str(self._normalize_value(number))) + api_path = api_path.replace('{number}', self._encode_path(number)) response = self.client.call( 'delete', @@ -3773,7 +3782,7 @@ def get_o_auth2_provider( api_params = {} if provider_id is None: raise AppwriteException('Missing required parameter: "provider_id"') - api_path = api_path.replace('{providerId}', str(self._normalize_value(provider_id))) + api_path = api_path.replace('{providerId}', self._encode_path(provider_id)) response = self.client.call( 'get', @@ -4068,7 +4077,7 @@ def update_android_platform( raise AppwriteException('Missing required parameter: "name"') if application_id is None: raise AppwriteException('Missing required parameter: "application_id"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['applicationId'] = self._normalize_value(application_id) if fingerprints is not None: @@ -4176,7 +4185,7 @@ def update_apple_platform( raise AppwriteException('Missing required parameter: "name"') if bundle_identifier is None: raise AppwriteException('Missing required parameter: "bundle_identifier"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['bundleIdentifier'] = self._normalize_value(bundle_identifier) @@ -4282,7 +4291,7 @@ def update_linux_platform( raise AppwriteException('Missing required parameter: "name"') if package_name is None: raise AppwriteException('Missing required parameter: "package_name"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['packageName'] = self._normalize_value(package_name) @@ -4388,7 +4397,7 @@ def update_web_platform( raise AppwriteException('Missing required parameter: "name"') if hostname is None: raise AppwriteException('Missing required parameter: "hostname"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['hostname'] = self._normalize_value(hostname) @@ -4494,7 +4503,7 @@ def update_windows_platform( raise AppwriteException('Missing required parameter: "name"') if package_identifier_name is None: raise AppwriteException('Missing required parameter: "package_identifier_name"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) api_params['name'] = self._normalize_value(name) api_params['packageIdentifierName'] = self._normalize_value(package_identifier_name) @@ -4543,7 +4552,7 @@ def get_platform( api_params = {} if platform_id is None or platform_id == '': raise AppwriteException('Missing required parameter: "platform_id"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) response = self.client.call( 'get', @@ -4600,7 +4609,7 @@ def delete_platform( api_params = {} if platform_id is None or platform_id == '': raise AppwriteException('Missing required parameter: "platform_id"') - api_path = api_path.replace('{platformId}', str(self._normalize_value(platform_id))) + api_path = api_path.replace('{platformId}', self._encode_path(platform_id)) response = self.client.call( 'delete', @@ -4823,6 +4832,47 @@ def update_deny_free_email_policy( return self._parse_response(response, model=ProjectModel) + def update_deny_realtime_guests_policy( + self, + enabled: bool, + ) -> ProjectModel: + """ + Configures if guests, connections without a signed-in user, may subscribe to realtime channels. When enabled, a guest can still connect and sign in over the connection, but cannot subscribe until it does. + + Parameters + ---------- + enabled : bool + Set whether or not to stop guests from subscribing to realtime channels. + Returns + ------- + ProjectModel + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/project/policies/deny-realtime-guests' + api_params = {} + if enabled is None: + raise AppwriteException('Missing required parameter: "enabled"') + api_params['enabled'] = self._normalize_value(enabled) + + response = self.client.call( + 'patch', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return self._parse_response(response, model=ProjectModel) + def update_membership_privacy_policy( self, user_id: Optional[bool] = None, @@ -5501,6 +5551,7 @@ def get_policy( PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, + PolicyDenyRealtimeGuests, PolicyPasskey, ]: """ @@ -5509,10 +5560,10 @@ def get_policy( Parameters ---------- policy_id : ProjectPolicyId - Policy ID. Can be one of: password-dictionary, password-history, password-strength, password-personal-data, password-pwned, password-rotation, session-alert, session-duration, session-invalidation, session-limit, user-limit, membership-privacy, mfa-factors, deny-aliased-email, deny-disposable-email, deny-free-email, deny-corporate-email, passkey. + Policy ID. Can be one of: password-dictionary, password-history, password-strength, password-personal-data, password-pwned, password-rotation, session-alert, session-duration, session-invalidation, session-limit, user-limit, membership-privacy, mfa-factors, deny-aliased-email, deny-disposable-email, deny-free-email, deny-corporate-email, deny-realtime-guests, passkey. Returns ------- - Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyPasskey] + Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyDenyRealtimeGuests, PolicyPasskey] API response as one of the typed response models Raises @@ -5525,7 +5576,7 @@ def get_policy( api_params = {} if policy_id is None: raise AppwriteException('Missing required parameter: "policy_id"') - api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) + api_path = api_path.replace('{policyId}', self._encode_path(policy_id)) response = self.client.call( 'get', @@ -5590,6 +5641,9 @@ def get_policy( if response.get('$id') == 'deny-corporate-email': return self._parse_response(response, model=PolicyDenyCorporateEmail) + if response.get('$id') == 'deny-realtime-guests': + return self._parse_response(response, model=PolicyDenyRealtimeGuests) + if response.get('$id') == 'passkey': return self._parse_response(response, model=PolicyPasskey) @@ -5626,7 +5680,7 @@ def update_protocol( raise AppwriteException('Missing required parameter: "protocol_id"') if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') - api_path = api_path.replace('{protocolId}', str(self._normalize_value(protocol_id))) + api_path = api_path.replace('{protocolId}', self._encode_path(protocol_id)) api_params['enabled'] = self._normalize_value(enabled) response = self.client.call( @@ -5673,7 +5727,7 @@ def update_service( raise AppwriteException('Missing required parameter: "service_id"') if enabled is None: raise AppwriteException('Missing required parameter: "enabled"') - api_path = api_path.replace('{serviceId}', str(self._normalize_value(service_id))) + api_path = api_path.replace('{serviceId}', self._encode_path(service_id)) api_params['enabled'] = self._normalize_value(enabled) response = self.client.call( @@ -5689,6 +5743,38 @@ def update_service( return self._parse_response(response, model=ProjectModel) + def delete_sessions( + self, + ) -> Dict[str, Any]: + """ + Delete all sessions in the project. Any session created before this request will be invalidated immediately: users will be signed out of every active client (web, mobile, server SDKs, JWTs), and will be able to sign in again afterwards. API keys are not affected by this action. + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/project/sessions' + api_params = {} + + response = self.client.call( + 'delete', + api_path, + { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, + api_params, + ) + + return response + def update_smtp( self, host: Optional[str] = None, @@ -5964,7 +6050,7 @@ def get_email_template( api_params = {} if template_id is None: raise AppwriteException('Missing required parameter: "template_id"') - api_path = api_path.replace('{templateId}', str(self._normalize_value(template_id))) + api_path = api_path.replace('{templateId}', self._encode_path(template_id)) if locale is not None: api_params['locale'] = self._normalize_value(locale) @@ -6113,7 +6199,7 @@ def get_variable( api_params = {} if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'get', @@ -6165,7 +6251,7 @@ def update_variable( api_params = {} if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) if key is not None: api_params['key'] = self._normalize_value(key) if value is not None: @@ -6214,7 +6300,7 @@ def delete_variable( api_params = {} if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/proxy.py b/appwrite/services/proxy.py index 5f4e5c68..273857c5 100644 --- a/appwrite/services/proxy.py +++ b/appwrite/services/proxy.py @@ -412,7 +412,7 @@ def get_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'get', @@ -452,7 +452,7 @@ def delete_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'delete', @@ -493,7 +493,7 @@ def update_rule_status( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'patch', diff --git a/appwrite/services/sites.py b/appwrite/services/sites.py index f3320934..23bbad9a 100644 --- a/appwrite/services/sites.py +++ b/appwrite/services/sites.py @@ -155,7 +155,7 @@ def create( runtime_specification : Optional[str] Runtime specification for the SSR executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. description : Optional[str] @@ -350,7 +350,7 @@ def get( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) response = self.client.call( 'get', @@ -442,7 +442,7 @@ def update( runtime_specification : Optional[str] Runtime specification for the SSR executions. deployment_retention : Optional[float] - Days to keep non-active deployments before deletion. Value 0 means all deployments will be kept. + Days to keep non-active deployments before deletion. Value 0 keeps all deployments unless the server applies a default retention. scopes : Optional[List[ProjectKeyScopes]] List of scopes allowed for API key auto-generated for every site build and SSR execution. Maximum of 200 scopes are allowed. description : Optional[str] @@ -466,7 +466,7 @@ def update( raise AppwriteException('Missing required parameter: "name"') if framework is None: raise AppwriteException('Missing required parameter: "framework"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['name'] = self._normalize_value(name) api_params['framework'] = self._normalize_value(framework) if enabled is not None: @@ -553,7 +553,7 @@ def delete( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) response = self.client.call( 'delete', @@ -599,7 +599,7 @@ def update_site_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) response = self.client.call( @@ -650,7 +650,7 @@ def list_deployments( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -717,7 +717,7 @@ def create_deployment( raise AppwriteException('Missing required parameter: "site_id"') if code is None: raise AppwriteException('Missing required parameter: "code"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if install_command is not None: api_params['installCommand'] = self._normalize_value(install_command) if build_command is not None: @@ -781,7 +781,7 @@ def create_duplicate_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None: raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['deploymentId'] = self._normalize_value(deployment_id) response = self.client.call( @@ -853,7 +853,7 @@ def create_template_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['repository'] = self._normalize_value(repository) api_params['owner'] = self._normalize_value(owner) api_params['rootDirectory'] = self._normalize_value(root_directory) @@ -916,7 +916,7 @@ def create_vcs_deployment( raise AppwriteException('Missing required parameter: "type"') if reference is None: raise AppwriteException('Missing required parameter: "reference"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['type'] = self._normalize_value(type) api_params['reference'] = self._normalize_value(reference) if activate is not None: @@ -966,8 +966,8 @@ def get_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'get', @@ -1012,8 +1012,8 @@ def delete_deployment( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'delete', @@ -1065,8 +1065,8 @@ def get_deployment_download( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) if type is not None: api_params['type'] = self._normalize_value(type) if token is not None: @@ -1115,8 +1115,8 @@ def update_deployment_status( raise AppwriteException('Missing required parameter: "site_id"') if deployment_id is None or deployment_id == '': raise AppwriteException('Missing required parameter: "deployment_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{deploymentId}', str(self._normalize_value(deployment_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{deploymentId}', self._encode_path(deployment_id)) response = self.client.call( 'patch', @@ -1163,7 +1163,7 @@ def list_logs( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1212,8 +1212,8 @@ def get_log( raise AppwriteException('Missing required parameter: "site_id"') if log_id is None or log_id == '': raise AppwriteException('Missing required parameter: "log_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{logId}', str(self._normalize_value(log_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{logId}', self._encode_path(log_id)) response = self.client.call( 'get', @@ -1258,8 +1258,8 @@ def delete_log( raise AppwriteException('Missing required parameter: "site_id"') if log_id is None or log_id == '': raise AppwriteException('Missing required parameter: "log_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{logId}', str(self._normalize_value(log_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{logId}', self._encode_path(log_id)) response = self.client.call( 'delete', @@ -1306,7 +1306,7 @@ def list_variables( api_params = {} if site_id is None or site_id == '': raise AppwriteException('Missing required parameter: "site_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1371,7 +1371,7 @@ def create_variable( raise AppwriteException('Missing required parameter: "key"') if value is None: raise AppwriteException('Missing required parameter: "value"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) api_params['variableId'] = self._normalize_value(variable_id) api_params['key'] = self._normalize_value(key) api_params['value'] = self._normalize_value(value) @@ -1424,8 +1424,8 @@ def get_variable( raise AppwriteException('Missing required parameter: "site_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'get', @@ -1482,8 +1482,8 @@ def update_variable( raise AppwriteException('Missing required parameter: "site_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) if key is not None: api_params['key'] = self._normalize_value(key) if value is not None: @@ -1537,8 +1537,8 @@ def delete_variable( raise AppwriteException('Missing required parameter: "site_id"') if variable_id is None or variable_id == '': raise AppwriteException('Missing required parameter: "variable_id"') - api_path = api_path.replace('{siteId}', str(self._normalize_value(site_id))) - api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) + api_path = api_path.replace('{siteId}', self._encode_path(site_id)) + api_path = api_path.replace('{variableId}', self._encode_path(variable_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/storage.py b/appwrite/services/storage.py index 2981f526..cce353ca 100644 --- a/appwrite/services/storage.py +++ b/appwrite/services/storage.py @@ -195,7 +195,7 @@ def get_bucket( api_params = {} if bucket_id is None or bucket_id == '': raise AppwriteException('Missing required parameter: "bucket_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) response = self.client.call( 'get', @@ -273,7 +273,7 @@ def update_bucket( raise AppwriteException('Missing required parameter: "bucket_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) api_params['name'] = self._normalize_value(name) if permissions is not None: api_params['permissions'] = self._normalize_value(permissions) @@ -337,7 +337,7 @@ def delete_bucket( api_params = {} if bucket_id is None or bucket_id == '': raise AppwriteException('Missing required parameter: "bucket_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) response = self.client.call( 'delete', @@ -387,7 +387,7 @@ def list_files( api_params = {} if bucket_id is None or bucket_id == '': raise AppwriteException('Missing required parameter: "bucket_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -459,7 +459,7 @@ def create_file( raise AppwriteException('Missing required parameter: "file_id"') if file is None: raise AppwriteException('Missing required parameter: "file"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) api_params['fileId'] = self._normalize_value(file_id) api_params['file'] = self._normalize_value(file) if permissions is not None: @@ -519,8 +519,8 @@ def get_file( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) response = self.client.call( 'get', @@ -571,8 +571,8 @@ def update_file( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if name is not None: api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -622,8 +622,8 @@ def delete_file( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) response = self.client.call( 'delete', @@ -672,8 +672,8 @@ def get_file_download( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if token is not None: api_params['token'] = self._normalize_value(token) @@ -756,8 +756,8 @@ def get_file_preview( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if width is not None: api_params['width'] = self._normalize_value(width) if height is not None: @@ -829,8 +829,8 @@ def get_file_view( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if token is not None: api_params['token'] = self._normalize_value(token) diff --git a/appwrite/services/tables_db.py b/appwrite/services/tables_db.py index 19f181a5..abeba563 100644 --- a/appwrite/services/tables_db.py +++ b/appwrite/services/tables_db.py @@ -314,7 +314,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -360,7 +360,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -405,7 +405,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -449,7 +449,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -492,7 +492,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -550,7 +550,7 @@ def update( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if name is not None: api_params['name'] = self._normalize_value(name) if enabled is not None: @@ -603,7 +603,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -647,7 +647,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -690,7 +690,7 @@ def list_migrations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -738,7 +738,7 @@ def create_migration( raise AppwriteException('Missing required parameter: "database_id"') if specification is None: raise AppwriteException('Missing required parameter: "specification"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['specification'] = self._normalize_value(specification) if auto_cutover is not None: api_params['autoCutover'] = self._normalize_value(auto_cutover) @@ -787,8 +787,8 @@ def get_migration( raise AppwriteException('Missing required parameter: "database_id"') if migration_id is None or migration_id == '': raise AppwriteException('Missing required parameter: "migration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{migrationId}', str(self._normalize_value(migration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) response = self.client.call( 'get', @@ -833,8 +833,8 @@ def delete_migration( raise AppwriteException('Missing required parameter: "database_id"') if migration_id is None or migration_id == '': raise AppwriteException('Missing required parameter: "migration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{migrationId}', str(self._normalize_value(migration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) response = self.client.call( 'delete', @@ -880,8 +880,8 @@ def create_cutover( raise AppwriteException('Missing required parameter: "database_id"') if migration_id is None or migration_id == '': raise AppwriteException('Missing required parameter: "migration_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{migrationId}', str(self._normalize_value(migration_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{migrationId}', self._encode_path(migration_id)) response = self.client.call( 'post', @@ -931,7 +931,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -977,7 +977,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1017,7 +1017,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -1066,7 +1066,7 @@ def list_tables( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -1140,7 +1140,7 @@ def create_table( raise AppwriteException('Missing required parameter: "table_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['tableId'] = self._normalize_value(table_id) api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -1200,8 +1200,8 @@ def get_table( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) response = self.client.call( 'get', @@ -1264,8 +1264,8 @@ def update_table( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if name is not None: api_params['name'] = self._normalize_value(name) if permissions is not None: @@ -1323,8 +1323,8 @@ def delete_table( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) response = self.client.call( 'delete', @@ -1376,8 +1376,8 @@ def list_columns( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1451,8 +1451,8 @@ def create_big_int_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -1535,9 +1535,9 @@ def update_big_int_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -1612,8 +1612,8 @@ def create_boolean_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1686,9 +1686,9 @@ def update_boolean_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1759,8 +1759,8 @@ def create_datetime_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1833,9 +1833,9 @@ def update_datetime_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -1906,8 +1906,8 @@ def create_email_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -1980,9 +1980,9 @@ def update_email_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2058,8 +2058,8 @@ def create_enum_column( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) @@ -2138,9 +2138,9 @@ def update_enum_column( raise AppwriteException('Missing required parameter: "elements"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) @@ -2218,8 +2218,8 @@ def create_float_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -2302,9 +2302,9 @@ def update_float_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -2385,8 +2385,8 @@ def create_integer_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if min is not None: @@ -2469,9 +2469,9 @@ def update_integer_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if min is not None: api_params['min'] = self._normalize_value(min) @@ -2546,8 +2546,8 @@ def create_ip_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2620,9 +2620,9 @@ def update_ip_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2690,8 +2690,8 @@ def create_line_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2762,9 +2762,9 @@ def update_line_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -2839,8 +2839,8 @@ def create_longtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -2915,9 +2915,9 @@ def update_longtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -2991,8 +2991,8 @@ def create_mediumtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3067,9 +3067,9 @@ def update_mediumtext_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3137,8 +3137,8 @@ def create_point_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3209,9 +3209,9 @@ def update_point_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -3280,8 +3280,8 @@ def create_polygon_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3352,9 +3352,9 @@ def update_polygon_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) if default is not None: api_params['default'] = self._normalize_value(default) @@ -3432,8 +3432,8 @@ def create_relationship_column( raise AppwriteException('Missing required parameter: "related_table_id"') if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['relatedTableId'] = self._normalize_value(related_table_id) api_params['type'] = self._normalize_value(type) if two_way is not None: @@ -3505,9 +3505,9 @@ def update_relationship_column( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) if new_key is not None: @@ -3589,8 +3589,8 @@ def create_string_column( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -3672,9 +3672,9 @@ def update_string_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -3750,8 +3750,8 @@ def create_text_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3826,9 +3826,9 @@ def update_text_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -3899,8 +3899,8 @@ def create_url_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) if default is not None: @@ -3973,9 +3973,9 @@ def update_url_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if new_key is not None: @@ -4054,8 +4054,8 @@ def create_varchar_column( raise AppwriteException('Missing required parameter: "size"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) @@ -4134,9 +4134,9 @@ def update_varchar_column( raise AppwriteException('Missing required parameter: "key"') if required is None: raise AppwriteException('Missing required parameter: "required"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) if size is not None: @@ -4214,9 +4214,9 @@ def get_column( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -4322,9 +4322,9 @@ def delete_column( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -4376,8 +4376,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -4451,8 +4451,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if columns is None: raise AppwriteException('Missing required parameter: "columns"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['columns'] = self._normalize_value(columns) @@ -4512,9 +4512,9 @@ def get_index( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -4564,9 +4564,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "table_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -4630,8 +4630,8 @@ def list_rows( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -4704,8 +4704,8 @@ def create_row( raise AppwriteException('Missing required parameter: "row_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['rowId'] = self._normalize_value(row_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -4769,8 +4769,8 @@ def create_rows( raise AppwriteException('Missing required parameter: "table_id"') if rows is None: raise AppwriteException('Missing required parameter: "rows"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['rows'] = self._normalize_value(rows) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4831,8 +4831,8 @@ def upsert_rows( raise AppwriteException('Missing required parameter: "table_id"') if rows is None: raise AppwriteException('Missing required parameter: "rows"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) api_params['rows'] = self._normalize_value(rows) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -4894,8 +4894,8 @@ def update_rows( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -4957,8 +4957,8 @@ def delete_rows( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -5024,8 +5024,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if table_id is None or table_id == '': raise AppwriteException('Missing required parameter: "table_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -5094,9 +5094,9 @@ def get_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -5163,9 +5163,9 @@ def upsert_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -5235,9 +5235,9 @@ def update_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -5297,9 +5297,9 @@ def delete_row( raise AppwriteException('Missing required parameter: "table_id"') if row_id is None or row_id == '': raise AppwriteException('Missing required parameter: "row_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -5370,10 +5370,10 @@ def decrement_row_column( raise AppwriteException('Missing required parameter: "row_id"') if column is None or column == '': raise AppwriteException('Missing required parameter: "column"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) - api_path = api_path.replace('{column}', str(self._normalize_value(column))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) + api_path = api_path.replace('{column}', self._encode_path(column)) if value is not None: api_params['value'] = self._normalize_value(value) if min is not None: @@ -5448,10 +5448,10 @@ def increment_row_column( raise AppwriteException('Missing required parameter: "row_id"') if column is None or column == '': raise AppwriteException('Missing required parameter: "column"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) - api_path = api_path.replace('{rowId}', str(self._normalize_value(row_id))) - api_path = api_path.replace('{column}', str(self._normalize_value(column))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{tableId}', self._encode_path(table_id)) + api_path = api_path.replace('{rowId}', self._encode_path(row_id)) + api_path = api_path.replace('{column}', self._encode_path(column)) if value is not None: api_params['value'] = self._normalize_value(value) if max is not None: diff --git a/appwrite/services/teams.py b/appwrite/services/teams.py index 00a695d3..1df920f0 100644 --- a/appwrite/services/teams.py +++ b/appwrite/services/teams.py @@ -158,7 +158,7 @@ def get( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) response = self.client.call( 'get', @@ -207,7 +207,7 @@ def update_name( raise AppwriteException('Missing required parameter: "team_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -249,7 +249,7 @@ def delete( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) response = self.client.call( 'delete', @@ -296,7 +296,7 @@ def list_installations( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -348,7 +348,7 @@ def create_installation( raise AppwriteException('Missing required parameter: "team_id"') if app_id is None: raise AppwriteException('Missing required parameter: "app_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) api_params['appId'] = self._normalize_value(app_id) if authorization_details is not None: api_params['authorizationDetails'] = self._normalize_value(authorization_details) @@ -397,8 +397,8 @@ def get_installation( raise AppwriteException('Missing required parameter: "team_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'get', @@ -446,8 +446,8 @@ def update_installation( raise AppwriteException('Missing required parameter: "team_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) if authorization_details is not None: api_params['authorizationDetails'] = self._normalize_value(authorization_details) @@ -495,8 +495,8 @@ def delete_installation( raise AppwriteException('Missing required parameter: "team_id"') if installation_id is None or installation_id == '': raise AppwriteException('Missing required parameter: "installation_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{installationId}', self._encode_path(installation_id)) response = self.client.call( 'delete', @@ -546,7 +546,7 @@ def list_memberships( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -621,7 +621,7 @@ def create_membership( raise AppwriteException('Missing required parameter: "team_id"') if roles is None: raise AppwriteException('Missing required parameter: "roles"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) if email is not None: api_params['email'] = self._normalize_value(email) if user_id is not None: @@ -680,8 +680,8 @@ def get_membership( raise AppwriteException('Missing required parameter: "team_id"') if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'get', @@ -731,8 +731,8 @@ def update_membership( raise AppwriteException('Missing required parameter: "membership_id"') if roles is None: raise AppwriteException('Missing required parameter: "roles"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) api_params['roles'] = self._normalize_value(roles) response = self.client.call( @@ -779,8 +779,8 @@ def delete_membership( raise AppwriteException('Missing required parameter: "team_id"') if membership_id is None or membership_id == '': raise AppwriteException('Missing required parameter: "membership_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) response = self.client.call( 'delete', @@ -838,8 +838,8 @@ def update_membership_status( raise AppwriteException('Missing required parameter: "user_id"') if secret is None: raise AppwriteException('Missing required parameter: "secret"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) - api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) + api_path = api_path.replace('{membershipId}', self._encode_path(membership_id)) api_params['userId'] = self._normalize_value(user_id) api_params['secret'] = self._normalize_value(secret) @@ -886,7 +886,7 @@ def get_prefs( api_params = {} if team_id is None or team_id == '': raise AppwriteException('Missing required parameter: "team_id"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) response = self.client.call( 'get', @@ -935,7 +935,7 @@ def update_prefs( raise AppwriteException('Missing required parameter: "team_id"') if prefs is None: raise AppwriteException('Missing required parameter: "prefs"') - api_path = api_path.replace('{teamId}', str(self._normalize_value(team_id))) + api_path = api_path.replace('{teamId}', self._encode_path(team_id)) api_params['prefs'] = self._normalize_value(prefs) response = self.client.call( diff --git a/appwrite/services/tokens.py b/appwrite/services/tokens.py index b3edf492..2edf9900 100644 --- a/appwrite/services/tokens.py +++ b/appwrite/services/tokens.py @@ -49,8 +49,8 @@ def list( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -102,8 +102,8 @@ def create_file_token( raise AppwriteException('Missing required parameter: "bucket_id"') if file_id is None or file_id == '': raise AppwriteException('Missing required parameter: "file_id"') - api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) - api_path = api_path.replace('{fileId}', str(self._normalize_value(file_id))) + api_path = api_path.replace('{bucketId}', self._encode_path(bucket_id)) + api_path = api_path.replace('{fileId}', self._encode_path(file_id)) if expire is not None: api_params['expire'] = self._normalize_value(expire) @@ -146,7 +146,7 @@ def get( api_params = {} if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'get', @@ -189,7 +189,7 @@ def update( api_params = {} if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) if expire is not None: api_params['expire'] = self._normalize_value(expire) @@ -232,7 +232,7 @@ def delete( api_params = {} if token_id is None or token_id == '': raise AppwriteException('Missing required parameter: "token_id"') - api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) + api_path = api_path.replace('{tokenId}', self._encode_path(token_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/users.py b/appwrite/services/users.py index 6e89654b..5dba7e46 100644 --- a/appwrite/services/users.py +++ b/appwrite/services/users.py @@ -348,7 +348,7 @@ def delete_identity( api_params = {} if identity_id is None or identity_id == '': raise AppwriteException('Missing required parameter: "identity_id"') - api_path = api_path.replace('{identityId}', str(self._normalize_value(identity_id))) + api_path = api_path.replace('{identityId}', self._encode_path(identity_id)) response = self.client.call( 'delete', @@ -514,13 +514,13 @@ def create_scrypt_user( password_salt : str Optional salt used to hash password. password_cpu : float - Optional CPU cost used to hash password. + Optional CPU cost used to hash password. Must be a power of two, at most 65536. password_memory : float - Optional memory cost used to hash password. + Optional memory cost used to hash password. Maximum is 16. password_parallel : float - Optional parallelization cost used to hash password. + Optional parallelization cost used to hash password. Maximum is 4. password_length : float - Optional hash length used to hash password. + Optional hash length used to hash password. Must be between 16 and 128. name : Optional[str] User name. Max length: 128 chars. model_type : Type[T], optional @@ -756,7 +756,7 @@ def get( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -796,7 +796,7 @@ def delete( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'delete', @@ -846,7 +846,7 @@ def update_email( raise AppwriteException('Missing required parameter: "user_id"') if email is None: raise AppwriteException('Missing required parameter: "email"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['email'] = self._normalize_value(email) response = self.client.call( @@ -897,7 +897,7 @@ def update_impersonator( raise AppwriteException('Missing required parameter: "user_id"') if impersonator is None: raise AppwriteException('Missing required parameter: "impersonator"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['impersonator'] = self._normalize_value(impersonator) response = self.client.call( @@ -945,7 +945,7 @@ def create_jwt( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if session_id is not None: api_params['sessionId'] = self._normalize_value(session_id) if duration is not None: @@ -1001,7 +1001,7 @@ def update_labels( raise AppwriteException('Missing required parameter: "user_id"') if labels is None: raise AppwriteException('Missing required parameter: "labels"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['labels'] = self._normalize_value(labels) response = self.client.call( @@ -1052,7 +1052,7 @@ def list_memberships( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -1107,7 +1107,7 @@ def update_mfa( raise AppwriteException('Missing required parameter: "user_id"') if mfa is None: raise AppwriteException('Missing required parameter: "mfa"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['mfa'] = self._normalize_value(mfa) response = self.client.call( @@ -1154,8 +1154,8 @@ def delete_mfa_authenticator( raise AppwriteException('Missing required parameter: "user_id"') if type is None: raise AppwriteException('Missing required parameter: "type"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{type}', str(self._normalize_value(type))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{type}', self._encode_path(type)) response = self.client.call( 'delete', @@ -1201,8 +1201,8 @@ def get_mfa_challenge( raise AppwriteException('Missing required parameter: "user_id"') if challenge_id is None or challenge_id == '': raise AppwriteException('Missing required parameter: "challenge_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{challengeId}', str(self._normalize_value(challenge_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{challengeId}', self._encode_path(challenge_id)) response = self.client.call( 'get', @@ -1242,7 +1242,7 @@ def list_mfa_factors( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -1282,7 +1282,7 @@ def get_mfa_recovery_codes( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -1322,7 +1322,7 @@ def update_mfa_recovery_codes( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'put', @@ -1363,7 +1363,7 @@ def create_mfa_recovery_codes( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'patch', @@ -1413,7 +1413,7 @@ def update_name( raise AppwriteException('Missing required parameter: "user_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1461,7 +1461,7 @@ def list_passkeys( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1510,8 +1510,8 @@ def get_passkey( raise AppwriteException('Missing required parameter: "user_id"') if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'get', @@ -1561,8 +1561,8 @@ def update_passkey( raise AppwriteException('Missing required parameter: "passkey_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) api_params['name'] = self._normalize_value(name) response = self.client.call( @@ -1609,8 +1609,8 @@ def delete_passkey( raise AppwriteException('Missing required parameter: "user_id"') if passkey_id is None or passkey_id == '': raise AppwriteException('Missing required parameter: "passkey_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{passkeyId}', str(self._normalize_value(passkey_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{passkeyId}', self._encode_path(passkey_id)) response = self.client.call( 'delete', @@ -1660,7 +1660,7 @@ def update_password( raise AppwriteException('Missing required parameter: "user_id"') if password is None: raise AppwriteException('Missing required parameter: "password"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['password'] = self._normalize_value(password) response = self.client.call( @@ -1711,7 +1711,7 @@ def update_phone( raise AppwriteException('Missing required parameter: "user_id"') if number is None: raise AppwriteException('Missing required parameter: "number"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['number'] = self._normalize_value(number) response = self.client.call( @@ -1757,7 +1757,7 @@ def get_prefs( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'get', @@ -1806,7 +1806,7 @@ def update_prefs( raise AppwriteException('Missing required parameter: "user_id"') if prefs is None: raise AppwriteException('Missing required parameter: "prefs"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['prefs'] = self._normalize_value(prefs) response = self.client.call( @@ -1851,7 +1851,7 @@ def list_sessions( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if total is not None: api_params['total'] = self._normalize_value(total) @@ -1895,7 +1895,7 @@ def create_session( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'post', @@ -1936,7 +1936,7 @@ def delete_sessions( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) response = self.client.call( 'delete', @@ -1982,8 +1982,8 @@ def delete_session( raise AppwriteException('Missing required parameter: "user_id"') if session_id is None or session_id == '': raise AppwriteException('Missing required parameter: "session_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{sessionId}', str(self._normalize_value(session_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{sessionId}', self._encode_path(session_id)) response = self.client.call( 'delete', @@ -2033,7 +2033,7 @@ def update_status( raise AppwriteException('Missing required parameter: "user_id"') if status is None: raise AppwriteException('Missing required parameter: "status"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['status'] = self._normalize_value(status) response = self.client.call( @@ -2081,7 +2081,7 @@ def list_targets( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -2146,7 +2146,7 @@ def create_target( raise AppwriteException('Missing required parameter: "provider_type"') if identifier is None: raise AppwriteException('Missing required parameter: "identifier"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['targetId'] = self._normalize_value(target_id) api_params['providerType'] = self._normalize_value(provider_type) api_params['identifier'] = self._normalize_value(identifier) @@ -2199,8 +2199,8 @@ def get_target( raise AppwriteException('Missing required parameter: "user_id"') if target_id is None or target_id == '': raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{targetId}', str(self._normalize_value(target_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{targetId}', self._encode_path(target_id)) response = self.client.call( 'get', @@ -2254,8 +2254,8 @@ def update_target( raise AppwriteException('Missing required parameter: "user_id"') if target_id is None or target_id == '': raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{targetId}', str(self._normalize_value(target_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{targetId}', self._encode_path(target_id)) if identifier is not None: api_params['identifier'] = self._normalize_value(identifier) if provider_id is not None: @@ -2307,8 +2307,8 @@ def delete_target( raise AppwriteException('Missing required parameter: "user_id"') if target_id is None or target_id == '': raise AppwriteException('Missing required parameter: "target_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) - api_path = api_path.replace('{targetId}', str(self._normalize_value(target_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) + api_path = api_path.replace('{targetId}', self._encode_path(target_id)) response = self.client.call( 'delete', @@ -2355,7 +2355,7 @@ def create_token( api_params = {} if user_id is None or user_id == '': raise AppwriteException('Missing required parameter: "user_id"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) if length is not None: api_params['length'] = self._normalize_value(length) if expire is not None: @@ -2409,7 +2409,7 @@ def update_email_verification( raise AppwriteException('Missing required parameter: "user_id"') if email_verification is None: raise AppwriteException('Missing required parameter: "email_verification"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['emailVerification'] = self._normalize_value(email_verification) response = self.client.call( @@ -2460,7 +2460,7 @@ def update_phone_verification( raise AppwriteException('Missing required parameter: "user_id"') if phone_verification is None: raise AppwriteException('Missing required parameter: "phone_verification"') - api_path = api_path.replace('{userId}', str(self._normalize_value(user_id))) + api_path = api_path.replace('{userId}', self._encode_path(user_id)) api_params['phoneVerification'] = self._normalize_value(phone_verification) response = self.client.call( diff --git a/appwrite/services/vectors_db.py b/appwrite/services/vectors_db.py index eda5f00d..dd304916 100644 --- a/appwrite/services/vectors_db.py +++ b/appwrite/services/vectors_db.py @@ -286,7 +286,7 @@ def get_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'get', @@ -332,7 +332,7 @@ def update_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if commit is not None: api_params['commit'] = self._normalize_value(commit) if rollback is not None: @@ -377,7 +377,7 @@ def delete_transaction( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) response = self.client.call( 'delete', @@ -421,7 +421,7 @@ def create_operations( api_params = {} if transaction_id is None or transaction_id == '': raise AppwriteException('Missing required parameter: "transaction_id"') - api_path = api_path.replace('{transactionId}', str(self._normalize_value(transaction_id))) + api_path = api_path.replace('{transactionId}', self._encode_path(transaction_id)) if operations is not None: api_params['operations'] = self._normalize_value(operations) @@ -464,7 +464,7 @@ def get( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -524,7 +524,7 @@ def update( raise AppwriteException('Missing required parameter: "database_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) @@ -576,7 +576,7 @@ def delete( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'delete', @@ -626,7 +626,7 @@ def list_collections( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if search is not None: @@ -699,7 +699,7 @@ def create_collection( raise AppwriteException('Missing required parameter: "name"') if dimension is None: raise AppwriteException('Missing required parameter: "dimension"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) api_params['collectionId'] = self._normalize_value(collection_id) api_params['name'] = self._normalize_value(name) api_params['dimension'] = self._normalize_value(dimension) @@ -756,8 +756,8 @@ def get_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'get', @@ -822,8 +822,8 @@ def update_collection( raise AppwriteException('Missing required parameter: "collection_id"') if name is None: raise AppwriteException('Missing required parameter: "name"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['name'] = self._normalize_value(name) if dimension is not None: api_params['dimension'] = self._normalize_value(dimension) @@ -880,8 +880,8 @@ def delete_collection( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) response = self.client.call( 'delete', @@ -943,8 +943,8 @@ def list_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1017,8 +1017,8 @@ def create_document( raise AppwriteException('Missing required parameter: "document_id"') if data is None: raise AppwriteException('Missing required parameter: "data"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documentId'] = self._normalize_value(document_id) api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1082,8 +1082,8 @@ def create_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1144,8 +1144,8 @@ def upsert_documents( raise AppwriteException('Missing required parameter: "collection_id"') if documents is None: raise AppwriteException('Missing required parameter: "documents"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['documents'] = self._normalize_value(documents) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1207,8 +1207,8 @@ def update_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if data is not None: api_params['data'] = self._normalize_value(data) if queries is not None: @@ -1270,8 +1270,8 @@ def delete_documents( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1337,8 +1337,8 @@ def create_query( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1407,9 +1407,9 @@ def get_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if transaction_id is not None: @@ -1476,9 +1476,9 @@ def upsert_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1548,9 +1548,9 @@ def update_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if data is not None: api_params['data'] = self._normalize_value(data) if permissions is not None: @@ -1610,9 +1610,9 @@ def delete_document( raise AppwriteException('Missing required parameter: "collection_id"') if document_id is None or document_id == '': raise AppwriteException('Missing required parameter: "document_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{documentId}', str(self._normalize_value(document_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{documentId}', self._encode_path(document_id)) if transaction_id is not None: api_params['transactionId'] = self._normalize_value(transaction_id) @@ -1666,8 +1666,8 @@ def list_indexes( raise AppwriteException('Missing required parameter: "database_id"') if collection_id is None or collection_id == '': raise AppwriteException('Missing required parameter: "collection_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) if queries is not None: api_params['queries'] = self._normalize_value(queries) if total is not None: @@ -1741,8 +1741,8 @@ def create_index( raise AppwriteException('Missing required parameter: "type"') if attributes is None: raise AppwriteException('Missing required parameter: "attributes"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) api_params['key'] = self._normalize_value(key) api_params['type'] = self._normalize_value(type) api_params['attributes'] = self._normalize_value(attributes) @@ -1802,9 +1802,9 @@ def get_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'get', @@ -1854,9 +1854,9 @@ def delete_index( raise AppwriteException('Missing required parameter: "collection_id"') if key is None or key == '': raise AppwriteException('Missing required parameter: "key"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) - api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) - api_path = api_path.replace('{key}', str(self._normalize_value(key))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) + api_path = api_path.replace('{collectionId}', self._encode_path(collection_id)) + api_path = api_path.replace('{key}', self._encode_path(key)) response = self.client.call( 'delete', @@ -1900,7 +1900,7 @@ def create_failover( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if target_replica_id is not None: api_params['targetReplicaId'] = self._normalize_value(target_replica_id) @@ -1952,7 +1952,7 @@ def list_operations( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) if status is not None: api_params['status'] = self._normalize_value(status) if limit is not None: @@ -1998,7 +1998,7 @@ def get_replicas( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', @@ -2038,7 +2038,7 @@ def get_status( api_params = {} if database_id is None or database_id == '': raise AppwriteException('Missing required parameter: "database_id"') - api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + api_path = api_path.replace('{databaseId}', self._encode_path(database_id)) response = self.client.call( 'get', diff --git a/appwrite/services/waf.py b/appwrite/services/waf.py index 8aaa3ebc..3d47de74 100644 --- a/appwrite/services/waf.py +++ b/appwrite/services/waf.py @@ -77,7 +77,7 @@ def create_bypass_rule( conditions: Optional[List[str]] = None, ) -> WafRuleBypass: """ - Create a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -155,7 +155,7 @@ def update_bypass_rule( conditions: Optional[List[str]] = None, ) -> WafRuleBypass: """ - Update a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a bypass WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -190,7 +190,7 @@ def update_bypass_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -234,7 +234,7 @@ def create_challenge_rule( ttl: Optional[float] = None, ) -> WafRuleChallenge: """ - Create a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -327,7 +327,7 @@ def update_challenge_rule( ttl: Optional[float] = None, ) -> WafRuleChallenge: """ - Update a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a challenge WAF rule. Use `difficulty` (1 easiest to 5 hardest) to tune the client-side proof-of-work cost, and `ttl` to control how long, in seconds, a visitor stays cleared after passing the challenge before being challenged again. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -368,7 +368,7 @@ def update_challenge_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -415,7 +415,7 @@ def create_deny_rule( conditions: Optional[List[str]] = None, ) -> WafRuleDeny: """ - Create a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. `sessionAge` and `mfaAge` hold the seconds since the request's session was created and since its last MFA challenge. Both are null for requests without a session, such as guests and API keys, while a request authenticated with the `x-appwrite-jwt` header carries the age of the session its token is bound to; on `functions` rules the session comes from the `x-appwrite-user-jwt` header, and `mfaAge` is null when the session never completed MFA. A deny matching on `mfaAge` returns `user_challenge_required` and one matching on `sessionAge` returns `user_reauthentication_required`, both with status 401, so the user can sign in again and retry. To also deny requests without a session, combine `isNull` and `greaterThan` in an `or` condition. Parameters ---------- @@ -493,7 +493,7 @@ def update_deny_rule( conditions: Optional[List[str]] = None, ) -> WafRuleDeny: """ - Update a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a deny WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. `sessionAge` and `mfaAge` hold the seconds since the request's session was created and since its last MFA challenge. Both are null for requests without a session, such as guests and API keys, while a request authenticated with the `x-appwrite-jwt` header carries the age of the session its token is bound to; on `functions` rules the session comes from the `x-appwrite-user-jwt` header, and `mfaAge` is null when the session never completed MFA. A deny matching on `mfaAge` returns `user_challenge_required` and one matching on `sessionAge` returns `user_reauthentication_required`, both with status 401, so the user can sign in again and retry. To also deny requests without a session, combine `isNull` and `greaterThan` in an `or` condition. Parameters ---------- @@ -528,7 +528,7 @@ def update_deny_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -574,7 +574,7 @@ def create_rate_limit_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRateLimit: """ - Create a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -678,7 +678,7 @@ def update_rate_limit_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRateLimit: """ - Update a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a rate limit WAF rule. Use `key` to choose the counter: `ip` limits per client IP, while `userId` limits per authenticated user (requests without an authenticated user skip `userId` rules). Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -721,7 +721,7 @@ def update_rate_limit_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -772,7 +772,7 @@ def create_redirect_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRedirect: """ - Create a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Create a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -862,7 +862,7 @@ def update_redirect_rule( conditions: Optional[List[str]] = None, ) -> WafRuleRedirect: """ - Update a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. + Update a redirect WAF rule. Conditions can match request attributes including `ip` (plain IPs or CIDR blocks like `10.0.0.0/8`), `method`, `path`, `host`, `country`, `continent`, `headers.<name>`, `query.<key>`, `queryKeys`, `accept`, `acceptLanguage`, `cookie`, `userAgent`, `os`, `osVersion`, `browser`, and `browserVersion`. Conditions on `city` and `state` require the premium Geo DB addon. Parameters ---------- @@ -901,7 +901,7 @@ def update_redirect_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) if resource_type is not None: api_params['resourceType'] = self._normalize_value(resource_type) if resource_id is not None: @@ -966,7 +966,7 @@ def get_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'get', @@ -1023,7 +1023,7 @@ def delete_rule( api_params = {} if rule_id is None or rule_id == '': raise AppwriteException('Missing required parameter: "rule_id"') - api_path = api_path.replace('{ruleId}', str(self._normalize_value(rule_id))) + api_path = api_path.replace('{ruleId}', self._encode_path(rule_id)) response = self.client.call( 'delete', diff --git a/appwrite/services/webhooks.py b/appwrite/services/webhooks.py index 41f4ef49..3a4003f3 100644 --- a/appwrite/services/webhooks.py +++ b/appwrite/services/webhooks.py @@ -171,7 +171,7 @@ def get( api_params = {} if webhook_id is None or webhook_id == '': raise AppwriteException('Missing required parameter: "webhook_id"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) response = self.client.call( 'get', @@ -241,7 +241,7 @@ def update( raise AppwriteException('Missing required parameter: "url"') if events is None: raise AppwriteException('Missing required parameter: "events"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) api_params['name'] = self._normalize_value(name) api_params['url'] = self._normalize_value(url) api_params['events'] = self._normalize_value(events) @@ -295,7 +295,7 @@ def delete( api_params = {} if webhook_id is None or webhook_id == '': raise AppwriteException('Missing required parameter: "webhook_id"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) response = self.client.call( 'delete', @@ -339,7 +339,7 @@ def update_secret( api_params = {} if webhook_id is None or webhook_id == '': raise AppwriteException('Missing required parameter: "webhook_id"') - api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) + api_path = api_path.replace('{webhookId}', self._encode_path(webhook_id)) if secret is not None: api_params['secret'] = self._normalize_value(secret) diff --git a/docs/examples/backups/update-policy.md b/docs/examples/backups/update-policy.md index 74fbdeb3..f812cb3a 100644 --- a/docs/examples/backups/update-policy.md +++ b/docs/examples/backups/update-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = backups.update_policy( policy_id = '<POLICY_ID>', name = '<NAME>', # optional retention = 1, # optional - schedule = '', # optional + schedule = None, # optional enabled = False # optional ) diff --git a/docs/examples/migrations/create-appwrite-migration.md b/docs/examples/migrations/create-appwrite-migration.md new file mode 100644 index 00000000..9acaa15f --- /dev/null +++ b/docs/examples/migrations/create-appwrite-migration.md @@ -0,0 +1,24 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.appwrite_migration_resource import AppwriteMigrationResource +from appwrite.enums.on_duplicate import OnDuplicate + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_appwrite_migration( + resources = [AppwriteMigrationResource.USER], + endpoint = 'https://example.com', + project_id = '<PROJECT_ID>', + api_key = '<API_KEY>', + on_duplicate = OnDuplicate.FAIL # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-csv-export.md b/docs/examples/migrations/create-csv-export.md new file mode 100644 index 00000000..e51ca7fe --- /dev/null +++ b/docs/examples/migrations/create-csv-export.md @@ -0,0 +1,27 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_csv_export( + database_id = '<DATABASE_ID>', + collection_id = '<COLLECTION_ID>', + filename = '<FILENAME>', + columns = [], # optional + queries = [], # optional + delimiter = '<DELIMITER>', # optional + enclosure = '<ENCLOSURE>', # optional + escape = '<ESCAPE>', # optional + header = False, # optional + notify = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-csv-import.md b/docs/examples/migrations/create-csv-import.md new file mode 100644 index 00000000..7546bade --- /dev/null +++ b/docs/examples/migrations/create-csv-import.md @@ -0,0 +1,24 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.on_duplicate import OnDuplicate + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_csv_import( + bucket_id = '<BUCKET_ID>', + file_id = '<FILE_ID>', + database_id = '<DATABASE_ID>', + collection_id = '<COLLECTION_ID>', + internal_file = False, # optional + on_duplicate = OnDuplicate.FAIL # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-firebase-migration.md b/docs/examples/migrations/create-firebase-migration.md new file mode 100644 index 00000000..4f6da77f --- /dev/null +++ b/docs/examples/migrations/create-firebase-migration.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.firebase_migration_resource import FirebaseMigrationResource + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_firebase_migration( + resources = [FirebaseMigrationResource.USER], + service_account = '<SERVICE_ACCOUNT>' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-json-export.md b/docs/examples/migrations/create-json-export.md new file mode 100644 index 00000000..abdce897 --- /dev/null +++ b/docs/examples/migrations/create-json-export.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_json_export( + database_id = '<DATABASE_ID>', + collection_id = '<COLLECTION_ID>', + filename = '<FILENAME>', + columns = [], # optional + queries = [], # optional + notify = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-json-import.md b/docs/examples/migrations/create-json-import.md new file mode 100644 index 00000000..cec5f7a3 --- /dev/null +++ b/docs/examples/migrations/create-json-import.md @@ -0,0 +1,24 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.on_duplicate import OnDuplicate + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_json_import( + bucket_id = '<BUCKET_ID>', + file_id = '<FILE_ID>', + database_id = '<DATABASE_ID>', + collection_id = '<COLLECTION_ID>', + internal_file = False, # optional + on_duplicate = OnDuplicate.FAIL # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-n-host-migration.md b/docs/examples/migrations/create-n-host-migration.md new file mode 100644 index 00000000..75273344 --- /dev/null +++ b/docs/examples/migrations/create-n-host-migration.md @@ -0,0 +1,26 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.n_host_migration_resource import NHostMigrationResource + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_n_host_migration( + resources = [NHostMigrationResource.USER], + subdomain = '<SUBDOMAIN>', + region = '<REGION>', + admin_secret = '<ADMIN_SECRET>', + database = '<DATABASE>', + username = '<USERNAME>', + password = 'password', + port = 5432 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/create-supabase-migration.md b/docs/examples/migrations/create-supabase-migration.md new file mode 100644 index 00000000..9d1fd650 --- /dev/null +++ b/docs/examples/migrations/create-supabase-migration.md @@ -0,0 +1,25 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration +from appwrite.enums.supabase_migration_resource import SupabaseMigrationResource + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.create_supabase_migration( + resources = [SupabaseMigrationResource.USER], + endpoint = 'https://example.com', + api_key = '<API_KEY>', + database_host = '<DATABASE_HOST>', + username = '<USERNAME>', + password = 'password', + port = 5432 # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/delete.md b/docs/examples/migrations/delete.md new file mode 100644 index 00000000..80093504 --- /dev/null +++ b/docs/examples/migrations/delete.md @@ -0,0 +1,15 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result = migrations.delete( + migration_id = '<MIGRATION_ID>' +) +``` diff --git a/docs/examples/migrations/get.md b/docs/examples/migrations/get.md new file mode 100644 index 00000000..4d676c1c --- /dev/null +++ b/docs/examples/migrations/get.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.get( + migration_id = '<MIGRATION_ID>' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/list.md b/docs/examples/migrations/list.md new file mode 100644 index 00000000..fae89b16 --- /dev/null +++ b/docs/examples/migrations/list.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import MigrationList + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: MigrationList = migrations.list( + queries = [], # optional + search = '<SEARCH>', # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/migrations/retry.md b/docs/examples/migrations/retry.md new file mode 100644 index 00000000..15e148ea --- /dev/null +++ b/docs/examples/migrations/retry.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.migrations import Migrations +from appwrite.models import Migration + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +migrations = Migrations(client) + +result: Migration = migrations.retry( + migration_id = '<MIGRATION_ID>' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/mongo/update-backup-policy.md b/docs/examples/mongo/update-backup-policy.md index 5a02ffe1..103f2221 100644 --- a/docs/examples/mongo/update-backup-policy.md +++ b/docs/examples/mongo/update-backup-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = mongo.update_backup_policy( database_id = '<DATABASE_ID>', policy_id = '<POLICY_ID>', name = '<NAME>', # optional - schedule = '', # optional + schedule = None, # optional retention = 1, # optional enabled = False # optional ) diff --git a/docs/examples/mongo/update-backup-storage.md b/docs/examples/mongo/update-backup-storage.md index 9a62d4fd..27e1a85d 100644 --- a/docs/examples/mongo/update-backup-storage.md +++ b/docs/examples/mongo/update-backup-storage.md @@ -18,7 +18,7 @@ result: DedicatedDatabaseBackupStorage = mongo.update_backup_storage( secret_key = '<SECRET_KEY>', region = '<REGION>', # optional prefix = '<PREFIX>', # optional - endpoint = '<ENDPOINT>' # optional + endpoint = 'https://example.com' # optional ) print(result.model_dump()) diff --git a/docs/examples/mysql/update-backup-policy.md b/docs/examples/mysql/update-backup-policy.md index 65f9a9a3..b3951d9e 100644 --- a/docs/examples/mysql/update-backup-policy.md +++ b/docs/examples/mysql/update-backup-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = mysql.update_backup_policy( database_id = '<DATABASE_ID>', policy_id = '<POLICY_ID>', name = '<NAME>', # optional - schedule = '', # optional + schedule = None, # optional retention = 1, # optional enabled = False # optional ) diff --git a/docs/examples/mysql/update-backup-storage.md b/docs/examples/mysql/update-backup-storage.md index ecc41eb9..cc3b9d48 100644 --- a/docs/examples/mysql/update-backup-storage.md +++ b/docs/examples/mysql/update-backup-storage.md @@ -18,7 +18,7 @@ result: DedicatedDatabaseBackupStorage = mysql.update_backup_storage( secret_key = '<SECRET_KEY>', region = '<REGION>', # optional prefix = '<PREFIX>', # optional - endpoint = '<ENDPOINT>' # optional + endpoint = 'https://example.com' # optional ) print(result.model_dump()) diff --git a/docs/examples/oauth2/authorize-post.md b/docs/examples/oauth2/authorize-post.md index 8cf274b7..f64d5c6f 100644 --- a/docs/examples/oauth2/authorize-post.md +++ b/docs/examples/oauth2/authorize-post.md @@ -13,7 +13,7 @@ oauth2 = Oauth2(client) result: Oauth2Authorize = oauth2.authorize_post( client_id = '<CLIENT_ID>', # optional redirect_uri = 'https://example.com', # optional - response_type = '', # optional + response_type = None, # optional scope = '<SCOPE>', # optional state = '<STATE>', # optional nonce = '<NONCE>', # optional diff --git a/docs/examples/oauth2/authorize.md b/docs/examples/oauth2/authorize.md index 32fbf4b2..4534915a 100644 --- a/docs/examples/oauth2/authorize.md +++ b/docs/examples/oauth2/authorize.md @@ -13,7 +13,7 @@ oauth2 = Oauth2(client) result: Oauth2Authorize = oauth2.authorize( client_id = '<CLIENT_ID>', # optional redirect_uri = 'https://example.com', # optional - response_type = '', # optional + response_type = None, # optional scope = '<SCOPE>', # optional state = '<STATE>', # optional nonce = '<NONCE>', # optional diff --git a/docs/examples/postgresql/update-backup-policy.md b/docs/examples/postgresql/update-backup-policy.md index 02fac735..ce3a7937 100644 --- a/docs/examples/postgresql/update-backup-policy.md +++ b/docs/examples/postgresql/update-backup-policy.md @@ -14,7 +14,7 @@ result: BackupPolicy = postgresql.update_backup_policy( database_id = '<DATABASE_ID>', policy_id = '<POLICY_ID>', name = '<NAME>', # optional - schedule = '', # optional + schedule = None, # optional retention = 1, # optional enabled = False # optional ) diff --git a/docs/examples/postgresql/update-backup-storage.md b/docs/examples/postgresql/update-backup-storage.md index d0431b4a..1fe8728a 100644 --- a/docs/examples/postgresql/update-backup-storage.md +++ b/docs/examples/postgresql/update-backup-storage.md @@ -18,7 +18,7 @@ result: DedicatedDatabaseBackupStorage = postgresql.update_backup_storage( secret_key = '<SECRET_KEY>', region = '<REGION>', # optional prefix = '<PREFIX>', # optional - endpoint = '<ENDPOINT>' # optional + endpoint = 'https://example.com' # optional ) print(result.model_dump()) diff --git a/docs/examples/project/create-environment.md b/docs/examples/project/create-environment.md index 41dc4bcd..cf7cd504 100644 --- a/docs/examples/project/create-environment.md +++ b/docs/examples/project/create-environment.md @@ -2,6 +2,7 @@ from appwrite.client import Client from appwrite.services.project import Project from appwrite.models import Project as ProjectModel +from appwrite.enums.environment_resource import EnvironmentResource client = Client() client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint @@ -12,7 +13,8 @@ project = Project(client) result: ProjectModel = project.create_environment( environment_id = '<ENVIRONMENT_ID>', - name = '<NAME>' + name = '<NAME>', + resources = [EnvironmentResource.DATABASE, EnvironmentResource.TABLE, EnvironmentResource.COLUMN, EnvironmentResource.INDEX, EnvironmentResource.BUCKET, EnvironmentResource.TEAM, EnvironmentResource.FUNCTION, EnvironmentResource.ENVIRONMENT_VARIABLE] # optional ) print(result.model_dump()) diff --git a/docs/examples/project/delete-sessions.md b/docs/examples/project/delete-sessions.md new file mode 100644 index 00000000..142267f4 --- /dev/null +++ b/docs/examples/project/delete-sessions.md @@ -0,0 +1,13 @@ +```python +from appwrite.client import Client +from appwrite.services.project import Project + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +project = Project(client) + +result = project.delete_sessions() +``` diff --git a/docs/examples/project/get-policy.md b/docs/examples/project/get-policy.md index 3e746d52..ec56a786 100644 --- a/docs/examples/project/get-policy.md +++ b/docs/examples/project/get-policy.md @@ -18,6 +18,7 @@ from appwrite.models import PolicyDenyAliasedEmail from appwrite.models import PolicyDenyDisposableEmail from appwrite.models import PolicyDenyFreeEmail from appwrite.models import PolicyDenyCorporateEmail +from appwrite.models import PolicyDenyRealtimeGuests from appwrite.models import PolicyPasskey from typing import Union from appwrite.enums.project_policy_id import ProjectPolicyId @@ -29,7 +30,7 @@ client.set_key('<YOUR_API_KEY>') # Your secret API key project = Project(client) -result: Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyPasskey] = project.get_policy( +result: Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicyPasswordPwned, PolicyPasswordRotation, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyMfaFactors, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail, PolicyDenyCorporateEmail, PolicyDenyRealtimeGuests, PolicyPasskey] = project.get_policy( policy_id = ProjectPolicyId.PASSWORD_DICTIONARY ) diff --git a/docs/examples/project/update-deny-realtime-guests-policy.md b/docs/examples/project/update-deny-realtime-guests-policy.md new file mode 100644 index 00000000..dfaeaae7 --- /dev/null +++ b/docs/examples/project/update-deny-realtime-guests-policy.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.project import Project +from appwrite.models import Project as ProjectModel + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('<YOUR_PROJECT_ID>') # Your project ID +client.set_key('<YOUR_API_KEY>') # Your secret API key + +project = Project(client) + +result: ProjectModel = project.update_deny_realtime_guests_policy( + enabled = False +) + +print(result.model_dump()) +``` diff --git a/docs/examples/storage/update-file.md b/docs/examples/storage/update-file.md index b0fad8d4..a6d7659c 100644 --- a/docs/examples/storage/update-file.md +++ b/docs/examples/storage/update-file.md @@ -15,7 +15,7 @@ storage = Storage(client) result: File = storage.update_file( bucket_id = '<BUCKET_ID>', file_id = '<FILE_ID>', - name = '<NAME>', # optional + name = None, # optional permissions = [Permission.read(Role.any())] # optional ) diff --git a/docs/examples/users/create-argon-2-user.md b/docs/examples/users/create-argon-2-user.md index cd3e2bc9..85b48a56 100644 --- a/docs/examples/users/create-argon-2-user.md +++ b/docs/examples/users/create-argon-2-user.md @@ -13,7 +13,7 @@ users = Users(client) result: User = users.create_argon2_user( user_id = '<USER_ID>', email = 'email@example.com', - password = 'password', + password = '$argon2id$v=19$m=65536,t=4,p=1$NC8xaW1CVXlIZXFSQi9FRg$0bd4pLo1ytyqvCwl5NSijpm5OIe8J6CjZ1vFemKAg+o', name = '<NAME>' # optional ) diff --git a/docs/examples/users/create-bcrypt-user.md b/docs/examples/users/create-bcrypt-user.md index d3ab13e6..e5c02957 100644 --- a/docs/examples/users/create-bcrypt-user.md +++ b/docs/examples/users/create-bcrypt-user.md @@ -13,7 +13,7 @@ users = Users(client) result: User = users.create_bcrypt_user( user_id = '<USER_ID>', email = 'email@example.com', - password = 'password', + password = '$2y$10$vBiMe4NOvlbYL1iJUNdqkO.QJy17fvslZQ5oiqKq1G4uwyRWvdrXC', name = '<NAME>' # optional ) diff --git a/docs/examples/users/create-ph-pass-user.md b/docs/examples/users/create-ph-pass-user.md index 456a04b0..50a6add2 100644 --- a/docs/examples/users/create-ph-pass-user.md +++ b/docs/examples/users/create-ph-pass-user.md @@ -13,7 +13,7 @@ users = Users(client) result: User = users.create_ph_pass_user( user_id = '<USER_ID>', email = 'email@example.com', - password = 'password', + password = '$P$BrOEFP1YR5W1yHpvvGuoeVUDPmw9ra/', name = '<NAME>' # optional ) diff --git a/docs/examples/users/create-scrypt-user.md b/docs/examples/users/create-scrypt-user.md index ecfa6029..042f3bcf 100644 --- a/docs/examples/users/create-scrypt-user.md +++ b/docs/examples/users/create-scrypt-user.md @@ -16,7 +16,7 @@ result: User = users.create_scrypt_user( password = 'password', password_salt = '<PASSWORD_SALT>', password_cpu = 8, - password_memory = 65536, + password_memory = 14, password_parallel = 1, password_length = 64, name = '<NAME>' # optional diff --git a/pyproject.toml b/pyproject.toml index a3ca05da..910edbea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "appwrite" -version = "27.0.0" +version = "27.1.0" description = "Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API" readme = "README.md" requires-python = ">=3.10" diff --git a/setup.py b/setup.py index 09438025..6d48fc4a 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name='appwrite', packages=setuptools.find_packages(), - version='27.0.0', + version='27.1.0', license='BSD-3-Clause', description='Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API', long_description=long_description, @@ -18,7 +18,7 @@ maintainer='Appwrite Team', maintainer_email='team@appwrite.io', url='https://appwrite.io/support', - download_url='https://github.com/appwrite/sdk-for-python/archive/27.0.0.tar.gz', + download_url='https://github.com/appwrite/sdk-for-python/archive/27.1.0.tar.gz', install_requires=[ 'requests>=2.34.2,<3', 'urllib3>=2.8.0,<3', diff --git a/test/services/test_account.py b/test/services/test_account.py index e37d5b47..4e816a23 100644 --- a/test/services/test_account.py +++ b/test/services/test_account.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -36,12 +38,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -66,7 +76,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -75,6 +85,23 @@ def test_create(self, m): '<USER_ID>', 'email@example.com', 'password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -86,12 +113,28 @@ def test_list_consents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_consents() + response = self.account.list_consents( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -110,7 +153,7 @@ def test_get_consent(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -118,6 +161,14 @@ def test_get_consent(self, m): response = self.account.get_consent( '<CONSENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents/<CONSENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -125,7 +176,7 @@ def test_delete_consent(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -133,6 +184,14 @@ def test_delete_consent(self, m): response = self.account.delete_consent( '<CONSENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents/<CONSENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -143,13 +202,28 @@ def test_list_consent_tokens(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.list_consent_tokens( '<CONSENT_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents/<CONSENT_ID>/tokens', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -170,7 +244,7 @@ def test_get_consent_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -179,6 +253,14 @@ def test_get_consent_token(self, m): '<CONSENT_ID>', '<TOKEN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents/<CONSENT_ID>/tokens/<TOKEN_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -186,7 +268,7 @@ def test_delete_consent_token(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -195,6 +277,14 @@ def test_delete_consent_token(self, m): '<CONSENT_ID>', '<TOKEN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/consents/<CONSENT_ID>/tokens/<TOKEN_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -219,7 +309,7 @@ def test_update_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -228,6 +318,20 @@ def test_update_email(self, m): 'email@example.com', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -238,12 +342,28 @@ def test_list_identities(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_identities() + response = self.account.list_identities( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/identities', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -251,7 +371,7 @@ def test_delete_identity(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -259,6 +379,14 @@ def test_delete_identity(self, m): response = self.account.delete_identity( '<IDENTITY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/identities/<IDENTITY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -269,12 +397,28 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_memberships() + response = self.account.list_memberships( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -299,13 +443,26 @@ def test_update_mfa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_mfa( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mfa': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -317,7 +474,7 @@ def test_create_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -325,6 +482,14 @@ def test_create_mfa_authenticator(self, m): response = self.account.create_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -335,7 +500,7 @@ def test_create_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -343,6 +508,14 @@ def test_create_mfa_authenticator(self, m): response = self.account.create_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -367,7 +540,7 @@ def test_update_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -376,6 +549,19 @@ def test_update_mfa_authenticator(self, m): 'totp', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -400,7 +586,7 @@ def test_update_mfa_authenticator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -409,6 +595,19 @@ def test_update_mfa_authenticator(self, m): 'totp', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -416,7 +615,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -424,6 +623,14 @@ def test_delete_mfa_authenticator(self, m): response = self.account.delete_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -431,7 +638,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -439,6 +646,14 @@ def test_delete_mfa_authenticator(self, m): response = self.account.delete_mfa_authenticator( 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -451,7 +666,7 @@ def test_create_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -459,6 +674,19 @@ def test_create_mfa_challenge(self, m): response = self.account.create_mfa_challenge( 'email', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'factor': 'email', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -471,7 +699,7 @@ def test_create_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -479,6 +707,19 @@ def test_create_mfa_challenge(self, m): response = self.account.create_mfa_challenge( 'email', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'factor': 'email', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -516,7 +757,7 @@ def test_update_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -525,6 +766,20 @@ def test_update_mfa_challenge(self, m): '<CHALLENGE_ID>', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'challengeId': '<CHALLENGE_ID>', + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -562,7 +817,7 @@ def test_update_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -571,6 +826,20 @@ def test_update_mfa_challenge(self, m): '<CHALLENGE_ID>', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/challenges', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'challengeId': '<CHALLENGE_ID>', + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -584,12 +853,20 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.list_mfa_factors() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -603,12 +880,20 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.list_mfa_factors() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -618,12 +903,20 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -633,12 +926,20 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -648,12 +949,20 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -663,12 +972,20 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -678,12 +995,20 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -693,12 +1018,20 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_mfa_recovery_codes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -723,7 +1056,7 @@ def test_update_name(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -731,6 +1064,19 @@ def test_update_name(self, m): response = self.account.update_name( '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/name', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -741,12 +1087,28 @@ def test_list_passkeys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_passkeys() + response = self.account.list_passkeys( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -760,12 +1122,29 @@ def test_create_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.create_passkey() + response = self.account.create_passkey( + passkey_id='<PASSKEY_ID>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'passkeyId': '<PASSKEY_ID>', + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -779,7 +1158,7 @@ def test_get_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -787,6 +1166,14 @@ def test_get_passkey(self, m): response = self.account.get_passkey( '<PASSKEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys/<PASSKEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -800,7 +1187,7 @@ def test_update_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -809,6 +1196,19 @@ def test_update_passkey(self, m): '<PASSKEY_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys/<PASSKEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -816,7 +1216,7 @@ def test_delete_passkey(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -824,6 +1224,14 @@ def test_delete_passkey(self, m): response = self.account.delete_passkey( '<PASSKEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys/<PASSKEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -837,7 +1245,7 @@ def test_update_passkey_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -846,6 +1254,19 @@ def test_update_passkey_verification(self, m): '<PASSKEY_ID>', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/passkeys/<PASSKEY_ID>/verification', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'credential': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -870,13 +1291,28 @@ def test_update_password(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_password( 'password', + old_password='password', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/password', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'password': 'password', + 'oldPassword': 'password', + }, ) self.assertEqual(response.to_dict(), data) @@ -902,7 +1338,7 @@ def test_update_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -911,6 +1347,20 @@ def test_update_phone(self, m): '+12065550100', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'phone': '+12065550100', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -918,12 +1368,20 @@ def test_get_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.get_prefs() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/prefs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -948,7 +1406,7 @@ def test_update_prefs(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -956,6 +1414,19 @@ def test_update_prefs(self, m): response = self.account.update_prefs( {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/prefs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'prefs': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -970,7 +1441,7 @@ def test_create_recovery(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -979,6 +1450,20 @@ def test_create_recovery(self, m): 'email@example.com', 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'url': 'https://example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -993,7 +1478,7 @@ def test_update_recovery(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1003,6 +1488,21 @@ def test_update_recovery(self, m): '<SECRET>', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1017,13 +1517,28 @@ def test_create_recovery_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_recovery_otp( 'email@example.com', + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'phrase': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1039,7 +1554,7 @@ def test_update_recovery_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1049,6 +1564,21 @@ def test_update_recovery_otp(self, m): '<SECRET>', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/recovery/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1059,12 +1589,27 @@ def test_list_sessions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.list_sessions() + response = self.account.list_sessions( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1072,12 +1617,27 @@ def test_delete_sessions(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.delete_sessions() + response = self.account.delete_sessions( + current=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'current': False, + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1115,12 +1675,20 @@ def test_create_anonymous_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_anonymous_session() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/anonymous', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1158,7 +1726,7 @@ def test_create_email_password_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1166,6 +1734,22 @@ def test_create_email_password_session(self, m): response = self.account.create_email_password_session( 'email@example.com', 'password', + duration=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'password': 'password', + 'duration': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1204,7 +1788,7 @@ def test_create_id_token_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1212,6 +1796,28 @@ def test_create_id_token_session(self, m): response = self.account.create_id_token_session( 'apple', '<ID_TOKEN>', + nonce='<NONCE>', + access_token='<ACCESS_TOKEN>', + access_token_expiry=1, + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/id-token', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 'apple', + 'idToken': '<ID_TOKEN>', + 'nonce': '<NONCE>', + 'accessToken': '<ACCESS_TOKEN>', + 'accessTokenExpiry': 1, + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1250,7 +1856,7 @@ def test_update_magic_url_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1259,6 +1865,20 @@ def test_update_magic_url_session(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/magic-url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1296,7 +1916,7 @@ def test_update_phone_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1305,6 +1925,20 @@ def test_update_phone_session(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1342,7 +1976,7 @@ def test_create_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1351,6 +1985,20 @@ def test_create_session(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/token', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1388,7 +2036,7 @@ def test_get_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1396,6 +2044,14 @@ def test_get_session(self, m): response = self.account.get_session( '<SESSION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/<SESSION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1433,7 +2089,7 @@ def test_update_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1441,6 +2097,14 @@ def test_update_session(self, m): response = self.account.update_session( '<SESSION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/<SESSION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1448,7 +2112,7 @@ def test_delete_session(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1456,6 +2120,14 @@ def test_delete_session(self, m): response = self.account.delete_session( '<SESSION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/sessions/<SESSION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1480,12 +2152,20 @@ def test_update_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.update_status() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1500,7 +2180,7 @@ def test_create_email_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1508,6 +2188,22 @@ def test_create_email_token(self, m): response = self.account.create_email_token( '<USER_ID>', 'email@example.com', + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'phrase': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1523,7 +2219,7 @@ def test_create_magic_url_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1531,6 +2227,24 @@ def test_create_magic_url_token(self, m): response = self.account.create_magic_url_token( '<USER_ID>', 'email@example.com', + url='https://example.com', + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/magic-url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'url': 'https://example.com', + 'phrase': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1539,13 +2253,32 @@ def test_create_o_auth2_token(self, m): data = None headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_o_auth2_token( 'amazon', + success='https://example.com', + failure='https://example.com', + scopes=[], + state='<STATE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/oauth2/amazon', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'success': ['https://example.com'], + 'failure': ['https://example.com'], + 'state': ['<STATE>'], + }, ) self.assertEqual(response, data) @@ -1560,12 +2293,20 @@ def test_create_passkey_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_passkey_token() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/passkey', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1580,7 +2321,7 @@ def test_update_passkey_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1589,6 +2330,20 @@ def test_update_passkey_token(self, m): '<CHALLENGE_ID>', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/passkey', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'challengeId': '<CHALLENGE_ID>', + 'credential': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1603,7 +2358,7 @@ def test_create_phone_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1612,6 +2367,20 @@ def test_create_phone_token(self, m): '<USER_ID>', '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/tokens/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'phone': '+12065550100', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1626,7 +2395,7 @@ def test_create_email_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1634,6 +2403,19 @@ def test_create_email_verification(self, m): response = self.account.create_email_verification( 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'url': 'https://example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1648,7 +2430,7 @@ def test_create_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1656,6 +2438,19 @@ def test_create_verification(self, m): response = self.account.create_verification( 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'url': 'https://example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1670,7 +2465,7 @@ def test_update_email_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1679,6 +2474,20 @@ def test_update_email_verification(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1693,7 +2502,7 @@ def test_update_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1702,6 +2511,20 @@ def test_update_verification(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1716,12 +2539,27 @@ def test_create_email_verification_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.account.create_email_verification_otp() + response = self.account.create_email_verification_otp( + phrase=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'phrase': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1736,7 +2574,7 @@ def test_update_email_verification_otp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1745,6 +2583,20 @@ def test_update_email_verification_otp(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/email/otp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1759,12 +2611,20 @@ def test_create_phone_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.account.create_phone_verification() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1779,7 +2639,7 @@ def test_update_phone_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1788,4 +2648,18 @@ def test_update_phone_verification(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/account/verifications/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_activities.py b/test/services/test_activities.py index d9d1e662..f6fe64d5 100644 --- a/test/services/test_activities.py +++ b/test/services/test_activities.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list_events(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.activities.list_events() + response = self.activities.list_events( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/activities/events', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -65,7 +83,7 @@ def test_get_event(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -73,4 +91,12 @@ def test_get_event(self, m): response = self.activities.get_event( '<EVENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/activities/events/<EVENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_advisor.py b/test/services/test_advisor.py index 74d8eb1d..f2dacf9a 100644 --- a/test/services/test_advisor.py +++ b/test/services/test_advisor.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list_reports(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.advisor.list_reports() + response = self.advisor.list_reports( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -47,7 +65,7 @@ def test_get_report(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -55,6 +73,14 @@ def test_get_report(self, m): response = self.advisor.get_report( '<REPORT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports/<REPORT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -62,7 +88,7 @@ def test_delete_report(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -70,6 +96,14 @@ def test_delete_report(self, m): response = self.advisor.delete_report( '<REPORT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports/<REPORT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -80,13 +114,28 @@ def test_list_insights(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.advisor.list_insights( '<REPORT_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports/<REPORT_ID>/insights', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -110,7 +159,7 @@ def test_get_insight(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -119,4 +168,12 @@ def test_get_insight(self, m): '<REPORT_ID>', '<INSIGHT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/reports/<REPORT_ID>/insights/<INSIGHT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_apps.py b/test/services/test_apps.py index 0e6c2628..6c71f4ae 100644 --- a/test/services/test_apps.py +++ b/test/services/test_apps.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.apps.list() + response = self.apps.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -62,7 +80,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -71,6 +89,53 @@ def test_create(self, m): '<APP_ID>', '<NAME>', [], + description='<DESCRIPTION>', + client_uri='https://example.com', + logo_uri='https://example.com', + privacy_policy_url='https://example.com', + terms_url='https://example.com', + contacts=[], + tagline='<TAGLINE>', + tags=[], + images=[], + support_url='https://example.com', + data_deletion_url='https://example.com', + post_logout_redirect_uris=[], + enabled=False, + type='public', + device_flow=False, + team_id='<TEAM_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '<APP_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'clientUri': 'https://example.com', + 'logoUri': 'https://example.com', + 'privacyPolicyUrl': 'https://example.com', + 'termsUrl': 'https://example.com', + 'contacts': [], + 'tagline': '<TAGLINE>', + 'tags': [], + 'images': [], + 'supportUrl': 'https://example.com', + 'dataDeletionUrl': 'https://example.com', + 'redirectUris': [], + 'postLogoutRedirectUris': [], + 'enabled': False, + 'type': 'public', + 'deviceFlow': False, + 'teamId': '<TEAM_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -82,12 +147,20 @@ def test_list_installation_scopes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_installation_scopes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/scopes/installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +171,20 @@ def test_list_o_auth2_scopes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_o_auth2_scopes() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/scopes/oauth2', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -138,7 +219,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -146,6 +227,14 @@ def test_get(self, m): response = self.apps.get( '<APP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -180,7 +269,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -188,6 +277,55 @@ def test_update(self, m): response = self.apps.update( '<APP_ID>', '<NAME>', + description='<DESCRIPTION>', + client_uri='https://example.com', + logo_uri='https://example.com', + privacy_policy_url='https://example.com', + terms_url='https://example.com', + contacts=[], + tagline='<TAGLINE>', + tags=[], + images=[], + support_url='https://example.com', + data_deletion_url='https://example.com', + enabled=False, + redirect_uris=[], + post_logout_redirect_uris=[], + type='public', + device_flow=False, + installation_scopes=[], + installation_redirect_url='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'clientUri': 'https://example.com', + 'logoUri': 'https://example.com', + 'privacyPolicyUrl': 'https://example.com', + 'termsUrl': 'https://example.com', + 'contacts': [], + 'tagline': '<TAGLINE>', + 'tags': [], + 'images': [], + 'supportUrl': 'https://example.com', + 'dataDeletionUrl': 'https://example.com', + 'enabled': False, + 'redirectUris': [], + 'postLogoutRedirectUris': [], + 'type': 'public', + 'deviceFlow': False, + 'installationScopes': [], + 'installationRedirectUrl': 'https://example.com', + }, ) self.assertEqual(response.to_dict(), data) @@ -196,7 +334,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -204,6 +342,14 @@ def test_delete(self, m): response = self.apps.delete( '<APP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -214,13 +360,28 @@ def test_list_installations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_installations( '<APP_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -239,7 +400,7 @@ def test_get_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -248,6 +409,14 @@ def test_get_installation(self, m): '<APP_ID>', '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/installations/<INSTALLATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -255,7 +424,7 @@ def test_delete_installation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -264,6 +433,14 @@ def test_delete_installation(self, m): '<APP_ID>', '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -277,7 +454,7 @@ def test_create_installation_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -286,6 +463,14 @@ def test_create_installation_token(self, m): '<APP_ID>', '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/installations/<INSTALLATION_ID>/tokens', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -296,13 +481,28 @@ def test_list_keys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_keys( '<APP_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/keys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -320,7 +520,7 @@ def test_create_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -328,6 +528,14 @@ def test_create_key(self, m): response = self.apps.create_key( '<APP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/keys', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -344,7 +552,7 @@ def test_get_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -353,6 +561,14 @@ def test_get_key(self, m): '<APP_ID>', '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/keys/<KEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -360,7 +576,7 @@ def test_delete_key(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -369,6 +585,14 @@ def test_delete_key(self, m): '<APP_ID>', '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -403,7 +627,7 @@ def test_update_labels(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -412,6 +636,19 @@ def test_update_labels(self, m): '<APP_ID>', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/labels', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'labels': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -422,13 +659,28 @@ def test_list_secrets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.apps.list_secrets( '<APP_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/secrets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -446,7 +698,7 @@ def test_create_secret(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -454,6 +706,14 @@ def test_create_secret(self, m): response = self.apps.create_secret( '<APP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/secrets', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -470,7 +730,7 @@ def test_get_secret(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -479,6 +739,14 @@ def test_get_secret(self, m): '<APP_ID>', '<SECRET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/secrets/<SECRET_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -486,7 +754,7 @@ def test_delete_secret(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +763,14 @@ def test_delete_secret(self, m): '<APP_ID>', '<SECRET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/secrets/<SECRET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -529,7 +805,7 @@ def test_update_team(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -538,6 +814,19 @@ def test_update_team(self, m): '<APP_ID>', '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/team', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '<TEAM_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -545,7 +834,7 @@ def test_delete_tokens(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -553,4 +842,12 @@ def test_delete_tokens(self, m): response = self.apps.delete_tokens( '<APP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/apps/<APP_ID>/tokens', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_avatars.py b/test/services/test_avatars.py index c54fe183..96f0e5ba 100644 --- a/test/services/test_avatars.py +++ b/test/services/test_avatars.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -19,13 +21,31 @@ def test_get_browser(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_browser( 'aa', + width=1, + height=1, + quality=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/browsers/aa', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + }, ) self.assertEqual(response, data) @@ -34,13 +54,31 @@ def test_get_credit_card(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_credit_card( 'amex', + width=1, + height=1, + quality=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/credit-cards/amex', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + }, ) self.assertEqual(response, data) @@ -49,7 +87,7 @@ def test_get_favicon(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -57,6 +95,19 @@ def test_get_favicon(self, m): response = self.avatars.get_favicon( 'https://example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/favicon', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'url': ['https://example.com'], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -64,13 +115,31 @@ def test_get_flag(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_flag( 'af', + width=1, + height=1, + quality=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/flags/af', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + }, ) self.assertEqual(response, data) @@ -79,13 +148,30 @@ def test_get_image(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_image( 'https://example.com', + width=1, + height=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/image', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'url': ['https://example.com'], + 'width': ['1'], + 'height': ['1'], + }, ) self.assertEqual(response, data) @@ -94,12 +180,33 @@ def test_get_initials(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) - response = self.avatars.get_initials() + response = self.avatars.get_initials( + name='<NAME>', + width=1, + height=1, + background='FFFFFF', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/initials', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'name': ['<NAME>'], + 'width': ['1'], + 'height': ['1'], + 'background': ['FFFFFF'], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -107,12 +214,41 @@ def test_get_photo(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) - response = self.avatars.get_photo() + response = self.avatars.get_photo( + width=1, + height=1, + quality=1, + output='png', + rating='g', + user_id='current()', + email_hash='<EMAIL_HASH>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/photo', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + 'output': ['png'], + 'rating': ['g'], + 'userId': ['current()'], + 'emailHash': ['<EMAIL_HASH>'], + 'name': ['<NAME>'], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -137,7 +273,7 @@ def test_update_photo(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -145,6 +281,25 @@ def test_update_photo(self, m): response = self.avatars.update_photo( InputFile.from_bytes(bytearray(), "example.file"), ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/photo', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'file': '', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -152,12 +307,20 @@ def test_delete_photo(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.avatars.delete_photo() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/photo', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -165,13 +328,32 @@ def test_get_qr(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_qr( '<TEXT>', + size=1, + margin=1, + download=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/qr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'text': ['<TEXT>'], + 'size': ['1'], + 'margin': ['1'], + 'download': ['false'], + }, ) self.assertEqual(response, data) @@ -180,12 +362,61 @@ def test_get_screenshot(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, ) response = self.avatars.get_screenshot( 'https://example.com', + headers={}, + viewport_width=1, + viewport_height=1, + scale=1.0, + theme='dark', + user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15', + fullpage=False, + locale='en-US', + timezone='America/New_York', + latitude=1.0, + longitude=1.0, + accuracy=1.0, + touch=False, + permissions=[], + sleep=1, + width=1, + height=1, + quality=1, + output='jpeg', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/avatars/screenshots', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'url': ['https://example.com'], + 'viewportWidth': ['1'], + 'viewportHeight': ['1'], + 'scale': ['1.0'], + 'theme': ['dark'], + 'userAgent': ['Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15'], + 'fullpage': ['false'], + 'locale': ['en-US'], + 'timezone': ['America/New_York'], + 'latitude': ['1.0'], + 'longitude': ['1.0'], + 'accuracy': ['1.0'], + 'touch': ['false'], + 'sleep': ['1'], + 'width': ['1'], + 'height': ['1'], + 'quality': ['1'], + 'output': ['jpeg'], + }, ) self.assertEqual(response, data) diff --git a/test/services/test_backups.py b/test/services/test_backups.py index 175b3515..2254017e 100644 --- a/test/services/test_backups.py +++ b/test/services/test_backups.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list_archives(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.backups.list_archives() + response = self.backups.list_archives( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -44,13 +62,28 @@ def test_create_archive(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.backups.create_archive( [], + resource_id='<RESOURCE_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'services': [], + 'resourceId': '<RESOURCE_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -68,7 +101,7 @@ def test_get_archive(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -76,6 +109,14 @@ def test_get_archive(self, m): response = self.backups.get_archive( '<ARCHIVE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives/<ARCHIVE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -83,7 +124,7 @@ def test_delete_archive(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -91,6 +132,14 @@ def test_delete_archive(self, m): response = self.backups.delete_archive( '<ARCHIVE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/archives/<ARCHIVE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -101,12 +150,28 @@ def test_list_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.backups.list_policies() + response = self.backups.list_policies( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -125,7 +190,7 @@ def test_create_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -135,6 +200,28 @@ def test_create_policy(self, m): [], 1, 'schedule', + name='<NAME>', + resource_id='<RESOURCE_ID>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '<POLICY_ID>', + 'name': '<NAME>', + 'services': [], + 'resourceId': '<RESOURCE_ID>', + 'enabled': False, + 'retention': 1, + 'schedule': 'schedule', + }, ) self.assertEqual(response.to_dict(), data) @@ -154,7 +241,7 @@ def test_get_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -162,6 +249,14 @@ def test_get_policy(self, m): response = self.backups.get_policy( '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies/<POLICY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -180,13 +275,33 @@ def test_update_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.backups.update_policy( '<POLICY_ID>', + name='<NAME>', + retention=1, + schedule='schedule', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'retention': 1, + 'schedule': 'schedule', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -195,7 +310,7 @@ def test_delete_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -203,6 +318,14 @@ def test_delete_policy(self, m): response = self.backups.delete_policy( '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -221,7 +344,7 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -229,6 +352,24 @@ def test_create_restoration(self, m): response = self.backups.create_restoration( '<ARCHIVE_ID>', [], + new_resource_id='<NEW_RESOURCE_ID>', + new_resource_name='<NEW_RESOURCE_NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/restoration', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'archiveId': '<ARCHIVE_ID>', + 'services': [], + 'newResourceId': '<NEW_RESOURCE_ID>', + 'newResourceName': '<NEW_RESOURCE_NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -240,12 +381,28 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.backups.list_restorations() + response = self.backups.list_restorations( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -264,7 +421,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -272,4 +429,12 @@ def test_get_restoration(self, m): response = self.backups.get_restoration( '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/backups/restorations/<RESTORATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_databases.py b/test/services/test_databases.py index 14d81297..65de0627 100644 --- a/test/services/test_databases.py +++ b/test/services/test_databases.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.databases.list() + response = self.databases.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +63,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +71,24 @@ def test_create(self, m): response = self.databases.create( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +100,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.databases.list_transactions() + response = self.databases.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -82,12 +136,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.databases.create_transaction() + response = self.databases.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -102,7 +171,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -110,6 +179,14 @@ def test_get_transaction(self, m): response = self.databases.get_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions/<TRANSACTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -124,13 +201,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.update_transaction( '<TRANSACTION_ID>', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -139,7 +232,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -147,6 +240,14 @@ def test_delete_transaction(self, m): response = self.databases.delete_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -161,13 +262,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.create_operations( '<TRANSACTION_ID>', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/transactions/<TRANSACTION_ID>/operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -184,7 +299,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -192,6 +307,14 @@ def test_get(self, m): response = self.databases.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -207,13 +330,31 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.update( '<DATABASE_ID>', + name='<NAME>', + enabled=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -222,7 +363,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -230,6 +371,14 @@ def test_delete(self, m): response = self.databases.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -240,13 +389,30 @@ def test_list_collections(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.databases.list_collections( '<DATABASE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -269,7 +435,7 @@ def test_create_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -278,6 +444,32 @@ def test_create_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<NAME>', + permissions=[], + document_security=False, + enabled=False, + attributes=[], + indexes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'collectionId': '<COLLECTION_ID>', + 'name': '<NAME>', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'attributes': [], + 'indexes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -300,7 +492,7 @@ def test_get_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -309,6 +501,14 @@ def test_get_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -330,7 +530,7 @@ def test_update_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -338,6 +538,30 @@ def test_update_collection(self, m): response = self.databases.update_collection( '<DATABASE_ID>', '<COLLECTION_ID>', + name='<NAME>', + permissions=[], + document_security=False, + enabled=False, + purge=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'purge': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -346,7 +570,7 @@ def test_delete_collection(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -355,6 +579,14 @@ def test_delete_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -365,7 +597,7 @@ def test_list_attributes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -373,6 +605,21 @@ def test_list_attributes(self, m): response = self.databases.list_attributes( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -390,7 +637,7 @@ def test_create_big_int_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -399,7 +646,31 @@ def test_create_big_int_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/bigint', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -417,7 +688,7 @@ def test_update_big_int_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -426,8 +697,30 @@ def test_update_big_int_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 1, + min=1, + max=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/bigint/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -445,7 +738,7 @@ def test_create_boolean_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -454,7 +747,27 @@ def test_create_boolean_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default=False, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/boolean', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': False, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -472,7 +785,7 @@ def test_update_boolean_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -481,8 +794,26 @@ def test_update_boolean_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, - True, + False, + False, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/boolean/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': False, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -501,7 +832,7 @@ def test_create_datetime_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -510,7 +841,27 @@ def test_create_datetime_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default='2020-10-15T06:38:00.000+00:00', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/datetime', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -529,7 +880,7 @@ def test_update_datetime_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -538,8 +889,26 @@ def test_update_datetime_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, '2020-10-15T06:38:00.000+00:00', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/datetime/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -558,7 +927,7 @@ def test_create_email_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -567,7 +936,27 @@ def test_create_email_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default='email@example.com', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'email@example.com', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -586,7 +975,7 @@ def test_update_email_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -595,8 +984,26 @@ def test_update_email_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 'email@example.com', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/email/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'email@example.com', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -616,7 +1023,7 @@ def test_create_enum_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -626,7 +1033,28 @@ def test_create_enum_attribute(self, m): '<COLLECTION_ID>', '<KEY>', [], - True, + False, + default='active', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/enum', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'elements': [], + 'required': False, + 'default': 'active', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -646,7 +1074,7 @@ def test_update_enum_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -656,8 +1084,27 @@ def test_update_enum_attribute(self, m): '<COLLECTION_ID>', '<KEY>', [], - True, + False, 'active', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/enum/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'elements': [], + 'required': False, + 'default': 'active', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -675,7 +1122,7 @@ def test_create_float_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -684,7 +1131,31 @@ def test_create_float_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + min=1.0, + max=1.0, + default=1.0, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/float', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -702,7 +1173,7 @@ def test_update_float_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -711,8 +1182,30 @@ def test_update_float_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 1.0, + min=1.0, + max=1.0, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/float/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -730,7 +1223,7 @@ def test_create_integer_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -739,7 +1232,31 @@ def test_create_integer_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/integer', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -757,7 +1274,7 @@ def test_update_integer_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -766,8 +1283,30 @@ def test_update_integer_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 1, + min=1, + max=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/integer/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -786,7 +1325,7 @@ def test_create_ip_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -795,7 +1334,27 @@ def test_create_ip_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default='192.0.2.0', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/ip', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': '192.0.2.0', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -814,7 +1373,7 @@ def test_update_ip_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -823,8 +1382,26 @@ def test_update_ip_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, '192.0.2.0', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/ip/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '192.0.2.0', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -842,7 +1419,7 @@ def test_create_line_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -851,7 +1428,25 @@ def test_create_line_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/line', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -869,7 +1464,7 @@ def test_update_line_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -878,7 +1473,26 @@ def test_update_line_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/line/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -896,7 +1510,7 @@ def test_create_longtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -905,7 +1519,29 @@ def test_create_longtext_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/longtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -923,7 +1559,7 @@ def test_update_longtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -932,8 +1568,26 @@ def test_update_longtext_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/longtext/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -951,7 +1605,7 @@ def test_create_mediumtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -960,7 +1614,29 @@ def test_create_mediumtext_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/mediumtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -978,7 +1654,7 @@ def test_update_mediumtext_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -987,8 +1663,26 @@ def test_update_mediumtext_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/mediumtext/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1006,7 +1700,7 @@ def test_create_point_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1015,7 +1709,25 @@ def test_create_point_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/point', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1033,7 +1745,7 @@ def test_update_point_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1042,7 +1754,26 @@ def test_update_point_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/point/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1060,7 +1791,7 @@ def test_create_polygon_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1069,7 +1800,25 @@ def test_create_polygon_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/polygon', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1087,7 +1836,7 @@ def test_update_polygon_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1096,7 +1845,26 @@ def test_update_polygon_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/polygon/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1120,7 +1888,7 @@ def test_create_relationship_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1130,6 +1898,30 @@ def test_create_relationship_attribute(self, m): '<COLLECTION_ID>', '<RELATED_COLLECTION_ID>', 'oneToOne', + two_way=False, + key='<KEY>', + two_way_key='<TWO_WAY_KEY>', + on_delete='cascade', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/relationship', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'relatedCollectionId': '<RELATED_COLLECTION_ID>', + 'type': 'oneToOne', + 'twoWay': False, + 'key': '<KEY>', + 'twoWayKey': '<TWO_WAY_KEY>', + 'onDelete': 'cascade', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1153,7 +1945,7 @@ def test_update_relationship_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1162,6 +1954,24 @@ def test_update_relationship_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', + on_delete='cascade', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/relationship/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'onDelete': 'cascade', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1180,7 +1990,7 @@ def test_create_string_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1190,7 +2000,30 @@ def test_create_string_attribute(self, m): '<COLLECTION_ID>', '<KEY>', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/string', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1209,7 +2042,7 @@ def test_update_string_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1218,8 +2051,28 @@ def test_update_string_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 'Hello World', + size=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/string/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1237,7 +2090,7 @@ def test_create_text_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1246,7 +2099,29 @@ def test_create_text_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/text', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1264,7 +2139,7 @@ def test_update_text_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1273,8 +2148,26 @@ def test_update_text_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/text/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1293,7 +2186,7 @@ def test_create_url_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1302,7 +2195,27 @@ def test_create_url_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, + default='https://example.com', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'https://example.com', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1321,7 +2234,7 @@ def test_update_url_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1330,8 +2243,26 @@ def test_update_url_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 'https://example.com', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/url/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'https://example.com', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1350,7 +2281,7 @@ def test_create_varchar_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1360,7 +2291,30 @@ def test_create_varchar_attribute(self, m): '<COLLECTION_ID>', '<KEY>', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/varchar', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1379,7 +2333,7 @@ def test_update_varchar_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1388,8 +2342,28 @@ def test_update_varchar_attribute(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<KEY>', - True, + False, 'Hello World', + size=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/varchar/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1407,7 +2381,7 @@ def test_get_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1417,6 +2391,14 @@ def test_get_attribute(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1424,7 +2406,7 @@ def test_delete_attribute(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1434,6 +2416,14 @@ def test_delete_attribute(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/attributes/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1444,7 +2434,7 @@ def test_list_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1452,6 +2442,25 @@ def test_list_documents(self, m): response = self.databases.list_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1468,7 +2477,7 @@ def test_create_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1478,6 +2487,24 @@ def test_create_document(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', {}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documentId': '<DOCUMENT_ID>', + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1490,7 +2517,7 @@ def test_create_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1499,6 +2526,21 @@ def test_create_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1510,7 +2552,7 @@ def test_upsert_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1519,6 +2561,21 @@ def test_upsert_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1530,7 +2587,7 @@ def test_update_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1538,6 +2595,24 @@ def test_update_documents(self, m): response = self.databases.update_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + data={}, + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1549,7 +2624,7 @@ def test_delete_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1557,6 +2632,22 @@ def test_delete_documents(self, m): response = self.databases.delete_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1568,7 +2659,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1576,6 +2667,26 @@ def test_create_query(self, m): response = self.databases.create_query( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1592,7 +2703,7 @@ def test_get_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1601,6 +2712,21 @@ def test_get_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1618,7 +2744,7 @@ def test_upsert_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1627,6 +2753,24 @@ def test_upsert_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1644,7 +2788,7 @@ def test_update_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1653,6 +2797,24 @@ def test_update_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1662,7 +2824,7 @@ def test_delete_document(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1671,6 +2833,20 @@ def test_delete_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response, data) @@ -1687,7 +2863,7 @@ def test_decrement_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1697,6 +2873,24 @@ def test_decrement_document_attribute(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', '<ATTRIBUTE>', + value=1.0, + min=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>/<ATTRIBUTE>/decrement', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'min': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1714,7 +2908,7 @@ def test_increment_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1724,6 +2918,24 @@ def test_increment_document_attribute(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', '<ATTRIBUTE>', + value=1.0, + max=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>/<ATTRIBUTE>/increment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'max': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1736,7 +2948,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1744,6 +2956,21 @@ def test_list_indexes(self, m): response = self.databases.list_indexes( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1763,7 +2990,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1774,6 +3001,27 @@ def test_create_index(self, m): '<KEY>', 'key', [], + orders=[], + lengths=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'type': 'key', + 'attributes': [], + 'orders': [], + 'lengths': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1793,7 +3041,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1803,6 +3051,14 @@ def test_get_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1810,7 +3066,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1820,4 +3076,12 @@ def test_delete_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_documents_db.py b/test/services/test_documents_db.py index c9840596..40ceecf5 100644 --- a/test/services/test_documents_db.py +++ b/test/services/test_documents_db.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.documents_db.list() + response = self.documents_db.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +61,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +69,30 @@ def test_create(self, m): response = self.documents_db.create( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +104,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -78,12 +128,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.documents_db.list_transactions() + response = self.documents_db.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +164,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.documents_db.create_transaction() + response = self.documents_db.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -118,7 +199,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -126,6 +207,14 @@ def test_get_transaction(self, m): response = self.documents_db.get_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions/<TRANSACTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -140,13 +229,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.update_transaction( '<TRANSACTION_ID>', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -155,7 +260,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -163,6 +268,14 @@ def test_delete_transaction(self, m): response = self.documents_db.delete_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -177,13 +290,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.create_operations( '<TRANSACTION_ID>', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/transactions/<TRANSACTION_ID>/operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -200,7 +327,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -208,6 +335,14 @@ def test_get(self, m): response = self.documents_db.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -223,7 +358,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -231,6 +366,29 @@ def test_update(self, m): response = self.documents_db.update( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -239,7 +397,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -247,6 +405,14 @@ def test_delete(self, m): response = self.documents_db.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -257,13 +423,30 @@ def test_list_collections(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.list_collections( '<DATABASE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -286,7 +469,7 @@ def test_create_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -295,6 +478,32 @@ def test_create_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<NAME>', + permissions=[], + document_security=False, + enabled=False, + attributes=[], + indexes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'collectionId': '<COLLECTION_ID>', + 'name': '<NAME>', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'attributes': [], + 'indexes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -317,7 +526,7 @@ def test_get_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -326,6 +535,14 @@ def test_get_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -347,7 +564,7 @@ def test_update_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -356,6 +573,29 @@ def test_update_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<NAME>', + permissions=[], + document_security=False, + enabled=False, + purge=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'purge': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -364,7 +604,7 @@ def test_delete_collection(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -373,6 +613,14 @@ def test_delete_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -383,7 +631,7 @@ def test_list_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -391,6 +639,25 @@ def test_list_documents(self, m): response = self.documents_db.list_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -407,7 +674,7 @@ def test_create_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -417,6 +684,24 @@ def test_create_document(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', {}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documentId': '<DOCUMENT_ID>', + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -429,7 +714,7 @@ def test_create_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -438,6 +723,21 @@ def test_create_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -449,7 +749,7 @@ def test_upsert_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -458,6 +758,21 @@ def test_upsert_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -469,7 +784,7 @@ def test_update_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +792,24 @@ def test_update_documents(self, m): response = self.documents_db.update_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + data={}, + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -488,7 +821,7 @@ def test_delete_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -496,6 +829,22 @@ def test_delete_documents(self, m): response = self.documents_db.delete_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -507,7 +856,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -515,6 +864,26 @@ def test_create_query(self, m): response = self.documents_db.create_query( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -531,7 +900,7 @@ def test_get_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -540,6 +909,21 @@ def test_get_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -557,7 +941,7 @@ def test_upsert_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -566,6 +950,24 @@ def test_upsert_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -583,7 +985,7 @@ def test_update_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -592,6 +994,24 @@ def test_update_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -601,7 +1021,7 @@ def test_delete_document(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -610,6 +1030,20 @@ def test_delete_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response, data) @@ -626,7 +1060,7 @@ def test_decrement_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -636,6 +1070,24 @@ def test_decrement_document_attribute(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', '<ATTRIBUTE>', + value=1.0, + min=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>/<ATTRIBUTE>/decrement', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'min': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -653,7 +1105,7 @@ def test_increment_document_attribute(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -663,6 +1115,24 @@ def test_increment_document_attribute(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', '<ATTRIBUTE>', + value=1.0, + max=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>/<ATTRIBUTE>/increment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'max': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -675,7 +1145,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -683,6 +1153,21 @@ def test_list_indexes(self, m): response = self.documents_db.list_indexes( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -702,7 +1187,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -713,6 +1198,27 @@ def test_create_index(self, m): '<KEY>', 'key', [], + orders=[], + lengths=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'type': 'key', + 'attributes': [], + 'orders': [], + 'lengths': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -732,7 +1238,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -742,6 +1248,14 @@ def test_get_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -749,7 +1263,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -759,6 +1273,14 @@ def test_delete_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -812,13 +1334,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -830,13 +1366,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.documents_db.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -852,7 +1406,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -860,6 +1414,14 @@ def test_get_replicas(self, m): response = self.documents_db.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -879,7 +1441,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -887,4 +1449,12 @@ def test_get_status(self, m): response = self.documents_db.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/documentsdb/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_domains.py b/test/services/test_domains.py index 8ae6d3c4..279d3402 100644 --- a/test/services/test_domains.py +++ b/test/services/test_domains.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.domains.list() + response = self.domains.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -48,7 +68,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -57,6 +77,20 @@ def test_create(self, m): '<TEAM_ID>', 'example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '<TEAM_ID>', + 'domain': 'example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -71,13 +105,30 @@ def test_get_price(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.get_price( 'example.com', + period_years=1, + registration_type='new', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/price', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'domain': ['example.com'], + 'periodYears': ['1'], + 'registrationType': ['new'], + }, ) self.assertEqual(response.to_dict(), data) @@ -89,13 +140,29 @@ def test_list_prices(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.list_prices( [], + period_years=1, + registration_type='new', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/prices', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'periodYears': ['1'], + 'registrationType': ['new'], + }, ) self.assertEqual(response.to_dict(), data) @@ -117,7 +184,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -125,6 +192,14 @@ def test_get(self, m): response = self.domains.get( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -132,7 +207,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -140,6 +215,14 @@ def test_delete(self, m): response = self.domains.delete( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -160,13 +243,27 @@ def test_update_nameservers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.update_nameservers( '<DOMAIN_ID>', + nameservers=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/nameservers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'nameservers': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -188,7 +285,7 @@ def test_verify_nameservers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -196,6 +293,14 @@ def test_verify_nameservers(self, m): response = self.domains.verify_nameservers( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/nameservers/verification', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -206,7 +311,7 @@ def test_get_preset_google_workspace(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -214,6 +319,14 @@ def test_get_preset_google_workspace(self, m): response = self.domains.get_preset_google_workspace( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/google-workspace', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -224,7 +337,7 @@ def test_create_preset_google_workspace(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -232,6 +345,14 @@ def test_create_preset_google_workspace(self, m): response = self.domains.create_preset_google_workspace( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/google-workspace', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -242,7 +363,7 @@ def test_get_preset_i_cloud(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -250,6 +371,14 @@ def test_get_preset_i_cloud(self, m): response = self.domains.get_preset_i_cloud( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/icloud', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -260,7 +389,7 @@ def test_create_preset_i_cloud(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -268,6 +397,14 @@ def test_create_preset_i_cloud(self, m): response = self.domains.create_preset_i_cloud( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/icloud', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -278,7 +415,7 @@ def test_get_preset_mailgun(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -286,6 +423,14 @@ def test_get_preset_mailgun(self, m): response = self.domains.get_preset_mailgun( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/mailgun', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -296,7 +441,7 @@ def test_create_preset_mailgun(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -304,6 +449,14 @@ def test_create_preset_mailgun(self, m): response = self.domains.create_preset_mailgun( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/mailgun', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -314,7 +467,7 @@ def test_get_preset_outlook(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -322,6 +475,14 @@ def test_get_preset_outlook(self, m): response = self.domains.get_preset_outlook( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/outlook', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -332,7 +493,7 @@ def test_create_preset_outlook(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -340,6 +501,14 @@ def test_create_preset_outlook(self, m): response = self.domains.create_preset_outlook( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/outlook', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -350,7 +519,7 @@ def test_get_preset_proton_mail(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -358,6 +527,14 @@ def test_get_preset_proton_mail(self, m): response = self.domains.get_preset_proton_mail( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/proton-mail', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -368,7 +545,7 @@ def test_create_preset_proton_mail(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -376,6 +553,14 @@ def test_create_preset_proton_mail(self, m): response = self.domains.create_preset_proton_mail( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/proton-mail', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -386,7 +571,7 @@ def test_get_preset_zoho(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -394,6 +579,14 @@ def test_get_preset_zoho(self, m): response = self.domains.get_preset_zoho( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/zoho', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -404,7 +597,7 @@ def test_create_preset_zoho(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -412,6 +605,14 @@ def test_create_preset_zoho(self, m): response = self.domains.create_preset_zoho( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/presets/zoho', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -422,13 +623,28 @@ def test_list_records(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.domains.list_records( '<DOMAIN_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -450,7 +666,7 @@ def test_create_record_a(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -460,6 +676,23 @@ def test_create_record_a(self, m): 'name', 'value', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/a', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -481,7 +714,7 @@ def test_update_record_a(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -492,6 +725,23 @@ def test_update_record_a(self, m): 'name', 'value', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/a/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -513,7 +763,7 @@ def test_create_record_aaaa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -523,6 +773,23 @@ def test_create_record_aaaa(self, m): 'name', 'value', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/aaaa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -544,7 +811,7 @@ def test_update_record_aaaa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -555,6 +822,23 @@ def test_update_record_aaaa(self, m): 'name', 'value', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/aaaa/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -576,7 +860,7 @@ def test_create_record_alias(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -586,6 +870,23 @@ def test_create_record_alias(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/alias', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -607,7 +908,7 @@ def test_update_record_alias(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +919,23 @@ def test_update_record_alias(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/alias/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -639,7 +957,7 @@ def test_create_record_caa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -649,6 +967,23 @@ def test_create_record_caa(self, m): 'name', 'value', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/caa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -670,7 +1005,7 @@ def test_update_record_caa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -681,6 +1016,23 @@ def test_update_record_caa(self, m): 'name', 'value', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/caa/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': 'value', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -702,7 +1054,7 @@ def test_create_record_cname(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -712,6 +1064,23 @@ def test_create_record_cname(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/cname', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -733,7 +1102,7 @@ def test_update_record_cname(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -744,6 +1113,23 @@ def test_update_record_cname(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/cname/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -765,7 +1151,7 @@ def test_create_record_https(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -775,6 +1161,23 @@ def test_create_record_https(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/https', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -796,7 +1199,7 @@ def test_update_record_https(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -807,6 +1210,23 @@ def test_update_record_https(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/https/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -828,7 +1248,7 @@ def test_create_record_mx(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -839,6 +1259,24 @@ def test_create_record_mx(self, m): '<VALUE>', 1, 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/mx', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'priority': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -860,7 +1298,7 @@ def test_update_record_mx(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -872,6 +1310,24 @@ def test_update_record_mx(self, m): '<VALUE>', 1, 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/mx/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'priority': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -893,7 +1349,7 @@ def test_create_record_ns(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -903,6 +1359,23 @@ def test_create_record_ns(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/ns', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -924,7 +1397,7 @@ def test_update_record_ns(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -935,6 +1408,23 @@ def test_update_record_ns(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/ns/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -956,7 +1446,7 @@ def test_create_record_srv(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -969,6 +1459,26 @@ def test_create_record_srv(self, m): 1, 1, 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/srv', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'priority': 1, + 'weight': 1, + 'port': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -990,7 +1500,7 @@ def test_update_record_srv(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1004,6 +1514,26 @@ def test_update_record_srv(self, m): 1, 1, 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/srv/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'priority': 1, + 'weight': 1, + 'port': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1025,7 +1555,7 @@ def test_create_record_txt(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1034,6 +1564,24 @@ def test_create_record_txt(self, m): '<DOMAIN_ID>', 'name', 1, + value='<VALUE>', + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/txt', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1055,7 +1603,7 @@ def test_update_record_txt(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1066,6 +1614,23 @@ def test_update_record_txt(self, m): 'name', '<VALUE>', 1, + comment='<COMMENT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/txt/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'value': '<VALUE>', + 'ttl': 1, + 'comment': '<COMMENT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1087,7 +1652,7 @@ def test_get_record(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1096,6 +1661,14 @@ def test_get_record(self, m): '<DOMAIN_ID>', '<RECORD_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/<RECORD_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1103,7 +1676,7 @@ def test_delete_record(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1112,6 +1685,14 @@ def test_delete_record(self, m): '<DOMAIN_ID>', '<RECORD_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/records/<RECORD_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1132,7 +1713,7 @@ def test_update_team(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1141,6 +1722,19 @@ def test_update_team(self, m): '<DOMAIN_ID>', '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/team', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '<TEAM_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1152,7 +1746,7 @@ def test_get_transfer_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1160,6 +1754,14 @@ def test_get_transfer_status(self, m): response = self.domains.get_transfer_status( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/transfers/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1167,7 +1769,7 @@ def test_get_zone(self, m): data = '; café zone\nwww 3600 IN A 192.0.2.1\n' headers = {'Content-Type': 'text/plain; charset=utf-8'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=data, headers=headers, @@ -1175,6 +1777,14 @@ def test_get_zone(self, m): response = self.domains.get_zone( '<DOMAIN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/zone', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1195,7 +1805,7 @@ def test_update_zone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1204,4 +1814,17 @@ def test_update_zone(self, m): '<DOMAIN_ID>', '<CONTENT>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/domains/<DOMAIN_ID>/zone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'content': '<CONTENT>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_embeddings.py b/test/services/test_embeddings.py index 4c37d10d..fde2f29b 100644 --- a/test/services/test_embeddings.py +++ b/test/services/test_embeddings.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,27 @@ def test_create_text_embeddings(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.embeddings.create_text_embeddings( [], + model='nomic-embed-text', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/embeddings/text', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'texts': [], + 'model': 'nomic-embed-text', + }, ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_functions.py b/test/services/test_functions.py index 1f63e700..c8c0593e 100644 --- a/test/services/test_functions.py +++ b/test/services/test_functions.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.functions.list() + response = self.functions.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -70,7 +90,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -79,6 +99,63 @@ def test_create(self, m): '<FUNCTION_ID>', '<NAME>', 'node-14.5', + execute=[], + events=[], + schedule='0 0 * * *', + timeout=1, + enabled=False, + logging=False, + entrypoint='<ENTRYPOINT>', + commands='<COMMANDS>', + scopes=[], + installation_id='<INSTALLATION_ID>', + provider_repository_id='<PROVIDER_REPOSITORY_ID>', + provider_branch='<PROVIDER_BRANCH>', + provider_silent_mode=False, + provider_root_directory='<PROVIDER_ROOT_DIRECTORY>', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + interval=1, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'functionId': '<FUNCTION_ID>', + 'name': '<NAME>', + 'runtime': 'node-14.5', + 'execute': [], + 'events': [], + 'schedule': '0 0 * * *', + 'timeout': 1, + 'enabled': False, + 'logging': False, + 'entrypoint': '<ENTRYPOINT>', + 'commands': '<COMMANDS>', + 'scopes': [], + 'installationId': '<INSTALLATION_ID>', + 'providerRepositoryId': '<PROVIDER_REPOSITORY_ID>', + 'providerBranch': '<PROVIDER_BRANCH>', + 'providerSilentMode': False, + 'providerRootDirectory': '<PROVIDER_ROOT_DIRECTORY>', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'interval': 1, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -90,12 +167,27 @@ def test_list_runtimes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.functions.list_runtimes() + response = self.functions.list_runtimes( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/runtimes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -106,12 +198,29 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.functions.list_specifications() + response = self.functions.list_specifications( + type='runtimes', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['runtimes'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -154,7 +263,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -162,6 +271,14 @@ def test_get(self, m): response = self.functions.get( '<FUNCTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -204,7 +321,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -212,6 +329,65 @@ def test_update(self, m): response = self.functions.update( '<FUNCTION_ID>', '<NAME>', + runtime='node-14.5', + execute=[], + events=[], + schedule='0 0 * * *', + timeout=1, + enabled=False, + logging=False, + entrypoint='<ENTRYPOINT>', + commands='<COMMANDS>', + scopes=[], + installation_id='<INSTALLATION_ID>', + provider_repository_id='<PROVIDER_REPOSITORY_ID>', + provider_branch='<PROVIDER_BRANCH>', + provider_silent_mode=False, + provider_root_directory='<PROVIDER_ROOT_DIRECTORY>', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + interval=1, + description='<DESCRIPTION>', + email_enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'runtime': 'node-14.5', + 'execute': [], + 'events': [], + 'schedule': '0 0 * * *', + 'timeout': 1, + 'enabled': False, + 'logging': False, + 'entrypoint': '<ENTRYPOINT>', + 'commands': '<COMMANDS>', + 'scopes': [], + 'installationId': '<INSTALLATION_ID>', + 'providerRepositoryId': '<PROVIDER_REPOSITORY_ID>', + 'providerBranch': '<PROVIDER_BRANCH>', + 'providerSilentMode': False, + 'providerRootDirectory': '<PROVIDER_ROOT_DIRECTORY>', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'interval': 1, + 'description': '<DESCRIPTION>', + 'emailEnabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -220,7 +396,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -228,6 +404,14 @@ def test_delete(self, m): response = self.functions.delete( '<FUNCTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -270,7 +454,7 @@ def test_update_function_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -279,6 +463,19 @@ def test_update_function_deployment(self, m): '<FUNCTION_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '<DEPLOYMENT_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -289,13 +486,30 @@ def test_list_deployments(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.list_deployments( '<FUNCTION_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -332,7 +546,7 @@ def test_create_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -340,7 +554,31 @@ def test_create_deployment(self, m): response = self.functions.create_deployment( '<FUNCTION_ID>', InputFile.from_bytes(bytearray(), "example.file"), - True, + False, + entrypoint='<ENTRYPOINT>', + commands='<COMMANDS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'entrypoint': '<ENTRYPOINT>', + 'commands': '<COMMANDS>', + 'code': '', + 'activate': 'false', + }, ) self.assertEqual(response.to_dict(), data) @@ -377,7 +615,7 @@ def test_create_duplicate_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -385,6 +623,21 @@ def test_create_duplicate_deployment(self, m): response = self.functions.create_duplicate_deployment( '<FUNCTION_ID>', '<DEPLOYMENT_ID>', + build_id='<BUILD_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments/duplicate', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '<DEPLOYMENT_ID>', + 'buildId': '<BUILD_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -421,7 +674,7 @@ def test_create_template_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -433,6 +686,25 @@ def test_create_template_deployment(self, m): '<ROOT_DIRECTORY>', 'commit', '<REFERENCE>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments/template', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'repository': '<REPOSITORY>', + 'owner': '<OWNER>', + 'rootDirectory': '<ROOT_DIRECTORY>', + 'type': 'commit', + 'reference': '<REFERENCE>', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -469,7 +741,7 @@ def test_create_vcs_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -478,6 +750,22 @@ def test_create_vcs_deployment(self, m): '<FUNCTION_ID>', 'branch', '<REFERENCE>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments/vcs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'branch', + 'reference': '<REFERENCE>', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -514,7 +802,7 @@ def test_get_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -523,6 +811,14 @@ def test_get_deployment(self, m): '<FUNCTION_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments/<DEPLOYMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -530,7 +826,7 @@ def test_delete_deployment(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -539,6 +835,14 @@ def test_delete_deployment(self, m): '<FUNCTION_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments/<DEPLOYMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -546,7 +850,7 @@ def test_get_deployment_download(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -554,6 +858,22 @@ def test_get_deployment_download(self, m): response = self.functions.get_deployment_download( '<FUNCTION_ID>', '<DEPLOYMENT_ID>', + type='source', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments/<DEPLOYMENT_ID>/download', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['source'], + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) @@ -590,7 +910,7 @@ def test_update_deployment_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -599,6 +919,14 @@ def test_update_deployment_status(self, m): '<FUNCTION_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/deployments/<DEPLOYMENT_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -609,13 +937,28 @@ def test_list_executions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.list_executions( '<FUNCTION_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/executions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -643,13 +986,37 @@ def test_create_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.create_execution( '<FUNCTION_ID>', + body='<BODY>', + xasync=False, + path='<PATH>', + method='GET', + headers={}, + scheduled_at='<SCHEDULED_AT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/executions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'body': '<BODY>', + 'async': False, + 'path': '<PATH>', + 'method': 'GET', + 'headers': {}, + 'scheduledAt': '<SCHEDULED_AT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -677,7 +1044,7 @@ def test_get_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -686,6 +1053,14 @@ def test_get_execution(self, m): '<FUNCTION_ID>', '<EXECUTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/executions/<EXECUTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -693,7 +1068,7 @@ def test_delete_execution(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -702,6 +1077,14 @@ def test_delete_execution(self, m): '<FUNCTION_ID>', '<EXECUTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/executions/<EXECUTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -712,13 +1095,28 @@ def test_list_variables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.functions.list_variables( '<FUNCTION_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/variables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -737,7 +1135,7 @@ def test_create_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -747,6 +1145,25 @@ def test_create_variable(self, m): '<VARIABLE_ID>', '<KEY>', '<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/variables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'variableId': '<VARIABLE_ID>', + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -765,7 +1182,7 @@ def test_get_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -774,6 +1191,14 @@ def test_get_variable(self, m): '<FUNCTION_ID>', '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/variables/<VARIABLE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -791,7 +1216,7 @@ def test_update_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -799,6 +1224,26 @@ def test_update_variable(self, m): response = self.functions.update_variable( '<FUNCTION_ID>', '<VARIABLE_ID>', + key='<KEY>', + value='<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -807,7 +1252,7 @@ def test_delete_variable(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -816,4 +1261,12 @@ def test_delete_variable(self, m): '<FUNCTION_ID>', '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/functions/<FUNCTION_ID>/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_graphql.py b/test/services/test_graphql.py index d9203da8..a95db416 100644 --- a/test/services/test_graphql.py +++ b/test/services/test_graphql.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -19,7 +21,7 @@ def test_query(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -27,6 +29,19 @@ def test_query(self, m): response = self.graphql.query( {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/graphql', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'query': {}, + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -34,7 +49,7 @@ def test_mutation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -42,4 +57,17 @@ def test_mutation(self, m): response = self.graphql.mutation( {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/graphql/mutation', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'query': {}, + }, + ) self.assertEqual(response, data) diff --git a/test/services/test_locale.py b/test/services/test_locale.py index 1438edfb..15c90cb0 100644 --- a/test/services/test_locale.py +++ b/test/services/test_locale.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -27,12 +29,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.locale.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,12 +53,27 @@ def test_list_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_codes() + response = self.locale.list_codes( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -59,12 +84,27 @@ def test_list_continents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_continents() + response = self.locale.list_continents( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/continents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -75,12 +115,27 @@ def test_list_countries(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_countries() + response = self.locale.list_countries( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/countries', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -91,12 +146,27 @@ def test_list_countries_eu(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_countries_eu() + response = self.locale.list_countries_eu( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/countries/eu', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -107,12 +177,27 @@ def test_list_countries_phones(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_countries_phones() + response = self.locale.list_countries_phones( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/countries/phones', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -123,12 +208,27 @@ def test_list_currencies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_currencies() + response = self.locale.list_currencies( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/currencies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -139,10 +239,25 @@ def test_list_languages(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.locale.list_languages() + response = self.locale.list_languages( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/locale/languages', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_messaging.py b/test/services/test_messaging.py index d22f32d6..0c3cf54c 100644 --- a/test/services/test_messaging.py +++ b/test/services/test_messaging.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list_messages(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.messaging.list_messages() + response = self.messaging.list_messages( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -46,7 +66,7 @@ def test_create_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -55,6 +75,45 @@ def test_create_email(self, m): '<MESSAGE_ID>', '<SUBJECT>', '<CONTENT>', + topics=[], + users=[], + targets=[], + cc=[], + bcc=[], + attachments=[], + draft=False, + html=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + reply_to_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + emails=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '<MESSAGE_ID>', + 'subject': '<SUBJECT>', + 'content': '<CONTENT>', + 'topics': [], + 'users': [], + 'targets': [], + 'cc': [], + 'bcc': [], + 'attachments': [], + 'draft': False, + 'html': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'replyToEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'emails': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -74,13 +133,53 @@ def test_update_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_email( '<MESSAGE_ID>', + topics=[], + users=[], + targets=[], + subject='<SUBJECT>', + content='<CONTENT>', + draft=False, + html=False, + cc=[], + bcc=[], + scheduled_at='2020-10-15T06:38:00.000+00:00', + attachments=[], + reply_to_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + emails=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/email/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'subject': '<SUBJECT>', + 'content': '<CONTENT>', + 'draft': False, + 'html': False, + 'cc': [], + 'bcc': [], + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'attachments': [], + 'replyToEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'emails': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -100,13 +199,64 @@ def test_create_push(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.create_push( '<MESSAGE_ID>', + title='<TITLE>', + body='<BODY>', + topics=[], + users=[], + targets=[], + data={}, + action='<ACTION>', + image='<ID1:ID2>', + icon='<ICON>', + sound='<SOUND>', + color='<COLOR>', + tag='<TAG>', + badge=1, + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + content_available=False, + critical=False, + priority='normal', + channel_id='<CHANNEL_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/push', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '<MESSAGE_ID>', + 'title': '<TITLE>', + 'body': '<BODY>', + 'topics': [], + 'users': [], + 'targets': [], + 'data': {}, + 'action': '<ACTION>', + 'image': '<ID1:ID2>', + 'icon': '<ICON>', + 'sound': '<SOUND>', + 'color': '<COLOR>', + 'tag': '<TAG>', + 'badge': 1, + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'contentAvailable': False, + 'critical': False, + 'priority': 'normal', + 'channelId': '<CHANNEL_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -126,13 +276,63 @@ def test_update_push(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_push( '<MESSAGE_ID>', + topics=[], + users=[], + targets=[], + title='<TITLE>', + body='<BODY>', + data={}, + action='<ACTION>', + image='<ID1:ID2>', + icon='<ICON>', + sound='<SOUND>', + color='<COLOR>', + tag='<TAG>', + badge=1, + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + content_available=False, + critical=False, + priority='normal', + channel_id='<CHANNEL_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/push/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'title': '<TITLE>', + 'body': '<BODY>', + 'data': {}, + 'action': '<ACTION>', + 'image': '<ID1:ID2>', + 'icon': '<ICON>', + 'sound': '<SOUND>', + 'color': '<COLOR>', + 'tag': '<TAG>', + 'badge': 1, + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + 'contentAvailable': False, + 'critical': False, + 'priority': 'normal', + 'channelId': '<CHANNEL_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -152,7 +352,7 @@ def test_create_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -160,6 +360,30 @@ def test_create_sms(self, m): response = self.messaging.create_sms( '<MESSAGE_ID>', '<CONTENT>', + topics=[], + users=[], + targets=[], + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '<MESSAGE_ID>', + 'content': '<CONTENT>', + 'topics': [], + 'users': [], + 'targets': [], + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -179,7 +403,7 @@ def test_create_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -187,6 +411,30 @@ def test_create_sms(self, m): response = self.messaging.create_sms( '<MESSAGE_ID>', '<CONTENT>', + topics=[], + users=[], + targets=[], + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'messageId': '<MESSAGE_ID>', + 'content': '<CONTENT>', + 'topics': [], + 'users': [], + 'targets': [], + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -206,13 +454,37 @@ def test_update_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_sms( '<MESSAGE_ID>', + topics=[], + users=[], + targets=[], + content='<CONTENT>', + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'content': '<CONTENT>', + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -232,13 +504,37 @@ def test_update_sms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_sms( '<MESSAGE_ID>', + topics=[], + users=[], + targets=[], + content='<CONTENT>', + draft=False, + scheduled_at='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/sms/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topics': [], + 'users': [], + 'targets': [], + 'content': '<CONTENT>', + 'draft': False, + 'scheduledAt': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -258,7 +554,7 @@ def test_get_message(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -266,6 +562,14 @@ def test_get_message(self, m): response = self.messaging.get_message( '<MESSAGE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/<MESSAGE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -273,7 +577,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -281,6 +585,14 @@ def test_delete(self, m): response = self.messaging.delete( '<MESSAGE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/<MESSAGE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -291,13 +603,28 @@ def test_list_targets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.list_targets( '<MESSAGE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/messages/<MESSAGE_ID>/targets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -309,12 +636,30 @@ def test_list_providers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.messaging.list_providers() + response = self.messaging.list_providers( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -331,7 +676,7 @@ def test_create_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -339,6 +684,32 @@ def test_create_apns_provider(self, m): response = self.messaging.create_apns_provider( '<PROVIDER_ID>', '<NAME>', + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -356,7 +727,7 @@ def test_create_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +735,32 @@ def test_create_apns_provider(self, m): response = self.messaging.create_apns_provider( '<PROVIDER_ID>', '<NAME>', + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -381,13 +778,39 @@ def test_update_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_apns_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -405,13 +828,39 @@ def test_update_apns_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_apns_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + auth_key='<AUTH_KEY>', + auth_key_id='<AUTH_KEY_ID>', + team_id='<TEAM_ID>', + bundle_id='<BUNDLE_ID>', + sandbox=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/apns/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'authKey': '<AUTH_KEY>', + 'authKeyId': '<AUTH_KEY_ID>', + 'teamId': '<TEAM_ID>', + 'bundleId': '<BUNDLE_ID>', + 'sandbox': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -429,7 +878,7 @@ def test_create_appwrite_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -437,6 +886,26 @@ def test_create_appwrite_provider(self, m): response = self.messaging.create_appwrite_provider( '<PROVIDER_ID>', '<NAME>', + enabled=False, + qos=1, + expiry=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/appwrite', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'enabled': False, + 'qos': 1, + 'expiry': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -454,13 +923,33 @@ def test_update_appwrite_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_appwrite_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + qos=1, + expiry=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/appwrite/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'qos': 1, + 'expiry': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -478,7 +967,7 @@ def test_create_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -486,6 +975,24 @@ def test_create_fcm_provider(self, m): response = self.messaging.create_fcm_provider( '<PROVIDER_ID>', '<NAME>', + service_account_json={}, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'serviceAccountJSON': {}, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -503,7 +1010,7 @@ def test_create_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -511,6 +1018,24 @@ def test_create_fcm_provider(self, m): response = self.messaging.create_fcm_provider( '<PROVIDER_ID>', '<NAME>', + service_account_json={}, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'serviceAccountJSON': {}, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -528,13 +1053,31 @@ def test_update_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_fcm_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + service_account_json={}, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'serviceAccountJSON': {}, + }, ) self.assertEqual(response.to_dict(), data) @@ -552,13 +1095,31 @@ def test_update_fcm_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_fcm_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + service_account_json={}, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/fcm/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'serviceAccountJSON': {}, + }, ) self.assertEqual(response.to_dict(), data) @@ -576,7 +1137,7 @@ def test_create_mailgun_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -584,6 +1145,36 @@ def test_create_mailgun_provider(self, m): response = self.messaging.create_mailgun_provider( '<PROVIDER_ID>', '<NAME>', + api_key='<API_KEY>', + domain='example.com', + is_eu_region=False, + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/mailgun', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'domain': 'example.com', + 'isEuRegion': False, + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -601,13 +1192,43 @@ def test_update_mailgun_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_mailgun_provider( '<PROVIDER_ID>', + name='<NAME>', + api_key='<API_KEY>', + domain='example.com', + is_eu_region=False, + enabled=False, + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/mailgun/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'domain': 'example.com', + 'isEuRegion': False, + 'enabled': False, + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -625,7 +1246,7 @@ def test_create_msg91_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -633,6 +1254,28 @@ def test_create_msg91_provider(self, m): response = self.messaging.create_msg91_provider( '<PROVIDER_ID>', '<NAME>', + template_id='<TEMPLATE_ID>', + sender_id='<SENDER_ID>', + auth_key='<AUTH_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/msg91', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'templateId': '<TEMPLATE_ID>', + 'senderId': '<SENDER_ID>', + 'authKey': '<AUTH_KEY>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -650,13 +1293,35 @@ def test_update_msg91_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_msg91_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + template_id='<TEMPLATE_ID>', + sender_id='<SENDER_ID>', + auth_key='<AUTH_KEY>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/msg91/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'templateId': '<TEMPLATE_ID>', + 'senderId': '<SENDER_ID>', + 'authKey': '<AUTH_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -674,7 +1339,7 @@ def test_create_resend_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -682,6 +1347,32 @@ def test_create_resend_provider(self, m): response = self.messaging.create_resend_provider( '<PROVIDER_ID>', '<NAME>', + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/resend', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -699,13 +1390,39 @@ def test_update_resend_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_resend_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/resend/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -723,7 +1440,7 @@ def test_create_sendgrid_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -731,6 +1448,32 @@ def test_create_sendgrid_provider(self, m): response = self.messaging.create_sendgrid_provider( '<PROVIDER_ID>', '<NAME>', + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/sendgrid', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -748,13 +1491,39 @@ def test_update_sendgrid_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_sendgrid_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + api_key='<API_KEY>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/sendgrid/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'apiKey': '<API_KEY>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -772,7 +1541,7 @@ def test_create_ses_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -780,6 +1549,36 @@ def test_create_ses_provider(self, m): response = self.messaging.create_ses_provider( '<PROVIDER_ID>', '<NAME>', + access_key='<ACCESS_KEY>', + secret_key='<SECRET_KEY>', + region='<REGION>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/ses', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + 'region': '<REGION>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -797,13 +1596,43 @@ def test_update_ses_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_ses_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + access_key='<ACCESS_KEY>', + secret_key='<SECRET_KEY>', + region='<REGION>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/ses/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + 'region': '<REGION>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + }, ) self.assertEqual(response.to_dict(), data) @@ -821,7 +1650,7 @@ def test_create_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -830,6 +1659,43 @@ def test_create_smtp_provider(self, m): '<PROVIDER_ID>', '<NAME>', '<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -847,7 +1713,7 @@ def test_create_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -856,6 +1722,43 @@ def test_create_smtp_provider(self, m): '<PROVIDER_ID>', '<NAME>', '<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='email@example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': 'email@example.com', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -873,13 +1776,51 @@ def test_update_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_smtp_provider( '<PROVIDER_ID>', + name='<NAME>', + host='<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -897,13 +1838,51 @@ def test_update_smtp_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_smtp_provider( '<PROVIDER_ID>', + name='<NAME>', + host='<HOST>', + port=1, + username='<USERNAME>', + password='password', + encryption='none', + auto_tls=False, + mailer='<MAILER>', + from_name='<FROM_NAME>', + from_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + reply_to_email='<REPLY_TO_EMAIL>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/smtp/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'host': '<HOST>', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'encryption': 'none', + 'autoTLS': False, + 'mailer': '<MAILER>', + 'fromName': '<FROM_NAME>', + 'fromEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'replyToEmail': '<REPLY_TO_EMAIL>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -921,7 +1900,7 @@ def test_create_telesign_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -929,6 +1908,28 @@ def test_create_telesign_provider(self, m): response = self.messaging.create_telesign_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='+12065550100', + customer_id='<CUSTOMER_ID>', + api_key='<API_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/telesign', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '+12065550100', + 'customerId': '<CUSTOMER_ID>', + 'apiKey': '<API_KEY>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -946,13 +1947,35 @@ def test_update_telesign_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_telesign_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + customer_id='<CUSTOMER_ID>', + api_key='<API_KEY>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/telesign/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'customerId': '<CUSTOMER_ID>', + 'apiKey': '<API_KEY>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -970,7 +1993,7 @@ def test_create_textmagic_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -978,6 +2001,28 @@ def test_create_textmagic_provider(self, m): response = self.messaging.create_textmagic_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='+12065550100', + username='<USERNAME>', + api_key='<API_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/textmagic', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '+12065550100', + 'username': '<USERNAME>', + 'apiKey': '<API_KEY>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -995,13 +2040,35 @@ def test_update_textmagic_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_textmagic_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + username='<USERNAME>', + api_key='<API_KEY>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/textmagic/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'username': '<USERNAME>', + 'apiKey': '<API_KEY>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1019,7 +2086,7 @@ def test_create_twilio_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1027,6 +2094,28 @@ def test_create_twilio_provider(self, m): response = self.messaging.create_twilio_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='<FROM>', + account_sid='<ACCOUNT_SID>', + auth_token='<AUTH_TOKEN>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/twilio', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '<FROM>', + 'accountSid': '<ACCOUNT_SID>', + 'authToken': '<AUTH_TOKEN>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1044,13 +2133,35 @@ def test_update_twilio_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_twilio_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + account_sid='<ACCOUNT_SID>', + auth_token='<AUTH_TOKEN>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/twilio/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'accountSid': '<ACCOUNT_SID>', + 'authToken': '<AUTH_TOKEN>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1068,7 +2179,7 @@ def test_create_vonage_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1076,6 +2187,28 @@ def test_create_vonage_provider(self, m): response = self.messaging.create_vonage_provider( '<PROVIDER_ID>', '<NAME>', + xfrom='+12065550100', + api_key='<API_KEY>', + api_secret='<API_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/vonage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + 'from': '+12065550100', + 'apiKey': '<API_KEY>', + 'apiSecret': '<API_SECRET>', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1093,13 +2226,35 @@ def test_update_vonage_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_vonage_provider( '<PROVIDER_ID>', + name='<NAME>', + enabled=False, + api_key='<API_KEY>', + api_secret='<API_SECRET>', + xfrom='<FROM>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/vonage/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'apiKey': '<API_KEY>', + 'apiSecret': '<API_SECRET>', + 'from': '<FROM>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1117,7 +2272,7 @@ def test_get_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1125,6 +2280,14 @@ def test_get_provider(self, m): response = self.messaging.get_provider( '<PROVIDER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/<PROVIDER_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1132,7 +2295,7 @@ def test_delete_provider(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1140,6 +2303,14 @@ def test_delete_provider(self, m): response = self.messaging.delete_provider( '<PROVIDER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/providers/<PROVIDER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1150,12 +2321,30 @@ def test_list_topics(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.messaging.list_topics() + response = self.messaging.list_topics( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1173,7 +2362,7 @@ def test_create_topic(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1181,6 +2370,28 @@ def test_create_topic(self, m): response = self.messaging.create_topic( '<TOPIC_ID>', '<NAME>', + subscribe=[], + qos=1, + expiry=1, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'topicId': '<TOPIC_ID>', + 'name': '<NAME>', + 'subscribe': [], + 'qos': 1, + 'expiry': 1, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1199,7 +2410,7 @@ def test_get_topic(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1207,6 +2418,14 @@ def test_get_topic(self, m): response = self.messaging.get_topic( '<TOPIC_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1224,13 +2443,35 @@ def test_update_topic(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.update_topic( '<TOPIC_ID>', + name='<NAME>', + subscribe=[], + qos=1, + expiry=1, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'subscribe': [], + 'qos': 1, + 'expiry': 1, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1239,7 +2480,7 @@ def test_delete_topic(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1247,6 +2488,14 @@ def test_delete_topic(self, m): response = self.messaging.delete_topic( '<TOPIC_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1257,13 +2506,30 @@ def test_list_subscribers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.messaging.list_subscribers( '<TOPIC_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1291,7 +2557,7 @@ def test_create_subscriber(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1301,6 +2567,20 @@ def test_create_subscriber(self, m): '<SUBSCRIBER_ID>', '<TARGET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'subscriberId': '<SUBSCRIBER_ID>', + 'targetId': '<TARGET_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1327,7 +2607,7 @@ def test_get_subscriber(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1336,6 +2616,14 @@ def test_get_subscriber(self, m): '<TOPIC_ID>', '<SUBSCRIBER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers/<SUBSCRIBER_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1343,7 +2631,7 @@ def test_delete_subscriber(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1352,4 +2640,12 @@ def test_delete_subscriber(self, m): '<TOPIC_ID>', '<SUBSCRIBER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/messaging/topics/<TOPIC_ID>/subscribers/<SUBSCRIBER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_migrations.py b/test/services/test_migrations.py new file mode 100644 index 00000000..e10c683f --- /dev/null +++ b/test/services/test_migrations.py @@ -0,0 +1,648 @@ +import json +import re +import requests_mock +import unittest +from urllib.parse import parse_qs, unquote, urlsplit + +from appwrite.client import Client +from appwrite.input_file import InputFile +from appwrite.models import * +from appwrite.services.migrations import Migrations + + +class MigrationsServiceTest(unittest.TestCase): + + def setUp(self): + self.client = Client() + self.migrations = Migrations(self.client) + + @requests_mock.Mocker() + def test_list(self, m): + data = { + "total": 5.0, + "migrations": [], + } + headers = {'Content-Type': 'application/json'} + m.request( + 'GET', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_appwrite_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_appwrite_migration( + [], + 'https://example.com', + '<PROJECT_ID>', + '<API_KEY>', + on_duplicate='fail', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/appwrite', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'endpoint': 'https://example.com', + 'projectId': '<PROJECT_ID>', + 'apiKey': '<API_KEY>', + 'onDuplicate': 'fail', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_csv_export(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_csv_export( + '<DATABASE_ID>', + '<COLLECTION_ID>', + '<FILENAME>', + columns=[], + queries=[], + delimiter='<DELIMITER>', + enclosure='<ENCLOSURE>', + escape='<ESCAPE>', + header=False, + notify=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/csv/exports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'filename': '<FILENAME>', + 'columns': [], + 'queries': [], + 'delimiter': '<DELIMITER>', + 'enclosure': '<ENCLOSURE>', + 'escape': '<ESCAPE>', + 'header': False, + 'notify': False, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_csv_import(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_csv_import( + '<BUCKET_ID>', + '<FILE_ID>', + '<DATABASE_ID>', + '<COLLECTION_ID>', + internal_file=False, + on_duplicate='fail', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/csv/imports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'bucketId': '<BUCKET_ID>', + 'fileId': '<FILE_ID>', + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'internalFile': False, + 'onDuplicate': 'fail', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_firebase_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_firebase_migration( + [], + '<SERVICE_ACCOUNT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/firebase', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'serviceAccount': '<SERVICE_ACCOUNT>', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_json_export(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_json_export( + '<DATABASE_ID>', + '<COLLECTION_ID>', + '<FILENAME>', + columns=[], + queries=[], + notify=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/json/exports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'filename': '<FILENAME>', + 'columns': [], + 'queries': [], + 'notify': False, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_json_import(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_json_import( + '<BUCKET_ID>', + '<FILE_ID>', + '<DATABASE_ID>', + '<COLLECTION_ID>', + internal_file=False, + on_duplicate='fail', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/json/imports', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'bucketId': '<BUCKET_ID>', + 'fileId': '<FILE_ID>', + 'databaseId': '<DATABASE_ID>', + 'collectionId': '<COLLECTION_ID>', + 'internalFile': False, + 'onDuplicate': 'fail', + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_n_host_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_n_host_migration( + [], + '<SUBDOMAIN>', + '<REGION>', + '<ADMIN_SECRET>', + '<DATABASE>', + '<USERNAME>', + 'password', + port=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/nhost', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'subdomain': '<SUBDOMAIN>', + 'region': '<REGION>', + 'adminSecret': '<ADMIN_SECRET>', + 'database': '<DATABASE>', + 'username': '<USERNAME>', + 'password': 'password', + 'port': 1, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_supabase_migration(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'POST', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.create_supabase_migration( + [], + 'https://example.com', + '<API_KEY>', + '<DATABASE_HOST>', + '<USERNAME>', + 'password', + port=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/supabase', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resources': [], + 'endpoint': 'https://example.com', + 'apiKey': '<API_KEY>', + 'databaseHost': '<DATABASE_HOST>', + 'username': '<USERNAME>', + 'password': 'password', + 'port': 1, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'GET', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.get( + '<MIGRATION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/<MIGRATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_retry(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "status": "pending", + "stage": "init", + "source": "Appwrite", + "destination": "Appwrite", + "resources": [], + "resourceId": "collectionId", + "resourceInternalId": "1", + "resourceType": "collection", + "parentResourceId": "databaseId", + "parentResourceInternalId": "1", + "parentResourceType": "database", + "destinationResourceId": "databaseId", + "destinationResourceInternalId": "1", + "destinationResourceType": "database", + "statusCounters": {}, + "resourceData": [], + "errors": [], + "options": {}, + } + headers = {'Content-Type': 'application/json'} + m.request( + 'PATCH', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.retry( + '<MIGRATION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/<MIGRATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request( + 'DELETE', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.migrations.delete( + '<MIGRATION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/migrations/<MIGRATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) + self.assertEqual(response, data) diff --git a/test/services/test_mongo.py b/test/services/test_mongo.py index 75b37bfa..5b9f8696 100644 --- a/test/services/test_mongo.py +++ b/test/services/test_mongo.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.mongo.list() + response = self.mongo.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -81,7 +99,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -89,6 +107,44 @@ def test_create(self, m): response = self.mongo.create( '<DATABASE_ID>', '<NAME>', + version='17', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'version': '17', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -100,12 +156,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +223,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +231,14 @@ def test_get(self, m): response = self.mongo.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -220,13 +292,65 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.update( '<DATABASE_ID>', + name='<NAME>', + status='ready', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + metrics_trace_sample_rate=1.0, + metrics_slow_query_log_threshold_ms=1, + sql_api_enabled=False, + sql_api_allowed_statements=[], + sql_api_max_rows=1, + sql_api_max_bytes=1, + sql_api_timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'status': 'ready', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + 'metricsTraceSampleRate': 1.0, + 'metricsSlowQueryLogThresholdMs': 1, + 'sqlApiEnabled': False, + 'sqlApiAllowedStatements': [], + 'sqlApiMaxRows': 1, + 'sqlApiMaxBytes': 1, + 'sqlApiTimeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -235,7 +359,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -243,6 +367,14 @@ def test_delete(self, m): response = self.mongo.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -253,13 +385,28 @@ def test_list_backups(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_backups( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -281,13 +428,27 @@ def test_create_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_backup( '<DATABASE_ID>', + type='full', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'full', + }, ) self.assertEqual(response.to_dict(), data) @@ -299,13 +460,28 @@ def test_list_backup_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_backup_policies( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -325,7 +501,7 @@ def test_create_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +512,26 @@ def test_create_backup_policy(self, m): '<NAME>', 'schedule', 1, + type='full', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '<POLICY_ID>', + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'type': 'full', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -355,7 +551,7 @@ def test_get_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +560,14 @@ def test_get_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -382,7 +586,7 @@ def test_update_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -390,6 +594,26 @@ def test_update_backup_policy(self, m): response = self.mongo.update_backup_policy( '<DATABASE_ID>', '<POLICY_ID>', + name='<NAME>', + schedule='schedule', + retention=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -398,7 +622,7 @@ def test_delete_backup_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,6 +631,14 @@ def test_delete_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -420,7 +652,7 @@ def test_update_backup_storage(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -431,6 +663,28 @@ def test_update_backup_storage(self, m): '<BUCKET>', '<ACCESS_KEY>', '<SECRET_KEY>', + region='<REGION>', + prefix='<PREFIX>', + endpoint='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/storage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 's3', + 'bucket': '<BUCKET>', + 'region': '<REGION>', + 'prefix': '<PREFIX>', + 'endpoint': 'https://example.com', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -452,7 +706,7 @@ def test_get_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -461,6 +715,14 @@ def test_get_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -468,7 +730,7 @@ def test_delete_backup(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +739,14 @@ def test_delete_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -487,7 +757,7 @@ def test_list_branches(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +765,14 @@ def test_list_branches(self, m): response = self.mongo.list_branches( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/branches', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -548,13 +826,29 @@ def test_create_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_branch( '<DATABASE_ID>', + branch_id='<BRANCH_ID>', + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/branches', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'branchId': '<BRANCH_ID>', + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -609,7 +903,7 @@ def test_delete_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +912,14 @@ def test_delete_branch(self, m): '<DATABASE_ID>', '<BRANCH_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/branches/<BRANCH_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -634,7 +936,7 @@ def test_update_credentials(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -642,6 +944,14 @@ def test_update_credentials(self, m): response = self.mongo.update_credentials( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/credentials', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -695,13 +1005,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -756,7 +1080,7 @@ def test_update_maintenance(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -766,6 +1090,20 @@ def test_update_maintenance(self, m): 'sun', 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/maintenance', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'day': 'sun', + 'hourUtc': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -819,7 +1157,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -827,6 +1165,21 @@ def test_create_migration(self, m): response = self.mongo.create_migration( '<DATABASE_ID>', 'shared', + specification='<SPECIFICATION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetType': 'shared', + 'specification': '<SPECIFICATION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -838,13 +1191,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -856,7 +1227,7 @@ def test_get_pitr(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -864,6 +1235,14 @@ def test_get_pitr(self, m): response = self.mongo.get_pitr( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/pitr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -878,7 +1257,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -886,6 +1265,14 @@ def test_get_replicas(self, m): response = self.mongo.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -896,13 +1283,33 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.list_restorations( '<DATABASE_ID>', + status='pending', + type='backup', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['pending'], + 'type': ['backup'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -920,13 +1327,39 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mongo.create_restoration( '<DATABASE_ID>', + type='backup', + backup_id='<BACKUP_ID>', + target_database_id='<TARGET_DATABASE_ID>', + target_time='2020-10-15T06:38:00.000+00:00', + restore_mode='in_place', + target_name='<TARGET_NAME>', + auto_promote=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'backup', + 'backupId': '<BACKUP_ID>', + 'targetDatabaseId': '<TARGET_DATABASE_ID>', + 'targetTime': '2020-10-15T06:38:00.000+00:00', + 'restoreMode': 'in_place', + 'targetName': '<TARGET_NAME>', + 'autoPromote': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -944,7 +1377,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -953,6 +1386,14 @@ def test_get_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations/<RESTORATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -969,7 +1410,7 @@ def test_promote_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -978,6 +1419,14 @@ def test_promote_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/restorations/<RESTORATION_ID>/promotions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -997,7 +1446,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1005,6 +1454,14 @@ def test_get_status(self, m): response = self.mongo.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1058,7 +1515,7 @@ def test_create_upgrade(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1067,4 +1524,17 @@ def test_create_upgrade(self, m): '<DATABASE_ID>', '<TARGET_VERSION>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mongo/<DATABASE_ID>/upgrades', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetVersion': '<TARGET_VERSION>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_mysql.py b/test/services/test_mysql.py index 3ea4f7d3..9bed7ea8 100644 --- a/test/services/test_mysql.py +++ b/test/services/test_mysql.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.mysql.list() + response = self.mysql.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -81,7 +99,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -89,6 +107,44 @@ def test_create(self, m): response = self.mysql.create( '<DATABASE_ID>', '<NAME>', + version='17', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'version': '17', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -100,12 +156,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +223,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +231,14 @@ def test_get(self, m): response = self.mysql.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -220,13 +292,65 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.update( '<DATABASE_ID>', + name='<NAME>', + status='ready', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + metrics_trace_sample_rate=1.0, + metrics_slow_query_log_threshold_ms=1, + sql_api_enabled=False, + sql_api_allowed_statements=[], + sql_api_max_rows=1, + sql_api_max_bytes=1, + sql_api_timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'status': 'ready', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + 'metricsTraceSampleRate': 1.0, + 'metricsSlowQueryLogThresholdMs': 1, + 'sqlApiEnabled': False, + 'sqlApiAllowedStatements': [], + 'sqlApiMaxRows': 1, + 'sqlApiMaxBytes': 1, + 'sqlApiTimeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -235,7 +359,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -243,6 +367,14 @@ def test_delete(self, m): response = self.mysql.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -253,13 +385,28 @@ def test_list_backups(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_backups( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -281,13 +428,27 @@ def test_create_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_backup( '<DATABASE_ID>', + type='full', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'full', + }, ) self.assertEqual(response.to_dict(), data) @@ -299,13 +460,28 @@ def test_list_backup_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_backup_policies( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -325,7 +501,7 @@ def test_create_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +512,26 @@ def test_create_backup_policy(self, m): '<NAME>', 'schedule', 1, + type='full', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '<POLICY_ID>', + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'type': 'full', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -355,7 +551,7 @@ def test_get_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +560,14 @@ def test_get_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -382,7 +586,7 @@ def test_update_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -390,6 +594,26 @@ def test_update_backup_policy(self, m): response = self.mysql.update_backup_policy( '<DATABASE_ID>', '<POLICY_ID>', + name='<NAME>', + schedule='schedule', + retention=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -398,7 +622,7 @@ def test_delete_backup_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,6 +631,14 @@ def test_delete_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -420,7 +652,7 @@ def test_update_backup_storage(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -431,6 +663,28 @@ def test_update_backup_storage(self, m): '<BUCKET>', '<ACCESS_KEY>', '<SECRET_KEY>', + region='<REGION>', + prefix='<PREFIX>', + endpoint='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/storage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 's3', + 'bucket': '<BUCKET>', + 'region': '<REGION>', + 'prefix': '<PREFIX>', + 'endpoint': 'https://example.com', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -452,7 +706,7 @@ def test_get_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -461,6 +715,14 @@ def test_get_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -468,7 +730,7 @@ def test_delete_backup(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +739,14 @@ def test_delete_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -487,7 +757,7 @@ def test_list_branches(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +765,14 @@ def test_list_branches(self, m): response = self.mysql.list_branches( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/branches', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -548,13 +826,29 @@ def test_create_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_branch( '<DATABASE_ID>', + branch_id='<BRANCH_ID>', + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/branches', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'branchId': '<BRANCH_ID>', + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -609,7 +903,7 @@ def test_delete_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +912,14 @@ def test_delete_branch(self, m): '<DATABASE_ID>', '<BRANCH_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/branches/<BRANCH_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -634,7 +936,7 @@ def test_update_credentials(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -642,6 +944,14 @@ def test_update_credentials(self, m): response = self.mysql.update_credentials( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/credentials', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -656,7 +966,7 @@ def test_create_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -664,6 +974,23 @@ def test_create_execution(self, m): response = self.mysql.create_execution( '<DATABASE_ID>', '<SQL>', + bindings={}, + timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/executions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'sql': '<SQL>', + 'bindings': {}, + 'timeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -718,13 +1045,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -779,7 +1120,7 @@ def test_update_maintenance(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -789,6 +1130,20 @@ def test_update_maintenance(self, m): 'sun', 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/maintenance', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'day': 'sun', + 'hourUtc': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -842,7 +1197,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -850,6 +1205,21 @@ def test_create_migration(self, m): response = self.mysql.create_migration( '<DATABASE_ID>', 'shared', + specification='<SPECIFICATION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetType': 'shared', + 'specification': '<SPECIFICATION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -861,13 +1231,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -879,7 +1267,7 @@ def test_get_pitr(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -887,6 +1275,14 @@ def test_get_pitr(self, m): response = self.mysql.get_pitr( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/pitr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -905,7 +1301,7 @@ def test_get_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -913,6 +1309,14 @@ def test_get_pooler(self, m): response = self.mysql.get_pooler( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/pooler', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -931,13 +1335,41 @@ def test_update_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.update_pooler( '<DATABASE_ID>', + mode='transaction', + max_connections=1, + default_pool_size=1, + read_write_splitting=False, + pooler_cpu_request='<POOLER_CPU_REQUEST>', + pooler_cpu_limit='<POOLER_CPU_LIMIT>', + pooler_memory_request='<POOLER_MEMORY_REQUEST>', + pooler_memory_limit='<POOLER_MEMORY_LIMIT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/pooler', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mode': 'transaction', + 'maxConnections': 1, + 'defaultPoolSize': 1, + 'readWriteSplitting': False, + 'poolerCpuRequest': '<POOLER_CPU_REQUEST>', + 'poolerCpuLimit': '<POOLER_CPU_LIMIT>', + 'poolerMemoryRequest': '<POOLER_MEMORY_REQUEST>', + 'poolerMemoryLimit': '<POOLER_MEMORY_LIMIT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -953,7 +1385,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -961,6 +1393,14 @@ def test_get_replicas(self, m): response = self.mysql.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -971,13 +1411,33 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.list_restorations( '<DATABASE_ID>', + status='pending', + type='backup', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['pending'], + 'type': ['backup'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -995,13 +1455,39 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.mysql.create_restoration( '<DATABASE_ID>', + type='backup', + backup_id='<BACKUP_ID>', + target_database_id='<TARGET_DATABASE_ID>', + target_time='2020-10-15T06:38:00.000+00:00', + restore_mode='in_place', + target_name='<TARGET_NAME>', + auto_promote=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'backup', + 'backupId': '<BACKUP_ID>', + 'targetDatabaseId': '<TARGET_DATABASE_ID>', + 'targetTime': '2020-10-15T06:38:00.000+00:00', + 'restoreMode': 'in_place', + 'targetName': '<TARGET_NAME>', + 'autoPromote': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1019,7 +1505,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1028,6 +1514,14 @@ def test_get_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations/<RESTORATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1044,7 +1538,7 @@ def test_promote_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1053,6 +1547,14 @@ def test_promote_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/restorations/<RESTORATION_ID>/promotions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1072,7 +1574,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1080,6 +1582,14 @@ def test_get_status(self, m): response = self.mysql.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1133,7 +1643,7 @@ def test_create_upgrade(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1142,4 +1652,17 @@ def test_create_upgrade(self, m): '<DATABASE_ID>', '<TARGET_VERSION>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/mysql/<DATABASE_ID>/upgrades', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetVersion': '<TARGET_VERSION>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_oauth2.py b/test/services/test_oauth2.py index da2520ee..fa7cf188 100644 --- a/test/services/test_oauth2.py +++ b/test/services/test_oauth2.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -21,13 +23,30 @@ def test_approve(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.approve( '<GRANT_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + scope='<SCOPE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//approve', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'grant_id': '<GRANT_ID>', + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'scope': '<SCOPE>', + }, ) self.assertEqual(response.to_dict(), data) @@ -39,12 +58,52 @@ def test_authorize(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.authorize() + response = self.oauth2.authorize( + client_id='<CLIENT_ID>', + redirect_uri='https://example.com', + response_type='response_type', + scope='<SCOPE>', + state='<STATE>', + nonce='<NONCE>', + code_challenge='<CODE_CHALLENGE>', + code_challenge_method='s256', + prompt='<PROMPT>', + max_age=1, + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + request_uri='<REQUEST_URI>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//authorize', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'client_id': ['<CLIENT_ID>'], + 'redirect_uri': ['https://example.com'], + 'response_type': ['response_type'], + 'scope': ['<SCOPE>'], + 'state': ['<STATE>'], + 'nonce': ['<NONCE>'], + 'code_challenge': ['<CODE_CHALLENGE>'], + 'code_challenge_method': ['s256'], + 'prompt': ['<PROMPT>'], + 'max_age': ['1'], + 'authorization_details': ['<AUTHORIZATION_DETAILS>'], + 'audience': ['<AUDIENCE>'], + 'request_uri': ['<REQUEST_URI>'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -55,12 +114,53 @@ def test_authorize_post(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.authorize_post() + response = self.oauth2.authorize_post( + client_id='<CLIENT_ID>', + redirect_uri='https://example.com', + response_type='response_type', + scope='<SCOPE>', + state='<STATE>', + nonce='<NONCE>', + code_challenge='<CODE_CHALLENGE>', + code_challenge_method='s256', + prompt='<PROMPT>', + max_age=1, + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + request_uri='<REQUEST_URI>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//authorize', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'client_id': '<CLIENT_ID>', + 'redirect_uri': 'https://example.com', + 'response_type': 'response_type', + 'scope': '<SCOPE>', + 'state': '<STATE>', + 'nonce': '<NONCE>', + 'code_challenge': '<CODE_CHALLENGE>', + 'code_challenge_method': 's256', + 'prompt': '<PROMPT>', + 'max_age': 1, + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'resource': [], + 'audience': '<AUDIENCE>', + 'request_uri': '<REQUEST_URI>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -75,12 +175,35 @@ def test_create_device_authorization(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.create_device_authorization() + response = self.oauth2.create_device_authorization( + client_id='<CLIENT_ID>', + scope='<SCOPE>', + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//device_authorization', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'client_id': '<CLIENT_ID>', + 'scope': '<SCOPE>', + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'resource': [], + 'audience': '<AUDIENCE>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -101,7 +224,7 @@ def test_create_grant(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -109,6 +232,19 @@ def test_create_grant(self, m): response = self.oauth2.create_grant( '<USER_CODE>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//grants', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'user_code': '<USER_CODE>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -129,7 +265,7 @@ def test_get_grant(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -137,6 +273,14 @@ def test_get_grant(self, m): response = self.oauth2.get_grant( '<GRANT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//grants/<GRANT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -146,13 +290,32 @@ def test_introspect(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.introspect( '<TOKEN>', + token_type_hint='access_token', + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//introspect', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'token': '<TOKEN>', + 'token_type_hint': 'access_token', + 'client_id': '<CLIENT_ID>', + 'client_secret': '<CLIENT_SECRET>', + }, ) self.assertEqual(response.to_dict(), data) @@ -164,12 +327,31 @@ def test_list_organizations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.list_organizations() + response = self.oauth2.list_organizations( + limit=1, + offset=1, + search='<SEARCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//organizations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'limit': ['1'], + 'offset': ['1'], + 'search': ['<SEARCH>'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -180,7 +362,7 @@ def test_create_par(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -189,6 +371,41 @@ def test_create_par(self, m): '<CLIENT_ID>', 'https://example.com', 'code', + scope='<SCOPE>', + state='<STATE>', + nonce='<NONCE>', + code_challenge='<CODE_CHALLENGE>', + code_challenge_method='s256', + prompt='<PROMPT>', + max_age=1, + authorization_details='<AUTHORIZATION_DETAILS>', + resource=[], + audience='<AUDIENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//par', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'client_id': '<CLIENT_ID>', + 'redirect_uri': 'https://example.com', + 'response_type': 'code', + 'scope': '<SCOPE>', + 'state': '<STATE>', + 'nonce': '<NONCE>', + 'code_challenge': '<CODE_CHALLENGE>', + 'code_challenge_method': 's256', + 'prompt': '<PROMPT>', + 'max_age': 1, + 'authorization_details': '<AUTHORIZATION_DETAILS>', + 'resource': [], + 'audience': '<AUDIENCE>', + }, ) self.assertEqual(response.to_dict(), data) @@ -200,12 +417,31 @@ def test_list_projects(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.oauth2.list_projects() + response = self.oauth2.list_projects( + limit=1, + offset=1, + search='<SEARCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//projects', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'limit': ['1'], + 'offset': ['1'], + 'search': ['<SEARCH>'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -215,7 +451,7 @@ def test_reject(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -223,6 +459,19 @@ def test_reject(self, m): response = self.oauth2.reject( '<GRANT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//reject', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'grant_id': '<GRANT_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -230,13 +479,32 @@ def test_revoke(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.revoke( '<TOKEN>', + token_type_hint='access_token', + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//revoke', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'token': '<TOKEN>', + 'token_type_hint': 'access_token', + 'client_id': '<CLIENT_ID>', + 'client_secret': '<CLIENT_SECRET>', + }, ) self.assertEqual(response, data) @@ -251,12 +519,43 @@ def test_create_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.oauth2.create_token( '<GRANT_TYPE>', + code='<CODE>', + refresh_token='<REFRESH_TOKEN>', + device_code='<DEVICE_CODE>', + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + code_verifier='<CODE_VERIFIER>', + redirect_uri='https://example.com', + resource=[], + audience='<AUDIENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/oauth2//token', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'grant_type': '<GRANT_TYPE>', + 'code': '<CODE>', + 'refresh_token': '<REFRESH_TOKEN>', + 'device_code': '<DEVICE_CODE>', + 'client_id': '<CLIENT_ID>', + 'client_secret': '<CLIENT_SECRET>', + 'code_verifier': '<CODE_VERIFIER>', + 'redirect_uri': 'https://example.com', + 'resource': [], + 'audience': '<AUDIENCE>', + }, ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_organization.py b/test/services/test_organization.py index 41206a71..6f7eee50 100644 --- a/test/services/test_organization.py +++ b/test/services/test_organization.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -105,12 +107,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -204,7 +214,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -212,6 +222,19 @@ def test_update(self, m): response = self.organization.update( '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -219,12 +242,20 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.delete() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -235,12 +266,28 @@ def test_list_installations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.organization.list_installations() + response = self.organization.list_installations( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -258,13 +305,28 @@ def test_create_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.create_installation( '<APP_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '<APP_ID>', + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -283,7 +345,7 @@ def test_get_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -291,6 +353,14 @@ def test_get_installation(self, m): response = self.organization.get_installation( '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations/<INSTALLATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -308,13 +378,27 @@ def test_update_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.update_installation( '<INSTALLATION_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -323,7 +407,7 @@ def test_delete_installation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -331,6 +415,14 @@ def test_delete_installation(self, m): response = self.organization.delete_installation( '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -341,12 +433,30 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.organization.list_memberships() + response = self.organization.list_memberships( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -370,13 +480,36 @@ def test_create_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.create_membership( [], + email='email@example.com', + user_id='<USER_ID>', + phone='+12065550100', + url='https://example.com', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'userId': '<USER_ID>', + 'phone': '+12065550100', + 'roles': [], + 'url': 'https://example.com', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -401,7 +534,7 @@ def test_get_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -409,6 +542,14 @@ def test_get_membership(self, m): response = self.organization.get_membership( '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships/<MEMBERSHIP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -432,7 +573,7 @@ def test_update_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -441,6 +582,19 @@ def test_update_membership(self, m): '<MEMBERSHIP_ID>', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'roles': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -448,7 +602,7 @@ def test_delete_membership(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -456,6 +610,14 @@ def test_delete_membership(self, m): response = self.organization.delete_membership( '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -466,12 +628,30 @@ def test_list_projects(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.organization.list_projects() + response = self.organization.list_projects( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -506,7 +686,7 @@ def test_create_project(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -514,6 +694,22 @@ def test_create_project(self, m): response = self.organization.create_project( '<PROJECT_ID>', '<NAME>', + region='fra', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'projectId': '<PROJECT_ID>', + 'name': '<NAME>', + 'region': 'fra', + }, ) self.assertEqual(response.to_dict(), data) @@ -549,7 +745,7 @@ def test_get_project(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -557,6 +753,14 @@ def test_get_project(self, m): response = self.organization.get_project( '<PROJECT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -591,7 +795,7 @@ def test_update_project(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -600,6 +804,19 @@ def test_update_project(self, m): '<PROJECT_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -607,7 +824,7 @@ def test_delete_project(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -615,6 +832,14 @@ def test_delete_project(self, m): response = self.organization.delete_project( '<PROJECT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -625,13 +850,28 @@ def test_list_project_keys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.organization.list_project_keys( '<PROJECT_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -651,7 +891,7 @@ def test_create_ephemeral_project_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -661,6 +901,20 @@ def test_create_ephemeral_project_key(self, m): [], 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/ephemeral', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'scopes': [], + 'duration': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -679,7 +933,7 @@ def test_get_project_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -688,6 +942,14 @@ def test_get_project_key(self, m): '<PROJECT_ID>', '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/<KEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -706,7 +968,7 @@ def test_update_project_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -716,6 +978,24 @@ def test_update_project_key(self, m): '<KEY_ID>', '<NAME>', [], + expire='2020-10-15T06:38:00.000+00:00', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'scopes': [], + 'expire': '2020-10-15T06:38:00.000+00:00', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -724,7 +1004,7 @@ def test_delete_project_key(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -733,4 +1013,12 @@ def test_delete_project_key(self, m): '<PROJECT_ID>', '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/organization/projects/<PROJECT_ID>/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_postgresql.py b/test/services/test_postgresql.py index 756a1305..62bb5214 100644 --- a/test/services/test_postgresql.py +++ b/test/services/test_postgresql.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.postgresql.list() + response = self.postgresql.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -81,7 +99,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -89,6 +107,44 @@ def test_create(self, m): response = self.postgresql.create( '<DATABASE_ID>', '<NAME>', + version='17', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'version': '17', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -100,12 +156,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +223,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +231,14 @@ def test_get(self, m): response = self.postgresql.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -220,13 +292,65 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.update( '<DATABASE_ID>', + name='<NAME>', + status='ready', + specification='<SPECIFICATION>', + replicas=1, + sync_mode='async', + network_idle_timeout_seconds=1, + network_ip_allowlist=[], + idle_timeout_minutes=1, + pitr=False, + pitr_retention_days=1, + storage_autoscaling=False, + storage_autoscaling_threshold_percent=1, + storage_autoscaling_max_gb=1, + metrics_trace_sample_rate=1.0, + metrics_slow_query_log_threshold_ms=1, + sql_api_enabled=False, + sql_api_allowed_statements=[], + sql_api_max_rows=1, + sql_api_max_bytes=1, + sql_api_timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'status': 'ready', + 'specification': '<SPECIFICATION>', + 'replicas': 1, + 'syncMode': 'async', + 'networkIdleTimeoutSeconds': 1, + 'networkIPAllowlist': [], + 'idleTimeoutMinutes': 1, + 'pitr': False, + 'pitrRetentionDays': 1, + 'storageAutoscaling': False, + 'storageAutoscalingThresholdPercent': 1, + 'storageAutoscalingMaxGb': 1, + 'metricsTraceSampleRate': 1.0, + 'metricsSlowQueryLogThresholdMs': 1, + 'sqlApiEnabled': False, + 'sqlApiAllowedStatements': [], + 'sqlApiMaxRows': 1, + 'sqlApiMaxBytes': 1, + 'sqlApiTimeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -235,7 +359,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -243,6 +367,14 @@ def test_delete(self, m): response = self.postgresql.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -253,13 +385,28 @@ def test_list_backups(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_backups( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -281,13 +428,27 @@ def test_create_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_backup( '<DATABASE_ID>', + type='full', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'full', + }, ) self.assertEqual(response.to_dict(), data) @@ -299,13 +460,28 @@ def test_list_backup_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_backup_policies( '<DATABASE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -325,7 +501,7 @@ def test_create_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +512,26 @@ def test_create_backup_policy(self, m): '<NAME>', 'schedule', 1, + type='full', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'policyId': '<POLICY_ID>', + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'type': 'full', + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -355,7 +551,7 @@ def test_get_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -364,6 +560,14 @@ def test_get_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -382,7 +586,7 @@ def test_update_backup_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -390,6 +594,26 @@ def test_update_backup_policy(self, m): response = self.postgresql.update_backup_policy( '<DATABASE_ID>', '<POLICY_ID>', + name='<NAME>', + schedule='schedule', + retention=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'schedule': 'schedule', + 'retention': 1, + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -398,7 +622,7 @@ def test_delete_backup_policy(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,6 +631,14 @@ def test_delete_backup_policy(self, m): '<DATABASE_ID>', '<POLICY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/policies/<POLICY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -420,7 +652,7 @@ def test_update_backup_storage(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -431,6 +663,28 @@ def test_update_backup_storage(self, m): '<BUCKET>', '<ACCESS_KEY>', '<SECRET_KEY>', + region='<REGION>', + prefix='<PREFIX>', + endpoint='https://example.com', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/storage', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'provider': 's3', + 'bucket': '<BUCKET>', + 'region': '<REGION>', + 'prefix': '<PREFIX>', + 'endpoint': 'https://example.com', + 'accessKey': '<ACCESS_KEY>', + 'secretKey': '<SECRET_KEY>', + }, ) self.assertEqual(response.to_dict(), data) @@ -452,7 +706,7 @@ def test_get_backup(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -461,6 +715,14 @@ def test_get_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -468,7 +730,7 @@ def test_delete_backup(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -477,6 +739,14 @@ def test_delete_backup(self, m): '<DATABASE_ID>', '<BACKUP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/backups/<BACKUP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -487,7 +757,7 @@ def test_list_branches(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -495,6 +765,14 @@ def test_list_branches(self, m): response = self.postgresql.list_branches( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/branches', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -548,13 +826,29 @@ def test_create_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_branch( '<DATABASE_ID>', + branch_id='<BRANCH_ID>', + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/branches', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'branchId': '<BRANCH_ID>', + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -609,7 +903,7 @@ def test_delete_branch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +912,14 @@ def test_delete_branch(self, m): '<DATABASE_ID>', '<BRANCH_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/branches/<BRANCH_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -634,7 +936,7 @@ def test_update_credentials(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -642,6 +944,14 @@ def test_update_credentials(self, m): response = self.postgresql.update_credentials( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/credentials', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -656,7 +966,7 @@ def test_create_execution(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -664,6 +974,23 @@ def test_create_execution(self, m): response = self.postgresql.create_execution( '<DATABASE_ID>', '<SQL>', + bindings={}, + timeout_seconds=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/executions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'sql': '<SQL>', + 'bindings': {}, + 'timeoutSeconds': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -676,7 +1003,7 @@ def test_list_extensions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -684,6 +1011,14 @@ def test_list_extensions(self, m): response = self.postgresql.list_extensions( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/extensions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -737,7 +1072,7 @@ def test_create_extension(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -746,6 +1081,19 @@ def test_create_extension(self, m): '<DATABASE_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/extensions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -799,7 +1147,7 @@ def test_delete_extension(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -808,6 +1156,14 @@ def test_delete_extension(self, m): '<DATABASE_ID>', '<EXTENSION_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/extensions/<EXTENSION_NAME>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -861,13 +1217,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -922,7 +1292,7 @@ def test_update_maintenance(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -932,6 +1302,20 @@ def test_update_maintenance(self, m): 'sun', 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/maintenance', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'day': 'sun', + 'hourUtc': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -985,7 +1369,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -993,6 +1377,21 @@ def test_create_migration(self, m): response = self.postgresql.create_migration( '<DATABASE_ID>', 'shared', + specification='<SPECIFICATION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetType': 'shared', + 'specification': '<SPECIFICATION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1004,13 +1403,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1022,7 +1439,7 @@ def test_get_pitr(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1030,6 +1447,14 @@ def test_get_pitr(self, m): response = self.postgresql.get_pitr( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/pitr', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1048,7 +1473,7 @@ def test_get_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1056,6 +1481,14 @@ def test_get_pooler(self, m): response = self.postgresql.get_pooler( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/pooler', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1074,13 +1507,41 @@ def test_update_pooler(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.update_pooler( '<DATABASE_ID>', + mode='transaction', + max_connections=1, + default_pool_size=1, + read_write_splitting=False, + pooler_cpu_request='<POOLER_CPU_REQUEST>', + pooler_cpu_limit='<POOLER_CPU_LIMIT>', + pooler_memory_request='<POOLER_MEMORY_REQUEST>', + pooler_memory_limit='<POOLER_MEMORY_LIMIT>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/pooler', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mode': 'transaction', + 'maxConnections': 1, + 'defaultPoolSize': 1, + 'readWriteSplitting': False, + 'poolerCpuRequest': '<POOLER_CPU_REQUEST>', + 'poolerCpuLimit': '<POOLER_CPU_LIMIT>', + 'poolerMemoryRequest': '<POOLER_MEMORY_REQUEST>', + 'poolerMemoryLimit': '<POOLER_MEMORY_LIMIT>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1096,7 +1557,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1104,6 +1565,14 @@ def test_get_replicas(self, m): response = self.postgresql.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1114,13 +1583,33 @@ def test_list_restorations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.list_restorations( '<DATABASE_ID>', + status='pending', + type='backup', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['pending'], + 'type': ['backup'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1138,13 +1627,39 @@ def test_create_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.postgresql.create_restoration( '<DATABASE_ID>', + type='backup', + backup_id='<BACKUP_ID>', + target_database_id='<TARGET_DATABASE_ID>', + target_time='2020-10-15T06:38:00.000+00:00', + restore_mode='in_place', + target_name='<TARGET_NAME>', + auto_promote=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'backup', + 'backupId': '<BACKUP_ID>', + 'targetDatabaseId': '<TARGET_DATABASE_ID>', + 'targetTime': '2020-10-15T06:38:00.000+00:00', + 'restoreMode': 'in_place', + 'targetName': '<TARGET_NAME>', + 'autoPromote': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1162,7 +1677,7 @@ def test_get_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1171,6 +1686,14 @@ def test_get_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations/<RESTORATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1187,7 +1710,7 @@ def test_promote_restoration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1196,6 +1719,14 @@ def test_promote_restoration(self, m): '<DATABASE_ID>', '<RESTORATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/restorations/<RESTORATION_ID>/promotions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1215,7 +1746,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1223,6 +1754,14 @@ def test_get_status(self, m): response = self.postgresql.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1276,7 +1815,7 @@ def test_create_upgrade(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1285,4 +1824,17 @@ def test_create_upgrade(self, m): '<DATABASE_ID>', '<TARGET_VERSION>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/postgresql/<DATABASE_ID>/upgrades', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetVersion': '<TARGET_VERSION>', + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_presences.py b/test/services/test_presences.py index 709ebb15..acd09a40 100644 --- a/test/services/test_presences.py +++ b/test/services/test_presences.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.presences.list() + response = self.presences.list( + queries=[], + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + 'ttl': ['1'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -42,7 +62,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -50,6 +70,14 @@ def test_get(self, m): response = self.presences.get( '<PRESENCE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -64,7 +92,7 @@ def test_upsert(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -73,6 +101,26 @@ def test_upsert(self, m): '<PRESENCE_ID>', '<USER_ID>', '<STATUS>', + permissions=[], + expires_at='2020-10-15T06:38:00.000+00:00', + metadata={}, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'status': '<STATUS>', + 'permissions': [], + 'expiresAt': '2020-10-15T06:38:00.000+00:00', + 'metadata': {}, + }, ) self.assertEqual(response.to_dict(), data) @@ -88,7 +136,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -96,6 +144,29 @@ def test_update(self, m): response = self.presences.update( '<PRESENCE_ID>', '<USER_ID>', + status='<STATUS>', + expires_at='2020-10-15T06:38:00.000+00:00', + metadata={}, + permissions=[], + purge=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'status': '<STATUS>', + 'expiresAt': '2020-10-15T06:38:00.000+00:00', + 'metadata': {}, + 'permissions': [], + 'purge': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -104,7 +175,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -112,4 +183,12 @@ def test_delete(self, m): response = self.presences.delete( '<PRESENCE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/presences/<PRESENCE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_project.py b/test/services/test_project.py index d0c94701..35d60355 100644 --- a/test/services/test_project.py +++ b/test/services/test_project.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -46,12 +48,20 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.get() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -59,12 +69,20 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.delete() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -99,14 +117,27 @@ def test_update_auth_method(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_auth_method( 'email-password', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/auth-methods/email-password', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -118,12 +149,28 @@ def test_list_environments(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_environments() + response = self.project.list_environments( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -158,7 +205,7 @@ def test_create_environment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -166,6 +213,22 @@ def test_create_environment(self, m): response = self.project.create_environment( '<ENVIRONMENT_ID>', '<NAME>', + resources=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'environmentId': '<ENVIRONMENT_ID>', + 'name': '<NAME>', + 'resources': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -201,7 +264,7 @@ def test_get_environment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -209,6 +272,14 @@ def test_get_environment(self, m): response = self.project.get_environment( '<ENVIRONMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments/<ENVIRONMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -243,7 +314,7 @@ def test_update_environment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -252,6 +323,19 @@ def test_update_environment(self, m): '<ENVIRONMENT_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments/<ENVIRONMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -259,7 +343,7 @@ def test_delete_environment(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -267,6 +351,14 @@ def test_delete_environment(self, m): response = self.project.delete_environment( '<ENVIRONMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/environments/<ENVIRONMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -277,12 +369,28 @@ def test_list_keys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_keys() + response = self.project.list_keys( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -301,7 +409,7 @@ def test_create_ephemeral_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -310,6 +418,20 @@ def test_create_ephemeral_key(self, m): [], 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/ephemeral', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'scopes': [], + 'duration': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -328,7 +450,7 @@ def test_get_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +458,14 @@ def test_get_key(self, m): response = self.project.get_key( '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/<KEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -354,7 +484,7 @@ def test_update_key(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -363,6 +493,24 @@ def test_update_key(self, m): '<KEY_ID>', '<NAME>', [], + expire='2020-10-15T06:38:00.000+00:00', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'scopes': [], + 'expire': '2020-10-15T06:38:00.000+00:00', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -371,7 +519,7 @@ def test_delete_key(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -379,6 +527,14 @@ def test_delete_key(self, m): response = self.project.delete_key( '<KEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/keys/<KEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -413,7 +569,7 @@ def test_update_labels(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -421,6 +577,19 @@ def test_update_labels(self, m): response = self.project.update_labels( [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/labels', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'labels': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -431,12 +600,28 @@ def test_list_mock_phones(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_mock_phones() + response = self.project.list_mock_phones( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -449,7 +634,7 @@ def test_create_mock_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -458,6 +643,20 @@ def test_create_mock_phone(self, m): '+12065550100', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'number': '+12065550100', + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -470,7 +669,7 @@ def test_get_mock_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -478,6 +677,14 @@ def test_get_mock_phone(self, m): response = self.project.get_mock_phone( '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones/+12065550100', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -490,7 +697,7 @@ def test_update_mock_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -499,6 +706,19 @@ def test_update_mock_phone(self, m): '+12065550100', '<OTP>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones/+12065550100', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'otp': '<OTP>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -506,7 +726,7 @@ def test_delete_mock_phone(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -514,6 +734,14 @@ def test_delete_mock_phone(self, m): response = self.project.delete_mock_phone( '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/mock-phones/+12065550100', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -524,12 +752,28 @@ def test_list_o_auth2_providers(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_o_auth2_providers() + response = self.project.list_o_auth2_providers( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -564,14 +808,56 @@ def test_update_o_auth2_server(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_o_auth2_server( - True, + False, 'https://example.com', + scopes=[], + authorization_details_types=[], + access_token_duration=1, + refresh_token_duration=1, + public_access_token_duration=1, + public_refresh_token_duration=1, + installation_access_token_duration=1, + confidential_pkce=False, + verification_url='https://example.com', + user_code_length=1, + user_code_format='numeric', + device_code_duration=1, + default_scopes=[], + installation_scopes=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2-server', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'authorizationUrl': 'https://example.com', + 'scopes': [], + 'authorizationDetailsTypes': [], + 'accessTokenDuration': 1, + 'refreshTokenDuration': 1, + 'publicAccessTokenDuration': 1, + 'publicRefreshTokenDuration': 1, + 'installationAccessTokenDuration': 1, + 'confidentialPkce': False, + 'verificationUrl': 'https://example.com', + 'userCodeLength': 1, + 'userCodeFormat': 'numeric', + 'deviceCodeDuration': 1, + 'defaultScopes': [], + 'installationScopes': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -585,12 +871,31 @@ def test_update_o_auth2_amazon(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_amazon() + response = self.project.update_o_auth2_amazon( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/amazon', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -607,12 +912,39 @@ def test_update_o_auth2_apple(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_apple() + response = self.project.update_o_auth2_apple( + service_id='<SERVICE_ID>', + key_id='<KEY_ID>', + team_id='<TEAM_ID>', + p8_file='<P8_FILE>', + enabled=False, + native_client_ids=[], + native_enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/apple', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'serviceId': '<SERVICE_ID>', + 'keyId': '<KEY_ID>', + 'teamId': '<TEAM_ID>', + 'p8File': '<P8_FILE>', + 'enabled': False, + 'nativeClientIds': [], + 'nativeEnabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -625,12 +957,31 @@ def test_update_o_auth2_appwrite(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_appwrite() + response = self.project.update_o_auth2_appwrite( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/appwrite', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -645,12 +996,35 @@ def test_update_o_auth2_auth0(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_auth0() + response = self.project.update_o_auth2_auth0( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/auth0', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -664,12 +1038,33 @@ def test_update_o_auth2_authentik(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_authentik() + response = self.project.update_o_auth2_authentik( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/authentik', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -682,12 +1077,31 @@ def test_update_o_auth2_autodesk(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_autodesk() + response = self.project.update_o_auth2_autodesk( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/autodesk', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -700,12 +1114,31 @@ def test_update_o_auth2_bitbucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_bitbucket() + response = self.project.update_o_auth2_bitbucket( + key='<KEY>', + secret='<SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/bitbucket', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'secret': '<SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -718,12 +1151,31 @@ def test_update_o_auth2_bitly(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_bitly() + response = self.project.update_o_auth2_bitly( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/bitly', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -736,12 +1188,31 @@ def test_update_o_auth2_box(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_box() + response = self.project.update_o_auth2_box( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/box', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -754,12 +1225,31 @@ def test_update_o_auth2_cloudflare(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_cloudflare() + response = self.project.update_o_auth2_cloudflare( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/cloudflare', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -772,12 +1262,31 @@ def test_update_o_auth2_dailymotion(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_dailymotion() + response = self.project.update_o_auth2_dailymotion( + api_key='<API_KEY>', + api_secret='<API_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/dailymotion', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'apiKey': '<API_KEY>', + 'apiSecret': '<API_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -791,12 +1300,33 @@ def test_update_o_auth2_discord(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_discord() + response = self.project.update_o_auth2_discord( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/discord', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -809,12 +1339,31 @@ def test_update_o_auth2_disqus(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_disqus() + response = self.project.update_o_auth2_disqus( + public_key='<PUBLIC_KEY>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/disqus', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'publicKey': '<PUBLIC_KEY>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -827,12 +1376,31 @@ def test_update_o_auth2_dropbox(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_dropbox() + response = self.project.update_o_auth2_dropbox( + app_key='<APP_KEY>', + app_secret='<APP_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/dropbox', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appKey': '<APP_KEY>', + 'appSecret': '<APP_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -845,12 +1413,31 @@ def test_update_o_auth2_etsy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_etsy() + response = self.project.update_o_auth2_etsy( + key_string='<KEY_STRING>', + shared_secret='<SHARED_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/etsy', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'keyString': '<KEY_STRING>', + 'sharedSecret': '<SHARED_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -863,12 +1450,31 @@ def test_update_o_auth2_facebook(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_facebook() + response = self.project.update_o_auth2_facebook( + app_id='<APP_ID>', + app_secret='<APP_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/facebook', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '<APP_ID>', + 'appSecret': '<APP_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -881,12 +1487,31 @@ def test_update_o_auth2_figma(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_figma() + response = self.project.update_o_auth2_figma( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/figma', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -900,12 +1525,33 @@ def test_update_o_auth2_fusion_auth(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_fusion_auth() + response = self.project.update_o_auth2_fusion_auth( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/fusionauth', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -919,12 +1565,33 @@ def test_update_o_auth2_git_hub(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_git_hub() + response = self.project.update_o_auth2_git_hub( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/github', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -938,12 +1605,33 @@ def test_update_o_auth2_gitlab(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_gitlab() + response = self.project.update_o_auth2_gitlab( + application_id='<APPLICATION_ID>', + secret='<SECRET>', + endpoint='https://example.com', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/gitlab', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'applicationId': '<APPLICATION_ID>', + 'secret': '<SECRET>', + 'endpoint': 'https://example.com', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -959,12 +1647,37 @@ def test_update_o_auth2_google(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_google() + response = self.project.update_o_auth2_google( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + prompt=[], + enabled=False, + native_client_ids=[], + native_enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/google', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'prompt': [], + 'enabled': False, + 'nativeClientIds': [], + 'nativeEnabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -977,12 +1690,31 @@ def test_update_o_auth2_hugging_face(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_hugging_face() + response = self.project.update_o_auth2_hugging_face( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/huggingface', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -996,12 +1728,33 @@ def test_update_o_auth2_kakao(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_kakao() + response = self.project.update_o_auth2_kakao( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/kakao', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1016,12 +1769,35 @@ def test_update_o_auth2_keycloak(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_keycloak() + response = self.project.update_o_auth2_keycloak( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + endpoint='<ENDPOINT>', + realm_name='<REALM_NAME>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/keycloak', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'endpoint': '<ENDPOINT>', + 'realmName': '<REALM_NAME>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1034,12 +1810,31 @@ def test_update_o_auth2_kick(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_kick() + response = self.project.update_o_auth2_kick( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/kick', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1052,12 +1847,31 @@ def test_update_o_auth2_linkedin(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_linkedin() + response = self.project.update_o_auth2_linkedin( + client_id='<CLIENT_ID>', + primary_client_secret='<PRIMARY_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/linkedin', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'primaryClientSecret': '<PRIMARY_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1072,12 +1886,35 @@ def test_update_o_auth2_microsoft(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_microsoft() + response = self.project.update_o_auth2_microsoft( + application_id='<APPLICATION_ID>', + application_secret='<APPLICATION_SECRET>', + tenant='<TENANT>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/microsoft', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'applicationId': '<APPLICATION_ID>', + 'applicationSecret': '<APPLICATION_SECRET>', + 'tenant': '<TENANT>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1090,12 +1927,31 @@ def test_update_o_auth2_notion(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_notion() + response = self.project.update_o_auth2_notion( + oauth_client_id='<OAUTH_CLIENT_ID>', + oauth_client_secret='<OAUTH_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/notion', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'oauthClientId': '<OAUTH_CLIENT_ID>', + 'oauthClientSecret': '<OAUTH_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1113,12 +1969,43 @@ def test_update_o_auth2_oidc(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_oidc() + response = self.project.update_o_auth2_oidc( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + well_known_url='https://example.com', + authorization_url='https://example.com', + token_url='https://example.com', + user_info_url='https://example.com', + prompt=[], + max_age=1, + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/oidc', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'wellKnownURL': 'https://example.com', + 'authorizationURL': 'https://example.com', + 'tokenURL': 'https://example.com', + 'userInfoURL': 'https://example.com', + 'prompt': [], + 'maxAge': 1, + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1134,12 +2021,37 @@ def test_update_o_auth2_okta(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_okta() + response = self.project.update_o_auth2_okta( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + domain='example.com', + authorization_server_id='<AUTHORIZATION_SERVER_ID>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/okta', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'domain': 'example.com', + 'authorizationServerId': '<AUTHORIZATION_SERVER_ID>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1152,12 +2064,31 @@ def test_update_o_auth2_paypal(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_paypal() + response = self.project.update_o_auth2_paypal( + client_id='<CLIENT_ID>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/paypal', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1170,12 +2101,31 @@ def test_update_o_auth2_paypal_sandbox(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_paypal_sandbox() + response = self.project.update_o_auth2_paypal_sandbox( + client_id='<CLIENT_ID>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/paypalSandbox', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1188,12 +2138,31 @@ def test_update_o_auth2_podio(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_podio() + response = self.project.update_o_auth2_podio( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/podio', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1206,12 +2175,31 @@ def test_update_o_auth2_resend(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_resend() + response = self.project.update_o_auth2_resend( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/resend', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1225,12 +2213,33 @@ def test_update_o_auth2_salesforce(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_salesforce() + response = self.project.update_o_auth2_salesforce( + customer_key='<CUSTOMER_KEY>', + customer_secret='<CUSTOMER_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/salesforce', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'customerKey': '<CUSTOMER_KEY>', + 'customerSecret': '<CUSTOMER_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1243,12 +2252,31 @@ def test_update_o_auth2_slack(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_slack() + response = self.project.update_o_auth2_slack( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/slack', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1261,12 +2289,31 @@ def test_update_o_auth2_spotify(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_spotify() + response = self.project.update_o_auth2_spotify( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/spotify', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1279,12 +2326,31 @@ def test_update_o_auth2_stripe(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_stripe() + response = self.project.update_o_auth2_stripe( + client_id='<CLIENT_ID>', + api_secret_key='<API_SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/stripe', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'apiSecretKey': '<API_SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1297,12 +2363,31 @@ def test_update_o_auth2_tik_tok(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_tik_tok() + response = self.project.update_o_auth2_tik_tok( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/tiktok', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1315,12 +2400,31 @@ def test_update_o_auth2_tradeshift(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_tradeshift() + response = self.project.update_o_auth2_tradeshift( + oauth2_client_id='<OAUTH2_CLIENT_ID>', + oauth2_client_secret='<OAUTH2_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/tradeshift', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'oauth2ClientId': '<OAUTH2_CLIENT_ID>', + 'oauth2ClientSecret': '<OAUTH2_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1333,12 +2437,31 @@ def test_update_o_auth2_tradeshift_sandbox(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_tradeshift_sandbox() + response = self.project.update_o_auth2_tradeshift_sandbox( + oauth2_client_id='<OAUTH2_CLIENT_ID>', + oauth2_client_secret='<OAUTH2_CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/tradeshiftBox', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'oauth2ClientId': '<OAUTH2_CLIENT_ID>', + 'oauth2ClientSecret': '<OAUTH2_CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1351,12 +2474,31 @@ def test_update_o_auth2_twitch(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_twitch() + response = self.project.update_o_auth2_twitch( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/twitch', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1369,12 +2511,31 @@ def test_update_o_auth2_webflow(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_webflow() + response = self.project.update_o_auth2_webflow( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/webflow', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1387,12 +2548,31 @@ def test_update_o_auth2_word_press(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_word_press() + response = self.project.update_o_auth2_word_press( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/wordpress', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1405,12 +2585,31 @@ def test_update_o_auth2_x(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_x() + response = self.project.update_o_auth2_x( + customer_key='<CUSTOMER_KEY>', + secret_key='<SECRET_KEY>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/x', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'customerKey': '<CUSTOMER_KEY>', + 'secretKey': '<SECRET_KEY>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1423,12 +2622,31 @@ def test_update_o_auth2_yahoo(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_yahoo() + response = self.project.update_o_auth2_yahoo( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/yahoo', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1441,12 +2659,31 @@ def test_update_o_auth2_yandex(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_yandex() + response = self.project.update_o_auth2_yandex( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/yandex', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1460,12 +2697,33 @@ def test_update_o_auth2_zoho(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_zoho() + response = self.project.update_o_auth2_zoho( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + prompt=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/zoho', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + 'prompt': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1478,12 +2736,31 @@ def test_update_o_auth2_zoom(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_o_auth2_zoom() + response = self.project.update_o_auth2_zoom( + client_id='<CLIENT_ID>', + client_secret='<CLIENT_SECRET>', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/zoom', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'clientId': '<CLIENT_ID>', + 'clientSecret': '<CLIENT_SECRET>', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1497,7 +2774,7 @@ def test_get_o_auth2_provider(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1505,6 +2782,14 @@ def test_get_o_auth2_provider(self, m): response = self.project.get_o_auth2_provider( 'amazon', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/oauth2/amazon', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1515,12 +2800,28 @@ def test_list_platforms(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_platforms() + response = self.project.list_platforms( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1536,7 +2837,7 @@ def test_create_android_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1545,6 +2846,23 @@ def test_create_android_platform(self, m): '<PLATFORM_ID>', '<NAME>', '<APPLICATION_ID>', + fingerprints=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/android', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'applicationId': '<APPLICATION_ID>', + 'fingerprints': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -1561,7 +2879,7 @@ def test_update_android_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1570,6 +2888,22 @@ def test_update_android_platform(self, m): '<PLATFORM_ID>', '<NAME>', '<APPLICATION_ID>', + fingerprints=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/android/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'applicationId': '<APPLICATION_ID>', + 'fingerprints': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -1585,7 +2919,7 @@ def test_create_apple_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1595,6 +2929,21 @@ def test_create_apple_platform(self, m): '<NAME>', '<BUNDLE_IDENTIFIER>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/apple', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'bundleIdentifier': '<BUNDLE_IDENTIFIER>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1609,7 +2958,7 @@ def test_update_apple_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1619,6 +2968,20 @@ def test_update_apple_platform(self, m): '<NAME>', '<BUNDLE_IDENTIFIER>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/apple/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'bundleIdentifier': '<BUNDLE_IDENTIFIER>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1633,7 +2996,7 @@ def test_create_linux_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1643,6 +3006,21 @@ def test_create_linux_platform(self, m): '<NAME>', '<PACKAGE_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/linux', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'packageName': '<PACKAGE_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1657,7 +3035,7 @@ def test_update_linux_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1667,6 +3045,20 @@ def test_update_linux_platform(self, m): '<NAME>', '<PACKAGE_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/linux/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'packageName': '<PACKAGE_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1681,7 +3073,7 @@ def test_create_web_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1691,6 +3083,21 @@ def test_create_web_platform(self, m): '<NAME>', 'app.example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/web', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'hostname': 'app.example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1705,7 +3112,7 @@ def test_update_web_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1715,6 +3122,20 @@ def test_update_web_platform(self, m): '<NAME>', 'app.example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/web/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'hostname': 'app.example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1729,7 +3150,7 @@ def test_create_windows_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1739,6 +3160,21 @@ def test_create_windows_platform(self, m): '<NAME>', '<PACKAGE_IDENTIFIER_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/windows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'platformId': '<PLATFORM_ID>', + 'name': '<NAME>', + 'packageIdentifierName': '<PACKAGE_IDENTIFIER_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1753,7 +3189,7 @@ def test_update_windows_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1763,6 +3199,20 @@ def test_update_windows_platform(self, m): '<NAME>', '<PACKAGE_IDENTIFIER_NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/windows/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'packageIdentifierName': '<PACKAGE_IDENTIFIER_NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1777,7 +3227,7 @@ def test_get_platform(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1785,6 +3235,14 @@ def test_get_platform(self, m): response = self.project.get_platform( '<PLATFORM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/<PLATFORM_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1792,7 +3250,7 @@ def test_delete_platform(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1800,6 +3258,14 @@ def test_delete_platform(self, m): response = self.project.delete_platform( '<PLATFORM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/platforms/<PLATFORM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1810,12 +3276,28 @@ def test_list_policies(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_policies() + response = self.project.list_policies( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1850,13 +3332,26 @@ def test_update_deny_aliased_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_aliased_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-aliased-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1892,13 +3387,26 @@ def test_update_deny_corporate_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_corporate_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-corporate-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1934,13 +3442,26 @@ def test_update_deny_disposable_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_disposable_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-disposable-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1976,18 +3497,31 @@ def test_update_deny_free_email_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_deny_free_email_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-free-email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() - def test_update_membership_privacy_policy(self, m): + def test_update_deny_realtime_guests_policy(self, m): data = { "$id": "5e5ea5c16897e", "$createdAt": "2020-10-15T06:38:00.000+00:00", @@ -2018,12 +3552,92 @@ def test_update_membership_privacy_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( + 'PATCH', requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.project.update_deny_realtime_guests_policy( + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/deny-realtime-guests', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, + ) + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_update_membership_privacy_policy(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "name": "New Project", + "teamId": "1592981250", + "region": "fra", + "smtpEnabled": True, + "smtpSenderName": "John Appwrite", + "smtpSenderEmail": "john@appwrite.io", + "smtpReplyToName": "Support Team", + "smtpReplyToEmail": "support@appwrite.io", + "smtpHost": "mail.appwrite.io", + "smtpPort": 25.0, + "smtpUsername": "emailuser", + "smtpPassword": "smtp-password", + "smtpSecure": "tls", + "pingCount": 1.0, + "pingedAt": "2020-10-15T06:38:00.000+00:00", + "labels": [], + "status": "active", + "onboarding": {}, + "authMethods": [], + "services": [], + "protocols": [], + "blocks": [], + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + } + headers = {'Content-Type': 'application/json'} + m.request( + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_membership_privacy_policy() + response = self.project.update_membership_privacy_policy( + user_id=False, + user_email=False, + user_phone=False, + user_name=False, + user_mfa=False, + user_accessed_at=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/membership-privacy', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': False, + 'userEmail': False, + 'userPhone': False, + 'userName': False, + 'userMFA': False, + 'userAccessedAt': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2058,12 +3672,33 @@ def test_update_mfa_factors_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_mfa_factors_policy() + response = self.project.update_mfa_factors_policy( + totp=False, + email=False, + phone=False, + custom=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/mfa-factors', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'totp': False, + 'email': False, + 'phone': False, + 'custom': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2098,7 +3733,7 @@ def test_update_passkey_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2106,6 +3741,19 @@ def test_update_passkey_policy(self, m): response = self.project.update_passkey_policy( '<RP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/passkey', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rpId': '<RP_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2140,13 +3788,28 @@ def test_update_password_dictionary_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_password_dictionary_policy( - True, + False, + deny_list=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-dictionary', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'denyList': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -2182,7 +3845,7 @@ def test_update_password_history_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2190,6 +3853,19 @@ def test_update_password_history_policy(self, m): response = self.project.update_password_history_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-history', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'total': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2224,13 +3900,28 @@ def test_update_password_personal_data_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_password_personal_data_policy( - True, + False, + deny_list=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-personal-data', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'denyList': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -2266,12 +3957,31 @@ def test_update_password_pwned_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_password_pwned_policy() + response = self.project.update_password_pwned_policy( + enabled=False, + sessions=False, + users=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-pwned', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'sessions': False, + 'users': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2306,12 +4016,29 @@ def test_update_password_rotation_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_password_rotation_policy() + response = self.project.update_password_rotation_policy( + enabled=False, + max_age=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-rotation', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + 'maxAge': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2326,12 +4053,35 @@ def test_update_password_strength_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_password_strength_policy() + response = self.project.update_password_strength_policy( + min=1, + uppercase=False, + lowercase=False, + number=False, + symbols=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-strength', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'min': 1, + 'uppercase': False, + 'lowercase': False, + 'number': False, + 'symbols': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2366,13 +4116,26 @@ def test_update_session_alert_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_session_alert_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-alert', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -2408,7 +4171,7 @@ def test_update_session_duration_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2416,6 +4179,19 @@ def test_update_session_duration_policy(self, m): response = self.project.update_session_duration_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-duration', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'duration': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2450,13 +4226,26 @@ def test_update_session_invalidation_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_session_invalidation_policy( - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-invalidation', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -2492,7 +4281,7 @@ def test_update_session_limit_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2500,6 +4289,19 @@ def test_update_session_limit_policy(self, m): response = self.project.update_session_limit_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/session-limit', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'total': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2534,7 +4336,7 @@ def test_update_user_limit_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2542,6 +4344,19 @@ def test_update_user_limit_policy(self, m): response = self.project.update_user_limit_policy( 1, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/user-limit', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'total': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2553,7 +4368,7 @@ def test_get_policy(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2561,6 +4376,14 @@ def test_get_policy(self, m): response = self.project.get_policy( 'password-dictionary', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/policies/password-dictionary', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2595,14 +4418,27 @@ def test_update_protocol(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_protocol( 'rest', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/protocols/rest', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -2638,17 +4474,51 @@ def test_update_service(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_service( 'account', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/services/account', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'enabled': False, + }, ) self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_delete_sessions(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request( + 'DELETE', + requests_mock.ANY, + text=json.dumps(data), + headers=headers, + ) + response = self.project.delete_sessions() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) + self.assertEqual(response, data) + @requests_mock.Mocker() def test_update_smtp(self, m): data = { @@ -2681,12 +4551,45 @@ def test_update_smtp(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.update_smtp() + response = self.project.update_smtp( + host='example.com', + port=1, + username='<USERNAME>', + password='password', + sender_email='email@example.com', + sender_name='<SENDER_NAME>', + reply_to_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + secure='tls', + enabled=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'host': 'example.com', + 'port': 1, + 'username': '<USERNAME>', + 'password': 'password', + 'senderEmail': 'email@example.com', + 'senderName': '<SENDER_NAME>', + 'replyToEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + 'secure': 'tls', + 'enabled': False, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2694,7 +4597,7 @@ def test_create_smtp_test(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2702,6 +4605,19 @@ def test_create_smtp_test(self, m): response = self.project.create_smtp_test( [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/smtp/tests', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'emails': [], + }, + ) self.assertEqual(response, data) @requests_mock.Mocker() @@ -2712,12 +4628,28 @@ def test_list_email_templates(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_email_templates() + response = self.project.list_email_templates( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/templates/email', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2734,13 +4666,40 @@ def test_update_email_template(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_email_template( 'verification', + locale='af', + subject='<SUBJECT>', + message='<MESSAGE>', + sender_name='<SENDER_NAME>', + sender_email='email@example.com', + reply_to_email='email@example.com', + reply_to_name='<REPLY_TO_NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/templates/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'templateId': 'verification', + 'locale': 'af', + 'subject': '<SUBJECT>', + 'message': '<MESSAGE>', + 'senderName': '<SENDER_NAME>', + 'senderEmail': 'email@example.com', + 'replyToEmail': 'email@example.com', + 'replyToName': '<REPLY_TO_NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -2758,13 +4717,27 @@ def test_get_email_template(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.get_email_template( 'verification', + locale='af', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/templates/email/verification', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'locale': ['af'], + }, ) self.assertEqual(response.to_dict(), data) @@ -2776,12 +4749,28 @@ def test_list_variables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.project.list_variables() + response = self.project.list_variables( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2799,7 +4788,7 @@ def test_create_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2808,6 +4797,25 @@ def test_create_variable(self, m): '<VARIABLE_ID>', '<KEY>', '<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'variableId': '<VARIABLE_ID>', + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -2826,7 +4834,7 @@ def test_get_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2834,6 +4842,14 @@ def test_get_variable(self, m): response = self.project.get_variable( '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables/<VARIABLE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -2851,13 +4867,33 @@ def test_update_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.project.update_variable( '<VARIABLE_ID>', + key='<KEY>', + value='<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -2866,7 +4902,7 @@ def test_delete_variable(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2874,4 +4910,12 @@ def test_delete_variable(self, m): response = self.project.delete_variable( '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/project/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_proxy.py b/test/services/test_proxy.py index c12c5271..f20f3d13 100644 --- a/test/services/test_proxy.py +++ b/test/services/test_proxy.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -24,7 +26,7 @@ def test_create_invalidation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -32,6 +34,22 @@ def test_create_invalidation(self, m): response = self.proxy.create_invalidation( 'example.com', 'tag', + reference='<REFERENCE>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/invalidations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'type': 'tag', + 'reference': '<REFERENCE>', + }, ) self.assertEqual(response.to_dict(), data) @@ -43,12 +61,28 @@ def test_list_rules(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.proxy.list_rules() + response = self.proxy.list_rules( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -72,7 +106,7 @@ def test_create_api_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -80,6 +114,19 @@ def test_create_api_rule(self, m): response = self.proxy.create_api_rule( 'example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/api', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -103,7 +150,7 @@ def test_create_function_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -111,6 +158,22 @@ def test_create_function_rule(self, m): response = self.proxy.create_function_rule( 'example.com', '<FUNCTION_ID>', + branch='<BRANCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/function', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'functionId': '<FUNCTION_ID>', + 'branch': '<BRANCH>', + }, ) self.assertEqual(response.to_dict(), data) @@ -135,7 +198,7 @@ def test_create_redirect_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -147,6 +210,23 @@ def test_create_redirect_rule(self, m): '<RESOURCE_ID>', 'site', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/redirect', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'url': 'https://example.com', + 'statusCode': '301', + 'resourceId': '<RESOURCE_ID>', + 'resourceType': 'site', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -170,7 +250,7 @@ def test_create_site_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -178,6 +258,22 @@ def test_create_site_rule(self, m): response = self.proxy.create_site_rule( 'example.com', '<SITE_ID>', + branch='<BRANCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/site', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'siteId': '<SITE_ID>', + 'branch': '<BRANCH>', + }, ) self.assertEqual(response.to_dict(), data) @@ -202,7 +298,7 @@ def test_create_smtp_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -210,6 +306,22 @@ def test_create_smtp_rule(self, m): response = self.proxy.create_smtp_rule( 'example.com', '<FUNCTION_ID>', + branch='<BRANCH>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/smtp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'domain': 'example.com', + 'functionId': '<FUNCTION_ID>', + 'branch': '<BRANCH>', + }, ) self.assertEqual(response.to_dict(), data) @@ -234,7 +346,7 @@ def test_get_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -242,6 +354,14 @@ def test_get_rule(self, m): response = self.proxy.get_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/<RULE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -249,7 +369,7 @@ def test_delete_rule(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -257,6 +377,14 @@ def test_delete_rule(self, m): response = self.proxy.delete_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -280,7 +408,7 @@ def test_update_rule_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -288,4 +416,12 @@ def test_update_rule_status(self, m): response = self.proxy.update_rule_status( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/proxy/rules/<RULE_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_sites.py b/test/services/test_sites.py index a1e4354e..9fedd1c3 100644 --- a/test/services/test_sites.py +++ b/test/services/test_sites.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.sites.list() + response = self.sites.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -72,7 +92,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -82,6 +102,64 @@ def test_create(self, m): '<NAME>', 'analog', 'node-14.5', + enabled=False, + logging=False, + timeout=1, + install_command='<INSTALL_COMMAND>', + build_command='<BUILD_COMMAND>', + start_command='<START_COMMAND>', + output_directory='<OUTPUT_DIRECTORY>', + adapter='static', + installation_id='<INSTALLATION_ID>', + fallback_file='<FALLBACK_FILE>', + provider_repository_id='<PROVIDER_REPOSITORY_ID>', + provider_branch='<PROVIDER_BRANCH>', + provider_silent_mode=False, + provider_root_directory='<PROVIDER_ROOT_DIRECTORY>', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + scopes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'siteId': '<SITE_ID>', + 'name': '<NAME>', + 'framework': 'analog', + 'enabled': False, + 'logging': False, + 'timeout': 1, + 'installCommand': '<INSTALL_COMMAND>', + 'buildCommand': '<BUILD_COMMAND>', + 'startCommand': '<START_COMMAND>', + 'outputDirectory': '<OUTPUT_DIRECTORY>', + 'buildRuntime': 'node-14.5', + 'adapter': 'static', + 'installationId': '<INSTALLATION_ID>', + 'fallbackFile': '<FALLBACK_FILE>', + 'providerRepositoryId': '<PROVIDER_REPOSITORY_ID>', + 'providerBranch': '<PROVIDER_BRANCH>', + 'providerSilentMode': False, + 'providerRootDirectory': '<PROVIDER_ROOT_DIRECTORY>', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'scopes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -93,12 +171,27 @@ def test_list_frameworks(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.sites.list_frameworks() + response = self.sites.list_frameworks( + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/frameworks', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -109,12 +202,29 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.sites.list_specifications() + response = self.sites.list_specifications( + type='runtimes', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['runtimes'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -159,7 +269,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -167,6 +277,14 @@ def test_get(self, m): response = self.sites.get( '<SITE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -211,7 +329,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -220,6 +338,64 @@ def test_update(self, m): '<SITE_ID>', '<NAME>', 'analog', + enabled=False, + logging=False, + timeout=1, + install_command='<INSTALL_COMMAND>', + build_command='<BUILD_COMMAND>', + start_command='<START_COMMAND>', + output_directory='<OUTPUT_DIRECTORY>', + build_runtime='node-14.5', + adapter='static', + fallback_file='<FALLBACK_FILE>', + installation_id='<INSTALLATION_ID>', + provider_repository_id='<PROVIDER_REPOSITORY_ID>', + provider_branch='<PROVIDER_BRANCH>', + provider_silent_mode=False, + provider_root_directory='<PROVIDER_ROOT_DIRECTORY>', + provider_branches=[], + provider_paths=[], + build_specification='s-1vcpu-512mb', + runtime_specification='s-1vcpu-512mb', + deployment_retention=1, + scopes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'framework': 'analog', + 'enabled': False, + 'logging': False, + 'timeout': 1, + 'installCommand': '<INSTALL_COMMAND>', + 'buildCommand': '<BUILD_COMMAND>', + 'startCommand': '<START_COMMAND>', + 'outputDirectory': '<OUTPUT_DIRECTORY>', + 'buildRuntime': 'node-14.5', + 'adapter': 'static', + 'fallbackFile': '<FALLBACK_FILE>', + 'installationId': '<INSTALLATION_ID>', + 'providerRepositoryId': '<PROVIDER_REPOSITORY_ID>', + 'providerBranch': '<PROVIDER_BRANCH>', + 'providerSilentMode': False, + 'providerRootDirectory': '<PROVIDER_ROOT_DIRECTORY>', + 'providerBranches': [], + 'providerPaths': [], + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', + 'deploymentRetention': 1, + 'scopes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -228,7 +404,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -236,6 +412,14 @@ def test_delete(self, m): response = self.sites.delete( '<SITE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -280,7 +464,7 @@ def test_update_site_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -289,6 +473,19 @@ def test_update_site_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '<DEPLOYMENT_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -299,13 +496,30 @@ def test_list_deployments(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.sites.list_deployments( '<SITE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -342,7 +556,7 @@ def test_create_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -350,6 +564,33 @@ def test_create_deployment(self, m): response = self.sites.create_deployment( '<SITE_ID>', InputFile.from_bytes(bytearray(), "example.file"), + install_command='<INSTALL_COMMAND>', + build_command='<BUILD_COMMAND>', + output_directory='<OUTPUT_DIRECTORY>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'installCommand': '<INSTALL_COMMAND>', + 'buildCommand': '<BUILD_COMMAND>', + 'outputDirectory': '<OUTPUT_DIRECTORY>', + 'code': '', + 'activate': 'false', + }, ) self.assertEqual(response.to_dict(), data) @@ -386,7 +627,7 @@ def test_create_duplicate_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -395,6 +636,19 @@ def test_create_duplicate_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/duplicate', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'deploymentId': '<DEPLOYMENT_ID>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -430,7 +684,7 @@ def test_create_template_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -442,6 +696,25 @@ def test_create_template_deployment(self, m): '<ROOT_DIRECTORY>', 'branch', '<REFERENCE>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/template', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'repository': '<REPOSITORY>', + 'owner': '<OWNER>', + 'rootDirectory': '<ROOT_DIRECTORY>', + 'type': 'branch', + 'reference': '<REFERENCE>', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -478,7 +751,7 @@ def test_create_vcs_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -487,6 +760,22 @@ def test_create_vcs_deployment(self, m): '<SITE_ID>', 'branch', '<REFERENCE>', + activate=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/vcs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'type': 'branch', + 'reference': '<REFERENCE>', + 'activate': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -523,7 +812,7 @@ def test_get_deployment(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -532,6 +821,14 @@ def test_get_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -539,7 +836,7 @@ def test_delete_deployment(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -548,6 +845,14 @@ def test_delete_deployment(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -555,7 +860,7 @@ def test_get_deployment_download(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -563,6 +868,22 @@ def test_get_deployment_download(self, m): response = self.sites.get_deployment_download( '<SITE_ID>', '<DEPLOYMENT_ID>', + type='source', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>/download', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'type': ['source'], + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) @@ -599,7 +920,7 @@ def test_update_deployment_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -608,6 +929,14 @@ def test_update_deployment_status(self, m): '<SITE_ID>', '<DEPLOYMENT_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/deployments/<DEPLOYMENT_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -618,13 +947,28 @@ def test_list_logs(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.sites.list_logs( '<SITE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/logs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -652,7 +996,7 @@ def test_get_log(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -661,6 +1005,14 @@ def test_get_log(self, m): '<SITE_ID>', '<LOG_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/logs/<LOG_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -668,7 +1020,7 @@ def test_delete_log(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -677,6 +1029,14 @@ def test_delete_log(self, m): '<SITE_ID>', '<LOG_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/logs/<LOG_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -687,13 +1047,28 @@ def test_list_variables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.sites.list_variables( '<SITE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -712,7 +1087,7 @@ def test_create_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -722,6 +1097,25 @@ def test_create_variable(self, m): '<VARIABLE_ID>', '<KEY>', '<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'variableId': '<VARIABLE_ID>', + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -740,7 +1134,7 @@ def test_get_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -749,6 +1143,14 @@ def test_get_variable(self, m): '<SITE_ID>', '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables/<VARIABLE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -766,7 +1168,7 @@ def test_update_variable(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -774,6 +1176,26 @@ def test_update_variable(self, m): response = self.sites.update_variable( '<SITE_ID>', '<VARIABLE_ID>', + key='<KEY>', + value='<VALUE>', + secret=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'value': '<VALUE>', + 'secret': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -782,7 +1204,7 @@ def test_delete_variable(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -791,4 +1213,12 @@ def test_delete_variable(self, m): '<SITE_ID>', '<VARIABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/sites/<SITE_ID>/variables/<VARIABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_storage.py b/test/services/test_storage.py index 15c0da0a..bf69311f 100644 --- a/test/services/test_storage.py +++ b/test/services/test_storage.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list_buckets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.storage.list_buckets() + response = self.storage.list_buckets( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -52,7 +72,7 @@ def test_create_bucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -60,6 +80,42 @@ def test_create_bucket(self, m): response = self.storage.create_bucket( '<BUCKET_ID>', '<NAME>', + permissions=[], + file_security=False, + enabled=False, + maximum_file_size=1, + allowed_file_extensions=[], + compression='none', + encryption=False, + antivirus=False, + transformations=False, + description='<DESCRIPTION>', + strip_metadata=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'bucketId': '<BUCKET_ID>', + 'name': '<NAME>', + 'permissions': [], + 'fileSecurity': False, + 'enabled': False, + 'maximumFileSize': 1, + 'allowedFileExtensions': [], + 'compression': 'none', + 'encryption': False, + 'antivirus': False, + 'transformations': False, + 'description': '<DESCRIPTION>', + 'stripMetadata': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -85,7 +141,7 @@ def test_get_bucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -93,6 +149,14 @@ def test_get_bucket(self, m): response = self.storage.get_bucket( '<BUCKET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -117,7 +181,7 @@ def test_update_bucket(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -125,6 +189,41 @@ def test_update_bucket(self, m): response = self.storage.update_bucket( '<BUCKET_ID>', '<NAME>', + permissions=[], + file_security=False, + enabled=False, + maximum_file_size=1, + allowed_file_extensions=[], + compression='none', + encryption=False, + antivirus=False, + transformations=False, + description='<DESCRIPTION>', + strip_metadata=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'permissions': [], + 'fileSecurity': False, + 'enabled': False, + 'maximumFileSize': 1, + 'allowedFileExtensions': [], + 'compression': 'none', + 'encryption': False, + 'antivirus': False, + 'transformations': False, + 'description': '<DESCRIPTION>', + 'stripMetadata': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -133,7 +232,7 @@ def test_delete_bucket(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -141,6 +240,14 @@ def test_delete_bucket(self, m): response = self.storage.delete_bucket( '<BUCKET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -151,13 +258,30 @@ def test_list_files(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.storage.list_files( '<BUCKET_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -183,7 +307,7 @@ def test_create_file(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -192,6 +316,29 @@ def test_create_file(self, m): '<BUCKET_ID>', '<FILE_ID>', InputFile.from_bytes(bytearray(), "example.file"), + permissions=[], + folder='photos/2026', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files', + ) + sent = dict( + re.findall( + r'form-data; name="([^"\[]+)[^\r\n]*\r\n(?:[^\r\n]+\r\n)*\r\n(.*?)\r\n--', + request.body.decode('latin-1'), + re.S, + ) + ) + self.assertEqual( + sent, + { + 'fileId': '<FILE_ID>', + 'file': '', + 'folder': 'photos/2026', + }, ) self.assertEqual(response.to_dict(), data) @@ -217,7 +364,7 @@ def test_get_file(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -226,6 +373,14 @@ def test_get_file(self, m): '<BUCKET_ID>', '<FILE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -250,7 +405,7 @@ def test_update_file(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -258,6 +413,22 @@ def test_update_file(self, m): response = self.storage.update_file( '<BUCKET_ID>', '<FILE_ID>', + name='name', + permissions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': 'name', + 'permissions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -266,7 +437,7 @@ def test_delete_file(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -275,6 +446,14 @@ def test_delete_file(self, m): '<BUCKET_ID>', '<FILE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -282,7 +461,7 @@ def test_get_file_download(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -290,6 +469,20 @@ def test_get_file_download(self, m): response = self.storage.get_file_download( '<BUCKET_ID>', '<FILE_ID>', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>/download', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) @@ -298,7 +491,7 @@ def test_get_file_preview(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -306,6 +499,42 @@ def test_get_file_preview(self, m): response = self.storage.get_file_preview( '<BUCKET_ID>', '<FILE_ID>', + width=1, + height=1, + gravity='auto', + quality=1, + border_width=1, + border_color='FFFFFF', + border_radius=1, + opacity=1.0, + rotation=1, + background='FFFFFF', + output='jpg', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>/preview', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'width': ['1'], + 'height': ['1'], + 'gravity': ['auto'], + 'quality': ['1'], + 'borderWidth': ['1'], + 'borderColor': ['FFFFFF'], + 'borderRadius': ['1'], + 'opacity': ['1.0'], + 'rotation': ['1'], + 'background': ['FFFFFF'], + 'output': ['jpg'], + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) @@ -314,7 +543,7 @@ def test_get_file_view(self, m): data = bytearray() headers = {'Content-Type': 'application/octet-stream'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, body=data, headers=headers, @@ -322,5 +551,19 @@ def test_get_file_view(self, m): response = self.storage.get_file_view( '<BUCKET_ID>', '<FILE_ID>', + token='<TOKEN>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/storage/buckets/<BUCKET_ID>/files/<FILE_ID>/view', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'token': ['<TOKEN>'], + }, ) self.assertEqual(response, data) diff --git a/test/services/test_tables_db.py b/test/services/test_tables_db.py index 31151822..e2300156 100644 --- a/test/services/test_tables_db.py +++ b/test/services/test_tables_db.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.tables_db.list() + response = self.tables_db.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +63,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +71,30 @@ def test_create(self, m): response = self.tables_db.create( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +106,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -78,12 +130,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.tables_db.list_transactions() + response = self.tables_db.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +166,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.tables_db.create_transaction() + response = self.tables_db.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -118,7 +201,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -126,6 +209,14 @@ def test_get_transaction(self, m): response = self.tables_db.get_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -140,13 +231,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.update_transaction( '<TRANSACTION_ID>', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -155,7 +262,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -163,6 +270,14 @@ def test_delete_transaction(self, m): response = self.tables_db.delete_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -177,13 +292,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.create_operations( '<TRANSACTION_ID>', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/transactions/<TRANSACTION_ID>/operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -200,7 +329,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -208,6 +337,14 @@ def test_get(self, m): response = self.tables_db.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -223,13 +360,37 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.update( '<DATABASE_ID>', + name='<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -238,7 +399,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -246,6 +407,14 @@ def test_delete(self, m): response = self.tables_db.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -299,13 +468,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -317,7 +500,7 @@ def test_list_migrations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -325,6 +508,14 @@ def test_list_migrations(self, m): response = self.tables_db.list_migrations( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -350,7 +541,7 @@ def test_create_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -358,6 +549,21 @@ def test_create_migration(self, m): response = self.tables_db.create_migration( '<DATABASE_ID>', 's-1vcpu-1gb', + auto_cutover=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'specification': 's-1vcpu-1gb', + 'autoCutover': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -384,7 +590,7 @@ def test_get_migration(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -393,6 +599,14 @@ def test_get_migration(self, m): '<DATABASE_ID>', '<MIGRATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations/<MIGRATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -400,7 +614,7 @@ def test_delete_migration(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -409,6 +623,14 @@ def test_delete_migration(self, m): '<DATABASE_ID>', '<MIGRATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations/<MIGRATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -434,7 +656,7 @@ def test_create_cutover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -443,6 +665,14 @@ def test_create_cutover(self, m): '<DATABASE_ID>', '<MIGRATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/migrations/<MIGRATION_ID>/cutovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -453,13 +683,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -475,7 +723,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -483,6 +731,14 @@ def test_get_replicas(self, m): response = self.tables_db.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -502,7 +758,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -510,6 +766,14 @@ def test_get_status(self, m): response = self.tables_db.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -520,13 +784,30 @@ def test_list_tables(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tables_db.list_tables( '<DATABASE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -549,7 +830,7 @@ def test_create_table(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -558,6 +839,32 @@ def test_create_table(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<NAME>', + permissions=[], + row_security=False, + enabled=False, + columns=[], + indexes=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'tableId': '<TABLE_ID>', + 'name': '<NAME>', + 'permissions': [], + 'rowSecurity': False, + 'enabled': False, + 'columns': [], + 'indexes': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -580,7 +887,7 @@ def test_get_table(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -589,6 +896,14 @@ def test_get_table(self, m): '<DATABASE_ID>', '<TABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -610,7 +925,7 @@ def test_update_table(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -618,6 +933,30 @@ def test_update_table(self, m): response = self.tables_db.update_table( '<DATABASE_ID>', '<TABLE_ID>', + name='<NAME>', + permissions=[], + row_security=False, + enabled=False, + purge=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'permissions': [], + 'rowSecurity': False, + 'enabled': False, + 'purge': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -626,7 +965,7 @@ def test_delete_table(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -635,6 +974,14 @@ def test_delete_table(self, m): '<DATABASE_ID>', '<TABLE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -645,7 +992,7 @@ def test_list_columns(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -653,6 +1000,21 @@ def test_list_columns(self, m): response = self.tables_db.list_columns( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -670,7 +1032,7 @@ def test_create_big_int_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -679,7 +1041,31 @@ def test_create_big_int_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/bigint', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -697,7 +1083,7 @@ def test_update_big_int_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -706,8 +1092,30 @@ def test_update_big_int_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 1, + min=1, + max=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/bigint/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -725,7 +1133,7 @@ def test_create_boolean_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -734,7 +1142,27 @@ def test_create_boolean_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=False, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/boolean', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': False, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -752,7 +1180,7 @@ def test_update_boolean_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -761,8 +1189,26 @@ def test_update_boolean_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, - True, + False, + False, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/boolean/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': False, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -781,7 +1227,7 @@ def test_create_datetime_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -790,7 +1236,27 @@ def test_create_datetime_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='2020-10-15T06:38:00.000+00:00', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/datetime', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -809,7 +1275,7 @@ def test_update_datetime_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -818,8 +1284,26 @@ def test_update_datetime_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, '2020-10-15T06:38:00.000+00:00', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/datetime/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '2020-10-15T06:38:00.000+00:00', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -838,7 +1322,7 @@ def test_create_email_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -847,7 +1331,27 @@ def test_create_email_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='email@example.com', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'email@example.com', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -866,7 +1370,7 @@ def test_update_email_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -875,8 +1379,26 @@ def test_update_email_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'email@example.com', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/email/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'email@example.com', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -896,7 +1418,7 @@ def test_create_enum_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -906,7 +1428,28 @@ def test_create_enum_column(self, m): '<TABLE_ID>', '<KEY>', [], - True, + False, + default='active', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/enum', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'elements': [], + 'required': False, + 'default': 'active', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -926,7 +1469,7 @@ def test_update_enum_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -936,8 +1479,27 @@ def test_update_enum_column(self, m): '<TABLE_ID>', '<KEY>', [], - True, + False, 'active', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/enum/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'elements': [], + 'required': False, + 'default': 'active', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -955,7 +1517,7 @@ def test_create_float_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -964,7 +1526,31 @@ def test_create_float_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + min=1.0, + max=1.0, + default=1.0, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/float', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -982,7 +1568,7 @@ def test_update_float_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -991,8 +1577,30 @@ def test_update_float_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 1.0, + min=1.0, + max=1.0, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/float/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1.0, + 'max': 1.0, + 'default': 1.0, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1010,7 +1618,7 @@ def test_create_integer_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1019,7 +1627,31 @@ def test_create_integer_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + min=1, + max=1, + default=1, + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/integer', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1037,7 +1669,7 @@ def test_update_integer_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1046,8 +1678,30 @@ def test_update_integer_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 1, + min=1, + max=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/integer/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'min': 1, + 'max': 1, + 'default': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1066,7 +1720,7 @@ def test_create_ip_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1075,7 +1729,27 @@ def test_create_ip_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='192.0.2.0', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/ip', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': '192.0.2.0', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1094,7 +1768,7 @@ def test_update_ip_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1103,8 +1777,26 @@ def test_update_ip_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, '192.0.2.0', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/ip/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': '192.0.2.0', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1122,7 +1814,7 @@ def test_create_line_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1131,7 +1823,25 @@ def test_create_line_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/line', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1149,7 +1859,7 @@ def test_update_line_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1158,7 +1868,26 @@ def test_update_line_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/line/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1176,7 +1905,7 @@ def test_create_longtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1185,7 +1914,29 @@ def test_create_longtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/longtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1203,7 +1954,7 @@ def test_update_longtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1212,8 +1963,26 @@ def test_update_longtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/longtext/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1231,7 +2000,7 @@ def test_create_mediumtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1240,7 +2009,29 @@ def test_create_mediumtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/mediumtext', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1258,7 +2049,7 @@ def test_update_mediumtext_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1267,8 +2058,26 @@ def test_update_mediumtext_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/mediumtext/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1286,7 +2095,7 @@ def test_create_point_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1295,7 +2104,25 @@ def test_create_point_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/point', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1313,7 +2140,7 @@ def test_update_point_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1322,7 +2149,26 @@ def test_update_point_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/point/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1340,7 +2186,7 @@ def test_create_polygon_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1349,7 +2195,25 @@ def test_create_polygon_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/polygon', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1367,7 +2231,7 @@ def test_update_polygon_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1376,7 +2240,26 @@ def test_update_polygon_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default=[], + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/polygon/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': [], + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1400,7 +2283,7 @@ def test_create_relationship_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1410,6 +2293,30 @@ def test_create_relationship_column(self, m): '<TABLE_ID>', '<RELATED_TABLE_ID>', 'oneToOne', + two_way=False, + key='<KEY>', + two_way_key='<TWO_WAY_KEY>', + on_delete='cascade', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/relationship', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'relatedTableId': '<RELATED_TABLE_ID>', + 'type': 'oneToOne', + 'twoWay': False, + 'key': '<KEY>', + 'twoWayKey': '<TWO_WAY_KEY>', + 'onDelete': 'cascade', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1433,7 +2340,7 @@ def test_update_relationship_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1442,6 +2349,24 @@ def test_update_relationship_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', + on_delete='cascade', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/relationship/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'onDelete': 'cascade', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1460,7 +2385,7 @@ def test_create_string_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1470,7 +2395,30 @@ def test_create_string_column(self, m): '<TABLE_ID>', '<KEY>', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/string', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1489,7 +2437,7 @@ def test_update_string_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1498,8 +2446,28 @@ def test_update_string_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + size=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/string/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1517,7 +2485,7 @@ def test_create_text_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1526,7 +2494,29 @@ def test_create_text_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/text', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1544,7 +2534,7 @@ def test_update_text_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1553,8 +2543,26 @@ def test_update_text_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/text/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1573,7 +2581,7 @@ def test_create_url_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1582,7 +2590,27 @@ def test_create_url_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, + default='https://example.com', + array=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/url', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'required': False, + 'default': 'https://example.com', + 'array': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1601,7 +2629,7 @@ def test_update_url_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1610,8 +2638,26 @@ def test_update_url_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'https://example.com', + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/url/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'https://example.com', + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1630,7 +2676,7 @@ def test_create_varchar_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1640,7 +2686,30 @@ def test_create_varchar_column(self, m): '<TABLE_ID>', '<KEY>', 1, - True, + False, + default='Hello World', + array=False, + encrypt=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/varchar', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'size': 1, + 'required': False, + 'default': 'Hello World', + 'array': False, + 'encrypt': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1659,7 +2728,7 @@ def test_update_varchar_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1668,8 +2737,28 @@ def test_update_varchar_column(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<KEY>', - True, + False, 'Hello World', + size=1, + new_key='<NEW_KEY>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/varchar/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'required': False, + 'default': 'Hello World', + 'size': 1, + 'newKey': '<NEW_KEY>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1687,7 +2776,7 @@ def test_get_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1697,6 +2786,14 @@ def test_get_column(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1704,7 +2801,7 @@ def test_delete_column(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1714,6 +2811,14 @@ def test_delete_column(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/columns/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1724,7 +2829,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1732,6 +2837,21 @@ def test_list_indexes(self, m): response = self.tables_db.list_indexes( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1751,7 +2871,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1762,6 +2882,27 @@ def test_create_index(self, m): '<KEY>', 'key', [], + orders=[], + lengths=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'type': 'key', + 'columns': [], + 'orders': [], + 'lengths': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1781,7 +2922,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1791,6 +2932,14 @@ def test_get_index(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1798,7 +2947,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1808,6 +2957,14 @@ def test_delete_index(self, m): '<TABLE_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/indexes/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1818,7 +2975,7 @@ def test_list_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1826,6 +2983,25 @@ def test_list_rows(self, m): response = self.tables_db.list_rows( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1842,7 +3018,7 @@ def test_create_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1852,6 +3028,24 @@ def test_create_row(self, m): '<TABLE_ID>', '<ROW_ID>', {}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rowId': '<ROW_ID>', + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1864,7 +3058,7 @@ def test_create_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1873,6 +3067,21 @@ def test_create_rows(self, m): '<DATABASE_ID>', '<TABLE_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rows': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1884,7 +3093,7 @@ def test_upsert_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1893,6 +3102,21 @@ def test_upsert_rows(self, m): '<DATABASE_ID>', '<TABLE_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'rows': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1904,7 +3128,7 @@ def test_update_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1912,6 +3136,24 @@ def test_update_rows(self, m): response = self.tables_db.update_rows( '<DATABASE_ID>', '<TABLE_ID>', + data={}, + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1923,7 +3165,7 @@ def test_delete_rows(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1931,6 +3173,22 @@ def test_delete_rows(self, m): response = self.tables_db.delete_rows( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1942,7 +3200,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1950,6 +3208,26 @@ def test_create_query(self, m): response = self.tables_db.create_query( '<DATABASE_ID>', '<TABLE_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1966,7 +3244,7 @@ def test_get_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1975,6 +3253,21 @@ def test_get_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -1992,7 +3285,7 @@ def test_upsert_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2001,6 +3294,24 @@ def test_upsert_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -2018,7 +3329,7 @@ def test_update_row(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2027,6 +3338,24 @@ def test_update_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -2036,7 +3365,7 @@ def test_delete_row(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2045,6 +3374,20 @@ def test_delete_row(self, m): '<DATABASE_ID>', '<TABLE_ID>', '<ROW_ID>', + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response, data) @@ -2061,7 +3404,7 @@ def test_decrement_row_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2071,6 +3414,24 @@ def test_decrement_row_column(self, m): '<TABLE_ID>', '<ROW_ID>', '<COLUMN>', + value=1.0, + min=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>/<COLUMN>/decrement', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'min': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -2088,7 +3449,7 @@ def test_increment_row_column(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -2098,6 +3459,24 @@ def test_increment_row_column(self, m): '<TABLE_ID>', '<ROW_ID>', '<COLUMN>', + value=1.0, + max=1.0, + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tablesdb/<DATABASE_ID>/tables/<TABLE_ID>/rows/<ROW_ID>/<COLUMN>/increment', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'value': 1.0, + 'max': 1.0, + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_teams.py b/test/services/test_teams.py index 24f7dbb5..20116807 100644 --- a/test/services/test_teams.py +++ b/test/services/test_teams.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.teams.list() + response = self.teams.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -42,7 +62,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -50,6 +70,22 @@ def test_create(self, m): response = self.teams.create( '<TEAM_ID>', '<NAME>', + roles=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'teamId': '<TEAM_ID>', + 'name': '<NAME>', + 'roles': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -65,7 +101,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -73,6 +109,14 @@ def test_get(self, m): response = self.teams.get( '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -87,7 +131,7 @@ def test_update_name(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -96,6 +140,19 @@ def test_update_name(self, m): '<TEAM_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -103,7 +160,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -111,6 +168,14 @@ def test_delete(self, m): response = self.teams.delete( '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -121,13 +186,28 @@ def test_list_installations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.teams.list_installations( '<TEAM_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -146,7 +226,7 @@ def test_create_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -154,6 +234,21 @@ def test_create_installation(self, m): response = self.teams.create_installation( '<TEAM_ID>', '<APP_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'appId': '<APP_ID>', + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -172,7 +267,7 @@ def test_get_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -181,6 +276,14 @@ def test_get_installation(self, m): '<TEAM_ID>', '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations/<INSTALLATION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -198,7 +301,7 @@ def test_update_installation(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -206,6 +309,20 @@ def test_update_installation(self, m): response = self.teams.update_installation( '<TEAM_ID>', '<INSTALLATION_ID>', + authorization_details='<AUTHORIZATION_DETAILS>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'authorizationDetails': '<AUTHORIZATION_DETAILS>', + }, ) self.assertEqual(response.to_dict(), data) @@ -214,7 +331,7 @@ def test_delete_installation(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -223,6 +340,14 @@ def test_delete_installation(self, m): '<TEAM_ID>', '<INSTALLATION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/installations/<INSTALLATION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -233,13 +358,30 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.teams.list_memberships( '<TEAM_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -264,7 +406,7 @@ def test_create_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -272,6 +414,31 @@ def test_create_membership(self, m): response = self.teams.create_membership( '<TEAM_ID>', [], + email='email@example.com', + user_id='<USER_ID>', + phone='+12065550100', + url='https://example.com', + name='<NAME>', + membership_id='<MEMBERSHIP_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + 'userId': '<USER_ID>', + 'phone': '+12065550100', + 'roles': [], + 'url': 'https://example.com', + 'name': '<NAME>', + 'membershipId': '<MEMBERSHIP_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -296,7 +463,7 @@ def test_get_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -305,6 +472,14 @@ def test_get_membership(self, m): '<TEAM_ID>', '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -328,7 +503,7 @@ def test_update_membership(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -338,6 +513,19 @@ def test_update_membership(self, m): '<MEMBERSHIP_ID>', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'roles': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -345,7 +533,7 @@ def test_delete_membership(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -354,6 +542,14 @@ def test_delete_membership(self, m): '<TEAM_ID>', '<MEMBERSHIP_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -377,7 +573,7 @@ def test_update_membership_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -388,6 +584,20 @@ def test_update_membership_status(self, m): '<USER_ID>', '<SECRET>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/memberships/<MEMBERSHIP_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'secret': '<SECRET>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -395,7 +605,7 @@ def test_get_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -403,6 +613,14 @@ def test_get_prefs(self, m): response = self.teams.get_prefs( '<TEAM_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/prefs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -410,7 +628,7 @@ def test_update_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -419,4 +637,17 @@ def test_update_prefs(self, m): '<TEAM_ID>', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/teams/<TEAM_ID>/prefs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'prefs': {}, + }, + ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_tokens.py b/test/services/test_tokens.py index de5f3f87..3883d77c 100644 --- a/test/services/test_tokens.py +++ b/test/services/test_tokens.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,7 +24,7 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -30,6 +32,21 @@ def test_list(self, m): response = self.tokens.list( '<BUCKET_ID>', '<FILE_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -46,7 +63,7 @@ def test_create_file_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -54,6 +71,20 @@ def test_create_file_token(self, m): response = self.tokens.create_file_token( '<BUCKET_ID>', '<FILE_ID>', + expire='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/buckets/<BUCKET_ID>/files/<FILE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'expire': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -70,7 +101,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -78,6 +109,14 @@ def test_get(self, m): response = self.tokens.get( '<TOKEN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/<TOKEN_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -93,13 +132,27 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.tokens.update( '<TOKEN_ID>', + expire='2020-10-15T06:38:00.000+00:00', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/<TOKEN_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'expire': '2020-10-15T06:38:00.000+00:00', + }, ) self.assertEqual(response.to_dict(), data) @@ -108,7 +161,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -116,4 +169,12 @@ def test_delete(self, m): response = self.tokens.delete( '<TOKEN_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/tokens/<TOKEN_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_users.py b/test/services/test_users.py index 4dad6e4c..5d762871 100644 --- a/test/services/test_users.py +++ b/test/services/test_users.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.users.list() + response = self.users.list( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -52,13 +72,34 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.create( '<USER_ID>', + email='email@example.com', + phone='+12065550100', + password='password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'phone': '+12065550100', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -84,7 +125,7 @@ def test_create_argon2_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -92,7 +133,24 @@ def test_create_argon2_user(self, m): response = self.users.create_argon2_user( '<USER_ID>', 'email@example.com', - 'password', + '$argon2id$v=19$m=65536,t=4,p=1$NC8xaW1CVXlIZXFSQi9FRg$0bd4pLo1ytyqvCwl5NSijpm5OIe8J6CjZ1vFemKAg+o', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/argon2', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': '$argon2id$v=19$m=65536,t=4,p=1$NC8xaW1CVXlIZXFSQi9FRg$0bd4pLo1ytyqvCwl5NSijpm5OIe8J6CjZ1vFemKAg+o', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -118,7 +176,7 @@ def test_create_bcrypt_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -126,7 +184,24 @@ def test_create_bcrypt_user(self, m): response = self.users.create_bcrypt_user( '<USER_ID>', 'email@example.com', - 'password', + '$2y$10$vBiMe4NOvlbYL1iJUNdqkO.QJy17fvslZQ5oiqKq1G4uwyRWvdrXC', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/bcrypt', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': '$2y$10$vBiMe4NOvlbYL1iJUNdqkO.QJy17fvslZQ5oiqKq1G4uwyRWvdrXC', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -138,12 +213,30 @@ def test_list_identities(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.users.list_identities() + response = self.users.list_identities( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/identities', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -151,7 +244,7 @@ def test_delete_identity(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -159,6 +252,14 @@ def test_delete_identity(self, m): response = self.users.delete_identity( '<IDENTITY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/identities/<IDENTITY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -183,7 +284,7 @@ def test_create_md5_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -192,6 +293,23 @@ def test_create_md5_user(self, m): '<USER_ID>', 'email@example.com', 'password', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/md5', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -217,7 +335,7 @@ def test_create_ph_pass_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -225,7 +343,24 @@ def test_create_ph_pass_user(self, m): response = self.users.create_ph_pass_user( '<USER_ID>', 'email@example.com', - 'password', + '$P$BrOEFP1YR5W1yHpvvGuoeVUDPmw9ra/', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/phpass', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': '$P$BrOEFP1YR5W1yHpvvGuoeVUDPmw9ra/', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -251,7 +386,7 @@ def test_create_scrypt_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -265,6 +400,28 @@ def test_create_scrypt_user(self, m): 1, 1, 1, + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/scrypt', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'passwordSalt': '<PASSWORD_SALT>', + 'passwordCpu': 1, + 'passwordMemory': 1, + 'passwordParallel': 1, + 'passwordLength': 1, + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -290,7 +447,7 @@ def test_create_scrypt_modified_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -302,6 +459,26 @@ def test_create_scrypt_modified_user(self, m): '<PASSWORD_SALT>', '<PASSWORD_SALT_SEPARATOR>', '<PASSWORD_SIGNER_KEY>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/scrypt-modified', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'passwordSalt': '<PASSWORD_SALT>', + 'passwordSaltSeparator': '<PASSWORD_SALT_SEPARATOR>', + 'passwordSignerKey': '<PASSWORD_SIGNER_KEY>', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -327,7 +504,7 @@ def test_create_sha_user(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -336,6 +513,25 @@ def test_create_sha_user(self, m): '<USER_ID>', 'email@example.com', 'password', + password_version='sha1', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/sha', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'userId': '<USER_ID>', + 'email': 'email@example.com', + 'password': 'password', + 'passwordVersion': 'sha1', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -361,7 +557,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -369,6 +565,14 @@ def test_get(self, m): response = self.users.get( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -376,7 +580,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -384,6 +588,14 @@ def test_delete(self, m): response = self.users.delete( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -408,7 +620,7 @@ def test_update_email(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -417,6 +629,19 @@ def test_update_email(self, m): '<USER_ID>', 'email@example.com', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/email', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'email': 'email@example.com', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -441,14 +666,27 @@ def test_update_impersonator(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_impersonator( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/impersonator', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'impersonator': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -459,13 +697,29 @@ def test_create_jwt(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.create_jwt( '<USER_ID>', + session_id='recent()', + duration=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/jwts', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'sessionId': 'recent()', + 'duration': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -491,7 +745,7 @@ def test_update_labels(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -500,6 +754,19 @@ def test_update_labels(self, m): '<USER_ID>', [], ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/labels', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'labels': [], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -510,13 +777,30 @@ def test_list_memberships(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_memberships( '<USER_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/memberships', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -542,14 +826,27 @@ def test_update_mfa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_mfa( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mfa': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -575,14 +872,27 @@ def test_update_mfa(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_mfa( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'mfa': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -591,7 +901,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -600,6 +910,14 @@ def test_delete_mfa_authenticator(self, m): '<USER_ID>', 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -607,7 +925,7 @@ def test_delete_mfa_authenticator(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -616,6 +934,14 @@ def test_delete_mfa_authenticator(self, m): '<USER_ID>', 'totp', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/authenticators/totp', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -629,7 +955,7 @@ def test_get_mfa_challenge(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -638,6 +964,14 @@ def test_get_mfa_challenge(self, m): '<USER_ID>', '<CHALLENGE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/challenges/<CHALLENGE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -651,7 +985,7 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -659,6 +993,14 @@ def test_list_mfa_factors(self, m): response = self.users.list_mfa_factors( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -672,7 +1014,7 @@ def test_list_mfa_factors(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -680,6 +1022,14 @@ def test_list_mfa_factors(self, m): response = self.users.list_mfa_factors( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/factors', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -689,7 +1039,7 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -697,6 +1047,14 @@ def test_get_mfa_recovery_codes(self, m): response = self.users.get_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -706,7 +1064,7 @@ def test_get_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -714,6 +1072,14 @@ def test_get_mfa_recovery_codes(self, m): response = self.users.get_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -723,7 +1089,7 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -731,6 +1097,14 @@ def test_update_mfa_recovery_codes(self, m): response = self.users.update_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -740,7 +1114,7 @@ def test_update_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -748,6 +1122,14 @@ def test_update_mfa_recovery_codes(self, m): response = self.users.update_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -757,7 +1139,7 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -765,6 +1147,14 @@ def test_create_mfa_recovery_codes(self, m): response = self.users.create_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -774,7 +1164,7 @@ def test_create_mfa_recovery_codes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -782,6 +1172,14 @@ def test_create_mfa_recovery_codes(self, m): response = self.users.create_mfa_recovery_codes( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/mfa/recovery-codes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -806,7 +1204,7 @@ def test_update_name(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -815,6 +1213,19 @@ def test_update_name(self, m): '<USER_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/name', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -825,13 +1236,28 @@ def test_list_passkeys(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_passkeys( '<USER_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -846,7 +1272,7 @@ def test_get_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -855,6 +1281,14 @@ def test_get_passkey(self, m): '<USER_ID>', '<PASSKEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys/<PASSKEY_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -868,7 +1302,7 @@ def test_update_passkey(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -878,6 +1312,19 @@ def test_update_passkey(self, m): '<PASSKEY_ID>', '<NAME>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys/<PASSKEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -885,7 +1332,7 @@ def test_delete_passkey(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -894,6 +1341,14 @@ def test_delete_passkey(self, m): '<USER_ID>', '<PASSKEY_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/passkeys/<PASSKEY_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -918,7 +1373,7 @@ def test_update_password(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -927,6 +1382,19 @@ def test_update_password(self, m): '<USER_ID>', 'password', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/password', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'password': 'password', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -951,7 +1419,7 @@ def test_update_phone(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -960,6 +1428,19 @@ def test_update_phone(self, m): '<USER_ID>', '+12065550100', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'number': '+12065550100', + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -967,7 +1448,7 @@ def test_get_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -975,6 +1456,14 @@ def test_get_prefs(self, m): response = self.users.get_prefs( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/prefs', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -982,7 +1471,7 @@ def test_update_prefs(self, m): data = {} headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -991,6 +1480,19 @@ def test_update_prefs(self, m): '<USER_ID>', {}, ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/prefs', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'prefs': {}, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1001,13 +1503,27 @@ def test_list_sessions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_sessions( '<USER_ID>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1046,7 +1562,7 @@ def test_create_session(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1054,6 +1570,14 @@ def test_create_session(self, m): response = self.users.create_session( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1061,7 +1585,7 @@ def test_delete_sessions(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1069,6 +1593,14 @@ def test_delete_sessions(self, m): response = self.users.delete_sessions( '<USER_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1076,7 +1608,7 @@ def test_delete_session(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1085,6 +1617,14 @@ def test_delete_session(self, m): '<USER_ID>', '<SESSION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/sessions/<SESSION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1109,14 +1649,27 @@ def test_update_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_status( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/status', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'status': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1128,13 +1681,28 @@ def test_list_targets(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.list_targets( '<USER_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -1152,7 +1720,7 @@ def test_create_target(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1162,6 +1730,25 @@ def test_create_target(self, m): '<TARGET_ID>', 'email', '<IDENTIFIER>', + provider_id='<PROVIDER_ID>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetId': '<TARGET_ID>', + 'providerType': 'email', + 'identifier': '<IDENTIFIER>', + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1179,7 +1766,7 @@ def test_get_target(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1188,6 +1775,14 @@ def test_get_target(self, m): '<USER_ID>', '<TARGET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets/<TARGET_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -1204,7 +1799,7 @@ def test_update_target(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1212,6 +1807,24 @@ def test_update_target(self, m): response = self.users.update_target( '<USER_ID>', '<TARGET_ID>', + identifier='<IDENTIFIER>', + provider_id='<PROVIDER_ID>', + name='<NAME>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets/<TARGET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'identifier': '<IDENTIFIER>', + 'providerId': '<PROVIDER_ID>', + 'name': '<NAME>', + }, ) self.assertEqual(response.to_dict(), data) @@ -1220,7 +1833,7 @@ def test_delete_target(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -1229,6 +1842,14 @@ def test_delete_target(self, m): '<USER_ID>', '<TARGET_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/targets/<TARGET_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -1243,13 +1864,29 @@ def test_create_token(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.create_token( '<USER_ID>', + length=1, + expire=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/tokens', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'length': 1, + 'expire': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -1275,14 +1912,27 @@ def test_update_email_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_email_verification( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/verification', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'emailVerification': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -1308,13 +1958,26 @@ def test_update_phone_verification(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.users.update_phone_verification( '<USER_ID>', - True, + False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/users/<USER_ID>/verification/phone', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'phoneVerification': False, + }, ) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_vectors_db.py b/test/services/test_vectors_db.py index 2c6f2240..42b73f4b 100644 --- a/test/services/test_vectors_db.py +++ b/test/services/test_vectors_db.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.vectors_db.list() + response = self.vectors_db.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -43,7 +61,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -51,6 +69,30 @@ def test_create(self, m): response = self.vectors_db.create( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'databaseId': '<DATABASE_ID>', + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -62,12 +104,20 @@ def test_list_specifications(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.list_specifications() + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/specifications', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -78,12 +128,28 @@ def test_list_transactions(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.vectors_db.list_transactions() + response = self.vectors_db.list_transactions( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -98,12 +164,27 @@ def test_create_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.vectors_db.create_transaction() + response = self.vectors_db.create_transaction( + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ttl': 1, + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -118,7 +199,7 @@ def test_get_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -126,6 +207,14 @@ def test_get_transaction(self, m): response = self.vectors_db.get_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -140,13 +229,29 @@ def test_update_transaction(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.update_transaction( '<TRANSACTION_ID>', + commit=False, + rollback=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'commit': False, + 'rollback': False, + }, ) self.assertEqual(response.to_dict(), data) @@ -155,7 +260,7 @@ def test_delete_transaction(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -163,6 +268,14 @@ def test_delete_transaction(self, m): response = self.vectors_db.delete_transaction( '<TRANSACTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -177,13 +290,27 @@ def test_create_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.create_operations( '<TRANSACTION_ID>', + operations=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/transactions/<TRANSACTION_ID>/operations', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'operations': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -200,7 +327,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -208,6 +335,14 @@ def test_get(self, m): response = self.vectors_db.get( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -223,7 +358,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -231,6 +366,29 @@ def test_update(self, m): response = self.vectors_db.update( '<DATABASE_ID>', '<NAME>', + enabled=False, + description='<DESCRIPTION>', + specification='serverless', + replicas=1, + sync_mode='async', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'enabled': False, + 'description': '<DESCRIPTION>', + 'specification': 'serverless', + 'replicas': 1, + 'syncMode': 'async', + }, ) self.assertEqual(response.to_dict(), data) @@ -239,7 +397,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -247,6 +405,14 @@ def test_delete(self, m): response = self.vectors_db.delete( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -257,13 +423,30 @@ def test_list_collections(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.list_collections( '<DATABASE_ID>', + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -287,7 +470,7 @@ def test_create_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -297,6 +480,29 @@ def test_create_collection(self, m): '<COLLECTION_ID>', '<NAME>', 1, + permissions=[], + document_security=False, + enabled=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'collectionId': '<COLLECTION_ID>', + 'name': '<NAME>', + 'dimension': 1, + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -320,7 +526,7 @@ def test_get_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -329,6 +535,14 @@ def test_get_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -351,7 +565,7 @@ def test_update_collection(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -360,6 +574,29 @@ def test_update_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<NAME>', + dimension=1, + permissions=[], + document_security=False, + enabled=False, + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'dimension': 1, + 'permissions': [], + 'documentSecurity': False, + 'enabled': False, + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -368,7 +605,7 @@ def test_delete_collection(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -377,6 +614,14 @@ def test_delete_collection(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -387,7 +632,7 @@ def test_list_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -395,6 +640,25 @@ def test_list_documents(self, m): response = self.vectors_db.list_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + 'total': ['false'], + 'ttl': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -411,7 +675,7 @@ def test_create_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -421,6 +685,24 @@ def test_create_document(self, m): '<COLLECTION_ID>', '<DOCUMENT_ID>', {}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documentId': '<DOCUMENT_ID>', + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -433,7 +715,7 @@ def test_create_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -442,6 +724,21 @@ def test_create_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -453,7 +750,7 @@ def test_upsert_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -462,6 +759,21 @@ def test_upsert_documents(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', [], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'documents': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -473,7 +785,7 @@ def test_update_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -481,6 +793,24 @@ def test_update_documents(self, m): response = self.vectors_db.update_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + data={}, + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -492,7 +822,7 @@ def test_delete_documents(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -500,6 +830,22 @@ def test_delete_documents(self, m): response = self.vectors_db.delete_documents( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -511,7 +857,7 @@ def test_create_query(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -519,6 +865,26 @@ def test_create_query(self, m): response = self.vectors_db.create_query( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + total=False, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/query', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'queries': [], + 'transactionId': '<TRANSACTION_ID>', + 'total': False, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -535,7 +901,7 @@ def test_get_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -544,6 +910,21 @@ def test_get_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + queries=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'transactionId': ['<TRANSACTION_ID>'], + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -561,7 +942,7 @@ def test_upsert_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -570,6 +951,24 @@ def test_upsert_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -587,7 +986,7 @@ def test_update_document(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -596,6 +995,24 @@ def test_update_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + data={}, + permissions=[], + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'data': {}, + 'permissions': [], + 'transactionId': '<TRANSACTION_ID>', + }, ) data['data'] = {} self.assertEqual(response.to_dict(), data) @@ -605,7 +1022,7 @@ def test_delete_document(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -614,6 +1031,20 @@ def test_delete_document(self, m): '<DATABASE_ID>', '<COLLECTION_ID>', '<DOCUMENT_ID>', + transaction_id='<TRANSACTION_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/documents/<DOCUMENT_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'transactionId': '<TRANSACTION_ID>', + }, ) self.assertEqual(response, data) @@ -625,7 +1056,7 @@ def test_list_indexes(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -633,6 +1064,21 @@ def test_list_indexes(self, m): response = self.vectors_db.list_indexes( '<DATABASE_ID>', '<COLLECTION_ID>', + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, ) self.assertEqual(response.to_dict(), data) @@ -652,7 +1098,7 @@ def test_create_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -663,6 +1109,27 @@ def test_create_index(self, m): '<KEY>', 'hnsw_euclidean', [], + orders=[], + lengths=[], + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'key': '<KEY>', + 'type': 'hnsw_euclidean', + 'attributes': [], + 'orders': [], + 'lengths': [], + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -682,7 +1149,7 @@ def test_get_index(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -692,6 +1159,14 @@ def test_get_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -699,7 +1174,7 @@ def test_delete_index(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -709,6 +1184,14 @@ def test_delete_index(self, m): '<COLLECTION_ID>', '<KEY>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/collections/<COLLECTION_ID>/indexes/<KEY>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -762,13 +1245,27 @@ def test_create_failover(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.create_failover( '<DATABASE_ID>', + target_replica_id='<TARGET_REPLICA_ID>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/failovers', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'targetReplicaId': '<TARGET_REPLICA_ID>', + }, ) self.assertEqual(response.to_dict(), data) @@ -780,13 +1277,31 @@ def test_list_operations(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.vectors_db.list_operations( '<DATABASE_ID>', + status='queued', + limit=1, + offset=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/operations', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'status': ['queued'], + 'limit': ['1'], + 'offset': ['1'], + }, ) self.assertEqual(response.to_dict(), data) @@ -802,7 +1317,7 @@ def test_get_replicas(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -810,6 +1325,14 @@ def test_get_replicas(self, m): response = self.vectors_db.get_replicas( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/replicas', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -829,7 +1352,7 @@ def test_get_status(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -837,4 +1360,12 @@ def test_get_status(self, m): response = self.vectors_db.get_status( '<DATABASE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/vectorsdb/<DATABASE_ID>/status', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) diff --git a/test/services/test_waf.py b/test/services/test_waf.py index 8c9e8bec..db44f9f5 100644 --- a/test/services/test_waf.py +++ b/test/services/test_waf.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,30 @@ def test_list_rules(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.waf.list_rules() + response = self.waf.list_rules( + queries=[], + search='<SEARCH>', + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'search': ['<SEARCH>'], + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -50,7 +70,7 @@ def test_create_bypass_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -59,6 +79,31 @@ def test_create_bypass_rule(self, m): '<RULE_ID>', 'api', '<NAME>', + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/bypass', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -82,13 +127,39 @@ def test_update_bypass_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_bypass_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/bypass/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -115,7 +186,7 @@ def test_create_challenge_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -124,6 +195,37 @@ def test_create_challenge_rule(self, m): '<RULE_ID>', 'api', '<NAME>', + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + challenge_type='compute', + priority=1, + enabled=False, + conditions=[], + difficulty=1, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/challenge', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'challengeType': 'compute', + 'priority': 1, + 'enabled': False, + 'conditions': [], + 'difficulty': 1, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -150,13 +252,45 @@ def test_update_challenge_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_challenge_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + challenge_type='compute', + priority=1, + enabled=False, + conditions=[], + difficulty=1, + ttl=1, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/challenge/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'challengeType': 'compute', + 'priority': 1, + 'enabled': False, + 'conditions': [], + 'difficulty': 1, + 'ttl': 1, + }, ) self.assertEqual(response.to_dict(), data) @@ -180,7 +314,7 @@ def test_create_deny_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -189,6 +323,31 @@ def test_create_deny_rule(self, m): '<RULE_ID>', 'api', '<NAME>', + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/deny', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -212,13 +371,39 @@ def test_update_deny_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_deny_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/deny/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -247,7 +432,7 @@ def test_create_rate_limit_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -258,6 +443,39 @@ def test_create_rate_limit_rule(self, m): '<NAME>', 1, 1, + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + key='ip', + strategy='fixedWindow', + max_bucket_size=1, + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/rate-limit', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'limit': 1, + 'interval': 1, + 'key': 'ip', + 'strategy': 'fixedWindow', + 'maxBucketSize': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -286,13 +504,47 @@ def test_update_rate_limit_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_rate_limit_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + limit=1, + interval=1, + key='ip', + max_bucket_size=1, + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/rate-limit/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'limit': 1, + 'interval': 1, + 'key': 'ip', + 'maxBucketSize': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -318,7 +570,7 @@ def test_create_redirect_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -329,6 +581,33 @@ def test_create_redirect_rule(self, m): '<NAME>', '<LOCATION>', 1, + resource_id='<RESOURCE_ID>', + description='<DESCRIPTION>', + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/redirect', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'ruleId': '<RULE_ID>', + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'location': '<LOCATION>', + 'statusCode': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -354,13 +633,43 @@ def test_update_redirect_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.waf.update_redirect_rule( '<RULE_ID>', + resource_type='api', + resource_id='<RESOURCE_ID>', + name='<NAME>', + description='<DESCRIPTION>', + location='<LOCATION>', + status_code=1, + priority=1, + enabled=False, + conditions=[], + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/redirect/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'resourceType': 'api', + 'resourceId': '<RESOURCE_ID>', + 'name': '<NAME>', + 'description': '<DESCRIPTION>', + 'location': '<LOCATION>', + 'statusCode': 1, + 'priority': 1, + 'enabled': False, + 'conditions': [], + }, ) self.assertEqual(response.to_dict(), data) @@ -384,7 +693,7 @@ def test_get_rule(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -392,6 +701,14 @@ def test_get_rule(self, m): response = self.waf.get_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/<RULE_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -399,7 +716,7 @@ def test_delete_rule(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -407,4 +724,12 @@ def test_delete_rule(self, m): response = self.waf.delete_rule( '<RULE_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/waf/rules/<RULE_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) diff --git a/test/services/test_webhooks.py b/test/services/test_webhooks.py index 0f65c6b2..50e6ee3d 100644 --- a/test/services/test_webhooks.py +++ b/test/services/test_webhooks.py @@ -1,6 +1,8 @@ import json +import re import requests_mock import unittest +from urllib.parse import parse_qs, unquote, urlsplit from appwrite.client import Client from appwrite.input_file import InputFile @@ -22,12 +24,28 @@ def test_list(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, ) - response = self.webhooks.list() + response = self.webhooks.list( + queries=[], + total=False, + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual( + sent, + { + 'total': ['false'], + }, + ) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -50,7 +68,7 @@ def test_create(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'POST', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -60,6 +78,34 @@ def test_create(self, m): 'https://example.com/webhook', '<NAME>', [], + enabled=False, + tls=False, + auth_username='<AUTH_USERNAME>', + auth_password='password', + secret='<SECRET>', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'webhookId': '<WEBHOOK_ID>', + 'url': 'https://example.com/webhook', + 'name': '<NAME>', + 'events': [], + 'enabled': False, + 'tls': False, + 'authUsername': '<AUTH_USERNAME>', + 'authPassword': 'password', + 'secret': '<SECRET>', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -83,7 +129,7 @@ def test_get(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'GET', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -91,6 +137,14 @@ def test_get(self, m): response = self.webhooks.get( '<WEBHOOK_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>', + ) + sent = parse_qs(url.query, keep_blank_values=True) + self.assertEqual(sent, {}) self.assertEqual(response.to_dict(), data) @requests_mock.Mocker() @@ -113,7 +167,7 @@ def test_update(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PUT', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -123,6 +177,31 @@ def test_update(self, m): '<NAME>', 'https://example.com/webhook', [], + enabled=False, + tls=False, + auth_username='<AUTH_USERNAME>', + auth_password='password', + description='<DESCRIPTION>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'name': '<NAME>', + 'url': 'https://example.com/webhook', + 'events': [], + 'enabled': False, + 'tls': False, + 'authUsername': '<AUTH_USERNAME>', + 'authPassword': 'password', + 'description': '<DESCRIPTION>', + }, ) self.assertEqual(response.to_dict(), data) @@ -131,7 +210,7 @@ def test_delete(self, m): data = '' headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'DELETE', requests_mock.ANY, text=json.dumps(data), headers=headers, @@ -139,6 +218,14 @@ def test_delete(self, m): response = self.webhooks.delete( '<WEBHOOK_ID>', ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>', + ) + sent = json.loads(request.body or '{}') + self.assertEqual(sent, {}) self.assertEqual(response, data) @requests_mock.Mocker() @@ -161,12 +248,26 @@ def test_update_secret(self, m): } headers = {'Content-Type': 'application/json'} m.request( - requests_mock.ANY, + 'PATCH', requests_mock.ANY, text=json.dumps(data), headers=headers, ) response = self.webhooks.update_secret( '<WEBHOOK_ID>', + secret='<SECRET>', + ) + request = m.last_request + url = urlsplit(request.url) + self.assertEqual( + unquote(url.path), + '/v1/webhooks/<WEBHOOK_ID>/secret', + ) + sent = json.loads(request.body or '{}') + self.assertEqual( + sent, + { + 'secret': '<SECRET>', + }, ) self.assertEqual(response.to_dict(), data)