From 5bd34b8d6f7d1736338bafbbb948565934620e72 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Jul 2026 10:47:33 +0530 Subject: [PATCH 1/5] chore: update Python SDK to 22.0.0 --- CHANGELOG.md | 24 + appwrite/client.py | 13 +- appwrite/encoders/value_class_encoder.py | 26 +- appwrite/enums/billing_plan_group.py | 6 + appwrite/enums/database_status.py | 6 + appwrite/enums/health_antivirus_status.py | 6 - appwrite/enums/health_check_status.py | 5 - appwrite/enums/health_queue_name.py | 16 - appwrite/enums/o_auth2_oidc_prompt.py | 7 + appwrite/enums/o_auth_provider.py | 1 + appwrite/enums/organization_key_scopes.py | 4 + appwrite/enums/project_key_scopes.py | 8 +- appwrite/enums/project_o_auth2_oidc_prompt.py | 7 + appwrite/enums/project_o_auth_provider_id.py | 1 + appwrite/models/__init__.py | 52 +- appwrite/models/activity_event.py | 6 - appwrite/models/additional_resource.py | 30 + appwrite/models/app.py | 3 + appwrite/models/billing_plan.py | 223 +++++ appwrite/models/billing_plan_addon.py | 19 + appwrite/models/billing_plan_addon_details.py | 36 + .../billing_plan_dedicated_database_limits.py | 69 ++ appwrite/models/billing_plan_limits.py | 18 + .../models/billing_plan_supported_addons.py | 21 + appwrite/models/block.py | 3 + appwrite/models/database.py | 4 + appwrite/models/document.py | 11 + appwrite/models/health_antivirus.py | 19 - appwrite/models/health_certificate.py | 30 - appwrite/models/health_status.py | 22 - appwrite/models/health_status_list.py | 19 - appwrite/models/health_time.py | 21 - appwrite/models/locale.py | 33 + appwrite/models/log.py | 2 +- appwrite/models/o_auth2_appwrite.py | 24 + appwrite/models/o_auth2_oidc.py | 7 + appwrite/models/o_auth2_provider_list.py | 5 +- appwrite/models/oauth2_grant.py | 3 + ...health_queue.py => oauth2_organization.py} | 10 +- appwrite/models/oauth2_organization_list.py | 19 + appwrite/models/oauth2_par.py | 18 + appwrite/models/oauth2_project.py | 21 + appwrite/models/oauth2_project_list.py | 19 + appwrite/models/organization.py | 132 +++ appwrite/models/preferences.py | 11 + appwrite/models/program.py | 39 + appwrite/models/project.py | 6 + appwrite/models/row.py | 11 + appwrite/models/usage_billing_plan.py | 46 + appwrite/models/usage_data_point.py | 60 -- appwrite/models/usage_event_list.py | 19 - appwrite/models/usage_gauge_list.py | 19 - appwrite/models/usage_metric.py | 19 - appwrite/query.py | 12 + appwrite/services/account.py | 2 +- appwrite/services/apps.py | 47 + appwrite/services/backups.py | 10 +- appwrite/services/functions.py | 25 +- appwrite/services/health.py | 882 ------------------ appwrite/services/messaging.py | 197 ---- appwrite/services/oauth2.py | 290 +++++- appwrite/services/organization.py | 357 ++++++- appwrite/services/project.py | 68 +- appwrite/services/sites.py | 14 +- appwrite/services/tables_db.py | 10 +- appwrite/services/usage.py | 202 ---- appwrite/services/users.py | 2 +- .../update-labels.md} | 11 +- docs/examples/backups/create-restoration.md | 3 +- .../functions/get-deployment-download.md | 3 +- .../examples/functions/list-specifications.md | 4 +- docs/examples/health/get-antivirus.md | 16 - docs/examples/health/get-certificate.md | 18 - docs/examples/health/get-db.md | 16 - docs/examples/health/get-failed-jobs.md | 20 - docs/examples/health/get-pub-sub.md | 16 - .../examples/health/get-queue-certificates.md | 18 - docs/examples/health/get-queue-databases.md | 19 - docs/examples/health/get-queue-deletes.md | 18 - docs/examples/health/get-queue-functions.md | 18 - docs/examples/health/get-queue-logs.md | 18 - docs/examples/health/get-queue-mails.md | 18 - docs/examples/health/get-queue-messaging.md | 18 - docs/examples/health/get-queue-migrations.md | 18 - .../health/get-queue-stats-resources.md | 18 - docs/examples/health/get-queue-usage.md | 18 - docs/examples/health/get-queue-webhooks.md | 18 - docs/examples/health/get-storage-local.md | 16 - docs/examples/health/get-storage.md | 16 - docs/examples/messaging/list-provider-logs.md | 20 - .../messaging/list-subscriber-logs.md | 20 - docs/examples/messaging/list-topic-logs.md | 20 - docs/examples/oauth2/approve.md | 3 +- docs/examples/oauth2/authorize.md | 13 +- .../oauth2/create-device-authorization.md | 4 +- docs/examples/oauth2/create-par.md | 30 + docs/examples/oauth2/create-token.md | 4 +- docs/examples/oauth2/list-organizations.md | 20 + docs/examples/oauth2/list-projects.md | 20 + .../organization/create-membership.md | 23 + .../delete-membership.md} | 11 +- .../get-time.md => organization/delete.md} | 9 +- .../get-membership.md} | 10 +- .../get-cache.md => organization/get.md} | 8 +- .../list-memberships.md} | 10 +- .../update-membership.md} | 12 +- .../update.md} | 10 +- .../project/update-o-auth-2-appwrite.md | 20 + docs/examples/project/update-o-auth-2-oidc.md | 3 + .../project/update-o-auth-2-server.md | 3 +- docs/examples/sites/list-specifications.md | 4 +- docs/examples/tablesdb/create.md | 2 +- docs/examples/usage/list-events.md | 28 - docs/examples/usage/list-gauges.md | 28 - pyproject.toml | 2 +- requirements.txt | 2 +- setup.py | 4 +- test/services/test_activities.py | 4 +- test/services/test_apps.py | 42 + test/services/test_backups.py | 4 +- test/services/test_health.py | 361 ------- test/services/test_messaging.py | 60 -- test/services/test_oauth2.py | 51 +- test/services/test_organization.py | 576 ++++++++++++ test/services/test_project.py | 39 +- test/services/test_usage.py | 45 - 126 files changed, 2633 insertions(+), 2607 deletions(-) create mode 100644 appwrite/enums/billing_plan_group.py create mode 100644 appwrite/enums/database_status.py delete mode 100644 appwrite/enums/health_antivirus_status.py delete mode 100644 appwrite/enums/health_check_status.py delete mode 100644 appwrite/enums/health_queue_name.py create mode 100644 appwrite/enums/o_auth2_oidc_prompt.py create mode 100644 appwrite/enums/project_o_auth2_oidc_prompt.py create mode 100644 appwrite/models/additional_resource.py create mode 100644 appwrite/models/billing_plan.py create mode 100644 appwrite/models/billing_plan_addon.py create mode 100644 appwrite/models/billing_plan_addon_details.py create mode 100644 appwrite/models/billing_plan_dedicated_database_limits.py create mode 100644 appwrite/models/billing_plan_limits.py create mode 100644 appwrite/models/billing_plan_supported_addons.py delete mode 100644 appwrite/models/health_antivirus.py delete mode 100644 appwrite/models/health_certificate.py delete mode 100644 appwrite/models/health_status.py delete mode 100644 appwrite/models/health_status_list.py delete mode 100644 appwrite/models/health_time.py create mode 100644 appwrite/models/o_auth2_appwrite.py rename appwrite/models/{health_queue.py => oauth2_organization.py} (55%) create mode 100644 appwrite/models/oauth2_organization_list.py create mode 100644 appwrite/models/oauth2_par.py create mode 100644 appwrite/models/oauth2_project.py create mode 100644 appwrite/models/oauth2_project_list.py create mode 100644 appwrite/models/organization.py create mode 100644 appwrite/models/program.py create mode 100644 appwrite/models/usage_billing_plan.py delete mode 100644 appwrite/models/usage_data_point.py delete mode 100644 appwrite/models/usage_event_list.py delete mode 100644 appwrite/models/usage_gauge_list.py delete mode 100644 appwrite/models/usage_metric.py delete mode 100644 appwrite/services/health.py delete mode 100644 appwrite/services/usage.py rename docs/examples/{health/get-audits-db.md => apps/update-labels.md} (63%) delete mode 100644 docs/examples/health/get-antivirus.md delete mode 100644 docs/examples/health/get-certificate.md delete mode 100644 docs/examples/health/get-db.md delete mode 100644 docs/examples/health/get-failed-jobs.md delete mode 100644 docs/examples/health/get-pub-sub.md delete mode 100644 docs/examples/health/get-queue-certificates.md delete mode 100644 docs/examples/health/get-queue-databases.md delete mode 100644 docs/examples/health/get-queue-deletes.md delete mode 100644 docs/examples/health/get-queue-functions.md delete mode 100644 docs/examples/health/get-queue-logs.md delete mode 100644 docs/examples/health/get-queue-mails.md delete mode 100644 docs/examples/health/get-queue-messaging.md delete mode 100644 docs/examples/health/get-queue-migrations.md delete mode 100644 docs/examples/health/get-queue-stats-resources.md delete mode 100644 docs/examples/health/get-queue-usage.md delete mode 100644 docs/examples/health/get-queue-webhooks.md delete mode 100644 docs/examples/health/get-storage-local.md delete mode 100644 docs/examples/health/get-storage.md delete mode 100644 docs/examples/messaging/list-provider-logs.md delete mode 100644 docs/examples/messaging/list-subscriber-logs.md delete mode 100644 docs/examples/messaging/list-topic-logs.md create mode 100644 docs/examples/oauth2/create-par.md create mode 100644 docs/examples/oauth2/list-organizations.md create mode 100644 docs/examples/oauth2/list-projects.md create mode 100644 docs/examples/organization/create-membership.md rename docs/examples/{health/get.md => organization/delete-membership.md} (60%) rename docs/examples/{health/get-time.md => organization/delete.md} (60%) rename docs/examples/{health/get-queue-audits.md => organization/get-membership.md} (57%) rename docs/examples/{health/get-cache.md => organization/get.md} (59%) rename docs/examples/{messaging/list-message-logs.md => organization/list-memberships.md} (60%) rename docs/examples/{health/get-console-pausing.md => organization/update-membership.md} (55%) rename docs/examples/{health/get-queue-builds.md => organization/update.md} (56%) create mode 100644 docs/examples/project/update-o-auth-2-appwrite.md delete mode 100644 docs/examples/usage/list-events.md delete mode 100644 docs/examples/usage/list-gauges.md delete mode 100644 test/services/test_health.py delete mode 100644 test/services/test_usage.py diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e7a93d..e66e10b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Change Log +## 22.0.0 + +* Breaking: removed `Health` service and all health models and enums +* Breaking: removed `Usage` service and `UsageMetric`, `UsageDataPoint`, `UsageEventList`, `UsageGaugeList` models +* Breaking: removed messaging `list_message_logs`, `list_provider_logs`, `list_subscriber_logs`, `list_topic_logs` methods +* Breaking: renamed `tables_db.create` parameter `dedicated_database_id` to `specification` +* Breaking: removed `countryCode` and `countryName` from `ActivityEvent` model +* Added: `Client.set_bearer` for OAuth access token authentication +* Added: `organization` service `get`, `update`, `delete` and membership CRUD methods +* Added: `oauth2` service `create_par`, `list_organizations`, `list_projects` methods +* Added: `resource`, `audience`, `request_uri` OAuth2 parameters; `scope` on `approve` +* Added: `appwrite` OAuth provider, `OAuth2Appwrite` model, `update_o_auth2_appwrite` method +* Added: `Query.vector_dot`, `Query.vector_cosine`, `Query.vector_euclidean` methods +* Added: `apps.update_labels` method and `labels` field on `App` model +* Added: `new_specification` parameter to `backups.create_restoration` +* Added: `type` parameter to `functions` and `sites` `list_specifications` +* Added: `token` parameter to `functions.get_deployment_download` +* Added: `prompt` and `max_age` parameters to `update_o_auth2_oidc`; `default_scopes` to `update_o_auth2_server` +* Added: `BillingPlan`, `Organization`, `Program`, `Oauth2PAR`, `DatabaseStatus`, `BillingPlanGroup` models and enums +* Added: `status` on `Database`, `mode` on `Block`, `onboarding` on `Project`, geolocation fields on `Locale` +* Added: organization, `project.oauth2`, and `stages` key scope enum values +* Fixed: booleans now serialized as `true`/`false` in flattened query parameters +* Fixed: `model_dump` on `Document`, `Row`, `Preferences` now includes dynamic `data` + ## 21.0.0 * Added: `apps` service for managing apps and app secrets diff --git a/appwrite/client.py b/appwrite/client.py index baafb0e7..83f61a8f 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -17,11 +17,11 @@ def __init__(self): self._endpoint = 'https://cloud.appwrite.io/v1' self._global_headers = { 'content-type': '', - 'user-agent' : f'AppwritePythonSDK/21.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', + 'user-agent' : f'AppwritePythonSDK/22.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': '21.0.0', + 'x-sdk-version': '22.0.0', 'X-Appwrite-Response-Format' : '1.9.5', } self._config = {} @@ -67,6 +67,13 @@ def set_jwt(self, value): self._config['jwt'] = value return self + def set_bearer(self, value): + """The OAuth access token to authenticate with""" + + self._global_headers['authorization'] = value + self._config['bearer'] = value + return self + def set_locale(self, value): self._global_headers['x-appwrite-locale'] = value self._config['locale'] = value @@ -341,6 +348,8 @@ def flatten(self, data, prefix='', stringify=False): if isinstance(value, list) or isinstance(value, dict): output = {**output, **self.flatten(value, finalKey, stringify)} + elif isinstance(value, bool): + output[finalKey] = 'true' if value else 'false' else: if stringify: output[finalKey] = str(value) diff --git a/appwrite/encoders/value_class_encoder.py b/appwrite/encoders/value_class_encoder.py index 5a4914b1..b725fb9b 100644 --- a/appwrite/encoders/value_class_encoder.py +++ b/appwrite/encoders/value_class_encoder.py @@ -21,13 +21,13 @@ from ..enums.vcs_reference_type import VCSReferenceType from ..enums.deployment_download_type import DeploymentDownloadType from ..enums.execution_method import ExecutionMethod -from ..enums.health_queue_name import HealthQueueName from ..enums.message_priority import MessagePriority from ..enums.smtp_encryption import SmtpEncryption from ..enums.organization_key_scopes import OrganizationKeyScopes from ..enums.region import Region from ..enums.project_auth_method_id import ProjectAuthMethodId from ..enums.project_o_auth2_google_prompt import ProjectOAuth2GooglePrompt +from ..enums.project_o_auth2_oidc_prompt import ProjectOAuth2OidcPrompt from ..enums.project_o_auth_provider_id import ProjectOAuthProviderId from ..enums.project_policy_id import ProjectPolicyId from ..enums.project_protocol_id import ProjectProtocolId @@ -46,6 +46,7 @@ from ..enums.password_hash import PasswordHash from ..enums.messaging_provider_type import MessagingProviderType from ..enums.database_type import DatabaseType +from ..enums.database_status import DatabaseStatus from ..enums.attribute_status import AttributeStatus from ..enums.column_status import ColumnStatus from ..enums.index_status import IndexStatus @@ -53,12 +54,12 @@ from ..enums.execution_trigger import ExecutionTrigger from ..enums.execution_status import ExecutionStatus from ..enums.o_auth2_google_prompt import OAuth2GooglePrompt +from ..enums.o_auth2_oidc_prompt import OAuth2OidcPrompt from ..enums.platform_type import PlatformType -from ..enums.health_antivirus_status import HealthAntivirusStatus -from ..enums.health_check_status import HealthCheckStatus from ..enums.proxy_rule_deployment_resource_type import ProxyRuleDeploymentResourceType from ..enums.proxy_rule_status import ProxyRuleStatus from ..enums.message_status import MessageStatus +from ..enums.billing_plan_group import BillingPlanGroup class ValueClassEncoder(json.JSONEncoder): def default(self, o): @@ -128,9 +129,6 @@ def default(self, o): if isinstance(o, ExecutionMethod): return o.value - if isinstance(o, HealthQueueName): - return o.value - if isinstance(o, MessagePriority): return o.value @@ -149,6 +147,9 @@ def default(self, o): if isinstance(o, ProjectOAuth2GooglePrompt): return o.value + if isinstance(o, ProjectOAuth2OidcPrompt): + return o.value + if isinstance(o, ProjectOAuthProviderId): return o.value @@ -203,6 +204,9 @@ def default(self, o): if isinstance(o, DatabaseType): return o.value + if isinstance(o, DatabaseStatus): + return o.value + if isinstance(o, AttributeStatus): return o.value @@ -224,13 +228,10 @@ def default(self, o): if isinstance(o, OAuth2GooglePrompt): return o.value - if isinstance(o, PlatformType): - return o.value - - if isinstance(o, HealthAntivirusStatus): + if isinstance(o, OAuth2OidcPrompt): return o.value - if isinstance(o, HealthCheckStatus): + if isinstance(o, PlatformType): return o.value if isinstance(o, ProxyRuleDeploymentResourceType): @@ -242,4 +243,7 @@ def default(self, o): if isinstance(o, MessageStatus): return o.value + if isinstance(o, BillingPlanGroup): + return o.value + return super().default(o) diff --git a/appwrite/enums/billing_plan_group.py b/appwrite/enums/billing_plan_group.py new file mode 100644 index 00000000..5a659196 --- /dev/null +++ b/appwrite/enums/billing_plan_group.py @@ -0,0 +1,6 @@ +from enum import Enum + +class BillingPlanGroup(Enum): + STARTER = "starter" + PRO = "pro" + SCALE = "scale" diff --git a/appwrite/enums/database_status.py b/appwrite/enums/database_status.py new file mode 100644 index 00000000..7c5f888f --- /dev/null +++ b/appwrite/enums/database_status.py @@ -0,0 +1,6 @@ +from enum import Enum + +class DatabaseStatus(Enum): + PROVISIONING = "provisioning" + READY = "ready" + FAILED = "failed" diff --git a/appwrite/enums/health_antivirus_status.py b/appwrite/enums/health_antivirus_status.py deleted file mode 100644 index 38c1684b..00000000 --- a/appwrite/enums/health_antivirus_status.py +++ /dev/null @@ -1,6 +0,0 @@ -from enum import Enum - -class HealthAntivirusStatus(Enum): - DISABLED = "disabled" - OFFLINE = "offline" - ONLINE = "online" diff --git a/appwrite/enums/health_check_status.py b/appwrite/enums/health_check_status.py deleted file mode 100644 index 2cc9c113..00000000 --- a/appwrite/enums/health_check_status.py +++ /dev/null @@ -1,5 +0,0 @@ -from enum import Enum - -class HealthCheckStatus(Enum): - PASS = "pass" - FAIL = "fail" diff --git a/appwrite/enums/health_queue_name.py b/appwrite/enums/health_queue_name.py deleted file mode 100644 index a04ab15f..00000000 --- a/appwrite/enums/health_queue_name.py +++ /dev/null @@ -1,16 +0,0 @@ -from enum import Enum - -class HealthQueueName(Enum): - V1_DATABASE = "v1-database" - V1_DELETES = "v1-deletes" - V1_AUDITS = "v1-audits" - V1_MAILS = "v1-mails" - V1_FUNCTIONS = "v1-functions" - V1_STATS_RESOURCES = "v1-stats-resources" - V1_STATS_USAGE = "v1-stats-usage" - V1_WEBHOOKS = "v1-webhooks" - V1_CERTIFICATES = "v1-certificates" - V1_BUILDS = "v1-builds" - V1_SCREENSHOTS = "v1-screenshots" - V1_MESSAGING = "v1-messaging" - V1_MIGRATIONS = "v1-migrations" diff --git a/appwrite/enums/o_auth2_oidc_prompt.py b/appwrite/enums/o_auth2_oidc_prompt.py new file mode 100644 index 00000000..0c21c203 --- /dev/null +++ b/appwrite/enums/o_auth2_oidc_prompt.py @@ -0,0 +1,7 @@ +from enum import Enum + +class OAuth2OidcPrompt(Enum): + NONE = "none" + LOGIN = "login" + CONSENT = "consent" + SELECT_ACCOUNT = "select_account" diff --git a/appwrite/enums/o_auth_provider.py b/appwrite/enums/o_auth_provider.py index 18b69a49..e0aa4ea5 100644 --- a/appwrite/enums/o_auth_provider.py +++ b/appwrite/enums/o_auth_provider.py @@ -3,6 +3,7 @@ class OAuthProvider(Enum): AMAZON = "amazon" APPLE = "apple" + APPWRITE = "appwrite" AUTH0 = "auth0" AUTHENTIK = "authentik" AUTODESK = "autodesk" diff --git a/appwrite/enums/organization_key_scopes.py b/appwrite/enums/organization_key_scopes.py index aa788f1a..ab4e4526 100644 --- a/appwrite/enums/organization_key_scopes.py +++ b/appwrite/enums/organization_key_scopes.py @@ -7,6 +7,10 @@ class OrganizationKeyScopes(Enum): DEVKEYS_WRITE = "devKeys.write" ORGANIZATION_KEYS_READ = "organization.keys.read" ORGANIZATION_KEYS_WRITE = "organization.keys.write" + ORGANIZATION_MEMBERSHIPS_READ = "organization.memberships.read" + ORGANIZATION_MEMBERSHIPS_WRITE = "organization.memberships.write" + ORGANIZATION_READ = "organization.read" + ORGANIZATION_WRITE = "organization.write" DOMAINS_READ = "domains.read" DOMAINS_WRITE = "domains.write" KEYS_READ = "keys.read" diff --git a/appwrite/enums/project_key_scopes.py b/appwrite/enums/project_key_scopes.py index a0356058..10099119 100644 --- a/appwrite/enums/project_key_scopes.py +++ b/appwrite/enums/project_key_scopes.py @@ -13,10 +13,12 @@ class ProjectKeyScopes(Enum): POLICIES_WRITE = "policies.write" PROJECT_POLICIES_READ = "project.policies.read" PROJECT_POLICIES_WRITE = "project.policies.write" + PROJECT_OAUTH2_READ = "project.oauth2.read" + PROJECT_OAUTH2_WRITE = "project.oauth2.write" TEMPLATES_READ = "templates.read" TEMPLATES_WRITE = "templates.write" - OAUTH2_READ = "oauth2.read" - OAUTH2_WRITE = "oauth2.write" + STAGES_READ = "stages.read" + STAGES_WRITE = "stages.write" USERS_READ = "users.read" USERS_WRITE = "users.write" SESSIONS_READ = "sessions.read" @@ -97,4 +99,6 @@ class ProjectKeyScopes(Enum): EVENTS_READ = "events.read" APPS_READ = "apps.read" APPS_WRITE = "apps.write" + OAUTH2_READ = "oauth2.read" + OAUTH2_WRITE = "oauth2.write" USAGE_READ = "usage.read" diff --git a/appwrite/enums/project_o_auth2_oidc_prompt.py b/appwrite/enums/project_o_auth2_oidc_prompt.py new file mode 100644 index 00000000..43c64713 --- /dev/null +++ b/appwrite/enums/project_o_auth2_oidc_prompt.py @@ -0,0 +1,7 @@ +from enum import Enum + +class ProjectOAuth2OidcPrompt(Enum): + NONE = "none" + LOGIN = "login" + CONSENT = "consent" + SELECT_ACCOUNT = "select_account" diff --git a/appwrite/enums/project_o_auth_provider_id.py b/appwrite/enums/project_o_auth_provider_id.py index 71ba1213..c61f6002 100644 --- a/appwrite/enums/project_o_auth_provider_id.py +++ b/appwrite/enums/project_o_auth_provider_id.py @@ -3,6 +3,7 @@ class ProjectOAuthProviderId(Enum): AMAZON = "amazon" APPLE = "apple" + APPWRITE = "appwrite" AUTH0 = "auth0" AUTHENTIK = "authentik" AUTODESK = "autodesk" diff --git a/appwrite/models/__init__.py b/appwrite/models/__init__.py index e8267dd9..8e75504a 100644 --- a/appwrite/models/__init__.py +++ b/appwrite/models/__init__.py @@ -34,7 +34,6 @@ from .mock_number_list import MockNumberList from .policy_list import PolicyList from .email_template_list import EmailTemplateList -from .health_status_list import HealthStatusList from .proxy_rule_list import ProxyRuleList from .locale_code_list import LocaleCodeList from .provider_list import ProviderList @@ -160,6 +159,7 @@ from .o_auth2_tradeshift import OAuth2Tradeshift from .o_auth2_paypal import OAuth2Paypal from .o_auth2_gitlab import OAuth2Gitlab +from .o_auth2_appwrite import OAuth2Appwrite from .o_auth2_authentik import OAuth2Authentik from .o_auth2_auth0 import OAuth2Auth0 from .o_auth2_fusion_auth import OAuth2FusionAuth @@ -192,11 +192,6 @@ from .language import Language from .currency import Currency from .phone import Phone -from .health_antivirus import HealthAntivirus -from .health_queue import HealthQueue -from .health_status import HealthStatus -from .health_certificate import HealthCertificate -from .health_time import HealthTime from .headers import Headers from .specification import Specification from .proxy_rule import ProxyRule @@ -215,19 +210,25 @@ from .insight_cta import InsightCTA from .report import Report from .activity_event import ActivityEvent +from .additional_resource import AdditionalResource from .backup_archive import BackupArchive from .billing_limits import BillingLimits +from .billing_plan import BillingPlan +from .billing_plan_addon import BillingPlanAddon +from .billing_plan_addon_details import BillingPlanAddonDetails +from .billing_plan_limits import BillingPlanLimits +from .billing_plan_dedicated_database_limits import BillingPlanDedicatedDatabaseLimits +from .billing_plan_supported_addons import BillingPlanSupportedAddons from .block import Block +from .organization import Organization from .backup_policy import BackupPolicy from .policy_deny_aliased_email import PolicyDenyAliasedEmail from .policy_deny_disposable_email import PolicyDenyDisposableEmail from .policy_deny_free_email import PolicyDenyFreeEmail from .policy_deny_corporate_email import PolicyDenyCorporateEmail +from .program import Program from .backup_restoration import BackupRestoration -from .usage_data_point import UsageDataPoint -from .usage_event_list import UsageEventList -from .usage_gauge_list import UsageGaugeList -from .usage_metric import UsageMetric +from .usage_billing_plan import UsageBillingPlan from .app import App from .app_secret import AppSecret from .app_secret_plaintext import AppSecretPlaintext @@ -236,7 +237,12 @@ from .oauth2_reject import Oauth2Reject from .oauth2_grant import Oauth2Grant from .oauth2_device_authorization import Oauth2DeviceAuthorization +from .oauth2_par import Oauth2PAR from .oauth2_token import Oauth2Token +from .oauth2_project import Oauth2Project +from .oauth2_organization import Oauth2Organization +from .oauth2_project_list import Oauth2ProjectList +from .oauth2_organization_list import Oauth2OrganizationList from .activity_event_list import ActivityEventList from .backup_archive_list import BackupArchiveList from .backup_policy_list import BackupPolicyList @@ -281,7 +287,6 @@ 'MockNumberList', 'PolicyList', 'EmailTemplateList', - 'HealthStatusList', 'ProxyRuleList', 'LocaleCodeList', 'ProviderList', @@ -407,6 +412,7 @@ 'OAuth2Tradeshift', 'OAuth2Paypal', 'OAuth2Gitlab', + 'OAuth2Appwrite', 'OAuth2Authentik', 'OAuth2Auth0', 'OAuth2FusionAuth', @@ -439,11 +445,6 @@ 'Language', 'Currency', 'Phone', - 'HealthAntivirus', - 'HealthQueue', - 'HealthStatus', - 'HealthCertificate', - 'HealthTime', 'Headers', 'Specification', 'ProxyRule', @@ -462,19 +463,25 @@ 'InsightCTA', 'Report', 'ActivityEvent', + 'AdditionalResource', 'BackupArchive', 'BillingLimits', + 'BillingPlan', + 'BillingPlanAddon', + 'BillingPlanAddonDetails', + 'BillingPlanLimits', + 'BillingPlanDedicatedDatabaseLimits', + 'BillingPlanSupportedAddons', 'Block', + 'Organization', 'BackupPolicy', 'PolicyDenyAliasedEmail', 'PolicyDenyDisposableEmail', 'PolicyDenyFreeEmail', 'PolicyDenyCorporateEmail', + 'Program', 'BackupRestoration', - 'UsageDataPoint', - 'UsageEventList', - 'UsageGaugeList', - 'UsageMetric', + 'UsageBillingPlan', 'App', 'AppSecret', 'AppSecretPlaintext', @@ -483,7 +490,12 @@ 'Oauth2Reject', 'Oauth2Grant', 'Oauth2DeviceAuthorization', + 'Oauth2PAR', 'Oauth2Token', + 'Oauth2Project', + 'Oauth2Organization', + 'Oauth2ProjectList', + 'Oauth2OrganizationList', 'ActivityEventList', 'BackupArchiveList', 'BackupPolicyList', diff --git a/appwrite/models/activity_event.py b/appwrite/models/activity_event.py index 9e2b6313..11265c1d 100644 --- a/appwrite/models/activity_event.py +++ b/appwrite/models/activity_event.py @@ -45,10 +45,6 @@ class ActivityEvent(AppwriteModel): Team ID. hostname : str Hostname. - countrycode : str - Country two-character ISO 3166-1 alpha code. - countryname : str - Country name. """ id: str = Field(..., alias='$id') actortype: str = Field(..., alias='actorType') @@ -68,5 +64,3 @@ class ActivityEvent(AppwriteModel): projectid: str = Field(..., alias='projectId') teamid: str = Field(..., alias='teamId') hostname: str = Field(..., alias='hostname') - countrycode: str = Field(..., alias='countryCode') - countryname: str = Field(..., alias='countryName') diff --git a/appwrite/models/additional_resource.py b/appwrite/models/additional_resource.py new file mode 100644 index 00000000..63e01170 --- /dev/null +++ b/appwrite/models/additional_resource.py @@ -0,0 +1,30 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class AdditionalResource(AppwriteModel): + """ + AdditionalResource + + Attributes + ---------- + name : str + Resource name + unit : str + Resource unit + currency : str + Price currency + price : float + Price + value : float + Resource value + invoicedesc : str + Description on invoice + """ + name: str = Field(..., alias='name') + unit: str = Field(..., alias='unit') + currency: str = Field(..., alias='currency') + price: float = Field(..., alias='price') + value: float = Field(..., alias='value') + invoicedesc: str = Field(..., alias='invoiceDesc') diff --git a/appwrite/models/app.py b/appwrite/models/app.py index 189910d2..f570a66c 100644 --- a/appwrite/models/app.py +++ b/appwrite/models/app.py @@ -34,6 +34,8 @@ class App(AppwriteModel): Application tagline shown to users during OAuth2 consent. tags : List[Any] Application tags shown to users during OAuth2 consent. + labels : List[Any] + Application labels. Read-only for clients; only a server SDK using a project API key can update them. images : List[Any] Application image URLs shown to users during OAuth2 consent. supporturl : str @@ -69,6 +71,7 @@ class App(AppwriteModel): contacts: List[Any] = Field(..., alias='contacts') tagline: str = Field(..., alias='tagline') tags: List[Any] = Field(..., alias='tags') + labels: List[Any] = Field(..., alias='labels') images: List[Any] = Field(..., alias='images') supporturl: str = Field(..., alias='supportUrl') datadeletionurl: str = Field(..., alias='dataDeletionUrl') diff --git a/appwrite/models/billing_plan.py b/appwrite/models/billing_plan.py new file mode 100644 index 00000000..64db521b --- /dev/null +++ b/appwrite/models/billing_plan.py @@ -0,0 +1,223 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .usage_billing_plan import UsageBillingPlan +from .billing_plan_addon import BillingPlanAddon +from .billing_plan_supported_addons import BillingPlanSupportedAddons +from .billing_plan_limits import BillingPlanLimits +from ..enums.billing_plan_group import BillingPlanGroup +from .program import Program +from .billing_plan_dedicated_database_limits import BillingPlanDedicatedDatabaseLimits + +class BillingPlan(AppwriteModel): + """ + billingPlan + + Attributes + ---------- + id : str + Plan ID. + name : str + Plan name + desc : str + Plan description + order : float + Plan order + price : float + Price + trial : float + Trial days + bandwidth : float + Bandwidth + storage : float + Storage + imagetransformations : float + Image Transformations + screenshotsgenerated : float + Screenshots generated + members : float + Members + webhooks : float + Webhooks + projects : float + Projects + platforms : float + Platforms + users : float + Users + teams : float + Teams + databases : float + Databases + databasesreads : float + Database reads per month + databaseswrites : float + Database writes per month + databasesbatchsize : float + Database batch size limit + buckets : float + Buckets + filesize : float + File size + functions : float + Functions + sites : float + Sites + executions : float + Function executions + executionsretentioncount : float + Rolling max executions retained per function/site + gbhours : float + GB hours for functions + realtime : float + Realtime connections + realtimemessages : float + Realtime messages + messages : float + Messages per month + topics : float + Topics for messaging + authphone : float + SMS authentications per month + domains : float + Custom domains + activitylogs : float + Activity log days + usagelogs : float + Usage history days + usagelogsintervals : Optional[List[Any]] + Usage log time intervals allowed for this plan (e.g. 15m, 1h, 1d). + projectinactivitydays : float + Number of days of console inactivity before a project is paused. 0 means pausing is disabled. + alertlimit : float + Alert threshold percentage + usage : UsageBillingPlan + Additional resources + addons : BillingPlanAddon + Addons + budgetcapenabled : bool + Budget cap enabled or disabled. + customsmtp : bool + Custom SMTP + emailbranding : bool + Appwrite branding in email + requirespaymentmethod : bool + Does plan require payment method + requiresbillingaddress : bool + Does plan require billing address + isavailable : bool + Is the billing plan available + selfservice : bool + Can user change the plan themselves + premiumsupport : bool + Does plan enable premium support + budgeting : bool + Does plan support budget cap + supportsmocknumbers : bool + Does plan support mock numbers + supportsorganizationroles : bool + Does plan support organization roles + supportscredits : bool + Does plan support credit + supportsdisposableemailvalidation : bool + Does plan support blocking disposable email addresses. + supportscanonicalemailvalidation : bool + Does plan support requiring canonical email addresses. + supportsfreeemailvalidation : bool + Does plan support blocking free email addresses. + supportscorporateemailvalidation : bool + Does plan support restricting sign-ups to corporate email addresses only. + supportsprojectspecificroles : bool + Does plan support project-specific member roles. + backupsenabled : bool + Does plan support backup policies. + usageperproject : bool + Whether usage addons are calculated per project. + supportedaddons : BillingPlanSupportedAddons + Supported addons for this plan + backuppolicies : float + How many policies does plan support + deploymentsize : float + Maximum function and site deployment size in MB + buildsize : float + Maximum function and site deployment size in MB + databasesallowencrypt : bool + Does the plan support encrypted string attributes or not. + limits : Optional[BillingPlanLimits] + Plan specific limits + group : BillingPlanGroup + Group of this billing plan for variants + program : Optional[Program] + Details of the program this plan is a part of. + dedicateddatabases : Optional[BillingPlanDedicatedDatabaseLimits] + Dedicated database limits available to this plan. + """ + id: str = Field(..., alias='$id') + name: str = Field(..., alias='name') + desc: str = Field(..., alias='desc') + order: float = Field(..., alias='order') + price: float = Field(..., alias='price') + trial: float = Field(..., alias='trial') + bandwidth: float = Field(..., alias='bandwidth') + storage: float = Field(..., alias='storage') + imagetransformations: float = Field(..., alias='imageTransformations') + screenshotsgenerated: float = Field(..., alias='screenshotsGenerated') + members: float = Field(..., alias='members') + webhooks: float = Field(..., alias='webhooks') + projects: float = Field(..., alias='projects') + platforms: float = Field(..., alias='platforms') + users: float = Field(..., alias='users') + teams: float = Field(..., alias='teams') + databases: float = Field(..., alias='databases') + databasesreads: float = Field(..., alias='databasesReads') + databaseswrites: float = Field(..., alias='databasesWrites') + databasesbatchsize: float = Field(..., alias='databasesBatchSize') + buckets: float = Field(..., alias='buckets') + filesize: float = Field(..., alias='fileSize') + functions: float = Field(..., alias='functions') + sites: float = Field(..., alias='sites') + executions: float = Field(..., alias='executions') + executionsretentioncount: float = Field(..., alias='executionsRetentionCount') + gbhours: float = Field(..., alias='GBHours') + realtime: float = Field(..., alias='realtime') + realtimemessages: float = Field(..., alias='realtimeMessages') + messages: float = Field(..., alias='messages') + topics: float = Field(..., alias='topics') + authphone: float = Field(..., alias='authPhone') + domains: float = Field(..., alias='domains') + activitylogs: float = Field(..., alias='activityLogs') + usagelogs: float = Field(..., alias='usageLogs') + usagelogsintervals: Optional[List[Any]] = Field(default=None, alias='usageLogsIntervals') + projectinactivitydays: float = Field(..., alias='projectInactivityDays') + alertlimit: float = Field(..., alias='alertLimit') + usage: UsageBillingPlan = Field(..., alias='usage') + addons: BillingPlanAddon = Field(..., alias='addons') + budgetcapenabled: bool = Field(..., alias='budgetCapEnabled') + customsmtp: bool = Field(..., alias='customSmtp') + emailbranding: bool = Field(..., alias='emailBranding') + requirespaymentmethod: bool = Field(..., alias='requiresPaymentMethod') + requiresbillingaddress: bool = Field(..., alias='requiresBillingAddress') + isavailable: bool = Field(..., alias='isAvailable') + selfservice: bool = Field(..., alias='selfService') + premiumsupport: bool = Field(..., alias='premiumSupport') + budgeting: bool = Field(..., alias='budgeting') + supportsmocknumbers: bool = Field(..., alias='supportsMockNumbers') + supportsorganizationroles: bool = Field(..., alias='supportsOrganizationRoles') + supportscredits: bool = Field(..., alias='supportsCredits') + supportsdisposableemailvalidation: bool = Field(..., alias='supportsDisposableEmailValidation') + supportscanonicalemailvalidation: bool = Field(..., alias='supportsCanonicalEmailValidation') + supportsfreeemailvalidation: bool = Field(..., alias='supportsFreeEmailValidation') + supportscorporateemailvalidation: bool = Field(..., alias='supportsCorporateEmailValidation') + supportsprojectspecificroles: bool = Field(..., alias='supportsProjectSpecificRoles') + backupsenabled: bool = Field(..., alias='backupsEnabled') + usageperproject: bool = Field(..., alias='usagePerProject') + supportedaddons: BillingPlanSupportedAddons = Field(..., alias='supportedAddons') + backuppolicies: float = Field(..., alias='backupPolicies') + deploymentsize: float = Field(..., alias='deploymentSize') + buildsize: float = Field(..., alias='buildSize') + databasesallowencrypt: bool = Field(..., alias='databasesAllowEncrypt') + limits: Optional[BillingPlanLimits] = Field(default=None, alias='limits') + group: BillingPlanGroup = Field(..., alias='group') + program: Optional[Program] = Field(default=None, alias='program') + dedicateddatabases: Optional[BillingPlanDedicatedDatabaseLimits] = Field(default=None, alias='dedicatedDatabases') diff --git a/appwrite/models/billing_plan_addon.py b/appwrite/models/billing_plan_addon.py new file mode 100644 index 00000000..b1d75b34 --- /dev/null +++ b/appwrite/models/billing_plan_addon.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .billing_plan_addon_details import BillingPlanAddonDetails + +class BillingPlanAddon(AppwriteModel): + """ + Addon + + Attributes + ---------- + seats : BillingPlanAddonDetails + Addon seats + projects : BillingPlanAddonDetails + Addon projects + """ + seats: BillingPlanAddonDetails = Field(..., alias='seats') + projects: BillingPlanAddonDetails = Field(..., alias='projects') diff --git a/appwrite/models/billing_plan_addon_details.py b/appwrite/models/billing_plan_addon_details.py new file mode 100644 index 00000000..8acd360c --- /dev/null +++ b/appwrite/models/billing_plan_addon_details.py @@ -0,0 +1,36 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class BillingPlanAddonDetails(AppwriteModel): + """ + Details + + Attributes + ---------- + supported : bool + Is the addon supported in the plan? + planincluded : float + Addon plan included value + limit : float + Addon limit + type : str + Addon type + currency : str + Price currency + price : float + Price + value : float + Resource value + invoicedesc : str + Description on invoice + """ + supported: bool = Field(..., alias='supported') + planincluded: float = Field(..., alias='planIncluded') + limit: float = Field(..., alias='limit') + type: str = Field(..., alias='type') + currency: str = Field(..., alias='currency') + price: float = Field(..., alias='price') + value: float = Field(..., alias='value') + invoicedesc: str = Field(..., alias='invoiceDesc') diff --git a/appwrite/models/billing_plan_dedicated_database_limits.py b/appwrite/models/billing_plan_dedicated_database_limits.py new file mode 100644 index 00000000..754c1666 --- /dev/null +++ b/appwrite/models/billing_plan_dedicated_database_limits.py @@ -0,0 +1,69 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class BillingPlanDedicatedDatabaseLimits(AppwriteModel): + """ + dedicatedDatabaseLimits + + Attributes + ---------- + mincpu : Optional[float] + Minimum CPU allocation in millicores. + maxcpu : Optional[float] + Maximum CPU allocation in millicores. + minmemorymb : Optional[float] + Minimum memory allocation in megabytes. + maxmemorymb : Optional[float] + Maximum memory allocation in megabytes. + minstoragegb : Optional[float] + Minimum storage allocation in gigabytes. + maxstoragegb : Optional[float] + Maximum storage allocation in gigabytes. + maxreplicas : Optional[float] + Maximum number of high-availability replicas per dedicated database. + maxconnections : Optional[float] + Maximum number of client connections. + maxipallowlistsize : Optional[float] + Maximum number of entries allowed in the IP allowlist. + maxextensions : Optional[float] + Maximum number of database extensions that can be enabled. + maxbackupretentiondays : Optional[float] + Maximum number of days a backup can be retained. + maxpitrretentiondays : Optional[float] + Maximum number of days of point-in-time recovery data that can be retained. + maxsqlapimaxrows : Optional[float] + Maximum number of rows a single SQL API query can return. + maxsqlapimaxbytes : Optional[float] + Maximum response size in bytes for a single SQL API query. + maxsqlapitimeoutseconds : Optional[float] + Maximum execution time in seconds for a single SQL API query. + maxsqlapiallowedstatements : Optional[float] + Maximum number of SQL statement types that can be permitted through the SQL API. + allowedsqlstatements : Optional[List[Any]] + SQL statement types permitted through the SQL API. + allowedstorageclasses : Optional[List[Any]] + Storage classes available for dedicated databases. + allowedsyncmodes : Optional[List[Any]] + Replica synchronization modes available for dedicated databases. + """ + mincpu: Optional[float] = Field(default=None, alias='minCpu') + maxcpu: Optional[float] = Field(default=None, alias='maxCpu') + minmemorymb: Optional[float] = Field(default=None, alias='minMemoryMb') + maxmemorymb: Optional[float] = Field(default=None, alias='maxMemoryMb') + minstoragegb: Optional[float] = Field(default=None, alias='minStorageGb') + maxstoragegb: Optional[float] = Field(default=None, alias='maxStorageGb') + maxreplicas: Optional[float] = Field(default=None, alias='maxReplicas') + maxconnections: Optional[float] = Field(default=None, alias='maxConnections') + maxipallowlistsize: Optional[float] = Field(default=None, alias='maxIpAllowlistSize') + maxextensions: Optional[float] = Field(default=None, alias='maxExtensions') + maxbackupretentiondays: Optional[float] = Field(default=None, alias='maxBackupRetentionDays') + maxpitrretentiondays: Optional[float] = Field(default=None, alias='maxPitrRetentionDays') + maxsqlapimaxrows: Optional[float] = Field(default=None, alias='maxSqlApiMaxRows') + maxsqlapimaxbytes: Optional[float] = Field(default=None, alias='maxSqlApiMaxBytes') + maxsqlapitimeoutseconds: Optional[float] = Field(default=None, alias='maxSqlApiTimeoutSeconds') + maxsqlapiallowedstatements: Optional[float] = Field(default=None, alias='maxSqlApiAllowedStatements') + allowedsqlstatements: Optional[List[Any]] = Field(default=None, alias='allowedSqlStatements') + allowedstorageclasses: Optional[List[Any]] = Field(default=None, alias='allowedStorageClasses') + allowedsyncmodes: Optional[List[Any]] = Field(default=None, alias='allowedSyncModes') diff --git a/appwrite/models/billing_plan_limits.py b/appwrite/models/billing_plan_limits.py new file mode 100644 index 00000000..4a5abbd9 --- /dev/null +++ b/appwrite/models/billing_plan_limits.py @@ -0,0 +1,18 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class BillingPlanLimits(AppwriteModel): + """ + PlanLimits + + Attributes + ---------- + credits : Optional[float] + Credits limit per billing cycle + dailycredits : Optional[float] + Daily credits limit (if applicable) + """ + credits: Optional[float] = Field(default=None, alias='credits') + dailycredits: Optional[float] = Field(default=None, alias='dailyCredits') diff --git a/appwrite/models/billing_plan_supported_addons.py b/appwrite/models/billing_plan_supported_addons.py new file mode 100644 index 00000000..be64dde3 --- /dev/null +++ b/appwrite/models/billing_plan_supported_addons.py @@ -0,0 +1,21 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class BillingPlanSupportedAddons(AppwriteModel): + """ + BillingPlanSupportedAddons + + Attributes + ---------- + baa : bool + Whether the plan supports BAA (Business Associate Agreement) addon + premiumgeodb : bool + Whether the plan supports Premium Geo DB addon (project-level) + premiumgeodborg : bool + Whether the plan supports Premium Geo DB addon (organization-level) + """ + baa: bool = Field(..., alias='baa') + premiumgeodb: bool = Field(..., alias='premiumGeoDB') + premiumgeodborg: bool = Field(..., alias='premiumGeoDBOrg') diff --git a/appwrite/models/block.py b/appwrite/models/block.py index 4e11336d..95ba2075 100644 --- a/appwrite/models/block.py +++ b/appwrite/models/block.py @@ -15,6 +15,8 @@ class Block(AppwriteModel): Resource type that is blocked resourceid : str Resource identifier that is blocked + mode : str + Block mode. full blocks reads and writes; readOnly blocks writes only. reason : Optional[str] Reason for the block. Can be null if no reason was provided. expiredat : Optional[str] @@ -33,6 +35,7 @@ class Block(AppwriteModel): createdat: str = Field(..., alias='$createdAt') resourcetype: str = Field(..., alias='resourceType') resourceid: str = Field(..., alias='resourceId') + mode: str = Field(..., alias='mode') reason: Optional[str] = Field(default=None, alias='reason') expiredat: Optional[str] = Field(default=None, alias='expiredAt') projectname: str = Field(..., alias='projectName') diff --git a/appwrite/models/database.py b/appwrite/models/database.py index 784fc2a7..b491a89a 100644 --- a/appwrite/models/database.py +++ b/appwrite/models/database.py @@ -3,6 +3,7 @@ from .base_model import AppwriteModel from ..enums.database_type import DatabaseType +from ..enums.database_status import DatabaseStatus from .backup_policy import BackupPolicy from .backup_archive import BackupArchive @@ -24,6 +25,8 @@ class Database(AppwriteModel): If database is enabled. Can be 'enabled' or 'disabled'. When disabled, the database is inaccessible to users, but remains accessible to Server SDKs using API keys. type : DatabaseType Database type. + status : Optional[DatabaseStatus] + Database status. Possible values: `provisioning`, `ready` or `failed` policies : List[BackupPolicy] Database backup policies. archives : List[BackupArchive] @@ -35,5 +38,6 @@ class Database(AppwriteModel): updatedat: str = Field(..., alias='$updatedAt') enabled: bool = Field(..., alias='enabled') type: DatabaseType = Field(..., alias='type') + status: Optional[DatabaseStatus] = Field(default=None, alias='status') policies: List[BackupPolicy] = Field(..., alias='policies') archives: List[BackupArchive] = Field(..., alias='archives') diff --git a/appwrite/models/document.py b/appwrite/models/document.py index dcff339a..4df880fc 100644 --- a/appwrite/models/document.py +++ b/appwrite/models/document.py @@ -57,6 +57,17 @@ def data(self) -> T: def data(self, value: T) -> None: object.__setattr__(self, '_data', value) + def model_dump(self, **kwargs) -> Dict[str, Any]: + result = super().model_dump(**kwargs) + if hasattr(self, '_data'): + if isinstance(self._data, dict): + result['data'] = self._data + elif hasattr(self._data, 'model_dump'): + result['data'] = self._data.model_dump(**kwargs) + else: + result['data'] = self._data + return result + def to_dict(self) -> Dict[str, Any]: result = super().to_dict() if hasattr(self, '_data'): diff --git a/appwrite/models/health_antivirus.py b/appwrite/models/health_antivirus.py deleted file mode 100644 index 4e15043d..00000000 --- a/appwrite/models/health_antivirus.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel -from ..enums.health_antivirus_status import HealthAntivirusStatus - -class HealthAntivirus(AppwriteModel): - """ - Health Antivirus - - Attributes - ---------- - version : str - Antivirus version. - status : HealthAntivirusStatus - Antivirus status. Possible values are: `disabled`, `offline`, `online` - """ - version: str = Field(..., alias='version') - status: HealthAntivirusStatus = Field(..., alias='status') diff --git a/appwrite/models/health_certificate.py b/appwrite/models/health_certificate.py deleted file mode 100644 index 58999e72..00000000 --- a/appwrite/models/health_certificate.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel - -class HealthCertificate(AppwriteModel): - """ - Health Certificate - - Attributes - ---------- - name : str - Certificate name - subjectsn : str - Subject SN - issuerorganisation : str - Issuer organisation - validfrom : str - Valid from - validto : str - Valid to - signaturetypesn : str - Signature type SN - """ - name: str = Field(..., alias='name') - subjectsn: str = Field(..., alias='subjectSN') - issuerorganisation: str = Field(..., alias='issuerOrganisation') - validfrom: str = Field(..., alias='validFrom') - validto: str = Field(..., alias='validTo') - signaturetypesn: str = Field(..., alias='signatureTypeSN') diff --git a/appwrite/models/health_status.py b/appwrite/models/health_status.py deleted file mode 100644 index 0ae11466..00000000 --- a/appwrite/models/health_status.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel -from ..enums.health_check_status import HealthCheckStatus - -class HealthStatus(AppwriteModel): - """ - Health Status - - Attributes - ---------- - name : str - Name of the service. - ping : float - Duration in milliseconds how long the health check took. - status : HealthCheckStatus - Service status. Possible values are: `pass`, `fail` - """ - name: str = Field(..., alias='name') - ping: float = Field(..., alias='ping') - status: HealthCheckStatus = Field(..., alias='status') diff --git a/appwrite/models/health_status_list.py b/appwrite/models/health_status_list.py deleted file mode 100644 index 26904371..00000000 --- a/appwrite/models/health_status_list.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel -from .health_status import HealthStatus - -class HealthStatusList(AppwriteModel): - """ - Status List - - Attributes - ---------- - total : float - Total number of statuses that matched your query. - statuses : List[HealthStatus] - List of statuses. - """ - total: float = Field(..., alias='total') - statuses: List[HealthStatus] = Field(..., alias='statuses') diff --git a/appwrite/models/health_time.py b/appwrite/models/health_time.py deleted file mode 100644 index 240694d9..00000000 --- a/appwrite/models/health_time.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel - -class HealthTime(AppwriteModel): - """ - Health Time - - Attributes - ---------- - remotetime : float - Current unix timestamp on trustful remote server. - localtime : float - Current unix timestamp of local server where Appwrite runs. - diff : float - Difference of unix remote and local timestamps in milliseconds. - """ - remotetime: float = Field(..., alias='remoteTime') - localtime: float = Field(..., alias='localTime') - diff: float = Field(..., alias='diff') diff --git a/appwrite/models/locale.py b/appwrite/models/locale.py index 95952e14..f8aa6de8 100644 --- a/appwrite/models/locale.py +++ b/appwrite/models/locale.py @@ -23,6 +23,28 @@ class Locale(AppwriteModel): True if country is part of the European Union. currency : str Currency code in [ISO 4217-1](http://en.wikipedia.org/wiki/ISO_4217) three-character format + city : Optional[str] + City + timezone : Optional[str] + Name of timezone + postalcode : Optional[str] + Postal code + latitude : Optional[float] + Latitude + longitude : Optional[float] + Longitude + autonomoussystemnumber : Optional[str] + Autonomous System Number (ASN) of the IP + autonomoussystemorganization : Optional[str] + Organization that owns the ASN + isp : Optional[str] + Internet service provider of the IP + connectiontype : Optional[str] + Connection type of the IP (e.g. cable, cellular, corporate) + connectionusagetype : Optional[str] + User type classification of the IP (e.g. residential, business, hosting) + connectionorganization : Optional[str] + Registered organization of the IP """ ip: str = Field(..., alias='ip') countrycode: str = Field(..., alias='countryCode') @@ -31,3 +53,14 @@ class Locale(AppwriteModel): continent: str = Field(..., alias='continent') eu: bool = Field(..., alias='eu') currency: str = Field(..., alias='currency') + city: Optional[str] = Field(default=None, alias='city') + timezone: Optional[str] = Field(default=None, alias='timeZone') + postalcode: Optional[str] = Field(default=None, alias='postalCode') + latitude: Optional[float] = Field(default=None, alias='latitude') + longitude: Optional[float] = Field(default=None, alias='longitude') + autonomoussystemnumber: Optional[str] = Field(default=None, alias='autonomousSystemNumber') + autonomoussystemorganization: Optional[str] = Field(default=None, alias='autonomousSystemOrganization') + isp: Optional[str] = Field(default=None, alias='isp') + connectiontype: Optional[str] = Field(default=None, alias='connectionType') + connectionusagetype: Optional[str] = Field(default=None, alias='connectionUsageType') + connectionorganization: Optional[str] = Field(default=None, alias='connectionOrganization') diff --git a/appwrite/models/log.py b/appwrite/models/log.py index dc0e0c67..6824028e 100644 --- a/appwrite/models/log.py +++ b/appwrite/models/log.py @@ -20,7 +20,7 @@ class Log(AppwriteModel): mode : str API mode when event triggered. usertype : str - User type who triggered the audit log. Possible values: user, admin, guest, keyProject, keyAccount, keyOrganization. + User type who triggered the audit log. Possible values: user, admin, guest, hidden, keyProject, keyAccount, keyOrganization. ip : str IP session in use when the session was created. time : str diff --git a/appwrite/models/o_auth2_appwrite.py b/appwrite/models/o_auth2_appwrite.py new file mode 100644 index 00000000..002a8408 --- /dev/null +++ b/appwrite/models/o_auth2_appwrite.py @@ -0,0 +1,24 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class OAuth2Appwrite(AppwriteModel): + """ + OAuth2Appwrite + + Attributes + ---------- + id : str + OAuth2 provider ID. + enabled : bool + OAuth2 provider is active and can be used to create sessions. + clientid : str + Appwrite OAuth2 client ID. + clientsecret : str + Appwrite OAuth2 client secret. + """ + id: str = Field(..., alias='$id') + enabled: bool = Field(..., alias='enabled') + clientid: str = Field(..., alias='clientId') + clientsecret: str = Field(..., alias='clientSecret') diff --git a/appwrite/models/o_auth2_oidc.py b/appwrite/models/o_auth2_oidc.py index 6b896196..0ad06fef 100644 --- a/appwrite/models/o_auth2_oidc.py +++ b/appwrite/models/o_auth2_oidc.py @@ -2,6 +2,7 @@ from pydantic import Field, PrivateAttr from .base_model import AppwriteModel +from ..enums.o_auth2_oidc_prompt import OAuth2OidcPrompt class OAuth2Oidc(AppwriteModel): """ @@ -25,6 +26,10 @@ class OAuth2Oidc(AppwriteModel): OpenID Connect token endpoint URL. userinfourl : str OpenID Connect user info endpoint URL. + prompt : List[OAuth2OidcPrompt] + OpenID Connect prompt values controlling the authentication and consent screens. + maxage : Optional[float] + Maximum authentication age in seconds. When set, the user must have authenticated within this many seconds. """ id: str = Field(..., alias='$id') enabled: bool = Field(..., alias='enabled') @@ -34,3 +39,5 @@ class OAuth2Oidc(AppwriteModel): authorizationurl: str = Field(..., alias='authorizationURL') tokenurl: str = Field(..., alias='tokenURL') userinfourl: str = Field(..., alias='userInfoURL') + prompt: List[OAuth2OidcPrompt] = Field(..., alias='prompt') + maxage: Optional[float] = Field(default=None, alias='maxAge') diff --git a/appwrite/models/o_auth2_provider_list.py b/appwrite/models/o_auth2_provider_list.py index b07ce754..e13f865a 100644 --- a/appwrite/models/o_auth2_provider_list.py +++ b/appwrite/models/o_auth2_provider_list.py @@ -33,6 +33,7 @@ from .o_auth2_tradeshift import OAuth2Tradeshift from .o_auth2_paypal import OAuth2Paypal from .o_auth2_gitlab import OAuth2Gitlab +from .o_auth2_appwrite import OAuth2Appwrite from .o_auth2_authentik import OAuth2Authentik from .o_auth2_auth0 import OAuth2Auth0 from .o_auth2_fusion_auth import OAuth2FusionAuth @@ -51,8 +52,8 @@ class OAuth2ProviderList(AppwriteModel): ---------- total : float Total number of OAuth2 providers in the given project. - providers : List[Union[OAuth2Github, OAuth2Discord, OAuth2Figma, OAuth2Dropbox, OAuth2Dailymotion, OAuth2Bitbucket, OAuth2Bitly, OAuth2Box, OAuth2Autodesk, OAuth2Google, OAuth2Zoom, OAuth2Zoho, OAuth2Yandex, OAuth2X, OAuth2WordPress, OAuth2Twitch, OAuth2Stripe, OAuth2Spotify, OAuth2Slack, OAuth2Podio, OAuth2Notion, OAuth2Salesforce, OAuth2Yahoo, OAuth2Linkedin, OAuth2Disqus, OAuth2Amazon, OAuth2Etsy, OAuth2Facebook, OAuth2Tradeshift, OAuth2Paypal, OAuth2Gitlab, OAuth2Authentik, OAuth2Auth0, OAuth2FusionAuth, OAuth2Keycloak, OAuth2Oidc, OAuth2Apple, OAuth2Okta, OAuth2Kick, OAuth2Microsoft]] + providers : List[Union[OAuth2Github, OAuth2Discord, OAuth2Figma, OAuth2Dropbox, OAuth2Dailymotion, OAuth2Bitbucket, OAuth2Bitly, OAuth2Box, OAuth2Autodesk, OAuth2Google, OAuth2Zoom, OAuth2Zoho, OAuth2Yandex, OAuth2X, OAuth2WordPress, OAuth2Twitch, OAuth2Stripe, OAuth2Spotify, OAuth2Slack, OAuth2Podio, OAuth2Notion, OAuth2Salesforce, OAuth2Yahoo, OAuth2Linkedin, OAuth2Disqus, OAuth2Amazon, OAuth2Etsy, OAuth2Facebook, OAuth2Tradeshift, OAuth2Paypal, OAuth2Gitlab, OAuth2Appwrite, OAuth2Authentik, OAuth2Auth0, OAuth2FusionAuth, OAuth2Keycloak, OAuth2Oidc, OAuth2Apple, OAuth2Okta, OAuth2Kick, OAuth2Microsoft]] List of OAuth2 providers. """ total: float = Field(..., alias='total') - providers: List[Union[OAuth2Github, OAuth2Discord, OAuth2Figma, OAuth2Dropbox, OAuth2Dailymotion, OAuth2Bitbucket, OAuth2Bitly, OAuth2Box, OAuth2Autodesk, OAuth2Google, OAuth2Zoom, OAuth2Zoho, OAuth2Yandex, OAuth2X, OAuth2WordPress, OAuth2Twitch, OAuth2Stripe, OAuth2Spotify, OAuth2Slack, OAuth2Podio, OAuth2Notion, OAuth2Salesforce, OAuth2Yahoo, OAuth2Linkedin, OAuth2Disqus, OAuth2Amazon, OAuth2Etsy, OAuth2Facebook, OAuth2Tradeshift, OAuth2Paypal, OAuth2Gitlab, OAuth2Authentik, OAuth2Auth0, OAuth2FusionAuth, OAuth2Keycloak, OAuth2Oidc, OAuth2Apple, OAuth2Okta, OAuth2Kick, OAuth2Microsoft]] = Field(..., alias='providers') + providers: List[Union[OAuth2Github, OAuth2Discord, OAuth2Figma, OAuth2Dropbox, OAuth2Dailymotion, OAuth2Bitbucket, OAuth2Bitly, OAuth2Box, OAuth2Autodesk, OAuth2Google, OAuth2Zoom, OAuth2Zoho, OAuth2Yandex, OAuth2X, OAuth2WordPress, OAuth2Twitch, OAuth2Stripe, OAuth2Spotify, OAuth2Slack, OAuth2Podio, OAuth2Notion, OAuth2Salesforce, OAuth2Yahoo, OAuth2Linkedin, OAuth2Disqus, OAuth2Amazon, OAuth2Etsy, OAuth2Facebook, OAuth2Tradeshift, OAuth2Paypal, OAuth2Gitlab, OAuth2Appwrite, OAuth2Authentik, OAuth2Auth0, OAuth2FusionAuth, OAuth2Keycloak, OAuth2Oidc, OAuth2Apple, OAuth2Okta, OAuth2Kick, OAuth2Microsoft]] = Field(..., alias='providers') diff --git a/appwrite/models/oauth2_grant.py b/appwrite/models/oauth2_grant.py index a17a74fd..58676082 100644 --- a/appwrite/models/oauth2_grant.py +++ b/appwrite/models/oauth2_grant.py @@ -21,6 +21,8 @@ class Oauth2Grant(AppwriteModel): ID of the OAuth2 client (app) the grant was requested for. scopes : List[Any] Requested OAuth2 scopes the user is being asked to consent to. + resources : List[Any] + Requested RFC 8707 resource indicators the user is being asked to consent to. authorizationdetails : str Requested authorization_details the user is being asked to consent to, as a JSON string. Each entry has a `type` plus project-defined fields. prompt : str @@ -38,6 +40,7 @@ class Oauth2Grant(AppwriteModel): userid: str = Field(..., alias='userId') appid: str = Field(..., alias='appId') scopes: List[Any] = Field(..., alias='scopes') + resources: List[Any] = Field(..., alias='resources') authorizationdetails: str = Field(..., alias='authorizationDetails') prompt: str = Field(..., alias='prompt') redirecturi: str = Field(..., alias='redirectUri') diff --git a/appwrite/models/health_queue.py b/appwrite/models/oauth2_organization.py similarity index 55% rename from appwrite/models/health_queue.py rename to appwrite/models/oauth2_organization.py index f9dcf66f..4481c4b2 100644 --- a/appwrite/models/health_queue.py +++ b/appwrite/models/oauth2_organization.py @@ -3,13 +3,13 @@ from .base_model import AppwriteModel -class HealthQueue(AppwriteModel): +class Oauth2Organization(AppwriteModel): """ - Health Queue + OAuth2 Organization Attributes ---------- - size : float - Amount of actions in the queue. + id : str + Organization ID. """ - size: float = Field(..., alias='size') + id: str = Field(..., alias='$id') diff --git a/appwrite/models/oauth2_organization_list.py b/appwrite/models/oauth2_organization_list.py new file mode 100644 index 00000000..aee823a0 --- /dev/null +++ b/appwrite/models/oauth2_organization_list.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .oauth2_organization import Oauth2Organization + +class Oauth2OrganizationList(AppwriteModel): + """ + OAuth2 accessible organizations list + + Attributes + ---------- + total : float + Total number of organizations that matched your query. + organizations : List[Oauth2Organization] + List of organizations. + """ + total: float = Field(..., alias='total') + organizations: List[Oauth2Organization] = Field(..., alias='organizations') diff --git a/appwrite/models/oauth2_par.py b/appwrite/models/oauth2_par.py new file mode 100644 index 00000000..a5942c86 --- /dev/null +++ b/appwrite/models/oauth2_par.py @@ -0,0 +1,18 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class Oauth2PAR(AppwriteModel): + """ + OAuth2 PAR + + Attributes + ---------- + request_uri : str + Authorization request handle to pass to the authorize endpoint. + expires_in : float + Lifetime of the authorization request handle in seconds. + """ + request_uri: str = Field(..., alias='request_uri') + expires_in: float = Field(..., alias='expires_in') diff --git a/appwrite/models/oauth2_project.py b/appwrite/models/oauth2_project.py new file mode 100644 index 00000000..f7bac058 --- /dev/null +++ b/appwrite/models/oauth2_project.py @@ -0,0 +1,21 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class Oauth2Project(AppwriteModel): + """ + OAuth2 Project + + Attributes + ---------- + id : str + Project ID. + region : str + Region ID the project is deployed in. + endpoint : str + API endpoint of the region the project is deployed in. Empty when the region has no public hostname configured. + """ + id: str = Field(..., alias='$id') + region: str = Field(..., alias='region') + endpoint: str = Field(..., alias='endpoint') diff --git a/appwrite/models/oauth2_project_list.py b/appwrite/models/oauth2_project_list.py new file mode 100644 index 00000000..e67b8929 --- /dev/null +++ b/appwrite/models/oauth2_project_list.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .oauth2_project import Oauth2Project + +class Oauth2ProjectList(AppwriteModel): + """ + OAuth2 accessible projects list + + Attributes + ---------- + total : float + Total number of projects that matched your query. + projects : List[Oauth2Project] + List of projects. + """ + total: float = Field(..., alias='total') + projects: List[Oauth2Project] = Field(..., alias='projects') diff --git a/appwrite/models/organization.py b/appwrite/models/organization.py new file mode 100644 index 00000000..95f4bdef --- /dev/null +++ b/appwrite/models/organization.py @@ -0,0 +1,132 @@ +from typing import Any, Dict, List, Optional, Union, cast, Generic, TypeVar, Type +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .preferences import Preferences +from .billing_plan import BillingPlan +from .billing_limits import BillingLimits + +T = TypeVar('T') + +class Organization(AppwriteModel, Generic[T]): + """ + Organization + + Attributes + ---------- + id : str + Team ID. + createdat : str + Team creation date in ISO 8601 format. + updatedat : str + Team update date in ISO 8601 format. + name : str + Team name. + total : float + Total number of team members. + prefs : Preferences[T] + Team preferences as a key-value object + billingbudget : float + Project budget limit + budgetalerts : List[Any] + Project budget limit + billingplan : str + Organization's billing plan ID. + billingplanid : str + Organization's billing plan ID. + billingplandetails : BillingPlan + Organization's billing plan. + billingemail : str + Billing email set for the organization. + billingstartdate : str + Billing cycle start date. + billingcurrentinvoicedate : str + Current invoice cycle start date. + billingnextinvoicedate : str + Next invoice cycle start date. + billingtrialstartdate : str + Start date of trial. + billingtrialdays : float + Number of trial days. + billingaggregationid : str + Current active aggregation id. + billinginvoiceid : str + Current active aggregation id. + paymentmethodid : str + Default payment method. + billingaddressid : str + Default payment method. + backuppaymentmethodid : str + Backup payment method. + status : str + Team status. + remarks : str + Remarks on team status. + agreementbaa : str + Organization agreements + programmanagername : str + Program manager's name. + programmanagercalendar : str + Program manager's calendar link. + programdiscordchannelname : str + Program's discord channel name. + programdiscordchannelurl : str + Program's discord channel URL. + billinglimits : Optional[BillingLimits] + Billing limits reached + billingplandowngrade : str + Billing plan selected for downgrade. + billingtaxid : str + Tax Id + markedfordeletion : bool + Marked for deletion + platform : str + Product with which the organization is associated (appwrite or imagine) + projects : List[Any] + Selected projects + """ + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + name: str = Field(..., alias='name') + total: float = Field(..., alias='total') + prefs: Preferences[T] = Field(..., alias='prefs') + billingbudget: float = Field(..., alias='billingBudget') + budgetalerts: List[Any] = Field(..., alias='budgetAlerts') + billingplan: str = Field(..., alias='billingPlan') + billingplanid: str = Field(..., alias='billingPlanId') + billingplandetails: BillingPlan = Field(..., alias='billingPlanDetails') + billingemail: str = Field(..., alias='billingEmail') + billingstartdate: str = Field(..., alias='billingStartDate') + billingcurrentinvoicedate: str = Field(..., alias='billingCurrentInvoiceDate') + billingnextinvoicedate: str = Field(..., alias='billingNextInvoiceDate') + billingtrialstartdate: str = Field(..., alias='billingTrialStartDate') + billingtrialdays: float = Field(..., alias='billingTrialDays') + billingaggregationid: str = Field(..., alias='billingAggregationId') + billinginvoiceid: str = Field(..., alias='billingInvoiceId') + paymentmethodid: str = Field(..., alias='paymentMethodId') + billingaddressid: str = Field(..., alias='billingAddressId') + backuppaymentmethodid: str = Field(..., alias='backupPaymentMethodId') + status: str = Field(..., alias='status') + remarks: str = Field(..., alias='remarks') + agreementbaa: str = Field(..., alias='agreementBAA') + programmanagername: str = Field(..., alias='programManagerName') + programmanagercalendar: str = Field(..., alias='programManagerCalendar') + programdiscordchannelname: str = Field(..., alias='programDiscordChannelName') + programdiscordchannelurl: str = Field(..., alias='programDiscordChannelUrl') + billinglimits: Optional[BillingLimits] = Field(default=None, alias='billingLimits') + billingplandowngrade: str = Field(..., alias='billingPlanDowngrade') + billingtaxid: str = Field(..., alias='billingTaxId') + markedfordeletion: bool = Field(..., alias='markedForDeletion') + platform: str = Field(..., alias='platform') + projects: List[Any] = Field(..., alias='projects') + + @classmethod + def with_data(cls, data: Dict[str, Any], model_type: Type[T] = dict) -> 'Organization[T]': + """Create Organization instance with typed data.""" + instance = cls.model_validate(data) + if 'prefs' in data and data['prefs'] is not None: + instance.prefs = Preferences.with_data( + data['prefs'], model_type + ) + return instance diff --git a/appwrite/models/preferences.py b/appwrite/models/preferences.py index ce469590..73a68a21 100644 --- a/appwrite/models/preferences.py +++ b/appwrite/models/preferences.py @@ -29,6 +29,17 @@ def data(self) -> T: def data(self, value: T) -> None: object.__setattr__(self, '_data', value) + def model_dump(self, **kwargs) -> Dict[str, Any]: + result = super().model_dump(**kwargs) + if hasattr(self, '_data'): + if isinstance(self._data, dict): + result['data'] = self._data + elif hasattr(self._data, 'model_dump'): + result['data'] = self._data.model_dump(**kwargs) + else: + result['data'] = self._data + return result + def to_dict(self) -> Dict[str, Any]: result = super().to_dict() if hasattr(self, '_data'): diff --git a/appwrite/models/program.py b/appwrite/models/program.py new file mode 100644 index 00000000..49f068e9 --- /dev/null +++ b/appwrite/models/program.py @@ -0,0 +1,39 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class Program(AppwriteModel): + """ + Program + + Attributes + ---------- + id : str + Program ID + title : str + Program title + description : str + Program description + tag : str + Program tag for highlighting on console + icon : str + Program icon for highlighting on console + url : str + URL for more information on this program + active : bool + Whether this program is active + external : bool + Whether this program is external + billingplanid : str + Billing plan ID that this is program is associated with. + """ + id: str = Field(..., alias='$id') + title: str = Field(..., alias='title') + description: str = Field(..., alias='description') + tag: str = Field(..., alias='tag') + icon: str = Field(..., alias='icon') + url: str = Field(..., alias='url') + active: bool = Field(..., alias='active') + external: bool = Field(..., alias='external') + billingplanid: str = Field(..., alias='billingPlanId') diff --git a/appwrite/models/project.py b/appwrite/models/project.py index afd4b5a5..38e41404 100644 --- a/appwrite/models/project.py +++ b/appwrite/models/project.py @@ -57,6 +57,8 @@ class Project(AppwriteModel): Labels for the project. status : str Project status + onboarding : Dict[str, Any] + Stage progress (completed or skipped) with timestamps and actor types, keyed by stage id. authmethods : List[ProjectAuthMethod] List of auth methods. services : List[ProjectService] @@ -75,6 +77,8 @@ class Project(AppwriteModel): OAuth2 server authorization URL oauth2serverscopes : Optional[List[Any]] OAuth2 server allowed scopes + oauth2serverdefaultscopes : Optional[List[Any]] + OAuth2 server scopes used when an authorization request omits the scope parameter oauth2serverauthorizationdetailstypes : Optional[List[Any]] OAuth2 server accepted RFC 9396 authorization_details types oauth2serveraccesstokenduration : Optional[float] @@ -119,6 +123,7 @@ class Project(AppwriteModel): pingedat: str = Field(..., alias='pingedAt') labels: List[Any] = Field(..., alias='labels') status: str = Field(..., alias='status') + onboarding: Dict[str, Any] = Field(..., alias='onboarding') authmethods: List[ProjectAuthMethod] = Field(..., alias='authMethods') services: List[ProjectService] = Field(..., alias='services') protocols: List[ProjectProtocol] = Field(..., alias='protocols') @@ -128,6 +133,7 @@ class Project(AppwriteModel): oauth2serverenabled: Optional[bool] = Field(default=None, alias='oAuth2ServerEnabled') oauth2serverauthorizationurl: Optional[str] = Field(default=None, alias='oAuth2ServerAuthorizationUrl') oauth2serverscopes: Optional[List[Any]] = Field(default=None, alias='oAuth2ServerScopes') + oauth2serverdefaultscopes: Optional[List[Any]] = Field(default=None, alias='oAuth2ServerDefaultScopes') oauth2serverauthorizationdetailstypes: Optional[List[Any]] = Field(default=None, alias='oAuth2ServerAuthorizationDetailsTypes') oauth2serveraccesstokenduration: Optional[float] = Field(default=None, alias='oAuth2ServerAccessTokenDuration') oauth2serverrefreshtokenduration: Optional[float] = Field(default=None, alias='oAuth2ServerRefreshTokenDuration') diff --git a/appwrite/models/row.py b/appwrite/models/row.py index 8ccc97e4..e6e15976 100644 --- a/appwrite/models/row.py +++ b/appwrite/models/row.py @@ -57,6 +57,17 @@ def data(self) -> T: def data(self, value: T) -> None: object.__setattr__(self, '_data', value) + def model_dump(self, **kwargs) -> Dict[str, Any]: + result = super().model_dump(**kwargs) + if hasattr(self, '_data'): + if isinstance(self._data, dict): + result['data'] = self._data + elif hasattr(self._data, 'model_dump'): + result['data'] = self._data.model_dump(**kwargs) + else: + result['data'] = self._data + return result + def to_dict(self) -> Dict[str, Any]: result = super().to_dict() if hasattr(self, '_data'): diff --git a/appwrite/models/usage_billing_plan.py b/appwrite/models/usage_billing_plan.py new file mode 100644 index 00000000..a078e726 --- /dev/null +++ b/appwrite/models/usage_billing_plan.py @@ -0,0 +1,46 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .additional_resource import AdditionalResource + +class UsageBillingPlan(AppwriteModel): + """ + usageBillingPlan + + Attributes + ---------- + bandwidth : AdditionalResource + Bandwidth additional resources + executions : AdditionalResource + Executions additional resources + member : AdditionalResource + Member additional resources + realtime : AdditionalResource + Realtime additional resources + realtimemessages : AdditionalResource + Realtime messages additional resources + realtimebandwidth : AdditionalResource + Realtime bandwidth additional resources + storage : AdditionalResource + Storage additional resources + users : AdditionalResource + User additional resources + gbhours : AdditionalResource + GBHour additional resources + imagetransformations : AdditionalResource + Image transformation additional resources + credits : AdditionalResource + Credits additional resources + """ + bandwidth: AdditionalResource = Field(..., alias='bandwidth') + executions: AdditionalResource = Field(..., alias='executions') + member: AdditionalResource = Field(..., alias='member') + realtime: AdditionalResource = Field(..., alias='realtime') + realtimemessages: AdditionalResource = Field(..., alias='realtimeMessages') + realtimebandwidth: AdditionalResource = Field(..., alias='realtimeBandwidth') + storage: AdditionalResource = Field(..., alias='storage') + users: AdditionalResource = Field(..., alias='users') + gbhours: AdditionalResource = Field(..., alias='GBHours') + imagetransformations: AdditionalResource = Field(..., alias='imageTransformations') + credits: AdditionalResource = Field(..., alias='credits') diff --git a/appwrite/models/usage_data_point.py b/appwrite/models/usage_data_point.py deleted file mode 100644 index a314956c..00000000 --- a/appwrite/models/usage_data_point.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel - -class UsageDataPoint(AppwriteModel): - """ - usageDataPoint - - Attributes - ---------- - time : str - Bucket start timestamp (ISO 8601). When `interval` is omitted this is the request end time, marking the aggregate as-of moment. - value : float - Aggregated value for the bucket. - path : Optional[str] - API endpoint path when broken down by `path`. - method : Optional[str] - HTTP method when broken down by `method`. - status : Optional[str] - HTTP status code when broken down by `status`. - service : Optional[str] - API service segment when broken down by `service`. - country : Optional[str] - Country code when broken down by `country`. - region : Optional[str] - Appwrite region when broken down by `region`. - hostname : Optional[str] - Caller origin hostname when broken down by `hostname`. - osname : Optional[str] - Operating system name when broken down by `osName`. - clienttype : Optional[str] - Client type when broken down by `clientType`. - clientname : Optional[str] - Client name when broken down by `clientName`. - devicename : Optional[str] - Device classification when broken down by `deviceName`. - teamid : Optional[str] - Owning team ID when broken down by `teamId`. - resourceid : Optional[str] - External resource ID when broken down by `resourceId`. - resource : Optional[str] - Resource type when broken down by `resource` (gauges only). - """ - time: str = Field(..., alias='time') - value: float = Field(..., alias='value') - path: Optional[str] = Field(default=None, alias='path') - method: Optional[str] = Field(default=None, alias='method') - status: Optional[str] = Field(default=None, alias='status') - service: Optional[str] = Field(default=None, alias='service') - country: Optional[str] = Field(default=None, alias='country') - region: Optional[str] = Field(default=None, alias='region') - hostname: Optional[str] = Field(default=None, alias='hostname') - osname: Optional[str] = Field(default=None, alias='osName') - clienttype: Optional[str] = Field(default=None, alias='clientType') - clientname: Optional[str] = Field(default=None, alias='clientName') - devicename: Optional[str] = Field(default=None, alias='deviceName') - teamid: Optional[str] = Field(default=None, alias='teamId') - resourceid: Optional[str] = Field(default=None, alias='resourceId') - resource: Optional[str] = Field(default=None, alias='resource') diff --git a/appwrite/models/usage_event_list.py b/appwrite/models/usage_event_list.py deleted file mode 100644 index c183c3e4..00000000 --- a/appwrite/models/usage_event_list.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel -from .usage_metric import UsageMetric - -class UsageEventList(AppwriteModel): - """ - usageEventList - - Attributes - ---------- - interval : str - Time interval size (1h or 1d). Empty when the request omits `interval` — points then carry the request end time as their as-of marker. - metrics : List[UsageMetric] - One entry per requested metric, each carrying its own points[] time series (sums per bucket / dimension over time). - """ - interval: str = Field(..., alias='interval') - metrics: List[UsageMetric] = Field(..., alias='metrics') diff --git a/appwrite/models/usage_gauge_list.py b/appwrite/models/usage_gauge_list.py deleted file mode 100644 index ee3727ae..00000000 --- a/appwrite/models/usage_gauge_list.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel -from .usage_metric import UsageMetric - -class UsageGaugeList(AppwriteModel): - """ - usageGaugeList - - Attributes - ---------- - interval : str - Time interval size (1h or 1d). Empty when the request omits `interval` — points then carry the request end time as their as-of marker. - metrics : List[UsageMetric] - One entry per requested metric, each carrying its own points[] time series (latest-snapshot per bucket / dimension via argMax over time). - """ - interval: str = Field(..., alias='interval') - metrics: List[UsageMetric] = Field(..., alias='metrics') diff --git a/appwrite/models/usage_metric.py b/appwrite/models/usage_metric.py deleted file mode 100644 index 3cd15d47..00000000 --- a/appwrite/models/usage_metric.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Any, Dict, List, Optional, Union, cast -from pydantic import Field, PrivateAttr - -from .base_model import AppwriteModel -from .usage_data_point import UsageDataPoint - -class UsageMetric(AppwriteModel): - """ - usageMetric - - Attributes - ---------- - metric : str - Metric key this series describes. - points : List[UsageDataPoint] - Data points for this metric, ordered by time ascending. With `interval`, each entry is one bucket; without, each entry is one row of the dimensional or aggregate breakdown. - """ - metric: str = Field(..., alias='metric') - points: List[UsageDataPoint] = Field(..., alias='points') diff --git a/appwrite/query.py b/appwrite/query.py index c223a4e1..3dafd4aa 100644 --- a/appwrite/query.py +++ b/appwrite/query.py @@ -248,6 +248,18 @@ def distance_greater_than(attribute, values, distance, meters=True): def distance_less_than(attribute, values, distance, meters=True): return str(Query("distanceLessThan", attribute, [[values, distance, meters]])) + @staticmethod + def vector_dot(attribute, vector): + return str(Query("vectorDot", attribute, [vector])) + + @staticmethod + def vector_cosine(attribute, vector): + return str(Query("vectorCosine", attribute, [vector])) + + @staticmethod + def vector_euclidean(attribute, vector): + return str(Query("vectorEuclidean", attribute, [vector])) + @staticmethod def intersects(attribute, values): return str(Query("intersects", attribute, [values])) diff --git a/appwrite/services/account.py b/appwrite/services/account.py index 21b734d1..5af533a9 100644 --- a/appwrite/services/account.py +++ b/appwrite/services/account.py @@ -1606,7 +1606,7 @@ def create_o_auth2_token( Parameters ---------- provider : OAuthProvider - OAuth2 Provider. Currently, supported providers are: amazon, apple, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, fusionauth, github, gitlab, google, keycloak, kick, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom. + OAuth2 Provider. Currently, supported providers are: amazon, apple, appwrite, auth0, authentik, autodesk, bitbucket, bitly, box, dailymotion, discord, disqus, dropbox, etsy, facebook, figma, fusionauth, github, gitlab, google, keycloak, kick, linkedin, microsoft, notion, oidc, okta, paypal, paypalSandbox, podio, salesforce, slack, spotify, stripe, tradeshift, tradeshiftBox, twitch, wordpress, x, yahoo, yammer, yandex, zoho, zoom. success : Optional[str] URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project's platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API. failure : Optional[str] diff --git a/appwrite/services/apps.py b/appwrite/services/apps.py index da3c25d3..ab3e9110 100644 --- a/appwrite/services/apps.py +++ b/appwrite/services/apps.py @@ -396,6 +396,53 @@ def delete( return response + def update_labels( + self, + app_id: str, + labels: List[str] + ) -> App: + """ + Update the labels of an application. Labels are read-only for clients; only a server SDK using a project API key can set them. Replaces the previous labels. + + Parameters + ---------- + app_id : str + Application unique ID. + labels : List[str] + Array of application labels. Replaces the previous labels. Maximum of 1000 labels are allowed, each up to 36 alphanumeric characters long. + + Returns + ------- + App + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/labels' + api_params = {} + if app_id is None: + 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_params['labels'] = self._normalize_value(labels) + + response = self.client.call('put', 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=App) + + def list_secrets( self, app_id: str, diff --git a/appwrite/services/backups.py b/appwrite/services/backups.py index a13743d6..fda14881 100644 --- a/appwrite/services/backups.py +++ b/appwrite/services/backups.py @@ -429,10 +429,14 @@ def create_restoration( archive_id: str, services: List[BackupServices], new_resource_id: Optional[str] = None, - new_resource_name: Optional[str] = None + new_resource_name: Optional[str] = None, + new_specification: Optional[str] = None ) -> BackupRestoration: """ Create and trigger a new restoration for a backup on a project. + + When restoring a DocumentsDB or VectorsDB database to a new resource, pass `newSpecification` to provision the restored database on a different specification than the archived one (for example, restoring onto a larger or smaller dedicated database). Use `serverless` to restore onto the shared pool, or a dedicated specification slug to restore onto a dedicated database of that size. The specification must be permitted by the organization's plan. `newSpecification` is not supported for legacy/TablesDB databases or for bucket restores. + Parameters ---------- @@ -444,6 +448,8 @@ def create_restoration( Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. new_resource_name : Optional[str] Database name. Max length: 128 chars. + new_specification : Optional[str] + Specification to provision the restored database on, when restoring a DocumentsDB or VectorsDB database to a new resource. Defaults to the archived database's specification. Use `serverless` for the shared pool or a dedicated specification slug. Returns ------- @@ -471,6 +477,8 @@ def create_restoration( api_params['newResourceId'] = self._normalize_value(new_resource_id) if new_resource_name is not None: api_params['newResourceName'] = self._normalize_value(new_resource_name) + if new_specification is not None: + api_params['newSpecification'] = self._normalize_value(new_specification) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/functions.py b/appwrite/services/functions.py index 7d9a0f14..18709fb2 100644 --- a/appwrite/services/functions.py +++ b/appwrite/services/functions.py @@ -126,7 +126,7 @@ def create( commands : Optional[str] Build Commands. scopes : Optional[List[ProjectKeyScopes]] - List of scopes allowed for API key auto-generated for every execution. Maximum of 100 scopes are allowed. + List of scopes allowed for API key auto-generated for every execution. Maximum of 200 scopes are allowed. installation_id : Optional[str] Appwrite Installation ID for VCS (Version Control System) deployment. provider_repository_id : Optional[str] @@ -251,11 +251,17 @@ def list_runtimes( def list_specifications( - self + self, + type: Optional[str] = None ) -> SpecificationList: """ List allowed function specifications for this instance. + Parameters + ---------- + type : Optional[str] + Specification type to list. Can be one of: runtimes, builds. + Returns ------- SpecificationList @@ -270,6 +276,9 @@ def list_specifications( api_path = '/functions/specifications' api_params = {} + if type is not None: + api_params['type'] = self._normalize_value(type) + response = self.client.call('get', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), 'accept': 'application/json', @@ -370,7 +379,7 @@ def update( commands : Optional[str] Build Commands. scopes : Optional[List[ProjectKeyScopes]] - List of scopes allowed for API Key auto-generated for every execution. Maximum of 100 scopes are allowed. + List of scopes allowed for API Key auto-generated for every execution. Maximum of 200 scopes are allowed. installation_id : Optional[str] Appwrite Installation ID for VCS (Version Controle System) deployment. provider_repository_id : Optional[str] @@ -445,8 +454,7 @@ def update( api_params['providerRootDirectory'] = self._normalize_value(provider_root_directory) api_params['providerBranches'] = self._normalize_value(provider_branches) api_params['providerPaths'] = self._normalize_value(provider_paths) - if build_specification is not None: - api_params['buildSpecification'] = self._normalize_value(build_specification) + api_params['buildSpecification'] = self._normalize_value(build_specification) if runtime_specification is not None: api_params['runtimeSpecification'] = self._normalize_value(runtime_specification) if deployment_retention is not None: @@ -968,7 +976,8 @@ def get_deployment_download( self, function_id: str, deployment_id: str, - type: Optional[DeploymentDownloadType] = None + type: Optional[DeploymentDownloadType] = None, + token: Optional[str] = None ) -> bytes: """ Get a function deployment content by its unique ID. The endpoint response return with a 'Content-Disposition: attachment' header that tells the browser to start downloading the file to user downloads directory. @@ -981,6 +990,8 @@ def get_deployment_download( Deployment ID. type : Optional[DeploymentDownloadType] Deployment file to download. Can be: "source", "output". + token : Optional[str] + Presigned source-download token for accessing this deployment without a session (jobs-service). Returns ------- @@ -1006,6 +1017,8 @@ def get_deployment_download( if type is not None: api_params['type'] = self._normalize_value(type) + if token is not None: + api_params['token'] = self._normalize_value(token) response = self.client.call('get', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/health.py b/appwrite/services/health.py deleted file mode 100644 index bbf93903..00000000 --- a/appwrite/services/health.py +++ /dev/null @@ -1,882 +0,0 @@ -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.health_status import HealthStatus -from ..models.health_antivirus import HealthAntivirus -from ..models.health_status_list import HealthStatusList -from ..models.health_certificate import HealthCertificate -from ..models.health_queue import HealthQueue -from ..enums.health_queue_name import HealthQueueName -from ..models.health_time import HealthTime - -class Health(Service): - - def __init__(self, client) -> None: - super(Health, self).__init__(client) - - def get( - self - ) -> HealthStatus: - """ - Check the Appwrite HTTP server is up and responsive. - - Returns - ------- - HealthStatus - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health' - api_params = {} - - 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=HealthStatus) - - - def get_antivirus( - self - ) -> HealthAntivirus: - """ - Check the Appwrite Antivirus server is up and connection is successful. - - Returns - ------- - HealthAntivirus - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/anti-virus' - api_params = {} - - 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=HealthAntivirus) - - - def get_audits_db( - self - ) -> HealthStatusList: - """ - Check the database that backs the audit and activity store. When the connection is reachable the endpoint returns a passing status with its response time. - - - Returns - ------- - HealthStatusList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/audits-db' - api_params = {} - - 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=HealthStatusList) - - - def get_cache( - self - ) -> HealthStatusList: - """ - Check the Appwrite in-memory cache servers are up and connection is successful. - - Returns - ------- - HealthStatusList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/cache' - api_params = {} - - 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=HealthStatusList) - - - def get_certificate( - self, - domain: Optional[str] = None - ) -> HealthCertificate: - """ - Get the SSL certificate for a domain - - Parameters - ---------- - domain : Optional[str] - string - - Returns - ------- - HealthCertificate - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/certificate' - api_params = {} - - if domain is not None: - api_params['domain'] = self._normalize_value(domain) - - 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=HealthCertificate) - - - def get_console_pausing( - self, - threshold: Optional[float] = None, - inactivity_days: Optional[float] = None - ) -> HealthStatus: - """ - Get console pausing health status. Monitors projects approaching the pause threshold to detect potential issues with console access tracking. - - - Parameters - ---------- - threshold : Optional[float] - Percentage threshold of projects approaching pause. When hit (equal or higher), endpoint returns server error. Default value is 10. - inactivity_days : Optional[float] - Number of days of inactivity before a project is paused. Should match the plan's projectInactivityDays setting. Default value is 7. - - Returns - ------- - HealthStatus - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/console-pausing' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - if inactivity_days is not None: - api_params['inactivityDays'] = self._normalize_value(inactivity_days) - - 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=HealthStatus) - - - def get_db( - self - ) -> HealthStatusList: - """ - Check the Appwrite database servers are up and connection is successful. - - Returns - ------- - HealthStatusList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/db' - api_params = {} - - 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=HealthStatusList) - - - def get_pub_sub( - self - ) -> HealthStatusList: - """ - Check the Appwrite pub-sub servers are up and connection is successful. - - Returns - ------- - HealthStatusList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/pubsub' - api_params = {} - - 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=HealthStatusList) - - - def get_queue_audits( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of audit logs that are waiting to be processed in the Appwrite internal queue server. - - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/audits' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_builds( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of builds that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/builds' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_certificates( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of certificates that are waiting to be issued against [Letsencrypt](https://letsencrypt.org/) in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/certificates' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_databases( - self, - name: Optional[str] = None, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of database changes that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - name : Optional[str] - Queue name for which to check the queue size - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/databases' - api_params = {} - - if name is not None: - api_params['name'] = self._normalize_value(name) - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_deletes( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of background destructive changes that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/deletes' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_failed_jobs( - self, - name: HealthQueueName, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Returns the amount of failed jobs in a given queue. - - - Parameters - ---------- - name : HealthQueueName - The name of the queue - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/failed/{name}' - api_params = {} - if name is None: - raise AppwriteException('Missing required parameter: "name"') - - api_path = api_path.replace('{name}', str(self._normalize_value(name))) - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_functions( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of function executions that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/functions' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_logs( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of logs that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/logs' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_mails( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of mails that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/mails' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_messaging( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of messages that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/messaging' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_migrations( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of migrations that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/migrations' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_stats_resources( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/stats-resources' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_usage( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of metrics that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/stats-usage' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_queue_webhooks( - self, - threshold: Optional[float] = None - ) -> HealthQueue: - """ - Get the number of webhooks that are waiting to be processed in the Appwrite internal queue server. - - Parameters - ---------- - threshold : Optional[float] - Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000. - - Returns - ------- - HealthQueue - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/queue/webhooks' - api_params = {} - - if threshold is not None: - api_params['threshold'] = self._normalize_value(threshold) - - 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=HealthQueue) - - - def get_storage( - self - ) -> HealthStatus: - """ - Check the Appwrite storage device is up and connection is successful. - - Returns - ------- - HealthStatus - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/storage' - api_params = {} - - 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=HealthStatus) - - - def get_storage_local( - self - ) -> HealthStatus: - """ - Check the Appwrite local storage device is up and connection is successful. - - Returns - ------- - HealthStatus - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/storage/local' - api_params = {} - - 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=HealthStatus) - - - def get_time( - self - ) -> HealthTime: - """ - Check the Appwrite server time is synced with Google remote NTP server. We use this technology to smoothly handle leap seconds with no disruptive events. The [Network Time Protocol](https://en.wikipedia.org/wiki/Network_Time_Protocol) (NTP) is used by hundreds of millions of computers and devices to synchronize their clocks over the Internet. If your computer sets its own clock, it likely uses NTP. - - Returns - ------- - HealthTime - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/health/time' - api_params = {} - - 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=HealthTime) - diff --git a/appwrite/services/messaging.py b/appwrite/services/messaging.py index 065a9537..3abb1004 100644 --- a/appwrite/services/messaging.py +++ b/appwrite/services/messaging.py @@ -6,7 +6,6 @@ from ..models.message_list import MessageList from ..models.message import Message from ..enums.message_priority import MessagePriority -from ..models.log_list import LogList from ..models.target_list import TargetList from ..models.provider_list import ProviderList from ..models.provider import Provider @@ -707,55 +706,6 @@ def delete( return response - def list_message_logs( - self, - message_id: str, - queries: Optional[List[str]] = None, - total: Optional[bool] = None - ) -> LogList: - """ - Get the message activity logs listed by its unique ID. - - Parameters - ---------- - message_id : str - Message 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). Only supported methods are limit and offset - total : Optional[bool] - When set to false, the total count returned will be 0 and will not be calculated. - - Returns - ------- - LogList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/messaging/messages/{messageId}/logs' - api_params = {} - if message_id is None: - raise AppwriteException('Missing required parameter: "message_id"') - - api_path = api_path.replace('{messageId}', str(self._normalize_value(message_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', api_path, { - 'X-Appwrite-Project': self.client.get_config('project'), - 'accept': 'application/json', - }, api_params) - - return self._parse_response(response, model=LogList) - - def list_targets( self, message_id: str, @@ -2686,104 +2636,6 @@ def delete_provider( return response - def list_provider_logs( - self, - provider_id: str, - queries: Optional[List[str]] = None, - total: Optional[bool] = None - ) -> LogList: - """ - Get the provider activity logs listed by its unique ID. - - Parameters - ---------- - provider_id : str - Provider 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). Only supported methods are limit and offset - total : Optional[bool] - When set to false, the total count returned will be 0 and will not be calculated. - - Returns - ------- - LogList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/messaging/providers/{providerId}/logs' - 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))) - - 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=LogList) - - - def list_subscriber_logs( - self, - subscriber_id: str, - queries: Optional[List[str]] = None, - total: Optional[bool] = None - ) -> LogList: - """ - Get the subscriber activity logs listed by its unique ID. - - Parameters - ---------- - subscriber_id : str - Subscriber 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). Only supported methods are limit and offset - total : Optional[bool] - When set to false, the total count returned will be 0 and will not be calculated. - - Returns - ------- - LogList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/messaging/subscribers/{subscriberId}/logs' - api_params = {} - if subscriber_id is None: - raise AppwriteException('Missing required parameter: "subscriber_id"') - - api_path = api_path.replace('{subscriberId}', str(self._normalize_value(subscriber_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', api_path, { - 'X-Appwrite-Project': self.client.get_config('project'), - 'accept': 'application/json', - }, api_params) - - return self._parse_response(response, model=LogList) - - def list_topics( self, queries: Optional[List[str]] = None, @@ -3011,55 +2863,6 @@ def delete_topic( return response - def list_topic_logs( - self, - topic_id: str, - queries: Optional[List[str]] = None, - total: Optional[bool] = None - ) -> LogList: - """ - Get the topic activity logs listed by its unique ID. - - Parameters - ---------- - topic_id : str - Topic 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). Only supported methods are limit and offset - total : Optional[bool] - When set to false, the total count returned will be 0 and will not be calculated. - - Returns - ------- - LogList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/messaging/topics/{topicId}/logs' - api_params = {} - if topic_id is None: - raise AppwriteException('Missing required parameter: "topic_id"') - - api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_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', api_path, { - 'X-Appwrite-Project': self.client.get_config('project'), - 'accept': 'application/json', - }, api_params) - - return self._parse_response(response, model=LogList) - - def list_subscribers( self, topic_id: str, diff --git a/appwrite/services/oauth2.py b/appwrite/services/oauth2.py index 581d4e4e..272fbc73 100644 --- a/appwrite/services/oauth2.py +++ b/appwrite/services/oauth2.py @@ -7,6 +7,9 @@ from ..models.oauth2_authorize import Oauth2Authorize from ..models.oauth2_device_authorization import Oauth2DeviceAuthorization from ..models.oauth2_grant import Oauth2Grant +from ..models.oauth2_organization_list import Oauth2OrganizationList +from ..models.oauth2_par import Oauth2PAR +from ..models.oauth2_project_list import Oauth2ProjectList from ..models.oauth2_reject import Oauth2Reject from ..models.oauth2_token import Oauth2Token @@ -18,7 +21,8 @@ def __init__(self, client) -> None: def approve( self, grant_id: str, - authorization_details: Optional[str] = None + authorization_details: Optional[str] = None, + scope: Optional[str] = None ) -> Oauth2Approve: """ Approve an OAuth2 grant after the user gives consent. Returns the `redirectUrl` the end user should be sent to. The consent screen may optionally pass enriched `authorization_details` to record the concrete resources the user selected. You can pass Accept header of `application/json` to receive a JSON response instead of a redirect. @@ -29,6 +33,8 @@ def approve( Grant ID made during authorization, provided to consent screen in URL search params. authorization_details : Optional[str] Enriched `authorization_details` the user consented to, replacing what the client requested. Each entry must use a `type` the project accepts. Optional; omit to keep the originally requested details. + scope : Optional[str] + Space-separated scopes the user consented to. Must be a subset of the scopes originally requested; identity scopes such as `openid` are always retained. Optional; omit to keep the originally requested scopes. Returns ------- @@ -51,6 +57,8 @@ def approve( api_params['grant_id'] = self._normalize_value(grant_id) if authorization_details is not None: api_params['authorization_details'] = self._normalize_value(authorization_details) + if scope is not None: + api_params['scope'] = self._normalize_value(scope) response = self.client.call('post', api_path, { 'content-type': 'application/json', @@ -62,31 +70,34 @@ def approve( def authorize( self, - client_id: str, - redirect_uri: str, - response_type: str, - scope: str, + client_id: Optional[str] = None, + redirect_uri: Optional[str] = None, + response_type: Optional[str] = None, + scope: Optional[str] = None, state: Optional[str] = None, nonce: Optional[str] = None, code_challenge: Optional[str] = None, code_challenge_method: Optional[str] = None, prompt: Optional[str] = None, max_age: Optional[float] = None, - authorization_details: Optional[str] = None + authorization_details: Optional[str] = None, + resource: Optional[str] = None, + audience: Optional[str] = None, + request_uri: Optional[str] = None ) -> Oauth2Authorize: """ Begin the OAuth2 authorization flow. When called without a session, the user is redirected to the consent screen without grant ID. When called with a session, the redirect URL includes param for grant ID. You can pass Accept header of `application/json` to receive a JSON response instead of a redirect. Parameters ---------- - client_id : str + client_id : Optional[str] OAuth2 client ID. - redirect_uri : str + redirect_uri : Optional[str] Redirect URI where visitor will be redirected after authorization, whether successful or not. - response_type : str + response_type : Optional[str] OAuth2 / OIDC response type. One of `code` (Authorization Code Flow), `id_token` (Implicit Flow, OIDC login only), or `code id_token` (Hybrid Flow). - scope : str - Space-separated OAuth2 scopes. Can include project scopes, and built-in scopes: `openid`, `email`, `profile`. + scope : Optional[str] + Space-separated OAuth2 scopes. Can include project scopes, and built-in scopes: `openid`, `email`, `profile`, `phone`. state : Optional[str] OAuth2 state. You receive this back in the redirect URI. nonce : Optional[str] @@ -101,6 +112,12 @@ def authorize( OIDC max_age paraleter for customization of consent screen. Maximum allowable elapsed time in seconds since the user last authenticated. If exceeded, re-authentication is required. authorization_details : Optional[str] Rich authorization request. JSON array of objects, each with a `type` and project-defined fields + resource : Optional[str] + RFC 8707 resource indicator URI or URI list. Each value must be an absolute URI without a fragment. + audience : Optional[str] + Compatibility alias for a single OAuth2 resource indicator URI. + request_uri : Optional[str] + OAuth2 authorization request handle returned by the pushed authorization request endpoint. Returns ------- @@ -115,24 +132,16 @@ def authorize( api_path = '/oauth2/{project_id}/authorize' api_params = {} - if client_id is None: - raise AppwriteException('Missing required parameter: "client_id"') - - if redirect_uri is None: - raise AppwriteException('Missing required parameter: "redirect_uri"') - - if response_type is None: - raise AppwriteException('Missing required parameter: "response_type"') - - if scope is None: - raise AppwriteException('Missing required parameter: "scope"') - api_path = api_path.replace('{project_id}', str(self._normalize_value(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) - api_params['scope'] = self._normalize_value(scope) + if client_id is not None: + api_params['client_id'] = self._normalize_value(client_id) + if redirect_uri is not None: + api_params['redirect_uri'] = self._normalize_value(redirect_uri) + if response_type is not None: + api_params['response_type'] = self._normalize_value(response_type) + if scope is not None: + api_params['scope'] = self._normalize_value(scope) if state is not None: api_params['state'] = self._normalize_value(state) if nonce is not None: @@ -147,6 +156,12 @@ def authorize( api_params['max_age'] = self._normalize_value(max_age) if authorization_details is not None: api_params['authorization_details'] = self._normalize_value(authorization_details) + if resource is not None: + api_params['resource'] = self._normalize_value(resource) + if audience is not None: + api_params['audience'] = self._normalize_value(audience) + if request_uri is not None: + api_params['request_uri'] = self._normalize_value(request_uri) response = self.client.call('get', api_path, { 'accept': 'application/json', @@ -159,7 +174,9 @@ def create_device_authorization( self, client_id: Optional[str] = None, scope: Optional[str] = None, - authorization_details: Optional[str] = None + authorization_details: Optional[str] = None, + resource: Optional[str] = None, + audience: Optional[str] = None ) -> Oauth2DeviceAuthorization: """ Start the OAuth2 Device Authorization Grant. Returns the device code, user code, verification URL, expiration, and polling interval. @@ -172,6 +189,10 @@ def create_device_authorization( Space-separated OAuth2 scopes. Can include project scopes, and built-in scopes: `openid`, `email`, `profile`. authorization_details : Optional[str] Rich authorization request. JSON array of objects, each with a `type` and project-defined fields + resource : Optional[str] + RFC 8707 resource indicator URI or URI list. Each value must be an absolute URI without a fragment. + audience : Optional[str] + Compatibility alias for a single OAuth2 resource indicator URI. Returns ------- @@ -194,6 +215,10 @@ def create_device_authorization( api_params['scope'] = self._normalize_value(scope) if authorization_details is not None: api_params['authorization_details'] = self._normalize_value(authorization_details) + if resource is not None: + api_params['resource'] = self._normalize_value(resource) + if audience is not None: + api_params['audience'] = self._normalize_value(audience) response = self.client.call('post', api_path, { 'content-type': 'application/json', @@ -282,6 +307,203 @@ def get_grant( return self._parse_response(response, model=Oauth2Grant) + def list_organizations( + self, + limit: Optional[float] = None, + offset: Optional[float] = None, + search: Optional[str] = None + ) -> Oauth2OrganizationList: + """ + List the organizations the OAuth2 access token can access. Resolves the token's `organization` authorization details, expanding the `*` wildcard into the concrete set of organizations the user can see. + + Parameters + ---------- + limit : Optional[float] + Maximum number of organizations to return. Between 1 and 5000. + offset : Optional[float] + Number of organizations to skip before returning results. Used for pagination. + search : Optional[str] + Search term to filter your list results. Max length: 256 chars. + + Returns + ------- + Oauth2OrganizationList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/oauth2/{project_id}/organizations' + api_params = {} + api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + + if limit is not None: + api_params['limit'] = self._normalize_value(limit) + if offset is not None: + api_params['offset'] = self._normalize_value(offset) + if search is not None: + api_params['search'] = self._normalize_value(search) + + response = self.client.call('get', api_path, { + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=Oauth2OrganizationList) + + + def create_par( + self, + client_id: str, + redirect_uri: str, + response_type: str, + scope: Optional[str] = None, + state: Optional[str] = None, + nonce: Optional[str] = None, + code_challenge: Optional[str] = None, + code_challenge_method: Optional[str] = None, + prompt: Optional[str] = None, + max_age: Optional[float] = None, + authorization_details: Optional[str] = None, + resource: Optional[str] = None, + audience: Optional[str] = None + ) -> Oauth2PAR: + """ + Store an OAuth2 authorization request server-side and receive a short-lived request_uri handle for the authorize endpoint. + + Parameters + ---------- + client_id : str + OAuth2 client ID. + redirect_uri : str + Redirect URI where visitor will be redirected after authorization, whether successful or not. + response_type : str + OAuth2 / OIDC response type. + scope : Optional[str] + Space-separated OAuth2 scopes. Can include project scopes, and built-in scopes: `openid`, `email`, `profile`, `phone`. + state : Optional[str] + OAuth2 state. You receive this back in the redirect URI. + nonce : Optional[str] + OIDC nonce parameter to prevent replay attacks. Required when response_type includes `id_token`. + code_challenge : Optional[str] + PKCE code challenge. Required when OAuth2 app is public. + code_challenge_method : Optional[str] + PKCE code challenge method. Required when OAuth2 app is public. + prompt : Optional[str] + OIDC prompt parameter for customization of consent screen. Space-separated list of: none, login, consent, select_account. + max_age : Optional[float] + OIDC max_age parameter for customization of consent screen. + authorization_details : Optional[str] + Rich authorization request. JSON array of objects, each with a `type` and project-defined fields + resource : Optional[str] + RFC 8707 resource indicator URI or URI list. Each value must be an absolute URI without a fragment. + audience : Optional[str] + Compatibility alias for a single OAuth2 resource indicator URI. + + Returns + ------- + Oauth2PAR + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/oauth2/{project_id}/par' + api_params = {} + if client_id is None: + raise AppwriteException('Missing required parameter: "client_id"') + + if redirect_uri is None: + 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_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) + if scope is not None: + api_params['scope'] = self._normalize_value(scope) + if state is not None: + api_params['state'] = self._normalize_value(state) + if nonce is not None: + api_params['nonce'] = self._normalize_value(nonce) + if code_challenge is not None: + api_params['code_challenge'] = self._normalize_value(code_challenge) + if code_challenge_method is not None: + api_params['code_challenge_method'] = self._normalize_value(code_challenge_method) + if prompt is not None: + api_params['prompt'] = self._normalize_value(prompt) + api_params['max_age'] = self._normalize_value(max_age) + if authorization_details is not None: + api_params['authorization_details'] = self._normalize_value(authorization_details) + if resource is not None: + api_params['resource'] = self._normalize_value(resource) + if audience is not None: + api_params['audience'] = self._normalize_value(audience) + + response = self.client.call('post', api_path, { + 'content-type': 'application/json', + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=Oauth2PAR) + + + def list_projects( + self, + limit: Optional[float] = None, + offset: Optional[float] = None, + search: Optional[str] = None + ) -> Oauth2ProjectList: + """ + List the projects the OAuth2 access token can access. Resolves the token's `project` authorization details, expanding the `*` wildcard into the concrete set of projects the user can see. + + Parameters + ---------- + limit : Optional[float] + Maximum number of projects to return. Between 1 and 5000. + offset : Optional[float] + Number of projects to skip before returning results. Used for pagination. + search : Optional[str] + Search term to filter your list results. Max length: 256 chars. + + Returns + ------- + Oauth2ProjectList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/oauth2/{project_id}/projects' + api_params = {} + api_path = api_path.replace('{project_id}', str(self._normalize_value(self.client.get_config('project')))) + + if limit is not None: + api_params['limit'] = self._normalize_value(limit) + if offset is not None: + api_params['offset'] = self._normalize_value(offset) + if search is not None: + api_params['search'] = self._normalize_value(search) + + response = self.client.call('get', api_path, { + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=Oauth2ProjectList) + + def reject( self, grant_id: str @@ -386,7 +608,9 @@ def create_token( client_id: Optional[str] = None, client_secret: Optional[str] = None, code_verifier: Optional[str] = None, - redirect_uri: Optional[str] = None + redirect_uri: Optional[str] = None, + resource: Optional[str] = None, + audience: Optional[str] = None ) -> Oauth2Token: """ Exchange an OAuth2 authorization code, refresh token, or device code for access and refresh tokens. @@ -409,6 +633,10 @@ def create_token( PKCE code verifier. Required for public apps. redirect_uri : Optional[str] Redirect URI. Required for `authorization_code` grant type. + resource : Optional[str] + RFC 8707 resource indicator URI or URI list. Each value must be an absolute URI without a fragment. + audience : Optional[str] + Compatibility alias for a single OAuth2 resource indicator URI. Returns ------- @@ -443,6 +671,10 @@ def create_token( api_params['code_verifier'] = self._normalize_value(code_verifier) if redirect_uri is not None: api_params['redirect_uri'] = self._normalize_value(redirect_uri) + if resource is not None: + api_params['resource'] = self._normalize_value(resource) + if audience is not None: + api_params['audience'] = self._normalize_value(audience) response = self.client.call('post', api_path, { 'content-type': 'application/json', diff --git a/appwrite/services/organization.py b/appwrite/services/organization.py index 7c63003a..eaf0ca88 100644 --- a/appwrite/services/organization.py +++ b/appwrite/services/organization.py @@ -1,20 +1,132 @@ from ..service import Service from urllib.parse import quote -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Optional, Union, Type, TypeVar from ..exception import AppwriteException from appwrite.utils.deprecated import deprecated +from ..models.organization import Organization as OrganizationModel from ..models.key_list import KeyList from ..enums.organization_key_scopes import OrganizationKeyScopes from ..models.key import Key +from ..models.membership_list import MembershipList +from ..models.membership import Membership from ..models.project_list import ProjectList from ..enums.region import Region from ..models.project import Project +T = TypeVar('T') + class Organization(Service): def __init__(self, client) -> None: super(Organization, self).__init__(client) + def get( + self, + model_type: Type[T] = dict + ) -> OrganizationModel[T]: + """ + Get the current organization. + + Parameters + ---------- + + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + OrganizationModel[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization' + api_params = {} + + response = self.client.call('get', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, api_params) + + return OrganizationModel.with_data(response, model_type) + + + def update( + self, + name: str, + model_type: Type[T] = dict + ) -> OrganizationModel[T]: + """ + Update the current organization's name. + + Parameters + ---------- + name : str + New organization name. Max length: 128 chars. + + model_type : Type[T], optional + Pydantic model class for the user-defined data. Defaults to dict for backward compatibility. + + Returns + ------- + OrganizationModel[T] + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization' + api_params = {} + if name is None: + raise AppwriteException('Missing required parameter: "name"') + + + api_params['name'] = self._normalize_value(name) + + response = self.client.call('put', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + 'accept': 'application/json', + }, api_params) + + return OrganizationModel.with_data(response, model_type) + + + def delete( + self + ) -> Dict[str, Any]: + """ + Delete the current organization. All projects that belong to the organization are deleted as well. + + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization' + api_params = {} + + response = self.client.call('delete', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + }, api_params) + + return response + + def list_keys( self, queries: Optional[List[str]] = None, @@ -74,7 +186,7 @@ def create_key( name : str Key name. Max length: 128 chars. scopes : List[OrganizationKeyScopes] - Key scopes list. Maximum of 100 scopes are allowed. + 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. @@ -171,7 +283,7 @@ def update_key( name : str Key name. Max length: 128 chars. scopes : List[OrganizationKeyScopes] - Key scopes list. Maximum of 100 scopes are allowed. + 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. @@ -251,6 +363,243 @@ def delete_key( return response + def list_memberships( + self, + queries: Optional[List[str]] = None, + search: Optional[str] = None, + total: Optional[bool] = None + ) -> MembershipList: + """ + Get a list of all memberships from the current organization. + + 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 + 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 + ------- + MembershipList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/memberships' + 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=MembershipList) + + + def create_membership( + self, + roles: List[str], + email: Optional[str] = None, + user_id: Optional[str] = None, + phone: Optional[str] = None, + url: Optional[str] = None, + name: Optional[str] = None + ) -> Membership: + """ + Invite a new member to join the current organization. An email with a link to join the organization will be sent to the new member's email address. If member doesn't exist in the project it will be automatically created. + + Parameters + ---------- + roles : List[str] + Array of strings. Use this param to set the user roles in the organization. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of 100 roles are allowed, each 81 characters long. + email : Optional[str] + Email of the new organization member. + user_id : Optional[str] + ID of the user to be added to the organization. + phone : Optional[str] + Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212. + url : Optional[str] + URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. + name : Optional[str] + Name of the new organization member. Max length: 128 chars. + + Returns + ------- + Membership + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/memberships' + api_params = {} + if roles is None: + raise AppwriteException('Missing required parameter: "roles"') + + + if email is not None: + api_params['email'] = self._normalize_value(email) + if user_id is not None: + api_params['userId'] = self._normalize_value(user_id) + if phone is not None: + api_params['phone'] = self._normalize_value(phone) + api_params['roles'] = self._normalize_value(roles) + if url is not None: + api_params['url'] = self._normalize_value(url) + if name is not None: + api_params['name'] = self._normalize_value(name) + + 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=Membership) + + + def get_membership( + self, + membership_id: str + ) -> Membership: + """ + Get a membership from the current organization by its unique ID. + + Parameters + ---------- + membership_id : str + Membership ID. + + Returns + ------- + Membership + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/memberships/{membershipId}' + api_params = {} + if membership_id is None: + raise AppwriteException('Missing required parameter: "membership_id"') + + api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_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=Membership) + + + def update_membership( + self, + membership_id: str, + roles: List[str] + ) -> Membership: + """ + Modify the roles of a member in the current organization. + + Parameters + ---------- + membership_id : str + Membership ID. + roles : List[str] + An array of strings. Use this param to set the user's roles in the organization. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of 100 roles are allowed, each 81 characters long. + + Returns + ------- + Membership + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/memberships/{membershipId}' + api_params = {} + if membership_id is None: + 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_params['roles'] = self._normalize_value(roles) + + 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=Membership) + + + def delete_membership( + self, + membership_id: str + ) -> Dict[str, Any]: + """ + Remove a member from the current organization. The member is removed whether they accepted the invitation or not; a pending invitation is revoked. + + Parameters + ---------- + membership_id : str + Membership ID. + + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/memberships/{membershipId}' + api_params = {} + if membership_id is None: + raise AppwriteException('Missing required parameter: "membership_id"') + + api_path = api_path.replace('{membershipId}', str(self._normalize_value(membership_id))) + + + response = self.client.call('delete', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'content-type': 'application/json', + }, api_params) + + return response + + def list_projects( self, queries: Optional[List[str]] = None, @@ -263,7 +612,7 @@ def list_projects( 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: name, teamId, labels, search + 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: name, teamId, labels, search, accessedAt search : Optional[str] Search term to filter your list results. Max length: 256 chars. total : Optional[bool] diff --git a/appwrite/services/project.py b/appwrite/services/project.py index cccde7a9..c9d5c9d4 100644 --- a/appwrite/services/project.py +++ b/appwrite/services/project.py @@ -14,6 +14,7 @@ from ..models.o_auth2_provider_list import OAuth2ProviderList from ..models.o_auth2_amazon import OAuth2Amazon from ..models.o_auth2_apple import OAuth2Apple +from ..models.o_auth2_appwrite import OAuth2Appwrite from ..models.o_auth2_auth0 import OAuth2Auth0 from ..models.o_auth2_authentik import OAuth2Authentik from ..models.o_auth2_autodesk import OAuth2Autodesk @@ -37,6 +38,7 @@ from ..models.o_auth2_linkedin import OAuth2Linkedin from ..models.o_auth2_microsoft import OAuth2Microsoft from ..models.o_auth2_notion import OAuth2Notion +from ..enums.project_o_auth2_oidc_prompt import ProjectOAuth2OidcPrompt from ..models.o_auth2_oidc import OAuth2Oidc from ..models.o_auth2_okta import OAuth2Okta from ..models.o_auth2_paypal import OAuth2Paypal @@ -254,7 +256,7 @@ def create_key( name : str Key name. Max length: 128 chars. scopes : List[ProjectKeyScopes] - Key scopes list. Maximum of 100 scopes are allowed. + 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. @@ -308,7 +310,7 @@ def create_ephemeral_key( Parameters ---------- scopes : List[ProjectKeyScopes] - Key scopes list. Maximum of 100 scopes are allowed. + Key scopes list. Maximum of 200 scopes are allowed. duration : float Time in seconds before ephemeral key expires. Maximum duration is 3600 seconds. @@ -400,7 +402,7 @@ def update_key( name : str Key name. Max length: 128 chars. scopes : List[ProjectKeyScopes] - Key scopes list. Maximum of 100 scopes are allowed. + 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. @@ -790,7 +792,8 @@ def update_o_auth2_server( verification_url: Optional[str] = None, user_code_length: Optional[float] = None, user_code_format: Optional[str] = None, - device_code_duration: Optional[float] = None + device_code_duration: Optional[float] = None, + default_scopes: Optional[List[str]] = None ) -> ProjectModel: """ Update the OAuth2 server (OIDC provider) configuration. @@ -823,6 +826,8 @@ def update_o_auth2_server( Character set for device flow user codes: `numeric` (digits only — best for numeric keypads and TV remotes), `alphabetic` (letters only), or `alphanumeric` (letters and digits — highest entropy per character). Defaults to `alphanumeric`. device_code_duration : Optional[float] Lifetime in seconds of device flow device codes and user codes. Device codes are intentionally short-lived. Leave empty to use default 600. + default_scopes : Optional[List[str]] + List of OAuth2 scopes used when an authorization request omits the scope parameter. Every default scope must also be allowed by the OAuth2 server. Maximum of 100 scopes are allowed, each up to 128 characters long. Returns ------- @@ -861,6 +866,8 @@ def update_o_auth2_server( if user_code_format is not None: api_params['userCodeFormat'] = self._normalize_value(user_code_format) api_params['deviceCodeDuration'] = self._normalize_value(device_code_duration) + if default_scopes is not None: + api_params['defaultScopes'] = self._normalize_value(default_scopes) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -969,6 +976,51 @@ def update_o_auth2_apple( return self._parse_response(response, model=OAuth2Apple) + def update_o_auth2_appwrite( + self, + client_id: Optional[str] = None, + client_secret: Optional[str] = None, + enabled: Optional[bool] = None + ) -> OAuth2Appwrite: + """ + Update the project OAuth2 Appwrite configuration. + + Parameters + ---------- + client_id : Optional[str] + 'Client ID' of Appwrite OAuth2 app. For example: 6a42000000000000b5a0 + client_secret : Optional[str] + 'Client Secret' of Appwrite OAuth2 app. For example: b86afd000000000000000000000000000000000000000000000000000ced5f93 + enabled : Optional[bool] + OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. + + Returns + ------- + OAuth2Appwrite + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/project/oauth2/appwrite' + api_params = {} + + api_params['clientId'] = self._normalize_value(client_id) + api_params['clientSecret'] = self._normalize_value(client_secret) + 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=OAuth2Appwrite) + + def update_o_auth2_auth0( self, client_id: Optional[str] = None, @@ -1999,6 +2051,8 @@ def update_o_auth2_oidc( authorization_url: Optional[str] = None, token_url: Optional[str] = None, user_info_url: Optional[str] = None, + prompt: Optional[List[ProjectOAuth2OidcPrompt]] = None, + max_age: Optional[float] = None, enabled: Optional[bool] = None ) -> OAuth2Oidc: """ @@ -2018,6 +2072,10 @@ def update_o_auth2_oidc( OpenID Connect token endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/token user_info_url : Optional[str] OpenID Connect user info endpoint URL. Required when wellKnownURL is not provided. For example: https://myoauth.com/oauth2/userinfo + prompt : Optional[List[ProjectOAuth2OidcPrompt]] + Array of OpenID Connect prompt values controlling the authentication and consent screens. If "none" is included, it must be the only element. "none" means: don't display any authentication or consent screens. "login" means: prompt the user to re-authenticate. "consent" means: prompt the user for consent. "select_account" means: prompt the user to select an account. + max_age : Optional[float] + Maximum authentication age in seconds. When set, the user must have authenticated within this many seconds, otherwise they are prompted to re-authenticate. enabled : Optional[bool] OAuth2 sign-in method status. Set to true to enable new session creation. Setting to true will trigger end-to-end credentials validation, and will throw if the credentials are invalid. @@ -2041,6 +2099,8 @@ def update_o_auth2_oidc( api_params['authorizationURL'] = self._normalize_value(authorization_url) api_params['tokenURL'] = self._normalize_value(token_url) api_params['userInfoURL'] = self._normalize_value(user_info_url) + api_params['prompt'] = self._normalize_value(prompt) + api_params['maxAge'] = self._normalize_value(max_age) api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { diff --git a/appwrite/services/sites.py b/appwrite/services/sites.py index ce0c23f1..4daf161f 100644 --- a/appwrite/services/sites.py +++ b/appwrite/services/sites.py @@ -258,11 +258,17 @@ def list_frameworks( def list_specifications( - self + self, + type: Optional[str] = None ) -> SpecificationList: """ List allowed site specifications for this instance. + Parameters + ---------- + type : Optional[str] + Specification type to list. Can be one of: runtimes, builds. + Returns ------- SpecificationList @@ -277,6 +283,9 @@ def list_specifications( api_path = '/sites/specifications' api_params = {} + if type is not None: + api_params['type'] = self._normalize_value(type) + response = self.client.call('get', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), 'accept': 'application/json', @@ -460,8 +469,7 @@ def update( api_params['providerRootDirectory'] = self._normalize_value(provider_root_directory) api_params['providerBranches'] = self._normalize_value(provider_branches) api_params['providerPaths'] = self._normalize_value(provider_paths) - if build_specification is not None: - api_params['buildSpecification'] = self._normalize_value(build_specification) + api_params['buildSpecification'] = self._normalize_value(build_specification) if runtime_specification is not None: api_params['runtimeSpecification'] = self._normalize_value(runtime_specification) if deployment_retention is not None: diff --git a/appwrite/services/tables_db.py b/appwrite/services/tables_db.py index 7ea86989..9733bc96 100644 --- a/appwrite/services/tables_db.py +++ b/appwrite/services/tables_db.py @@ -96,7 +96,7 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, - dedicated_database_id: Optional[str] = None + specification: Optional[str] = None ) -> Database: """ Create a new Database. @@ -110,8 +110,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. - dedicated_database_id : Optional[str] - Optional dedicated database (compute) ID to attach this database to. Leave empty to create a database on the shared pool. + 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. Returns ------- @@ -137,8 +137,8 @@ def create( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) - if dedicated_database_id is not None: - api_params['dedicatedDatabaseId'] = self._normalize_value(dedicated_database_id) + if specification is not None: + api_params['specification'] = self._normalize_value(specification) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/usage.py b/appwrite/services/usage.py deleted file mode 100644 index de300c7f..00000000 --- a/appwrite/services/usage.py +++ /dev/null @@ -1,202 +0,0 @@ -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.usage_event_list import UsageEventList -from ..models.usage_gauge_list import UsageGaugeList - -class Usage(Service): - - def __init__(self, client) -> None: - super(Usage, self).__init__(client) - - def list_events( - self, - metrics: List[str], - resource: Optional[str] = None, - resource_id: Optional[str] = None, - interval: Optional[str] = None, - dimensions: Optional[List[str]] = None, - start_at: Optional[str] = None, - end_at: Optional[str] = None, - order_by: Optional[str] = None, - order_dir: Optional[str] = None, - limit: Optional[float] = None, - offset: Optional[float] = None - ) -> UsageEventList: - """ - Aggregate usage event metrics. `metrics[]` (1-10) is required; the response always contains one entry per requested metric, each with its own `points[]` time series. - - **Two response shapes**: - - Omit `interval` for a flat top-N table — one point per dimension combination, no time axis. Useful for "top 10 paths by bandwidth in the last 7 days". - - Pass `interval` (`1m`, `15m`, `30m`, `1h`, `1d`) for a time series — one point per (time bucket × dimension combination). - - `dimensions[]` breaks each point down by one or more attributes (service, path, status, country, …). Pass multiple metrics to render stacked charts in one round-trip. `resource` and `resourceId` filter the underlying events. `orderBy=value`+`orderDir=desc`+`limit=N` returns the top-N by aggregated value. When `startAt` is omitted, the default window adapts to `interval` (or 7d when interval is omitted). - - Parameters - ---------- - metrics : List[str] - One to ten metric names. Single-metric callers pass a one-element array. Example: `metrics[]=executions` or `metrics[]=executions&metrics[]=executions.compute` for stacked charts. - resource : Optional[str] - Resource type filter (singular form). Common values: function, site, database, bucket, file, webhook, team, user, project. - resource_id : Optional[str] - Resource id filter. - interval : Optional[str] - Time interval size. Omit (null) for a flat aggregate over the whole window. Allowed: 1m, 15m, 30m, 1h, 1d. - dimensions : Optional[List[str]] - Break-down dimensions (max 10). Allowed: path, method, status, service, country, region, hostname, osName, clientType, clientName, deviceName, teamId, resourceId. - start_at : Optional[str] - Range start in ISO 8601. Defaults adapt to interval (7d for the no-interval aggregate). - end_at : Optional[str] - Range end in ISO 8601. Defaults to the current time. - order_by : Optional[str] - Column to order by. Allowed: time, value. Default time when an interval is set; otherwise value. - order_dir : Optional[str] - Sort direction: asc or desc. Default desc — paired with the default limit, returns the most recent / highest-value groups first. - limit : Optional[float] - Maximum rows to return (1-5000). - offset : Optional[float] - Pagination offset (0-100000). - - Returns - ------- - UsageEventList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/usage/events' - api_params = {} - if metrics is None: - raise AppwriteException('Missing required parameter: "metrics"') - - - api_params['metrics'] = self._normalize_value(metrics) - if resource is not None: - api_params['resource'] = self._normalize_value(resource) - if resource_id is not None: - api_params['resourceId'] = self._normalize_value(resource_id) - if interval is not None: - api_params['interval'] = self._normalize_value(interval) - if dimensions is not None: - api_params['dimensions'] = self._normalize_value(dimensions) - if start_at is not None: - api_params['startAt'] = self._normalize_value(start_at) - if end_at is not None: - api_params['endAt'] = self._normalize_value(end_at) - if order_by is not None: - api_params['orderBy'] = self._normalize_value(order_by) - if order_dir is not None: - api_params['orderDir'] = self._normalize_value(order_dir) - if limit is not None: - api_params['limit'] = self._normalize_value(limit) - if offset is not None: - api_params['offset'] = self._normalize_value(offset) - - 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=UsageEventList) - - - def list_gauges( - self, - metrics: List[str], - resource_id: Optional[str] = None, - team_id: Optional[str] = None, - interval: Optional[str] = None, - dimensions: Optional[List[str]] = None, - start_at: Optional[str] = None, - end_at: Optional[str] = None, - order_by: Optional[str] = None, - order_dir: Optional[str] = None, - limit: Optional[float] = None, - offset: Optional[float] = None - ) -> UsageGaugeList: - """ - Aggregate usage gauge snapshots. Gauges are point-in-time values (storage totals, resource counts, …); each point carries the latest snapshot in its interval via `argMax(value, time)`. `metrics[]` (1-10) is required; the response always contains one entry per requested metric, each with its own `points[]` time series. - - **Two response shapes**: - - Omit `interval` for a flat top-N table — `argMax(value, time)` per dimension combination over the whole window, no time axis. Useful for "top 10 resources by current storage". - - Pass `interval` (`1m`, `15m`, `30m`, `1h`, `1d`) for a time series — one snapshot per (time bucket × dimension combination). - - `dimensions[]` breaks each point down further. Supported on gauges: `resourceId`, `teamId`, `service`, `resource`. `service` and `resource` enable per-service / per-resource-type panels (e.g. storage-by-service: group `files.storage`, `deployments.storage`, `builds.storage`, `databases.storage` by `service`). Pass multiple metrics to render stacked charts in one round-trip. `resourceId` and `teamId` parameters filter the underlying rows. `orderBy=value`+`orderDir=desc`+`limit=N` returns the top-N. When `startAt` is omitted, the default window adapts to interval (or 7d when interval is omitted). - - Parameters - ---------- - metrics : List[str] - One to ten metric names. Single-metric callers pass a one-element array. Example: `metrics[]=files.storage` or `metrics[]=files.storage&metrics[]=deployments.storage` for stacked charts. - resource_id : Optional[str] - Resource id filter. - team_id : Optional[str] - Team id filter. - interval : Optional[str] - Time interval size. Omit (null) for a flat aggregate over the whole window. Allowed: 1m, 15m, 30m, 1h, 1d. - dimensions : Optional[List[str]] - Break-down dimensions. Allowed: resourceId, teamId, service, resource. - start_at : Optional[str] - Range start in ISO 8601. Defaults to endAt - 7d. - end_at : Optional[str] - Range end in ISO 8601. Defaults to the current time. - order_by : Optional[str] - Column to order by. Allowed: time, value. Default time. - order_dir : Optional[str] - Sort direction: asc or desc. Default desc — paired with the default limit, this returns the most recent groups first. Pass asc for chronological charting. - limit : Optional[float] - Maximum rows to return (1-5000). - offset : Optional[float] - Pagination offset (0-100000). - - Returns - ------- - UsageGaugeList - API response as a typed Pydantic model - - Raises - ------ - AppwriteException - If API request fails - """ - - api_path = '/usage/gauges' - api_params = {} - if metrics is None: - raise AppwriteException('Missing required parameter: "metrics"') - - - api_params['metrics'] = self._normalize_value(metrics) - if resource_id is not None: - api_params['resourceId'] = self._normalize_value(resource_id) - if team_id is not None: - api_params['teamId'] = self._normalize_value(team_id) - if interval is not None: - api_params['interval'] = self._normalize_value(interval) - if dimensions is not None: - api_params['dimensions'] = self._normalize_value(dimensions) - if start_at is not None: - api_params['startAt'] = self._normalize_value(start_at) - if end_at is not None: - api_params['endAt'] = self._normalize_value(end_at) - if order_by is not None: - api_params['orderBy'] = self._normalize_value(order_by) - if order_dir is not None: - api_params['orderDir'] = self._normalize_value(order_dir) - if limit is not None: - api_params['limit'] = self._normalize_value(limit) - if offset is not None: - api_params['offset'] = self._normalize_value(offset) - - 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=UsageGaugeList) - diff --git a/appwrite/services/users.py b/appwrite/services/users.py index 00bd630f..c0e282e6 100644 --- a/appwrite/services/users.py +++ b/appwrite/services/users.py @@ -41,7 +41,7 @@ def list( 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: name, email, phone, status, passwordUpdate, registration, emailVerification, phoneVerification, labels, impersonator + 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: name, email, phone, status, passwordUpdate, registration, emailVerification, phoneVerification, labels, impersonator, accessedAt search : Optional[str] Search term to filter your list results. Max length: 256 chars. total : Optional[bool] diff --git a/docs/examples/health/get-audits-db.md b/docs/examples/apps/update-labels.md similarity index 63% rename from docs/examples/health/get-audits-db.md rename to docs/examples/apps/update-labels.md index 774edec1..0601df3f 100644 --- a/docs/examples/health/get-audits-db.md +++ b/docs/examples/apps/update-labels.md @@ -1,16 +1,19 @@ ```python from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatusList +from appwrite.services.apps import Apps +from appwrite.models import App 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 -health = Health(client) +apps = Apps(client) -result: HealthStatusList = health.get_audits_db() +result: App = apps.update_labels( + app_id = '', + labels = [] +) print(result.model_dump()) ``` diff --git a/docs/examples/backups/create-restoration.md b/docs/examples/backups/create-restoration.md index e2966caa..03f3f0a9 100644 --- a/docs/examples/backups/create-restoration.md +++ b/docs/examples/backups/create-restoration.md @@ -15,7 +15,8 @@ result: BackupRestoration = backups.create_restoration( archive_id = '', services = [BackupServices.DATABASES], new_resource_id = '', # optional - new_resource_name = '' # optional + new_resource_name = '', # optional + new_specification = 'serverless' # optional ) print(result.model_dump()) diff --git a/docs/examples/functions/get-deployment-download.md b/docs/examples/functions/get-deployment-download.md index 7f6b2f6b..49df861b 100644 --- a/docs/examples/functions/get-deployment-download.md +++ b/docs/examples/functions/get-deployment-download.md @@ -13,6 +13,7 @@ functions = Functions(client) result: bytes = functions.get_deployment_download( function_id = '', deployment_id = '', - type = DeploymentDownloadType.SOURCE # optional + type = DeploymentDownloadType.SOURCE, # optional + token = '' # optional ) ``` diff --git a/docs/examples/functions/list-specifications.md b/docs/examples/functions/list-specifications.md index 5b76860d..7cf529a9 100644 --- a/docs/examples/functions/list-specifications.md +++ b/docs/examples/functions/list-specifications.md @@ -10,7 +10,9 @@ client.set_key('') # Your secret API key functions = Functions(client) -result: SpecificationList = functions.list_specifications() +result: SpecificationList = functions.list_specifications( + type = 'runtimes' # optional +) print(result.model_dump()) ``` diff --git a/docs/examples/health/get-antivirus.md b/docs/examples/health/get-antivirus.md deleted file mode 100644 index 1ab51503..00000000 --- a/docs/examples/health/get-antivirus.md +++ /dev/null @@ -1,16 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthAntivirus - -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 - -health = Health(client) - -result: HealthAntivirus = health.get_antivirus() - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-certificate.md b/docs/examples/health/get-certificate.md deleted file mode 100644 index 53dde7fb..00000000 --- a/docs/examples/health/get-certificate.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthCertificate - -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 - -health = Health(client) - -result: HealthCertificate = health.get_certificate( - domain = '' # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-db.md b/docs/examples/health/get-db.md deleted file mode 100644 index 47b6dd98..00000000 --- a/docs/examples/health/get-db.md +++ /dev/null @@ -1,16 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatusList - -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 - -health = Health(client) - -result: HealthStatusList = health.get_db() - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-failed-jobs.md b/docs/examples/health/get-failed-jobs.md deleted file mode 100644 index da08c619..00000000 --- a/docs/examples/health/get-failed-jobs.md +++ /dev/null @@ -1,20 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue -from appwrite.enums import HealthQueueName - -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 - -health = Health(client) - -result: HealthQueue = health.get_failed_jobs( - name = HealthQueueName.V1_DATABASE, - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-pub-sub.md b/docs/examples/health/get-pub-sub.md deleted file mode 100644 index 588a2bac..00000000 --- a/docs/examples/health/get-pub-sub.md +++ /dev/null @@ -1,16 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatusList - -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 - -health = Health(client) - -result: HealthStatusList = health.get_pub_sub() - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-certificates.md b/docs/examples/health/get-queue-certificates.md deleted file mode 100644 index 6666463f..00000000 --- a/docs/examples/health/get-queue-certificates.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_certificates( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-databases.md b/docs/examples/health/get-queue-databases.md deleted file mode 100644 index 44e165ed..00000000 --- a/docs/examples/health/get-queue-databases.md +++ /dev/null @@ -1,19 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_databases( - name = '', # optional - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-deletes.md b/docs/examples/health/get-queue-deletes.md deleted file mode 100644 index fece6301..00000000 --- a/docs/examples/health/get-queue-deletes.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_deletes( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-functions.md b/docs/examples/health/get-queue-functions.md deleted file mode 100644 index 03d9afe8..00000000 --- a/docs/examples/health/get-queue-functions.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_functions( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-logs.md b/docs/examples/health/get-queue-logs.md deleted file mode 100644 index 5191fa5a..00000000 --- a/docs/examples/health/get-queue-logs.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_logs( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-mails.md b/docs/examples/health/get-queue-mails.md deleted file mode 100644 index aed05e1d..00000000 --- a/docs/examples/health/get-queue-mails.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_mails( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-messaging.md b/docs/examples/health/get-queue-messaging.md deleted file mode 100644 index 5da876a6..00000000 --- a/docs/examples/health/get-queue-messaging.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_messaging( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-migrations.md b/docs/examples/health/get-queue-migrations.md deleted file mode 100644 index 749675d7..00000000 --- a/docs/examples/health/get-queue-migrations.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_migrations( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-stats-resources.md b/docs/examples/health/get-queue-stats-resources.md deleted file mode 100644 index 60c0dd57..00000000 --- a/docs/examples/health/get-queue-stats-resources.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_stats_resources( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-usage.md b/docs/examples/health/get-queue-usage.md deleted file mode 100644 index 10fa4a0f..00000000 --- a/docs/examples/health/get-queue-usage.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_usage( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-queue-webhooks.md b/docs/examples/health/get-queue-webhooks.md deleted file mode 100644 index 24423761..00000000 --- a/docs/examples/health/get-queue-webhooks.md +++ /dev/null @@ -1,18 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue - -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 - -health = Health(client) - -result: HealthQueue = health.get_queue_webhooks( - threshold = None # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-storage-local.md b/docs/examples/health/get-storage-local.md deleted file mode 100644 index 57b8ec40..00000000 --- a/docs/examples/health/get-storage-local.md +++ /dev/null @@ -1,16 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatus - -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 - -health = Health(client) - -result: HealthStatus = health.get_storage_local() - -print(result.model_dump()) -``` diff --git a/docs/examples/health/get-storage.md b/docs/examples/health/get-storage.md deleted file mode 100644 index a17ee4e3..00000000 --- a/docs/examples/health/get-storage.md +++ /dev/null @@ -1,16 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatus - -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 - -health = Health(client) - -result: HealthStatus = health.get_storage() - -print(result.model_dump()) -``` diff --git a/docs/examples/messaging/list-provider-logs.md b/docs/examples/messaging/list-provider-logs.md deleted file mode 100644 index ad271428..00000000 --- a/docs/examples/messaging/list-provider-logs.md +++ /dev/null @@ -1,20 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.messaging import Messaging -from appwrite.models import LogList - -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 - -messaging = Messaging(client) - -result: LogList = messaging.list_provider_logs( - provider_id = '', - queries = [], # optional - total = False # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/messaging/list-subscriber-logs.md b/docs/examples/messaging/list-subscriber-logs.md deleted file mode 100644 index b02c2b1b..00000000 --- a/docs/examples/messaging/list-subscriber-logs.md +++ /dev/null @@ -1,20 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.messaging import Messaging -from appwrite.models import LogList - -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 - -messaging = Messaging(client) - -result: LogList = messaging.list_subscriber_logs( - subscriber_id = '', - queries = [], # optional - total = False # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/messaging/list-topic-logs.md b/docs/examples/messaging/list-topic-logs.md deleted file mode 100644 index 3ed2d7e3..00000000 --- a/docs/examples/messaging/list-topic-logs.md +++ /dev/null @@ -1,20 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.messaging import Messaging -from appwrite.models import LogList - -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 - -messaging = Messaging(client) - -result: LogList = messaging.list_topic_logs( - topic_id = '', - queries = [], # optional - total = False # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/oauth2/approve.md b/docs/examples/oauth2/approve.md index b3313599..12bc9c0d 100644 --- a/docs/examples/oauth2/approve.md +++ b/docs/examples/oauth2/approve.md @@ -12,7 +12,8 @@ oauth2 = Oauth2(client) result: Oauth2Approve = oauth2.approve( grant_id = '', - authorization_details = '' # optional + authorization_details = '', # optional + scope = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/oauth2/authorize.md b/docs/examples/oauth2/authorize.md index 3ea83a66..9d7bb486 100644 --- a/docs/examples/oauth2/authorize.md +++ b/docs/examples/oauth2/authorize.md @@ -11,17 +11,20 @@ client.set_project('') # Your project ID oauth2 = Oauth2(client) result: Oauth2Authorize = oauth2.authorize( - client_id = '', - redirect_uri = 'https://example.com', - response_type = 'code', - scope = '', + client_id = '', # optional + redirect_uri = 'https://example.com', # optional + response_type = '', # optional + scope = '', # optional state = '', # optional nonce = '', # optional code_challenge = '', # optional code_challenge_method = 's256', # optional prompt = '', # optional max_age = 0, # optional - authorization_details = '' # optional + authorization_details = '', # optional + resource = '', # optional + audience = '', # optional + request_uri = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/oauth2/create-device-authorization.md b/docs/examples/oauth2/create-device-authorization.md index 4ba20f77..0fa3852d 100644 --- a/docs/examples/oauth2/create-device-authorization.md +++ b/docs/examples/oauth2/create-device-authorization.md @@ -13,7 +13,9 @@ oauth2 = Oauth2(client) result: Oauth2DeviceAuthorization = oauth2.create_device_authorization( client_id = '', # optional scope = '', # optional - authorization_details = '' # optional + authorization_details = '', # optional + resource = '', # optional + audience = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/oauth2/create-par.md b/docs/examples/oauth2/create-par.md new file mode 100644 index 00000000..2018794c --- /dev/null +++ b/docs/examples/oauth2/create-par.md @@ -0,0 +1,30 @@ +```python +from appwrite.client import Client +from appwrite.services.oauth2 import Oauth2 +from appwrite.models import Oauth2PAR + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_session('') # The user session to authenticate with +client.set_project('') # Your project ID + +oauth2 = Oauth2(client) + +result: Oauth2PAR = oauth2.create_par( + client_id = '', + redirect_uri = 'https://example.com', + response_type = 'code', + scope = '', # optional + state = '', # optional + nonce = '', # optional + code_challenge = '', # optional + code_challenge_method = 's256', # optional + prompt = '', # optional + max_age = 0, # optional + authorization_details = '', # optional + resource = '', # optional + audience = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/oauth2/create-token.md b/docs/examples/oauth2/create-token.md index ac698593..1d52bbc9 100644 --- a/docs/examples/oauth2/create-token.md +++ b/docs/examples/oauth2/create-token.md @@ -18,7 +18,9 @@ result: Oauth2Token = oauth2.create_token( client_id = '', # optional client_secret = '', # optional code_verifier = '', # optional - redirect_uri = 'https://example.com' # optional + redirect_uri = 'https://example.com', # optional + resource = '', # optional + audience = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/oauth2/list-organizations.md b/docs/examples/oauth2/list-organizations.md new file mode 100644 index 00000000..42ece3a8 --- /dev/null +++ b/docs/examples/oauth2/list-organizations.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.oauth2 import Oauth2 +from appwrite.models import Oauth2OrganizationList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_session('') # The user session to authenticate with +client.set_project('') # Your project ID + +oauth2 = Oauth2(client) + +result: Oauth2OrganizationList = oauth2.list_organizations( + limit = 1, # optional + offset = 0, # optional + search = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/oauth2/list-projects.md b/docs/examples/oauth2/list-projects.md new file mode 100644 index 00000000..97a58b7b --- /dev/null +++ b/docs/examples/oauth2/list-projects.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.oauth2 import Oauth2 +from appwrite.models import Oauth2ProjectList + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_session('') # The user session to authenticate with +client.set_project('') # Your project ID + +oauth2 = Oauth2(client) + +result: Oauth2ProjectList = oauth2.list_projects( + limit = 1, # optional + offset = 0, # optional + search = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/organization/create-membership.md b/docs/examples/organization/create-membership.md new file mode 100644 index 00000000..0801bdd7 --- /dev/null +++ b/docs/examples/organization/create-membership.md @@ -0,0 +1,23 @@ +```python +from appwrite.client import Client +from appwrite.services.organization import Organization +from appwrite.models import Membership + +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 + +organization = Organization(client) + +result: Membership = organization.create_membership( + roles = [], + email = 'email@example.com', # optional + user_id = '', # optional + phone = '+12065550100', # optional + url = 'https://example.com', # optional + name = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/health/get.md b/docs/examples/organization/delete-membership.md similarity index 60% rename from docs/examples/health/get.md rename to docs/examples/organization/delete-membership.md index 5a3b3d06..dee81d19 100644 --- a/docs/examples/health/get.md +++ b/docs/examples/organization/delete-membership.md @@ -1,16 +1,15 @@ ```python from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatus +from appwrite.services.organization import Organization 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 -health = Health(client) +organization = Organization(client) -result: HealthStatus = health.get() - -print(result.model_dump()) +result = organization.delete_membership( + membership_id = '' +) ``` diff --git a/docs/examples/health/get-time.md b/docs/examples/organization/delete.md similarity index 60% rename from docs/examples/health/get-time.md rename to docs/examples/organization/delete.md index 5ffae25e..94b7c2a7 100644 --- a/docs/examples/health/get-time.md +++ b/docs/examples/organization/delete.md @@ -1,16 +1,13 @@ ```python from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthTime +from appwrite.services.organization import Organization 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 -health = Health(client) +organization = Organization(client) -result: HealthTime = health.get_time() - -print(result.model_dump()) +result = organization.delete() ``` diff --git a/docs/examples/health/get-queue-audits.md b/docs/examples/organization/get-membership.md similarity index 57% rename from docs/examples/health/get-queue-audits.md rename to docs/examples/organization/get-membership.md index a892ca25..b3fcd9de 100644 --- a/docs/examples/health/get-queue-audits.md +++ b/docs/examples/organization/get-membership.md @@ -1,17 +1,17 @@ ```python from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue +from appwrite.services.organization import Organization +from appwrite.models import Membership 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 -health = Health(client) +organization = Organization(client) -result: HealthQueue = health.get_queue_audits( - threshold = None # optional +result: Membership = organization.get_membership( + membership_id = '' ) print(result.model_dump()) diff --git a/docs/examples/health/get-cache.md b/docs/examples/organization/get.md similarity index 59% rename from docs/examples/health/get-cache.md rename to docs/examples/organization/get.md index 39050878..b99b6a7f 100644 --- a/docs/examples/health/get-cache.md +++ b/docs/examples/organization/get.md @@ -1,16 +1,16 @@ ```python from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatusList +from appwrite.services.organization import Organization +from appwrite.models import Organization as OrganizationModel 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 -health = Health(client) +organization = Organization(client) -result: HealthStatusList = health.get_cache() +result: OrganizationModel = organization.get() print(result.model_dump()) ``` diff --git a/docs/examples/messaging/list-message-logs.md b/docs/examples/organization/list-memberships.md similarity index 60% rename from docs/examples/messaging/list-message-logs.md rename to docs/examples/organization/list-memberships.md index 27d6b9b6..e49e1793 100644 --- a/docs/examples/messaging/list-message-logs.md +++ b/docs/examples/organization/list-memberships.md @@ -1,18 +1,18 @@ ```python from appwrite.client import Client -from appwrite.services.messaging import Messaging -from appwrite.models import LogList +from appwrite.services.organization import Organization +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_key('') # Your secret API key -messaging = Messaging(client) +organization = Organization(client) -result: LogList = messaging.list_message_logs( - message_id = '', +result: MembershipList = organization.list_memberships( queries = [], # optional + search = '', # optional total = False # optional ) diff --git a/docs/examples/health/get-console-pausing.md b/docs/examples/organization/update-membership.md similarity index 55% rename from docs/examples/health/get-console-pausing.md rename to docs/examples/organization/update-membership.md index 633c175a..a4f9c5a0 100644 --- a/docs/examples/health/get-console-pausing.md +++ b/docs/examples/organization/update-membership.md @@ -1,18 +1,18 @@ ```python from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthStatus +from appwrite.services.organization import Organization +from appwrite.models import Membership 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 -health = Health(client) +organization = Organization(client) -result: HealthStatus = health.get_console_pausing( - threshold = None, # optional - inactivity_days = None # optional +result: Membership = organization.update_membership( + membership_id = '', + roles = [] ) print(result.model_dump()) diff --git a/docs/examples/health/get-queue-builds.md b/docs/examples/organization/update.md similarity index 56% rename from docs/examples/health/get-queue-builds.md rename to docs/examples/organization/update.md index 0ae87b2c..6d456c57 100644 --- a/docs/examples/health/get-queue-builds.md +++ b/docs/examples/organization/update.md @@ -1,17 +1,17 @@ ```python from appwrite.client import Client -from appwrite.services.health import Health -from appwrite.models import HealthQueue +from appwrite.services.organization import Organization +from appwrite.models import Organization as OrganizationModel 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 -health = Health(client) +organization = Organization(client) -result: HealthQueue = health.get_queue_builds( - threshold = None # optional +result: OrganizationModel = organization.update( + name = '' ) print(result.model_dump()) diff --git a/docs/examples/project/update-o-auth-2-appwrite.md b/docs/examples/project/update-o-auth-2-appwrite.md new file mode 100644 index 00000000..7b765921 --- /dev/null +++ b/docs/examples/project/update-o-auth-2-appwrite.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.project import Project +from appwrite.models import OAuth2Appwrite + +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: OAuth2Appwrite = project.update_o_auth2_appwrite( + client_id = '', # optional + client_secret = '', # optional + enabled = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/project/update-o-auth-2-oidc.md b/docs/examples/project/update-o-auth-2-oidc.md index 3913262d..e2df2e3c 100644 --- a/docs/examples/project/update-o-auth-2-oidc.md +++ b/docs/examples/project/update-o-auth-2-oidc.md @@ -2,6 +2,7 @@ from appwrite.client import Client from appwrite.services.project import Project from appwrite.models import OAuth2Oidc +from appwrite.enums import ProjectOAuth2OidcPrompt client = Client() client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint @@ -17,6 +18,8 @@ result: OAuth2Oidc = project.update_o_auth2_oidc( authorization_url = 'https://example.com', # optional token_url = 'https://example.com', # optional user_info_url = 'https://example.com', # optional + prompt = [ProjectOAuth2OidcPrompt.NONE], # optional + max_age = 0, # optional enabled = False # optional ) diff --git a/docs/examples/project/update-o-auth-2-server.md b/docs/examples/project/update-o-auth-2-server.md index 6c5b2578..6be24c54 100644 --- a/docs/examples/project/update-o-auth-2-server.md +++ b/docs/examples/project/update-o-auth-2-server.md @@ -23,7 +23,8 @@ result: ProjectModel = project.update_o_auth2_server( verification_url = 'https://example.com', # optional user_code_length = 6, # optional user_code_format = 'numeric', # optional - device_code_duration = 60 # optional + device_code_duration = 60, # optional + default_scopes = [] # optional ) print(result.model_dump()) diff --git a/docs/examples/sites/list-specifications.md b/docs/examples/sites/list-specifications.md index c1229a30..2f04ddf4 100644 --- a/docs/examples/sites/list-specifications.md +++ b/docs/examples/sites/list-specifications.md @@ -10,7 +10,9 @@ client.set_key('') # Your secret API key sites = Sites(client) -result: SpecificationList = sites.list_specifications() +result: SpecificationList = sites.list_specifications( + type = 'runtimes' # optional +) print(result.model_dump()) ``` diff --git a/docs/examples/tablesdb/create.md b/docs/examples/tablesdb/create.md index c50f28e7..a816905f 100644 --- a/docs/examples/tablesdb/create.md +++ b/docs/examples/tablesdb/create.md @@ -14,7 +14,7 @@ result: Database = tables_db.create( database_id = '', name = '', enabled = False, # optional - dedicated_database_id = '' # optional + specification = 'serverless' # optional ) print(result.model_dump()) diff --git a/docs/examples/usage/list-events.md b/docs/examples/usage/list-events.md deleted file mode 100644 index 83f8d725..00000000 --- a/docs/examples/usage/list-events.md +++ /dev/null @@ -1,28 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.usage import Usage -from appwrite.models import UsageEventList - -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 - -usage = Usage(client) - -result: UsageEventList = usage.list_events( - metrics = [], - resource = '', # optional - resource_id = '', # optional - interval = '1m', # optional - dimensions = [], # optional - start_at = '2020-10-15T06:38:00.000+00:00', # optional - end_at = '2020-10-15T06:38:00.000+00:00', # optional - order_by = 'time', # optional - order_dir = 'asc', # optional - limit = 1, # optional - offset = 0 # optional -) - -print(result.model_dump()) -``` diff --git a/docs/examples/usage/list-gauges.md b/docs/examples/usage/list-gauges.md deleted file mode 100644 index fb221f9a..00000000 --- a/docs/examples/usage/list-gauges.md +++ /dev/null @@ -1,28 +0,0 @@ -```python -from appwrite.client import Client -from appwrite.services.usage import Usage -from appwrite.models import UsageGaugeList - -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 - -usage = Usage(client) - -result: UsageGaugeList = usage.list_gauges( - metrics = [], - resource_id = '', # optional - team_id = '', # optional - interval = '1m', # optional - dimensions = [], # optional - start_at = '2020-10-15T06:38:00.000+00:00', # optional - end_at = '2020-10-15T06:38:00.000+00:00', # optional - order_by = 'time', # optional - order_dir = 'asc', # optional - limit = 1, # optional - offset = 0 # optional -) - -print(result.model_dump()) -``` diff --git a/pyproject.toml b/pyproject.toml index 64acd0d7..10559426 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "appwrite" -version = "21.0.0" +version = "22.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" diff --git a/requirements.txt b/requirements.txt index 43f14202..b3386211 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ requests>=2.31,<3 -requests_mock==1.11.0 +requests_mock==1.12.1 pydantic>=2,<3 diff --git a/setup.py b/setup.py index bedddde5..9557066e 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name = 'appwrite', packages = setuptools.find_packages(), - version = '21.0.0', + version = '22.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/21.0.0.tar.gz', + download_url='https://github.com/appwrite/sdk-for-python/archive/22.0.0.tar.gz', install_requires=[ 'requests', 'pydantic>=2,<3', diff --git a/test/services/test_activities.py b/test/services/test_activities.py index 41d9d8b1..9fcc5e4d 100644 --- a/test/services/test_activities.py +++ b/test/services/test_activities.py @@ -47,9 +47,7 @@ def test_get_event(self, m): "time": "2020-10-15T06:38:00.000+00:00", "projectId": "610fc2f985ee0", "teamId": "610fc2f985ee0", - "hostname": "appwrite.io", - "countryCode": "US", - "countryName": "United States" + "hostname": "appwrite.io" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) diff --git a/test/services/test_apps.py b/test/services/test_apps.py index a8d5984d..4520a42b 100644 --- a/test/services/test_apps.py +++ b/test/services/test_apps.py @@ -42,6 +42,7 @@ def test_create(self, m): "contacts": [], "tagline": "Automate your workspace.", "tags": [], + "labels": [], "images": [], "supportUrl": "https:\/\/example.com\/support", "dataDeletionUrl": "https:\/\/example.com\/data-deletion", @@ -80,6 +81,7 @@ def test_get(self, m): "contacts": [], "tagline": "Automate your workspace.", "tags": [], + "labels": [], "images": [], "supportUrl": "https:\/\/example.com\/support", "dataDeletionUrl": "https:\/\/example.com\/data-deletion", @@ -116,6 +118,7 @@ def test_update(self, m): "contacts": [], "tagline": "Automate your workspace.", "tags": [], + "labels": [], "images": [], "supportUrl": "https:\/\/example.com\/support", "dataDeletionUrl": "https:\/\/example.com\/data-deletion", @@ -150,6 +153,44 @@ def test_delete(self, m): self.assertEqual(response, data) + @requests_mock.Mocker() + def test_update_labels(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "name": "My Application", + "description": "Connect your workspace to My Application.", + "clientUri": "https:\/\/example.com", + "logoUri": "https:\/\/example.com\/logo.png", + "privacyPolicyUrl": "https:\/\/example.com\/privacy", + "termsUrl": "https:\/\/example.com\/terms", + "contacts": [], + "tagline": "Automate your workspace.", + "tags": [], + "labels": [], + "images": [], + "supportUrl": "https:\/\/example.com\/support", + "dataDeletionUrl": "https:\/\/example.com\/data-deletion", + "redirectUris": [], + "postLogoutRedirectUris": [], + "enabled": True, + "type": "confidential", + "deviceFlow": True, + "teamId": "5e5ea5c16897e", + "userId": "5e5ea5c16897e", + "secrets": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.update_labels( + '', + [], + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_list_secrets(self, m): data = { @@ -236,6 +277,7 @@ def test_update_team(self, m): "contacts": [], "tagline": "Automate your workspace.", "tags": [], + "labels": [], "images": [], "supportUrl": "https:\/\/example.com\/support", "dataDeletionUrl": "https:\/\/example.com\/data-deletion", diff --git a/test/services/test_backups.py b/test/services/test_backups.py index 83c59835..dfa1f5a2 100644 --- a/test/services/test_backups.py +++ b/test/services/test_backups.py @@ -196,7 +196,7 @@ def test_create_restoration(self, m): "migrationId": "did8jx6ws45jana098ab7", "services": [], "resources": [], - "options": "{databases.database[{oldId, newId, newName}]}" + "options": "{databases.database[{oldId, newId, newName, newSpecification}]}" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -235,7 +235,7 @@ def test_get_restoration(self, m): "migrationId": "did8jx6ws45jana098ab7", "services": [], "resources": [], - "options": "{databases.database[{oldId, newId, newName}]}" + "options": "{databases.database[{oldId, newId, newName, newSpecification}]}" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) diff --git a/test/services/test_health.py b/test/services/test_health.py deleted file mode 100644 index 930aeb62..00000000 --- a/test/services/test_health.py +++ /dev/null @@ -1,361 +0,0 @@ -import json -import requests_mock -import unittest - -from appwrite.client import Client -from appwrite.input_file import InputFile -from appwrite.models import * -from appwrite.services.health import Health - -class HealthServiceTest(unittest.TestCase): - - def setUp(self): - self.client = Client() - self.health = Health(self.client) - - @requests_mock.Mocker() - def test_get(self, m): - data = { - "name": "database", - "ping": 128.0, - "status": "pass" -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_antivirus(self, m): - data = { - "version": "1.0.0", - "status": "online" -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_antivirus( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_audits_db(self, m): - data = { - "total": 5.0, - "statuses": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_audits_db( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_cache(self, m): - data = { - "total": 5.0, - "statuses": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_cache( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_certificate(self, m): - data = { - "name": "\/CN=www.google.com", - "subjectSN": "", - "issuerOrganisation": "", - "validFrom": "1704200998", - "validTo": "1711458597", - "signatureTypeSN": "RSA-SHA256" -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_certificate( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_console_pausing(self, m): - data = { - "name": "database", - "ping": 128.0, - "status": "pass" -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_console_pausing( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_db(self, m): - data = { - "total": 5.0, - "statuses": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_db( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_pub_sub(self, m): - data = { - "total": 5.0, - "statuses": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_pub_sub( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_audits(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_audits( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_builds(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_builds( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_certificates(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_certificates( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_databases(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_databases( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_deletes(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_deletes( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_failed_jobs(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_failed_jobs( - 'v1-database', - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_functions(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_functions( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_logs(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_logs( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_mails(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_mails( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_messaging(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_messaging( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_migrations(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_migrations( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_stats_resources(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_stats_resources( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_usage(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_usage( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_queue_webhooks(self, m): - data = { - "size": 8.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_queue_webhooks( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_storage(self, m): - data = { - "name": "database", - "ping": 128.0, - "status": "pass" -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_storage( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_storage_local(self, m): - data = { - "name": "database", - "ping": 128.0, - "status": "pass" -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_storage_local( - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_get_time(self, m): - data = { - "remoteTime": 1639490751.0, - "localTime": 1639490844.0, - "diff": 93.0 -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.health.get_time( - ) - - self.assertEqual(response.to_dict(), data) - diff --git a/test/services/test_messaging.py b/test/services/test_messaging.py index 1424b6a1..ef6d618d 100644 --- a/test/services/test_messaging.py +++ b/test/services/test_messaging.py @@ -250,21 +250,6 @@ def test_delete(self, m): self.assertEqual(response, data) - @requests_mock.Mocker() - def test_list_message_logs(self, m): - data = { - "total": 5.0, - "logs": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.messaging.list_message_logs( - '', - ) - - self.assertEqual(response.to_dict(), data) - @requests_mock.Mocker() def test_list_targets(self, m): data = { @@ -974,36 +959,6 @@ def test_delete_provider(self, m): self.assertEqual(response, data) - @requests_mock.Mocker() - def test_list_provider_logs(self, m): - data = { - "total": 5.0, - "logs": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.messaging.list_provider_logs( - '', - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_list_subscriber_logs(self, m): - data = { - "total": 5.0, - "logs": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.messaging.list_subscriber_logs( - '', - ) - - self.assertEqual(response.to_dict(), data) - @requests_mock.Mocker() def test_list_topics(self, m): data = { @@ -1094,21 +1049,6 @@ def test_delete_topic(self, m): self.assertEqual(response, data) - @requests_mock.Mocker() - def test_list_topic_logs(self, m): - data = { - "total": 5.0, - "logs": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.messaging.list_topic_logs( - '', - ) - - self.assertEqual(response.to_dict(), data) - @requests_mock.Mocker() def test_list_subscribers(self, m): data = { diff --git a/test/services/test_oauth2.py b/test/services/test_oauth2.py index 4096d693..3b0bc38e 100644 --- a/test/services/test_oauth2.py +++ b/test/services/test_oauth2.py @@ -37,10 +37,6 @@ def test_authorize(self, m): m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) response = self.oauth2.authorize( - '', - 'https://example.com', - 'code', - '', ) self.assertEqual(response.to_dict(), data) @@ -72,6 +68,7 @@ def test_create_grant(self, m): "userId": "5e5ea5c16897e", "appId": "5e5ea5c16897e", "scopes": [], + "resources": [], "authorizationDetails": "[{\"type\":\"calendar\",\"identifier\":\"primary\",\"actions\":[\"read_events\",\"create_event\"]}]", "prompt": "login", "redirectUri": "https:\/\/example.com\/callback", @@ -96,6 +93,7 @@ def test_get_grant(self, m): "userId": "5e5ea5c16897e", "appId": "5e5ea5c16897e", "scopes": [], + "resources": [], "authorizationDetails": "[{\"type\":\"calendar\",\"identifier\":\"primary\",\"actions\":[\"read_events\",\"create_event\"]}]", "prompt": "login", "redirectUri": "https:\/\/example.com\/callback", @@ -111,6 +109,51 @@ def test_get_grant(self, m): self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_list_organizations(self, m): + data = { + "total": 5.0, + "organizations": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.oauth2.list_organizations( + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_par(self, m): + data = { + "request_uri": "urn:appwrite:oauth2:request:5e5ea5c16897e", + "expires_in": 600.0 +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.oauth2.create_par( + '', + 'https://example.com', + 'code', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_list_projects(self, m): + data = { + "total": 5.0, + "projects": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.oauth2.list_projects( + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_reject(self, m): data = { diff --git a/test/services/test_organization.py b/test/services/test_organization.py index a4e32d24..7cd67f61 100644 --- a/test/services/test_organization.py +++ b/test/services/test_organization.py @@ -13,6 +13,468 @@ def setUp(self): self.client = Client() self.organization = Organization(self.client) + @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", + "name": "VIP", + "total": 7.0, + "prefs": {}, + "billingBudget": 50.0, + "budgetAlerts": [], + "billingPlan": "tier-1", + "billingPlanId": "tier-1", + "billingPlanDetails": { + "$id": "tier-0", + "name": "Hobby", + "desc": "Hobby plan", + "order": 0.0, + "price": 25, + "trial": 14.0, + "bandwidth": 25.0, + "storage": 25.0, + "imageTransformations": 100.0, + "screenshotsGenerated": 50.0, + "members": 25.0, + "webhooks": 25.0, + "projects": 2.0, + "platforms": 3.0, + "users": 25.0, + "teams": 25.0, + "databases": 25.0, + "databasesReads": 500000.0, + "databasesWrites": 250000.0, + "databasesBatchSize": 100.0, + "buckets": 25.0, + "fileSize": 25.0, + "functions": 25.0, + "sites": 1.0, + "executions": 25.0, + "executionsRetentionCount": 10000.0, + "GBHours": 100.0, + "realtime": 25.0, + "realtimeMessages": 100000.0, + "messages": 1000.0, + "topics": 1.0, + "authPhone": 10.0, + "domains": 5.0, + "activityLogs": 7.0, + "usageLogs": 30.0, + "projectInactivityDays": 7.0, + "alertLimit": 80.0, + "usage": { + "bandwidth": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "executions": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "member": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "realtime": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "realtimeMessages": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "realtimeBandwidth": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "storage": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "users": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "GBHours": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "imageTransformations": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "credits": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + } + }, + "addons": { + "seats": { + "supported": True, + "planIncluded": 1.0, + "limit": 5.0, + "type": "numeric", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "projects": { + "supported": True, + "planIncluded": 1.0, + "limit": 5.0, + "type": "numeric", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + } + }, + "budgetCapEnabled": True, + "customSmtp": True, + "emailBranding": True, + "requiresPaymentMethod": True, + "requiresBillingAddress": True, + "isAvailable": True, + "selfService": True, + "premiumSupport": True, + "budgeting": True, + "supportsMockNumbers": True, + "supportsOrganizationRoles": True, + "supportsCredits": True, + "supportsDisposableEmailValidation": True, + "supportsCanonicalEmailValidation": True, + "supportsFreeEmailValidation": True, + "supportsCorporateEmailValidation": True, + "supportsProjectSpecificRoles": True, + "backupsEnabled": True, + "usagePerProject": True, + "supportedAddons": { + "baa": True, + "premiumGeoDB": True, + "premiumGeoDBOrg": True + }, + "backupPolicies": 1.0, + "deploymentSize": 30.0, + "buildSize": 2000.0, + "databasesAllowEncrypt": True, + "group": "pro" + }, + "billingEmail": "billing@org.example", + "billingStartDate": "2020-10-15T06:38:00.000+00:00", + "billingCurrentInvoiceDate": "2020-10-15T06:38:00.000+00:00", + "billingNextInvoiceDate": "2020-10-15T06:38:00.000+00:00", + "billingTrialStartDate": "2020-10-15T06:38:00.000+00:00", + "billingTrialDays": 14.0, + "billingAggregationId": "adbc3de4rddfsd", + "billingInvoiceId": "adbc3de4rddfsd", + "paymentMethodId": "adbc3de4rddfsd", + "billingAddressId": "adbc3de4rddfsd", + "backupPaymentMethodId": "adbc3de4rddfsd", + "status": "active", + "remarks": "Pending initial payment", + "agreementBAA": "", + "programManagerName": "", + "programManagerCalendar": "", + "programDiscordChannelName": "", + "programDiscordChannelUrl": "", + "billingPlanDowngrade": "tier-1", + "billingTaxId": "", + "markedForDeletion": True, + "platform": "imagine", + "projects": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.get( + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_update(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "name": "VIP", + "total": 7.0, + "prefs": {}, + "billingBudget": 50.0, + "budgetAlerts": [], + "billingPlan": "tier-1", + "billingPlanId": "tier-1", + "billingPlanDetails": { + "$id": "tier-0", + "name": "Hobby", + "desc": "Hobby plan", + "order": 0.0, + "price": 25, + "trial": 14.0, + "bandwidth": 25.0, + "storage": 25.0, + "imageTransformations": 100.0, + "screenshotsGenerated": 50.0, + "members": 25.0, + "webhooks": 25.0, + "projects": 2.0, + "platforms": 3.0, + "users": 25.0, + "teams": 25.0, + "databases": 25.0, + "databasesReads": 500000.0, + "databasesWrites": 250000.0, + "databasesBatchSize": 100.0, + "buckets": 25.0, + "fileSize": 25.0, + "functions": 25.0, + "sites": 1.0, + "executions": 25.0, + "executionsRetentionCount": 10000.0, + "GBHours": 100.0, + "realtime": 25.0, + "realtimeMessages": 100000.0, + "messages": 1000.0, + "topics": 1.0, + "authPhone": 10.0, + "domains": 5.0, + "activityLogs": 7.0, + "usageLogs": 30.0, + "projectInactivityDays": 7.0, + "alertLimit": 80.0, + "usage": { + "bandwidth": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "executions": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "member": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "realtime": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "realtimeMessages": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "realtimeBandwidth": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "storage": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "users": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "GBHours": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "imageTransformations": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "credits": { + "name": "", + "unit": "GB", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + } + }, + "addons": { + "seats": { + "supported": True, + "planIncluded": 1.0, + "limit": 5.0, + "type": "numeric", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + }, + "projects": { + "supported": True, + "planIncluded": 1.0, + "limit": 5.0, + "type": "numeric", + "currency": "USD", + "price": 5, + "value": 25.0, + "invoiceDesc": "" + } + }, + "budgetCapEnabled": True, + "customSmtp": True, + "emailBranding": True, + "requiresPaymentMethod": True, + "requiresBillingAddress": True, + "isAvailable": True, + "selfService": True, + "premiumSupport": True, + "budgeting": True, + "supportsMockNumbers": True, + "supportsOrganizationRoles": True, + "supportsCredits": True, + "supportsDisposableEmailValidation": True, + "supportsCanonicalEmailValidation": True, + "supportsFreeEmailValidation": True, + "supportsCorporateEmailValidation": True, + "supportsProjectSpecificRoles": True, + "backupsEnabled": True, + "usagePerProject": True, + "supportedAddons": { + "baa": True, + "premiumGeoDB": True, + "premiumGeoDBOrg": True + }, + "backupPolicies": 1.0, + "deploymentSize": 30.0, + "buildSize": 2000.0, + "databasesAllowEncrypt": True, + "group": "pro" + }, + "billingEmail": "billing@org.example", + "billingStartDate": "2020-10-15T06:38:00.000+00:00", + "billingCurrentInvoiceDate": "2020-10-15T06:38:00.000+00:00", + "billingNextInvoiceDate": "2020-10-15T06:38:00.000+00:00", + "billingTrialStartDate": "2020-10-15T06:38:00.000+00:00", + "billingTrialDays": 14.0, + "billingAggregationId": "adbc3de4rddfsd", + "billingInvoiceId": "adbc3de4rddfsd", + "paymentMethodId": "adbc3de4rddfsd", + "billingAddressId": "adbc3de4rddfsd", + "backupPaymentMethodId": "adbc3de4rddfsd", + "status": "active", + "remarks": "Pending initial payment", + "agreementBAA": "", + "programManagerName": "", + "programManagerCalendar": "", + "programDiscordChannelName": "", + "programDiscordChannelUrl": "", + "billingPlanDowngrade": "tier-1", + "billingTaxId": "", + "markedForDeletion": True, + "platform": "imagine", + "projects": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.update( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.delete( + ) + + self.assertEqual(response, data) + @requests_mock.Mocker() def test_list_keys(self, m): data = { @@ -109,6 +571,117 @@ def test_delete_key(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.organization.list_memberships( + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_membership(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "userId": "5e5ea5c16897e", + "userName": "John Doe", + "userEmail": "john@appwrite.io", + "userPhone": "+1 555 555 5555", + "teamId": "5e5ea5c16897e", + "teamName": "VIP", + "invited": "2020-10-15T06:38:00.000+00:00", + "joined": "2020-10-15T06:38:00.000+00:00", + "confirm": True, + "mfa": True, + "userAccessedAt": "2020-10-15T06:38:00.000+00:00", + "roles": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.create_membership( + [], + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_membership(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "userId": "5e5ea5c16897e", + "userName": "John Doe", + "userEmail": "john@appwrite.io", + "userPhone": "+1 555 555 5555", + "teamId": "5e5ea5c16897e", + "teamName": "VIP", + "invited": "2020-10-15T06:38:00.000+00:00", + "joined": "2020-10-15T06:38:00.000+00:00", + "confirm": True, + "mfa": True, + "userAccessedAt": "2020-10-15T06:38:00.000+00:00", + "roles": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.get_membership( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_update_membership(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "userId": "5e5ea5c16897e", + "userName": "John Doe", + "userEmail": "john@appwrite.io", + "userPhone": "+1 555 555 5555", + "teamId": "5e5ea5c16897e", + "teamName": "VIP", + "invited": "2020-10-15T06:38:00.000+00:00", + "joined": "2020-10-15T06:38:00.000+00:00", + "confirm": True, + "mfa": True, + "userAccessedAt": "2020-10-15T06:38:00.000+00:00", + "roles": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.update_membership( + '', + [], + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete_membership(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.delete_membership( + '', + ) + + self.assertEqual(response, data) + @requests_mock.Mocker() def test_list_projects(self, m): data = { @@ -147,6 +720,7 @@ def test_create_project(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -187,6 +761,7 @@ def test_get_project(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -226,6 +801,7 @@ def test_update_project(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], diff --git a/test/services/test_project.py b/test/services/test_project.py index 9440e29e..c3e87cf3 100644 --- a/test/services/test_project.py +++ b/test/services/test_project.py @@ -37,6 +37,7 @@ def test_get(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -86,6 +87,7 @@ def test_update_auth_method(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -245,6 +247,7 @@ def test_update_labels(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -377,6 +380,7 @@ def test_update_o_auth2_server(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -427,6 +431,22 @@ def test_update_o_auth2_apple(self, m): self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_update_o_auth2_appwrite(self, m): + data = { + "$id": "github", + "enabled": True, + "clientId": "6a42000000000000b5a0", + "clientSecret": "b86afd000000000000000000000000000000000000000000000000000ced5f93" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.project.update_o_auth2_appwrite( + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_update_o_auth2_auth0(self, m): data = { @@ -797,7 +817,8 @@ def test_update_o_auth2_oidc(self, m): "wellKnownURL": "https:\/\/myoauth.com\/.well-known\/openid-configuration", "authorizationURL": "https:\/\/myoauth.com\/oauth2\/authorize", "tokenURL": "https:\/\/myoauth.com\/oauth2\/token", - "userInfoURL": "https:\/\/myoauth.com\/oauth2\/userinfo" + "userInfoURL": "https:\/\/myoauth.com\/oauth2\/userinfo", + "prompt": [] } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1391,6 +1412,7 @@ def test_update_deny_aliased_email_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1430,6 +1452,7 @@ def test_update_deny_corporate_email_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1469,6 +1492,7 @@ def test_update_deny_disposable_email_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1508,6 +1532,7 @@ def test_update_deny_free_email_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1547,6 +1572,7 @@ def test_update_membership_privacy_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1585,6 +1611,7 @@ def test_update_password_dictionary_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1624,6 +1651,7 @@ def test_update_password_history_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1663,6 +1691,7 @@ def test_update_password_personal_data_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1720,6 +1749,7 @@ def test_update_session_alert_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1759,6 +1789,7 @@ def test_update_session_duration_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1798,6 +1829,7 @@ def test_update_session_invalidation_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1837,6 +1869,7 @@ def test_update_session_limit_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1876,6 +1909,7 @@ def test_update_user_limit_policy(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1930,6 +1964,7 @@ def test_update_protocol(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -1970,6 +2005,7 @@ def test_update_service(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], @@ -2010,6 +2046,7 @@ def test_update_smtp(self, m): "pingedAt": "2020-10-15T06:38:00.000+00:00", "labels": [], "status": "active", + "onboarding": {}, "authMethods": [], "services": [], "protocols": [], diff --git a/test/services/test_usage.py b/test/services/test_usage.py deleted file mode 100644 index d54ff32f..00000000 --- a/test/services/test_usage.py +++ /dev/null @@ -1,45 +0,0 @@ -import json -import requests_mock -import unittest - -from appwrite.client import Client -from appwrite.input_file import InputFile -from appwrite.models import * -from appwrite.services.usage import Usage - -class UsageServiceTest(unittest.TestCase): - - def setUp(self): - self.client = Client() - self.usage = Usage(self.client) - - @requests_mock.Mocker() - def test_list_events(self, m): - data = { - "interval": "1d", - "metrics": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.usage.list_events( - [], - ) - - self.assertEqual(response.to_dict(), data) - - @requests_mock.Mocker() - def test_list_gauges(self, m): - data = { - "interval": "1d", - "metrics": [] -} - headers = {'Content-Type': 'application/json'} - m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) - - response = self.usage.list_gauges( - [], - ) - - self.assertEqual(response.to_dict(), data) - From 0f3c9d2a792f2066c689d4581b0965139b39b188 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Mon, 13 Jul 2026 12:11:04 +0530 Subject: [PATCH 2/5] chore: update Python SDK to 22.0.0 --- appwrite/client.py | 2 +- appwrite/services/backups.py | 15 +- appwrite/services/databases.py | 207 ++++++++---- appwrite/services/functions.py | 24 +- appwrite/services/messaging.py | 216 ++++++++---- appwrite/services/oauth2.py | 3 +- appwrite/services/organization.py | 6 +- appwrite/services/project.py | 525 ++++++++++++++++++++---------- appwrite/services/sites.py | 18 +- appwrite/services/storage.py | 9 +- appwrite/services/tables_db.py | 210 ++++++++---- appwrite/services/tokens.py | 6 +- appwrite/services/users.py | 6 +- appwrite/services/webhooks.py | 6 +- 14 files changed, 835 insertions(+), 418 deletions(-) diff --git a/appwrite/client.py b/appwrite/client.py index 83f61a8f..0fc438b7 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -70,7 +70,7 @@ def set_jwt(self, value): def set_bearer(self, value): """The OAuth access token to authenticate with""" - self._global_headers['authorization'] = value + self._global_headers['authorization'] = 'Bearer ' + value self._config['bearer'] = value return self diff --git a/appwrite/services/backups.py b/appwrite/services/backups.py index fda14881..5aac7329 100644 --- a/appwrite/services/backups.py +++ b/appwrite/services/backups.py @@ -86,7 +86,8 @@ def create_archive( api_params['services'] = self._normalize_value(services) - api_params['resourceId'] = self._normalize_value(resource_id) + if resource_id is not None: + api_params['resourceId'] = self._normalize_value(resource_id) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -273,7 +274,8 @@ def create_policy( if name is not None: api_params['name'] = self._normalize_value(name) api_params['services'] = self._normalize_value(services) - api_params['resourceId'] = self._normalize_value(resource_id) + if resource_id is not None: + api_params['resourceId'] = self._normalize_value(resource_id) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) api_params['retention'] = self._normalize_value(retention) @@ -369,11 +371,14 @@ def update_policy( api_path = api_path.replace('{policyId}', str(self._normalize_value(policy_id))) - api_params['name'] = self._normalize_value(name) - api_params['retention'] = self._normalize_value(retention) + if name is not None: + api_params['name'] = self._normalize_value(name) + if retention is not None: + api_params['retention'] = self._normalize_value(retention) if schedule is not None: api_params['schedule'] = self._normalize_value(schedule) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/databases.py b/appwrite/services/databases.py index fdc30428..51831aec 100644 --- a/appwrite/services/databases.py +++ b/appwrite/services/databases.py @@ -672,7 +672,8 @@ def create_collection( api_params['collectionId'] = self._normalize_value(collection_id) api_params['name'] = self._normalize_value(name) - api_params['permissions'] = self._normalize_value(permissions) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) if document_security is not None: api_params['documentSecurity'] = self._normalize_value(document_security) if enabled is not None: @@ -797,7 +798,8 @@ def update_collection( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['permissions'] = self._normalize_value(permissions) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) if document_security is not None: api_params['documentSecurity'] = self._normalize_value(document_security) if enabled is not None: @@ -989,9 +991,12 @@ def create_big_int_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) - api_params['default'] = self._normalize_value(default) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1071,10 +1076,13 @@ def update_big_int_attribute( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1146,7 +1154,8 @@ def create_boolean_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1220,7 +1229,8 @@ def update_boolean_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1291,7 +1301,8 @@ def create_datetime_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1365,7 +1376,8 @@ def update_datetime_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1437,7 +1449,8 @@ def create_email_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1512,7 +1525,8 @@ def update_email_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1591,7 +1605,8 @@ def create_enum_attribute( api_params['key'] = self._normalize_value(key) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1673,7 +1688,8 @@ def update_enum_attribute( api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1751,9 +1767,12 @@ def create_float_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) - api_params['default'] = self._normalize_value(default) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1833,10 +1852,13 @@ def update_float_attribute( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1914,9 +1936,12 @@ def create_integer_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) - api_params['default'] = self._normalize_value(default) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1996,10 +2021,13 @@ def update_integer_attribute( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2071,7 +2099,8 @@ def create_ip_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -2146,7 +2175,8 @@ def update_ip_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2214,7 +2244,8 @@ def create_line_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2285,8 +2316,10 @@ def update_line_attribute( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if default is not None: + api_params['default'] = self._normalize_value(default) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2361,7 +2394,8 @@ def create_longtext_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -2438,7 +2472,8 @@ def update_longtext_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2513,7 +2548,8 @@ def create_mediumtext_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -2590,7 +2626,8 @@ def update_mediumtext_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2658,7 +2695,8 @@ def create_point_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2729,8 +2767,10 @@ def update_point_attribute( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if default is not None: + api_params['default'] = self._normalize_value(default) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2798,7 +2838,8 @@ def create_polygon_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2869,8 +2910,10 @@ def update_polygon_attribute( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if default is not None: + api_params['default'] = self._normalize_value(default) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2950,8 +2993,10 @@ def create_relationship_attribute( api_params['type'] = self._normalize_value(type) if two_way is not None: api_params['twoWay'] = self._normalize_value(two_way) - api_params['key'] = self._normalize_value(key) - api_params['twoWayKey'] = self._normalize_value(two_way_key) + if key is not None: + api_params['key'] = self._normalize_value(key) + if two_way_key is not None: + api_params['twoWayKey'] = self._normalize_value(two_way_key) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) @@ -3020,7 +3065,8 @@ def update_relationship_attribute( if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3102,7 +3148,8 @@ def create_string_attribute( api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -3182,8 +3229,10 @@ def update_string_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['size'] = self._normalize_value(size) - api_params['newKey'] = self._normalize_value(new_key) + if size is not None: + api_params['size'] = self._normalize_value(size) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3258,7 +3307,8 @@ def create_text_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -3335,7 +3385,8 @@ def update_text_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3407,7 +3458,8 @@ def create_url_attribute( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -3482,7 +3534,8 @@ def update_url_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3564,7 +3617,8 @@ def create_varchar_attribute( api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -3644,8 +3698,10 @@ def update_varchar_attribute( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['size'] = self._normalize_value(size) - api_params['newKey'] = self._normalize_value(new_key) + if size is not None: + api_params['size'] = self._normalize_value(size) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3937,8 +3993,10 @@ def create_document( api_params['documentId'] = self._normalize_value(document_id) api_params['data'] = self._normalize_value(data) - api_params['permissions'] = self._normalize_value(permissions) - api_params['transactionId'] = self._normalize_value(transaction_id) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4003,7 +4061,8 @@ def create_documents( api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) api_params['documents'] = self._normalize_value(documents) - api_params['transactionId'] = self._normalize_value(transaction_id) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4069,7 +4128,8 @@ def upsert_documents( api_path = api_path.replace('{collectionId}', str(self._normalize_value(collection_id))) api_params['documents'] = self._normalize_value(documents) - api_params['transactionId'] = self._normalize_value(transaction_id) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4137,7 +4197,8 @@ def update_documents( api_params['data'] = self._normalize_value(data) if queries is not None: api_params['queries'] = self._normalize_value(queries) - api_params['transactionId'] = self._normalize_value(transaction_id) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4344,8 +4405,10 @@ def upsert_document( if data is not None: api_params['data'] = self._normalize_value(data) - api_params['permissions'] = self._normalize_value(permissions) - api_params['transactionId'] = self._normalize_value(transaction_id) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4418,8 +4481,10 @@ def update_document( if data is not None: api_params['data'] = self._normalize_value(data) - api_params['permissions'] = self._normalize_value(permissions) - api_params['transactionId'] = self._normalize_value(transaction_id) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4560,8 +4625,10 @@ def decrement_document_attribute( if value is not None: api_params['value'] = self._normalize_value(value) - api_params['min'] = self._normalize_value(min) - api_params['transactionId'] = self._normalize_value(transaction_id) + if min is not None: + api_params['min'] = self._normalize_value(min) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4641,8 +4708,10 @@ def increment_document_attribute( if value is not None: api_params['value'] = self._normalize_value(value) - api_params['max'] = self._normalize_value(max) - api_params['transactionId'] = self._normalize_value(transaction_id) + if max is not None: + api_params['max'] = self._normalize_value(max) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/functions.py b/appwrite/services/functions.py index 18709fb2..4b0f552f 100644 --- a/appwrite/services/functions.py +++ b/appwrite/services/functions.py @@ -445,16 +445,20 @@ def update( api_params['scopes'] = self._normalize_value(scopes) if installation_id is not None: api_params['installationId'] = self._normalize_value(installation_id) - api_params['providerRepositoryId'] = self._normalize_value(provider_repository_id) + if provider_repository_id is not None: + api_params['providerRepositoryId'] = self._normalize_value(provider_repository_id) if provider_branch is not None: api_params['providerBranch'] = self._normalize_value(provider_branch) if provider_silent_mode is not None: api_params['providerSilentMode'] = self._normalize_value(provider_silent_mode) if provider_root_directory is not None: api_params['providerRootDirectory'] = self._normalize_value(provider_root_directory) - api_params['providerBranches'] = self._normalize_value(provider_branches) - api_params['providerPaths'] = self._normalize_value(provider_paths) - api_params['buildSpecification'] = self._normalize_value(build_specification) + if provider_branches is not None: + api_params['providerBranches'] = self._normalize_value(provider_branches) + if provider_paths is not None: + api_params['providerPaths'] = self._normalize_value(provider_paths) + if build_specification is not None: + api_params['buildSpecification'] = self._normalize_value(build_specification) if runtime_specification is not None: api_params['runtimeSpecification'] = self._normalize_value(runtime_specification) if deployment_retention is not None: @@ -1182,7 +1186,8 @@ def create_execution( api_params['method'] = self._normalize_value(method) if headers is not None: api_params['headers'] = self._normalize_value(headers) - api_params['scheduledAt'] = self._normalize_value(scheduled_at) + if scheduled_at is not None: + api_params['scheduledAt'] = self._normalize_value(scheduled_at) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1492,9 +1497,12 @@ def update_variable( 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_params['key'] = self._normalize_value(key) - api_params['value'] = self._normalize_value(value) - api_params['secret'] = self._normalize_value(secret) + if key is not None: + api_params['key'] = self._normalize_value(key) + if value is not None: + api_params['value'] = self._normalize_value(value) + if secret is not None: + api_params['secret'] = self._normalize_value(secret) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/messaging.py b/appwrite/services/messaging.py index 3abb1004..2ebe2bd6 100644 --- a/appwrite/services/messaging.py +++ b/appwrite/services/messaging.py @@ -154,7 +154,8 @@ def create_email( api_params['draft'] = self._normalize_value(draft) if html is not None: api_params['html'] = self._normalize_value(html) - api_params['scheduledAt'] = self._normalize_value(scheduled_at) + if scheduled_at is not None: + api_params['scheduledAt'] = self._normalize_value(scheduled_at) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -229,17 +230,28 @@ def update_email( api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) - api_params['topics'] = self._normalize_value(topics) - api_params['users'] = self._normalize_value(users) - api_params['targets'] = self._normalize_value(targets) - api_params['subject'] = self._normalize_value(subject) - api_params['content'] = self._normalize_value(content) - api_params['draft'] = self._normalize_value(draft) - api_params['html'] = self._normalize_value(html) - api_params['cc'] = self._normalize_value(cc) - api_params['bcc'] = self._normalize_value(bcc) - api_params['scheduledAt'] = self._normalize_value(scheduled_at) - api_params['attachments'] = self._normalize_value(attachments) + if topics is not None: + api_params['topics'] = self._normalize_value(topics) + if users is not None: + api_params['users'] = self._normalize_value(users) + if targets is not None: + api_params['targets'] = self._normalize_value(targets) + if subject is not None: + api_params['subject'] = self._normalize_value(subject) + if content is not None: + api_params['content'] = self._normalize_value(content) + if draft is not None: + api_params['draft'] = self._normalize_value(draft) + if html is not None: + api_params['html'] = self._normalize_value(html) + if cc is not None: + api_params['cc'] = self._normalize_value(cc) + if bcc is not None: + api_params['bcc'] = self._normalize_value(bcc) + if scheduled_at is not None: + api_params['scheduledAt'] = self._normalize_value(scheduled_at) + if attachments is not None: + api_params['attachments'] = self._normalize_value(attachments) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -344,7 +356,8 @@ def create_push( api_params['users'] = self._normalize_value(users) if targets is not None: api_params['targets'] = self._normalize_value(targets) - api_params['data'] = self._normalize_value(data) + if data is not None: + api_params['data'] = self._normalize_value(data) if action is not None: api_params['action'] = self._normalize_value(action) if image is not None: @@ -361,7 +374,8 @@ def create_push( api_params['badge'] = self._normalize_value(badge) if draft is not None: api_params['draft'] = self._normalize_value(draft) - api_params['scheduledAt'] = self._normalize_value(scheduled_at) + if scheduled_at is not None: + api_params['scheduledAt'] = self._normalize_value(scheduled_at) if content_available is not None: api_params['contentAvailable'] = self._normalize_value(content_available) if critical is not None: @@ -463,24 +477,42 @@ def update_push( api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) - api_params['topics'] = self._normalize_value(topics) - api_params['users'] = self._normalize_value(users) - api_params['targets'] = self._normalize_value(targets) - api_params['title'] = self._normalize_value(title) - api_params['body'] = self._normalize_value(body) - api_params['data'] = self._normalize_value(data) - api_params['action'] = self._normalize_value(action) - api_params['image'] = self._normalize_value(image) - api_params['icon'] = self._normalize_value(icon) - api_params['sound'] = self._normalize_value(sound) - api_params['color'] = self._normalize_value(color) - api_params['tag'] = self._normalize_value(tag) - api_params['badge'] = self._normalize_value(badge) - api_params['draft'] = self._normalize_value(draft) - api_params['scheduledAt'] = self._normalize_value(scheduled_at) - api_params['contentAvailable'] = self._normalize_value(content_available) - api_params['critical'] = self._normalize_value(critical) - api_params['priority'] = self._normalize_value(priority) + if topics is not None: + api_params['topics'] = self._normalize_value(topics) + if users is not None: + api_params['users'] = self._normalize_value(users) + if targets is not None: + api_params['targets'] = self._normalize_value(targets) + if title is not None: + api_params['title'] = self._normalize_value(title) + if body is not None: + api_params['body'] = self._normalize_value(body) + if data is not None: + api_params['data'] = self._normalize_value(data) + if action is not None: + api_params['action'] = self._normalize_value(action) + if image is not None: + api_params['image'] = self._normalize_value(image) + if icon is not None: + api_params['icon'] = self._normalize_value(icon) + if sound is not None: + api_params['sound'] = self._normalize_value(sound) + if color is not None: + api_params['color'] = self._normalize_value(color) + if tag is not None: + api_params['tag'] = self._normalize_value(tag) + if badge is not None: + api_params['badge'] = self._normalize_value(badge) + if draft is not None: + api_params['draft'] = self._normalize_value(draft) + if scheduled_at is not None: + api_params['scheduledAt'] = self._normalize_value(scheduled_at) + if content_available is not None: + api_params['contentAvailable'] = self._normalize_value(content_available) + if critical is not None: + api_params['critical'] = self._normalize_value(critical) + if priority is not None: + api_params['priority'] = self._normalize_value(priority) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -551,7 +583,8 @@ def create_sms( api_params['targets'] = self._normalize_value(targets) if draft is not None: api_params['draft'] = self._normalize_value(draft) - api_params['scheduledAt'] = self._normalize_value(scheduled_at) + if scheduled_at is not None: + api_params['scheduledAt'] = self._normalize_value(scheduled_at) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -611,12 +644,18 @@ def update_sms( api_path = api_path.replace('{messageId}', str(self._normalize_value(message_id))) - api_params['topics'] = self._normalize_value(topics) - api_params['users'] = self._normalize_value(users) - api_params['targets'] = self._normalize_value(targets) - api_params['content'] = self._normalize_value(content) - api_params['draft'] = self._normalize_value(draft) - api_params['scheduledAt'] = self._normalize_value(scheduled_at) + if topics is not None: + api_params['topics'] = self._normalize_value(topics) + if users is not None: + api_params['users'] = self._normalize_value(users) + if targets is not None: + api_params['targets'] = self._normalize_value(targets) + if content is not None: + api_params['content'] = self._normalize_value(content) + if draft is not None: + api_params['draft'] = self._normalize_value(draft) + if scheduled_at is not None: + api_params['scheduledAt'] = self._normalize_value(scheduled_at) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -867,7 +906,8 @@ def create_apns_provider( api_params['bundleId'] = self._normalize_value(bundle_id) if sandbox is not None: api_params['sandbox'] = self._normalize_value(sandbox) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -931,7 +971,8 @@ def update_apns_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if auth_key is not None: api_params['authKey'] = self._normalize_value(auth_key) if auth_key_id is not None: @@ -940,7 +981,8 @@ def update_apns_provider( api_params['teamId'] = self._normalize_value(team_id) if bundle_id is not None: api_params['bundleId'] = self._normalize_value(bundle_id) - api_params['sandbox'] = self._normalize_value(sandbox) + if sandbox is not None: + api_params['sandbox'] = self._normalize_value(sandbox) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -994,8 +1036,10 @@ def create_fcm_provider( api_params['providerId'] = self._normalize_value(provider_id) api_params['name'] = self._normalize_value(name) - api_params['serviceAccountJSON'] = self._normalize_value(service_account_json) - api_params['enabled'] = self._normalize_value(enabled) + if service_account_json is not None: + api_params['serviceAccountJSON'] = self._normalize_value(service_account_json) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1047,8 +1091,10 @@ def update_fcm_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) - api_params['serviceAccountJSON'] = self._normalize_value(service_account_json) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) + if service_account_json is not None: + api_params['serviceAccountJSON'] = self._normalize_value(service_account_json) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1124,7 +1170,8 @@ def create_mailgun_provider( api_params['apiKey'] = self._normalize_value(api_key) if domain is not None: api_params['domain'] = self._normalize_value(domain) - api_params['isEuRegion'] = self._normalize_value(is_eu_region) + if is_eu_region is not None: + api_params['isEuRegion'] = self._normalize_value(is_eu_region) if from_name is not None: api_params['fromName'] = self._normalize_value(from_name) if from_email is not None: @@ -1133,7 +1180,8 @@ def create_mailgun_provider( api_params['replyToName'] = self._normalize_value(reply_to_name) if reply_to_email is not None: api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1207,8 +1255,10 @@ def update_mailgun_provider( api_params['apiKey'] = self._normalize_value(api_key) if domain is not None: api_params['domain'] = self._normalize_value(domain) - api_params['isEuRegion'] = self._normalize_value(is_eu_region) - api_params['enabled'] = self._normalize_value(enabled) + if is_eu_region is not None: + api_params['isEuRegion'] = self._normalize_value(is_eu_region) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if from_name is not None: api_params['fromName'] = self._normalize_value(from_name) if from_email is not None: @@ -1282,7 +1332,8 @@ def create_msg91_provider( api_params['senderId'] = self._normalize_value(sender_id) if auth_key is not None: api_params['authKey'] = self._normalize_value(auth_key) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1340,7 +1391,8 @@ def update_msg91_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if template_id is not None: api_params['templateId'] = self._normalize_value(template_id) if sender_id is not None: @@ -1422,7 +1474,8 @@ def create_resend_provider( api_params['replyToName'] = self._normalize_value(reply_to_name) if reply_to_email is not None: api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1486,7 +1539,8 @@ def update_resend_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if api_key is not None: api_params['apiKey'] = self._normalize_value(api_key) if from_name is not None: @@ -1572,7 +1626,8 @@ def create_sendgrid_provider( api_params['replyToName'] = self._normalize_value(reply_to_name) if reply_to_email is not None: api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1636,7 +1691,8 @@ def update_sendgrid_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if api_key is not None: api_params['apiKey'] = self._normalize_value(api_key) if from_name is not None: @@ -1732,7 +1788,8 @@ def create_ses_provider( api_params['replyToName'] = self._normalize_value(reply_to_name) if reply_to_email is not None: api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1802,7 +1859,8 @@ def update_ses_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if access_key is not None: api_params['accessKey'] = self._normalize_value(access_key) if secret_key is not None: @@ -1924,7 +1982,8 @@ def create_smtp_provider( api_params['replyToName'] = self._normalize_value(reply_to_name) if reply_to_email is not None: api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2008,14 +2067,16 @@ def update_smtp_provider( api_params['name'] = self._normalize_value(name) if host is not None: api_params['host'] = self._normalize_value(host) - api_params['port'] = self._normalize_value(port) + if port is not None: + api_params['port'] = self._normalize_value(port) if username is not None: api_params['username'] = self._normalize_value(username) if password is not None: api_params['password'] = self._normalize_value(password) if encryption is not None: api_params['encryption'] = self._normalize_value(encryption) - api_params['autoTLS'] = self._normalize_value(auto_tls) + if auto_tls is not None: + api_params['autoTLS'] = self._normalize_value(auto_tls) if mailer is not None: api_params['mailer'] = self._normalize_value(mailer) if from_name is not None: @@ -2026,7 +2087,8 @@ def update_smtp_provider( api_params['replyToName'] = self._normalize_value(reply_to_name) if reply_to_email is not None: api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2092,7 +2154,8 @@ def create_telesign_provider( api_params['customerId'] = self._normalize_value(customer_id) if api_key is not None: api_params['apiKey'] = self._normalize_value(api_key) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2150,7 +2213,8 @@ def update_telesign_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if customer_id is not None: api_params['customerId'] = self._normalize_value(customer_id) if api_key is not None: @@ -2222,7 +2286,8 @@ def create_textmagic_provider( api_params['username'] = self._normalize_value(username) if api_key is not None: api_params['apiKey'] = self._normalize_value(api_key) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2280,7 +2345,8 @@ def update_textmagic_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if username is not None: api_params['username'] = self._normalize_value(username) if api_key is not None: @@ -2352,7 +2418,8 @@ def create_twilio_provider( api_params['accountSid'] = self._normalize_value(account_sid) if auth_token is not None: api_params['authToken'] = self._normalize_value(auth_token) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2410,7 +2477,8 @@ def update_twilio_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if account_sid is not None: api_params['accountSid'] = self._normalize_value(account_sid) if auth_token is not None: @@ -2482,7 +2550,8 @@ def create_vonage_provider( api_params['apiKey'] = self._normalize_value(api_key) if api_secret is not None: api_params['apiSecret'] = self._normalize_value(api_secret) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2540,7 +2609,8 @@ def update_vonage_provider( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['enabled'] = self._normalize_value(enabled) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) if api_key is not None: api_params['apiKey'] = self._normalize_value(api_key) if api_secret is not None: @@ -2812,8 +2882,10 @@ def update_topic( api_path = api_path.replace('{topicId}', str(self._normalize_value(topic_id))) - api_params['name'] = self._normalize_value(name) - api_params['subscribe'] = self._normalize_value(subscribe) + if name is not None: + api_params['name'] = self._normalize_value(name) + if subscribe is not None: + api_params['subscribe'] = self._normalize_value(subscribe) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/oauth2.py b/appwrite/services/oauth2.py index 272fbc73..21612207 100644 --- a/appwrite/services/oauth2.py +++ b/appwrite/services/oauth2.py @@ -441,7 +441,8 @@ def create_par( api_params['code_challenge_method'] = self._normalize_value(code_challenge_method) if prompt is not None: api_params['prompt'] = self._normalize_value(prompt) - api_params['max_age'] = self._normalize_value(max_age) + if max_age is not None: + api_params['max_age'] = self._normalize_value(max_age) if authorization_details is not None: api_params['authorization_details'] = self._normalize_value(authorization_details) if resource is not None: diff --git a/appwrite/services/organization.py b/appwrite/services/organization.py index eaf0ca88..e3f11e50 100644 --- a/appwrite/services/organization.py +++ b/appwrite/services/organization.py @@ -216,7 +216,8 @@ def create_key( api_params['keyId'] = self._normalize_value(key_id) api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) - api_params['expire'] = self._normalize_value(expire) + if expire is not None: + api_params['expire'] = self._normalize_value(expire) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -313,7 +314,8 @@ def update_key( api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) - api_params['expire'] = self._normalize_value(expire) + if expire is not None: + api_params['expire'] = self._normalize_value(expire) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/project.py b/appwrite/services/project.py index c9d5c9d4..573abdae 100644 --- a/appwrite/services/project.py +++ b/appwrite/services/project.py @@ -286,7 +286,8 @@ def create_key( api_params['keyId'] = self._normalize_value(key_id) api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) - api_params['expire'] = self._normalize_value(expire) + if expire is not None: + api_params['expire'] = self._normalize_value(expire) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -432,7 +433,8 @@ def update_key( api_params['name'] = self._normalize_value(name) api_params['scopes'] = self._normalize_value(scopes) - api_params['expire'] = self._normalize_value(expire) + if expire is not None: + api_params['expire'] = self._normalize_value(expire) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -855,17 +857,24 @@ def update_o_auth2_server( api_params['scopes'] = self._normalize_value(scopes) if authorization_details_types is not None: api_params['authorizationDetailsTypes'] = self._normalize_value(authorization_details_types) - api_params['accessTokenDuration'] = self._normalize_value(access_token_duration) - api_params['refreshTokenDuration'] = self._normalize_value(refresh_token_duration) - api_params['publicAccessTokenDuration'] = self._normalize_value(public_access_token_duration) - api_params['publicRefreshTokenDuration'] = self._normalize_value(public_refresh_token_duration) - api_params['confidentialPkce'] = self._normalize_value(confidential_pkce) + if access_token_duration is not None: + api_params['accessTokenDuration'] = self._normalize_value(access_token_duration) + if refresh_token_duration is not None: + api_params['refreshTokenDuration'] = self._normalize_value(refresh_token_duration) + if public_access_token_duration is not None: + api_params['publicAccessTokenDuration'] = self._normalize_value(public_access_token_duration) + if public_refresh_token_duration is not None: + api_params['publicRefreshTokenDuration'] = self._normalize_value(public_refresh_token_duration) + if confidential_pkce is not None: + api_params['confidentialPkce'] = self._normalize_value(confidential_pkce) if verification_url is not None: api_params['verificationUrl'] = self._normalize_value(verification_url) - api_params['userCodeLength'] = self._normalize_value(user_code_length) + if user_code_length is not None: + api_params['userCodeLength'] = self._normalize_value(user_code_length) if user_code_format is not None: api_params['userCodeFormat'] = self._normalize_value(user_code_format) - api_params['deviceCodeDuration'] = self._normalize_value(device_code_duration) + if device_code_duration is not None: + api_params['deviceCodeDuration'] = self._normalize_value(device_code_duration) if default_scopes is not None: api_params['defaultScopes'] = self._normalize_value(default_scopes) @@ -910,9 +919,12 @@ def update_o_auth2_amazon( api_path = '/project/oauth2/amazon' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -961,11 +973,16 @@ def update_o_auth2_apple( api_path = '/project/oauth2/apple' api_params = {} - api_params['serviceId'] = self._normalize_value(service_id) - api_params['keyId'] = self._normalize_value(key_id) - api_params['teamId'] = self._normalize_value(team_id) - api_params['p8File'] = self._normalize_value(p8_file) - api_params['enabled'] = self._normalize_value(enabled) + if service_id is not None: + api_params['serviceId'] = self._normalize_value(service_id) + if key_id is not None: + api_params['keyId'] = self._normalize_value(key_id) + if team_id is not None: + api_params['teamId'] = self._normalize_value(team_id) + if p8_file is not None: + api_params['p8File'] = self._normalize_value(p8_file) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1008,9 +1025,12 @@ def update_o_auth2_appwrite( api_path = '/project/oauth2/appwrite' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1056,10 +1076,14 @@ def update_o_auth2_auth0( api_path = '/project/oauth2/auth0' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['endpoint'] = self._normalize_value(endpoint) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if endpoint is not None: + api_params['endpoint'] = self._normalize_value(endpoint) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1105,10 +1129,14 @@ def update_o_auth2_authentik( api_path = '/project/oauth2/authentik' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['endpoint'] = self._normalize_value(endpoint) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if endpoint is not None: + api_params['endpoint'] = self._normalize_value(endpoint) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1151,9 +1179,12 @@ def update_o_auth2_autodesk( api_path = '/project/oauth2/autodesk' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1196,9 +1227,12 @@ def update_o_auth2_bitbucket( api_path = '/project/oauth2/bitbucket' api_params = {} - api_params['key'] = self._normalize_value(key) - api_params['secret'] = self._normalize_value(secret) - api_params['enabled'] = self._normalize_value(enabled) + if key is not None: + api_params['key'] = self._normalize_value(key) + if secret is not None: + api_params['secret'] = self._normalize_value(secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1241,9 +1275,12 @@ def update_o_auth2_bitly( api_path = '/project/oauth2/bitly' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1286,9 +1323,12 @@ def update_o_auth2_box( api_path = '/project/oauth2/box' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1331,9 +1371,12 @@ def update_o_auth2_dailymotion( api_path = '/project/oauth2/dailymotion' api_params = {} - api_params['apiKey'] = self._normalize_value(api_key) - api_params['apiSecret'] = self._normalize_value(api_secret) - api_params['enabled'] = self._normalize_value(enabled) + if api_key is not None: + api_params['apiKey'] = self._normalize_value(api_key) + if api_secret is not None: + api_params['apiSecret'] = self._normalize_value(api_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1376,9 +1419,12 @@ def update_o_auth2_discord( api_path = '/project/oauth2/discord' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1421,9 +1467,12 @@ def update_o_auth2_disqus( api_path = '/project/oauth2/disqus' api_params = {} - api_params['publicKey'] = self._normalize_value(public_key) - api_params['secretKey'] = self._normalize_value(secret_key) - api_params['enabled'] = self._normalize_value(enabled) + if public_key is not None: + api_params['publicKey'] = self._normalize_value(public_key) + if secret_key is not None: + api_params['secretKey'] = self._normalize_value(secret_key) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1466,9 +1515,12 @@ def update_o_auth2_dropbox( api_path = '/project/oauth2/dropbox' api_params = {} - api_params['appKey'] = self._normalize_value(app_key) - api_params['appSecret'] = self._normalize_value(app_secret) - api_params['enabled'] = self._normalize_value(enabled) + if app_key is not None: + api_params['appKey'] = self._normalize_value(app_key) + if app_secret is not None: + api_params['appSecret'] = self._normalize_value(app_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1511,9 +1563,12 @@ def update_o_auth2_etsy( api_path = '/project/oauth2/etsy' api_params = {} - api_params['keyString'] = self._normalize_value(key_string) - api_params['sharedSecret'] = self._normalize_value(shared_secret) - api_params['enabled'] = self._normalize_value(enabled) + if key_string is not None: + api_params['keyString'] = self._normalize_value(key_string) + if shared_secret is not None: + api_params['sharedSecret'] = self._normalize_value(shared_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1556,9 +1611,12 @@ def update_o_auth2_facebook( api_path = '/project/oauth2/facebook' api_params = {} - api_params['appId'] = self._normalize_value(app_id) - api_params['appSecret'] = self._normalize_value(app_secret) - api_params['enabled'] = self._normalize_value(enabled) + if app_id is not None: + api_params['appId'] = self._normalize_value(app_id) + if app_secret is not None: + api_params['appSecret'] = self._normalize_value(app_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1601,9 +1659,12 @@ def update_o_auth2_figma( api_path = '/project/oauth2/figma' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1649,10 +1710,14 @@ def update_o_auth2_fusion_auth( api_path = '/project/oauth2/fusionauth' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['endpoint'] = self._normalize_value(endpoint) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if endpoint is not None: + api_params['endpoint'] = self._normalize_value(endpoint) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1695,9 +1760,12 @@ def update_o_auth2_git_hub( api_path = '/project/oauth2/github' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1743,10 +1811,14 @@ def update_o_auth2_gitlab( api_path = '/project/oauth2/gitlab' api_params = {} - api_params['applicationId'] = self._normalize_value(application_id) - api_params['secret'] = self._normalize_value(secret) - api_params['endpoint'] = self._normalize_value(endpoint) - api_params['enabled'] = self._normalize_value(enabled) + if application_id is not None: + api_params['applicationId'] = self._normalize_value(application_id) + if secret is not None: + api_params['secret'] = self._normalize_value(secret) + if endpoint is not None: + api_params['endpoint'] = self._normalize_value(endpoint) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1792,10 +1864,14 @@ def update_o_auth2_google( api_path = '/project/oauth2/google' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['prompt'] = self._normalize_value(prompt) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if prompt is not None: + api_params['prompt'] = self._normalize_value(prompt) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1844,11 +1920,16 @@ def update_o_auth2_keycloak( api_path = '/project/oauth2/keycloak' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['endpoint'] = self._normalize_value(endpoint) - api_params['realmName'] = self._normalize_value(realm_name) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if endpoint is not None: + api_params['endpoint'] = self._normalize_value(endpoint) + if realm_name is not None: + api_params['realmName'] = self._normalize_value(realm_name) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1891,9 +1972,12 @@ def update_o_auth2_kick( api_path = '/project/oauth2/kick' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1936,9 +2020,12 @@ def update_o_auth2_linkedin( api_path = '/project/oauth2/linkedin' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['primaryClientSecret'] = self._normalize_value(primary_client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if primary_client_secret is not None: + api_params['primaryClientSecret'] = self._normalize_value(primary_client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1984,10 +2071,14 @@ def update_o_auth2_microsoft( api_path = '/project/oauth2/microsoft' api_params = {} - api_params['applicationId'] = self._normalize_value(application_id) - api_params['applicationSecret'] = self._normalize_value(application_secret) - api_params['tenant'] = self._normalize_value(tenant) - api_params['enabled'] = self._normalize_value(enabled) + if application_id is not None: + api_params['applicationId'] = self._normalize_value(application_id) + if application_secret is not None: + api_params['applicationSecret'] = self._normalize_value(application_secret) + if tenant is not None: + api_params['tenant'] = self._normalize_value(tenant) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2030,9 +2121,12 @@ def update_o_auth2_notion( api_path = '/project/oauth2/notion' api_params = {} - api_params['oauthClientId'] = self._normalize_value(oauth_client_id) - api_params['oauthClientSecret'] = self._normalize_value(oauth_client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if oauth_client_id is not None: + api_params['oauthClientId'] = self._normalize_value(oauth_client_id) + if oauth_client_secret is not None: + api_params['oauthClientSecret'] = self._normalize_value(oauth_client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2093,15 +2187,24 @@ def update_o_auth2_oidc( api_path = '/project/oauth2/oidc' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['wellKnownURL'] = self._normalize_value(well_known_url) - api_params['authorizationURL'] = self._normalize_value(authorization_url) - api_params['tokenURL'] = self._normalize_value(token_url) - api_params['userInfoURL'] = self._normalize_value(user_info_url) - api_params['prompt'] = self._normalize_value(prompt) - api_params['maxAge'] = self._normalize_value(max_age) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if well_known_url is not None: + api_params['wellKnownURL'] = self._normalize_value(well_known_url) + if authorization_url is not None: + api_params['authorizationURL'] = self._normalize_value(authorization_url) + if token_url is not None: + api_params['tokenURL'] = self._normalize_value(token_url) + if user_info_url is not None: + api_params['userInfoURL'] = self._normalize_value(user_info_url) + if prompt is not None: + api_params['prompt'] = self._normalize_value(prompt) + if max_age is not None: + api_params['maxAge'] = self._normalize_value(max_age) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2150,11 +2253,16 @@ def update_o_auth2_okta( api_path = '/project/oauth2/okta' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['domain'] = self._normalize_value(domain) - api_params['authorizationServerId'] = self._normalize_value(authorization_server_id) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if domain is not None: + api_params['domain'] = self._normalize_value(domain) + if authorization_server_id is not None: + api_params['authorizationServerId'] = self._normalize_value(authorization_server_id) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2197,9 +2305,12 @@ def update_o_auth2_paypal( api_path = '/project/oauth2/paypal' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['secretKey'] = self._normalize_value(secret_key) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if secret_key is not None: + api_params['secretKey'] = self._normalize_value(secret_key) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2242,9 +2353,12 @@ def update_o_auth2_paypal_sandbox( api_path = '/project/oauth2/paypalSandbox' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['secretKey'] = self._normalize_value(secret_key) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if secret_key is not None: + api_params['secretKey'] = self._normalize_value(secret_key) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2287,9 +2401,12 @@ def update_o_auth2_podio( api_path = '/project/oauth2/podio' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2332,9 +2449,12 @@ def update_o_auth2_salesforce( api_path = '/project/oauth2/salesforce' api_params = {} - api_params['customerKey'] = self._normalize_value(customer_key) - api_params['customerSecret'] = self._normalize_value(customer_secret) - api_params['enabled'] = self._normalize_value(enabled) + if customer_key is not None: + api_params['customerKey'] = self._normalize_value(customer_key) + if customer_secret is not None: + api_params['customerSecret'] = self._normalize_value(customer_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2377,9 +2497,12 @@ def update_o_auth2_slack( api_path = '/project/oauth2/slack' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2422,9 +2545,12 @@ def update_o_auth2_spotify( api_path = '/project/oauth2/spotify' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2467,9 +2593,12 @@ def update_o_auth2_stripe( api_path = '/project/oauth2/stripe' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['apiSecretKey'] = self._normalize_value(api_secret_key) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if api_secret_key is not None: + api_params['apiSecretKey'] = self._normalize_value(api_secret_key) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2512,9 +2641,12 @@ def update_o_auth2_tradeshift( api_path = '/project/oauth2/tradeshift' api_params = {} - api_params['oauth2ClientId'] = self._normalize_value(oauth2_client_id) - api_params['oauth2ClientSecret'] = self._normalize_value(oauth2_client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if oauth2_client_id is not None: + api_params['oauth2ClientId'] = self._normalize_value(oauth2_client_id) + if oauth2_client_secret is not None: + api_params['oauth2ClientSecret'] = self._normalize_value(oauth2_client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2557,9 +2689,12 @@ def update_o_auth2_tradeshift_sandbox( api_path = '/project/oauth2/tradeshiftBox' api_params = {} - api_params['oauth2ClientId'] = self._normalize_value(oauth2_client_id) - api_params['oauth2ClientSecret'] = self._normalize_value(oauth2_client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if oauth2_client_id is not None: + api_params['oauth2ClientId'] = self._normalize_value(oauth2_client_id) + if oauth2_client_secret is not None: + api_params['oauth2ClientSecret'] = self._normalize_value(oauth2_client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2602,9 +2737,12 @@ def update_o_auth2_twitch( api_path = '/project/oauth2/twitch' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2647,9 +2785,12 @@ def update_o_auth2_word_press( api_path = '/project/oauth2/wordpress' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2692,9 +2833,12 @@ def update_o_auth2_x( api_path = '/project/oauth2/x' api_params = {} - api_params['customerKey'] = self._normalize_value(customer_key) - api_params['secretKey'] = self._normalize_value(secret_key) - api_params['enabled'] = self._normalize_value(enabled) + if customer_key is not None: + api_params['customerKey'] = self._normalize_value(customer_key) + if secret_key is not None: + api_params['secretKey'] = self._normalize_value(secret_key) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2737,9 +2881,12 @@ def update_o_auth2_yahoo( api_path = '/project/oauth2/yahoo' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2782,9 +2929,12 @@ def update_o_auth2_yandex( api_path = '/project/oauth2/yandex' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2827,9 +2977,12 @@ def update_o_auth2_zoho( api_path = '/project/oauth2/zoho' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2872,9 +3025,12 @@ def update_o_auth2_zoom( api_path = '/project/oauth2/zoom' api_params = {} - api_params['clientId'] = self._normalize_value(client_id) - api_params['clientSecret'] = self._normalize_value(client_secret) - api_params['enabled'] = self._normalize_value(enabled) + if client_id is not None: + api_params['clientId'] = self._normalize_value(client_id) + if client_secret is not None: + api_params['clientSecret'] = self._normalize_value(client_secret) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4589,16 +4745,26 @@ def update_smtp( api_path = '/project/smtp' api_params = {} - api_params['host'] = self._normalize_value(host) - api_params['port'] = self._normalize_value(port) - api_params['username'] = self._normalize_value(username) - api_params['password'] = self._normalize_value(password) - api_params['senderEmail'] = self._normalize_value(sender_email) - api_params['senderName'] = self._normalize_value(sender_name) - api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['replyToName'] = self._normalize_value(reply_to_name) - api_params['secure'] = self._normalize_value(secure) - api_params['enabled'] = self._normalize_value(enabled) + if host is not None: + api_params['host'] = self._normalize_value(host) + if port is not None: + api_params['port'] = self._normalize_value(port) + if username is not None: + api_params['username'] = self._normalize_value(username) + if password is not None: + api_params['password'] = self._normalize_value(password) + if sender_email is not None: + api_params['senderEmail'] = self._normalize_value(sender_email) + if sender_name is not None: + api_params['senderName'] = self._normalize_value(sender_name) + if reply_to_email is not None: + 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 secure is not None: + api_params['secure'] = self._normalize_value(secure) + if enabled is not None: + api_params['enabled'] = self._normalize_value(enabled) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4743,12 +4909,18 @@ def update_email_template( api_params['templateId'] = self._normalize_value(template_id) if locale is not None: api_params['locale'] = self._normalize_value(locale) - api_params['subject'] = self._normalize_value(subject) - api_params['message'] = self._normalize_value(message) - api_params['senderName'] = self._normalize_value(sender_name) - api_params['senderEmail'] = self._normalize_value(sender_email) - api_params['replyToEmail'] = self._normalize_value(reply_to_email) - api_params['replyToName'] = self._normalize_value(reply_to_name) + if subject is not None: + api_params['subject'] = self._normalize_value(subject) + if message is not None: + api_params['message'] = self._normalize_value(message) + if sender_name is not None: + api_params['senderName'] = self._normalize_value(sender_name) + if sender_email is not None: + api_params['senderEmail'] = self._normalize_value(sender_email) + if reply_to_email is not None: + 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) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4982,9 +5154,12 @@ def update_variable( api_path = api_path.replace('{variableId}', str(self._normalize_value(variable_id))) - api_params['key'] = self._normalize_value(key) - api_params['value'] = self._normalize_value(value) - api_params['secret'] = self._normalize_value(secret) + if key is not None: + api_params['key'] = self._normalize_value(key) + if value is not None: + api_params['value'] = self._normalize_value(value) + if secret is not None: + api_params['secret'] = self._normalize_value(secret) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/sites.py b/appwrite/services/sites.py index 4daf161f..611dcb02 100644 --- a/appwrite/services/sites.py +++ b/appwrite/services/sites.py @@ -467,9 +467,12 @@ def update( api_params['providerSilentMode'] = self._normalize_value(provider_silent_mode) if provider_root_directory is not None: api_params['providerRootDirectory'] = self._normalize_value(provider_root_directory) - api_params['providerBranches'] = self._normalize_value(provider_branches) - api_params['providerPaths'] = self._normalize_value(provider_paths) - api_params['buildSpecification'] = self._normalize_value(build_specification) + if provider_branches is not None: + api_params['providerBranches'] = self._normalize_value(provider_branches) + if provider_paths is not None: + api_params['providerPaths'] = self._normalize_value(provider_paths) + if build_specification is not None: + api_params['buildSpecification'] = self._normalize_value(build_specification) if runtime_specification is not None: api_params['runtimeSpecification'] = self._normalize_value(runtime_specification) if deployment_retention is not None: @@ -1428,9 +1431,12 @@ def update_variable( 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_params['key'] = self._normalize_value(key) - api_params['value'] = self._normalize_value(value) - api_params['secret'] = self._normalize_value(secret) + if key is not None: + api_params['key'] = self._normalize_value(key) + if value is not None: + api_params['value'] = self._normalize_value(value) + if secret is not None: + api_params['secret'] = self._normalize_value(secret) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/storage.py b/appwrite/services/storage.py index 7d1a1eaf..a450e58b 100644 --- a/appwrite/services/storage.py +++ b/appwrite/services/storage.py @@ -128,7 +128,8 @@ def create_bucket( api_params['bucketId'] = self._normalize_value(bucket_id) api_params['name'] = self._normalize_value(name) - api_params['permissions'] = self._normalize_value(permissions) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) if file_security is not None: api_params['fileSecurity'] = self._normalize_value(file_security) if enabled is not None: @@ -258,7 +259,8 @@ def update_bucket( api_path = api_path.replace('{bucketId}', str(self._normalize_value(bucket_id))) api_params['name'] = self._normalize_value(name) - api_params['permissions'] = self._normalize_value(permissions) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) if file_security is not None: api_params['fileSecurity'] = self._normalize_value(file_security) if enabled is not None: @@ -544,7 +546,8 @@ def update_file( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['permissions'] = self._normalize_value(permissions) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/tables_db.py b/appwrite/services/tables_db.py index 9733bc96..806bdc84 100644 --- a/appwrite/services/tables_db.py +++ b/appwrite/services/tables_db.py @@ -638,7 +638,8 @@ def create_table( api_params['tableId'] = self._normalize_value(table_id) api_params['name'] = self._normalize_value(name) - api_params['permissions'] = self._normalize_value(permissions) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) if row_security is not None: api_params['rowSecurity'] = self._normalize_value(row_security) if enabled is not None: @@ -757,7 +758,8 @@ def update_table( if name is not None: api_params['name'] = self._normalize_value(name) - api_params['permissions'] = self._normalize_value(permissions) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) if row_security is not None: api_params['rowSecurity'] = self._normalize_value(row_security) if enabled is not None: @@ -940,9 +942,12 @@ def create_big_int_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) - api_params['default'] = self._normalize_value(default) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1019,10 +1024,13 @@ def update_big_int_column( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1091,7 +1099,8 @@ def create_boolean_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1162,7 +1171,8 @@ def update_boolean_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1230,7 +1240,8 @@ def create_datetime_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1301,7 +1312,8 @@ def update_datetime_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1370,7 +1382,8 @@ def create_email_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1442,7 +1455,8 @@ def update_email_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1517,7 +1531,8 @@ def create_enum_column( api_params['key'] = self._normalize_value(key) api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1596,7 +1611,8 @@ def update_enum_column( api_params['elements'] = self._normalize_value(elements) api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1671,9 +1687,12 @@ def create_float_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) - api_params['default'] = self._normalize_value(default) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1750,10 +1769,13 @@ def update_float_column( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1828,9 +1850,12 @@ def create_integer_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) - api_params['default'] = self._normalize_value(default) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -1907,10 +1932,13 @@ def update_integer_column( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['min'] = self._normalize_value(min) - api_params['max'] = self._normalize_value(max) + if min is not None: + api_params['min'] = self._normalize_value(min) + if max is not None: + api_params['max'] = self._normalize_value(max) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -1979,7 +2007,8 @@ def create_ip_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -2051,7 +2080,8 @@ def update_ip_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2116,7 +2146,8 @@ def create_line_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2184,8 +2215,10 @@ def update_line_column( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if default is not None: + api_params['default'] = self._normalize_value(default) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2257,7 +2290,8 @@ def create_longtext_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -2331,7 +2365,8 @@ def update_longtext_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2403,7 +2438,8 @@ def create_mediumtext_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -2477,7 +2513,8 @@ def update_mediumtext_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2542,7 +2579,8 @@ def create_point_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2610,8 +2648,10 @@ def update_point_column( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if default is not None: + api_params['default'] = self._normalize_value(default) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2676,7 +2716,8 @@ def create_polygon_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2744,8 +2785,10 @@ def update_polygon_column( api_path = api_path.replace('{key}', str(self._normalize_value(key))) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if default is not None: + api_params['default'] = self._normalize_value(default) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -2822,8 +2865,10 @@ def create_relationship_column( api_params['type'] = self._normalize_value(type) if two_way is not None: api_params['twoWay'] = self._normalize_value(two_way) - api_params['key'] = self._normalize_value(key) - api_params['twoWayKey'] = self._normalize_value(two_way_key) + if key is not None: + api_params['key'] = self._normalize_value(key) + if two_way_key is not None: + api_params['twoWayKey'] = self._normalize_value(two_way_key) if on_delete is not None: api_params['onDelete'] = self._normalize_value(on_delete) @@ -2907,7 +2952,8 @@ def create_string_column( api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -2987,8 +3033,10 @@ def update_string_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['size'] = self._normalize_value(size) - api_params['newKey'] = self._normalize_value(new_key) + if size is not None: + api_params['size'] = self._normalize_value(size) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3060,7 +3108,8 @@ def create_text_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -3134,7 +3183,8 @@ def update_text_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3203,7 +3253,8 @@ def create_url_column( api_params['key'] = self._normalize_value(key) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) @@ -3275,7 +3326,8 @@ def update_url_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['newKey'] = self._normalize_value(new_key) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3354,7 +3406,8 @@ def create_varchar_column( api_params['key'] = self._normalize_value(key) api_params['size'] = self._normalize_value(size) api_params['required'] = self._normalize_value(required) - api_params['default'] = self._normalize_value(default) + if default is not None: + api_params['default'] = self._normalize_value(default) if array is not None: api_params['array'] = self._normalize_value(array) if encrypt is not None: @@ -3431,8 +3484,10 @@ def update_varchar_column( api_params['required'] = self._normalize_value(required) api_params['default'] = self._normalize_value(default) - api_params['size'] = self._normalize_value(size) - api_params['newKey'] = self._normalize_value(new_key) + if size is not None: + api_params['size'] = self._normalize_value(size) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -3632,8 +3687,10 @@ def update_relationship_column( api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) api_path = api_path.replace('{key}', str(self._normalize_value(key))) - api_params['onDelete'] = self._normalize_value(on_delete) - api_params['newKey'] = self._normalize_value(new_key) + if on_delete is not None: + api_params['onDelete'] = self._normalize_value(on_delete) + if new_key is not None: + api_params['newKey'] = self._normalize_value(new_key) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4016,8 +4073,10 @@ def create_row( api_params['rowId'] = self._normalize_value(row_id) api_params['data'] = self._normalize_value(data) - api_params['permissions'] = self._normalize_value(permissions) - api_params['transactionId'] = self._normalize_value(transaction_id) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4079,7 +4138,8 @@ def create_rows( api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) api_params['rows'] = self._normalize_value(rows) - api_params['transactionId'] = self._normalize_value(transaction_id) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4142,7 +4202,8 @@ def upsert_rows( api_path = api_path.replace('{tableId}', str(self._normalize_value(table_id))) api_params['rows'] = self._normalize_value(rows) - api_params['transactionId'] = self._normalize_value(transaction_id) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4207,7 +4268,8 @@ def update_rows( api_params['data'] = self._normalize_value(data) if queries is not None: api_params['queries'] = self._normalize_value(queries) - api_params['transactionId'] = self._normalize_value(transaction_id) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4405,8 +4467,10 @@ def upsert_row( if data is not None: api_params['data'] = self._normalize_value(data) - api_params['permissions'] = self._normalize_value(permissions) - api_params['transactionId'] = self._normalize_value(transaction_id) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4476,8 +4540,10 @@ def update_row( if data is not None: api_params['data'] = self._normalize_value(data) - api_params['permissions'] = self._normalize_value(permissions) - api_params['transactionId'] = self._normalize_value(transaction_id) + if permissions is not None: + api_params['permissions'] = self._normalize_value(permissions) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4612,8 +4678,10 @@ def decrement_row_column( if value is not None: api_params['value'] = self._normalize_value(value) - api_params['min'] = self._normalize_value(min) - api_params['transactionId'] = self._normalize_value(transaction_id) + if min is not None: + api_params['min'] = self._normalize_value(min) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -4690,8 +4758,10 @@ def increment_row_column( if value is not None: api_params['value'] = self._normalize_value(value) - api_params['max'] = self._normalize_value(max) - api_params['transactionId'] = self._normalize_value(transaction_id) + if max is not None: + api_params['max'] = self._normalize_value(max) + if transaction_id is not None: + api_params['transactionId'] = self._normalize_value(transaction_id) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/tokens.py b/appwrite/services/tokens.py index 0c04dfe3..bdff3a61 100644 --- a/appwrite/services/tokens.py +++ b/appwrite/services/tokens.py @@ -107,7 +107,8 @@ def create_file_token( 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_params['expire'] = self._normalize_value(expire) + if expire is not None: + api_params['expire'] = self._normalize_value(expire) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -190,7 +191,8 @@ def update( api_path = api_path.replace('{tokenId}', str(self._normalize_value(token_id))) - api_params['expire'] = self._normalize_value(expire) + if expire is not None: + api_params['expire'] = self._normalize_value(expire) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/users.py b/appwrite/services/users.py index c0e282e6..8eab2dc7 100644 --- a/appwrite/services/users.py +++ b/appwrite/services/users.py @@ -125,8 +125,10 @@ def create( api_params['userId'] = self._normalize_value(user_id) - api_params['email'] = self._normalize_value(email) - api_params['phone'] = self._normalize_value(phone) + if email is not None: + api_params['email'] = self._normalize_value(email) + if phone is not None: + api_params['phone'] = self._normalize_value(phone) if password is not None: api_params['password'] = self._normalize_value(password) if name is not None: diff --git a/appwrite/services/webhooks.py b/appwrite/services/webhooks.py index b9c99e8e..9e998f36 100644 --- a/appwrite/services/webhooks.py +++ b/appwrite/services/webhooks.py @@ -127,7 +127,8 @@ def create( api_params['authUsername'] = self._normalize_value(auth_username) if auth_password is not None: api_params['authPassword'] = self._normalize_value(auth_password) - api_params['secret'] = self._normalize_value(secret) + if secret is not None: + api_params['secret'] = self._normalize_value(secret) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -330,7 +331,8 @@ def update_secret( api_path = api_path.replace('{webhookId}', str(self._normalize_value(webhook_id))) - api_params['secret'] = self._normalize_value(secret) + if secret is not None: + api_params['secret'] = self._normalize_value(secret) response = self.client.call('patch', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), From 122cd9b2dd4a79046d52f7f1eb11533f0ff2e9a9 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Wed, 22 Jul 2026 11:02:50 +0530 Subject: [PATCH 3/5] chore: update Python SDK to 22.1.0 --- CHANGELOG.md | 12 + appwrite/client.py | 4 +- appwrite/enums/database_status.py | 11 + appwrite/enums/database_type.py | 3 + appwrite/enums/project_key_scopes.py | 3 + appwrite/models/__init__.py | 32 +++ appwrite/models/activity_event.py | 33 +++ appwrite/models/app_scope.py | 27 ++ appwrite/models/app_scope_list.py | 19 ++ appwrite/models/app_secret.py | 2 +- appwrite/models/app_secret_plaintext.py | 2 +- appwrite/models/billing_plan.py | 3 + appwrite/models/database.py | 13 +- appwrite/models/database_status.py | 39 +++ .../models/database_status_connections.py | 18 ++ appwrite/models/database_status_replica.py | 24 ++ appwrite/models/database_status_volume.py | 24 ++ appwrite/models/dedicated_database.py | 159 +++++++++++ appwrite/models/dedicated_database_member.py | 24 ++ .../models/dedicated_database_replicas.py | 22 ++ .../dedicated_database_specification.py | 39 +++ .../dedicated_database_specification_list.py | 23 ++ ...edicated_database_specification_pricing.py | 27 ++ appwrite/models/oauth2_consent.py | 42 +++ appwrite/models/oauth2_consent_list.py | 19 ++ appwrite/models/oauth2_consent_token.py | 45 +++ appwrite/models/oauth2_consent_token_list.py | 19 ++ appwrite/models/project.py | 3 + appwrite/services/account.py | 267 ++++++++++++++++++ appwrite/services/apps.py | 39 ++- appwrite/services/backups.py | 17 +- appwrite/services/oauth2.py | 113 +++++++- appwrite/services/sites.py | 7 +- appwrite/services/tables_db.py | 169 ++++++++++- docs/examples/account/delete-consent-token.md | 16 ++ docs/examples/account/delete-consent.md | 15 + docs/examples/account/get-consent-token.md | 19 ++ docs/examples/account/get-consent.md | 18 ++ docs/examples/account/list-consent-tokens.md | 20 ++ docs/examples/account/list-consents.md | 19 ++ docs/examples/apps/list-o-auth-2-scopes.md | 16 ++ docs/examples/backups/create-restoration.md | 3 +- docs/examples/oauth2/authorize-post.md | 31 ++ .../examples/sites/get-deployment-download.md | 3 +- docs/examples/tablesdb/create-failover.md | 19 ++ docs/examples/tablesdb/create.md | 3 +- docs/examples/tablesdb/get-replicas.md | 18 ++ docs/examples/tablesdb/get-status.md | 18 ++ docs/examples/tablesdb/list-specifications.md | 16 ++ docs/examples/tablesdb/update.md | 3 +- pyproject.toml | 2 +- setup.py | 4 +- test/services/test_account.py | 102 +++++++ test/services/test_activities.py | 13 +- test/services/test_apps.py | 16 +- test/services/test_backups.py | 4 +- test/services/test_databases.py | 12 +- test/services/test_oauth2.py | 14 + test/services/test_organization.py | 11 +- test/services/test_project.py | 60 ++-- test/services/test_tables_db.py | 133 ++++++++- 61 files changed, 1828 insertions(+), 83 deletions(-) create mode 100644 appwrite/models/app_scope.py create mode 100644 appwrite/models/app_scope_list.py create mode 100644 appwrite/models/database_status.py create mode 100644 appwrite/models/database_status_connections.py create mode 100644 appwrite/models/database_status_replica.py create mode 100644 appwrite/models/database_status_volume.py create mode 100644 appwrite/models/dedicated_database.py create mode 100644 appwrite/models/dedicated_database_member.py create mode 100644 appwrite/models/dedicated_database_replicas.py create mode 100644 appwrite/models/dedicated_database_specification.py create mode 100644 appwrite/models/dedicated_database_specification_list.py create mode 100644 appwrite/models/dedicated_database_specification_pricing.py create mode 100644 appwrite/models/oauth2_consent.py create mode 100644 appwrite/models/oauth2_consent_list.py create mode 100644 appwrite/models/oauth2_consent_token.py create mode 100644 appwrite/models/oauth2_consent_token_list.py create mode 100644 docs/examples/account/delete-consent-token.md create mode 100644 docs/examples/account/delete-consent.md create mode 100644 docs/examples/account/get-consent-token.md create mode 100644 docs/examples/account/get-consent.md create mode 100644 docs/examples/account/list-consent-tokens.md create mode 100644 docs/examples/account/list-consents.md create mode 100644 docs/examples/apps/list-o-auth-2-scopes.md create mode 100644 docs/examples/oauth2/authorize-post.md create mode 100644 docs/examples/tablesdb/create-failover.md create mode 100644 docs/examples/tablesdb/get-replicas.md create mode 100644 docs/examples/tablesdb/get-status.md create mode 100644 docs/examples/tablesdb/list-specifications.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e66e10b9..d0f3fdfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## 22.1.0 + +* Updated: removed `new_specification` parameter from `backups.create_restoration` +* Updated: `Database.policies` and `Database.archives` are now optional +* Added: account OAuth2 consent methods `list_consents`, `get_consent`, `delete_consent` and consent token methods +* Added: `Oauth2Consent` and `Oauth2ConsentToken` models +* Added: `oauth2.authorize_post` method and `oauth2.introspect` key scope +* Added: `apps.list_o_auth2_scopes` method and `AppScope` model +* Added: geolocation, connection, and SDK attribution fields on `ActivityEvent` +* Added: `token` parameter to `sites.get_deployment_download` +* Updated: `AppSecret.secret` is always empty; the secret is returned only on creation + ## 22.0.0 * Breaking: removed `Health` service and all health models and enums diff --git a/appwrite/client.py b/appwrite/client.py index 0fc438b7..3588e3a7 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -17,11 +17,11 @@ def __init__(self): self._endpoint = 'https://cloud.appwrite.io/v1' self._global_headers = { 'content-type': '', - 'user-agent' : f'AppwritePythonSDK/22.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', + 'user-agent' : f'AppwritePythonSDK/22.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': '22.0.0', + 'x-sdk-version': '22.1.0', 'X-Appwrite-Response-Format' : '1.9.5', } self._config = {} diff --git a/appwrite/enums/database_status.py b/appwrite/enums/database_status.py index 7c5f888f..ed43ed1a 100644 --- a/appwrite/enums/database_status.py +++ b/appwrite/enums/database_status.py @@ -3,4 +3,15 @@ class DatabaseStatus(Enum): PROVISIONING = "provisioning" READY = "ready" + INACTIVE = "inactive" + PAUSED = "paused" FAILED = "failed" + DELETING = "deleting" + DELETED = "deleted" + RESTORING = "restoring" + SCALING = "scaling" + UPGRADING = "upgrading" + MIGRATING = "migrating" + PAUSING = "pausing" + RESUMING = "resuming" + FAILING_OVER = "failing-over" diff --git a/appwrite/enums/database_type.py b/appwrite/enums/database_type.py index 5d2d0aeb..0b713567 100644 --- a/appwrite/enums/database_type.py +++ b/appwrite/enums/database_type.py @@ -5,3 +5,6 @@ class DatabaseType(Enum): TABLESDB = "tablesdb" DOCUMENTSDB = "documentsdb" VECTORSDB = "vectorsdb" + MYSQL = "mysql" + POSTGRESQL = "postgresql" + MONGODB = "mongodb" diff --git a/appwrite/enums/project_key_scopes.py b/appwrite/enums/project_key_scopes.py index 10099119..e29a2454 100644 --- a/appwrite/enums/project_key_scopes.py +++ b/appwrite/enums/project_key_scopes.py @@ -96,9 +96,12 @@ class ProjectKeyScopes(Enum): DEDICATEDDATABASES_EXECUTE = "dedicatedDatabases.execute" DOMAINS_READ = "domains.read" DOMAINS_WRITE = "domains.write" + WAFRULES_READ = "wafRules.read" + WAFRULES_WRITE = "wafRules.write" EVENTS_READ = "events.read" APPS_READ = "apps.read" APPS_WRITE = "apps.write" OAUTH2_READ = "oauth2.read" OAUTH2_WRITE = "oauth2.write" + OAUTH2_INTROSPECT = "oauth2.introspect" USAGE_READ = "usage.read" diff --git a/appwrite/models/__init__.py b/appwrite/models/__init__.py index 8e75504a..07a73030 100644 --- a/appwrite/models/__init__.py +++ b/appwrite/models/__init__.py @@ -220,6 +220,10 @@ from .billing_plan_dedicated_database_limits import BillingPlanDedicatedDatabaseLimits from .billing_plan_supported_addons import BillingPlanSupportedAddons from .block import Block +from .dedicated_database import DedicatedDatabase +from .database_status import DatabaseStatus +from .dedicated_database_member import DedicatedDatabaseMember +from .dedicated_database_replicas import DedicatedDatabaseReplicas from .organization import Organization from .backup_policy import BackupPolicy from .policy_deny_aliased_email import PolicyDenyAliasedEmail @@ -228,10 +232,17 @@ from .policy_deny_corporate_email import PolicyDenyCorporateEmail from .program import Program from .backup_restoration import BackupRestoration +from .dedicated_database_specification import DedicatedDatabaseSpecification +from .dedicated_database_specification_list import DedicatedDatabaseSpecificationList +from .dedicated_database_specification_pricing import DedicatedDatabaseSpecificationPricing +from .database_status_connections import DatabaseStatusConnections +from .database_status_replica import DatabaseStatusReplica +from .database_status_volume import DatabaseStatusVolume from .usage_billing_plan import UsageBillingPlan from .app import App from .app_secret import AppSecret from .app_secret_plaintext import AppSecretPlaintext +from .app_scope import AppScope from .oauth2_authorize import Oauth2Authorize from .oauth2_approve import Oauth2Approve from .oauth2_reject import Oauth2Reject @@ -239,16 +250,21 @@ from .oauth2_device_authorization import Oauth2DeviceAuthorization from .oauth2_par import Oauth2PAR from .oauth2_token import Oauth2Token +from .oauth2_consent import Oauth2Consent +from .oauth2_consent_token import Oauth2ConsentToken from .oauth2_project import Oauth2Project from .oauth2_organization import Oauth2Organization from .oauth2_project_list import Oauth2ProjectList from .oauth2_organization_list import Oauth2OrganizationList +from .oauth2_consent_list import Oauth2ConsentList +from .oauth2_consent_token_list import Oauth2ConsentTokenList from .activity_event_list import ActivityEventList from .backup_archive_list import BackupArchiveList from .backup_policy_list import BackupPolicyList from .backup_restoration_list import BackupRestorationList from .apps_list import AppsList from .app_secret_list import AppSecretList +from .app_scope_list import AppScopeList __all__ = [ 'AppwriteModel', @@ -473,6 +489,10 @@ 'BillingPlanDedicatedDatabaseLimits', 'BillingPlanSupportedAddons', 'Block', + 'DedicatedDatabase', + 'DatabaseStatus', + 'DedicatedDatabaseMember', + 'DedicatedDatabaseReplicas', 'Organization', 'BackupPolicy', 'PolicyDenyAliasedEmail', @@ -481,10 +501,17 @@ 'PolicyDenyCorporateEmail', 'Program', 'BackupRestoration', + 'DedicatedDatabaseSpecification', + 'DedicatedDatabaseSpecificationList', + 'DedicatedDatabaseSpecificationPricing', + 'DatabaseStatusConnections', + 'DatabaseStatusReplica', + 'DatabaseStatusVolume', 'UsageBillingPlan', 'App', 'AppSecret', 'AppSecretPlaintext', + 'AppScope', 'Oauth2Authorize', 'Oauth2Approve', 'Oauth2Reject', @@ -492,14 +519,19 @@ 'Oauth2DeviceAuthorization', 'Oauth2PAR', 'Oauth2Token', + 'Oauth2Consent', + 'Oauth2ConsentToken', 'Oauth2Project', 'Oauth2Organization', 'Oauth2ProjectList', 'Oauth2OrganizationList', + 'Oauth2ConsentList', + 'Oauth2ConsentTokenList', 'ActivityEventList', 'BackupArchiveList', 'BackupPolicyList', 'BackupRestorationList', 'AppsList', 'AppSecretList', + 'AppScopeList', ] diff --git a/appwrite/models/activity_event.py b/appwrite/models/activity_event.py index 11265c1d..eddec37d 100644 --- a/appwrite/models/activity_event.py +++ b/appwrite/models/activity_event.py @@ -37,6 +37,24 @@ class ActivityEvent(AppwriteModel): API mode when event triggered. country : str Location. + continentcode : str + Continent code. + city : str + City name. + subdivisions : str + Region/state chain. + isp : str + Internet service provider. + autonomoussystemnumber : str + Autonomous System Number (ASN). + autonomoussystemorganization : str + Organization that owns the ASN. + connectiontype : str + Connection type (e.g. cable, cellular, corporate). + connectionusagetype : str + User type (e.g. residential, business, hosting). + connectionorganization : str + Registered organization of the IP. time : str Log creation date in ISO 8601 format. projectid : str @@ -45,6 +63,10 @@ class ActivityEvent(AppwriteModel): Team ID. hostname : str Hostname. + sdk : str + Name of the SDK that triggered the event. + sdkversion : str + Version of the SDK that triggered the event. """ id: str = Field(..., alias='$id') actortype: str = Field(..., alias='actorType') @@ -60,7 +82,18 @@ class ActivityEvent(AppwriteModel): ip: str = Field(..., alias='ip') mode: str = Field(..., alias='mode') country: str = Field(..., alias='country') + continentcode: str = Field(..., alias='continentCode') + city: str = Field(..., alias='city') + subdivisions: str = Field(..., alias='subdivisions') + isp: str = Field(..., alias='isp') + autonomoussystemnumber: str = Field(..., alias='autonomousSystemNumber') + autonomoussystemorganization: str = Field(..., alias='autonomousSystemOrganization') + connectiontype: str = Field(..., alias='connectionType') + connectionusagetype: str = Field(..., alias='connectionUsageType') + connectionorganization: str = Field(..., alias='connectionOrganization') time: str = Field(..., alias='time') projectid: str = Field(..., alias='projectId') teamid: str = Field(..., alias='teamId') hostname: str = Field(..., alias='hostname') + sdk: str = Field(..., alias='sdk') + sdkversion: str = Field(..., alias='sdkVersion') diff --git a/appwrite/models/app_scope.py b/appwrite/models/app_scope.py new file mode 100644 index 00000000..4dca2ae2 --- /dev/null +++ b/appwrite/models/app_scope.py @@ -0,0 +1,27 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class AppScope(AppwriteModel): + """ + AppScope + + Attributes + ---------- + value : str + Scope value as requested by apps. + description : str + Human-readable description of what the scope grants. + type : str + What the scope grants access to. One of `account`, `project`, or `organization`. Only `project` and `organization` scopes are installable. + category : str + Scope category, used to group scopes on consent and installation screens. + deprecated : bool + Whether the scope is deprecated. Deprecated scopes can still be requested but should not be offered for new grants. + """ + value: str = Field(..., alias='value') + description: str = Field(..., alias='description') + type: str = Field(..., alias='type') + category: str = Field(..., alias='category') + deprecated: bool = Field(..., alias='deprecated') diff --git a/appwrite/models/app_scope_list.py b/appwrite/models/app_scope_list.py new file mode 100644 index 00000000..f92908e7 --- /dev/null +++ b/appwrite/models/app_scope_list.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .app_scope import AppScope + +class AppScopeList(AppwriteModel): + """ + App scopes list + + Attributes + ---------- + total : float + Total number of scopes that matched your query. + scopes : List[AppScope] + List of scopes. + """ + total: float = Field(..., alias='total') + scopes: List[AppScope] = Field(..., alias='scopes') diff --git a/appwrite/models/app_secret.py b/appwrite/models/app_secret.py index dc7def83..348b2cbf 100644 --- a/appwrite/models/app_secret.py +++ b/appwrite/models/app_secret.py @@ -18,7 +18,7 @@ class AppSecret(AppwriteModel): appid : str Application ID this secret belongs to. secret : str - Hashed application client secret. + Always empty. The application client secret is returned only once, in the response of the createSecret method. hint : str Last few characters of the client secret, used to help identify it. createdbyid : str diff --git a/appwrite/models/app_secret_plaintext.py b/appwrite/models/app_secret_plaintext.py index ec6d9ae5..18a32d13 100644 --- a/appwrite/models/app_secret_plaintext.py +++ b/appwrite/models/app_secret_plaintext.py @@ -18,7 +18,7 @@ class AppSecretPlaintext(AppwriteModel): appid : str Application ID this secret belongs to. secret : str - Application client secret. Returned in full only when the secret is created; subsequent reads return a masked value. + Application client secret. Returned only when the secret is created; subsequent reads always return an empty value. hint : str Last few characters of the client secret, used to help identify it. createdbyid : str diff --git a/appwrite/models/billing_plan.py b/appwrite/models/billing_plan.py index 64db521b..c37db9d3 100644 --- a/appwrite/models/billing_plan.py +++ b/appwrite/models/billing_plan.py @@ -40,6 +40,8 @@ class BillingPlan(AppwriteModel): Members webhooks : float Webhooks + wafrules : float + Maximum WAF rules per project projects : float Projects platforms : float @@ -165,6 +167,7 @@ class BillingPlan(AppwriteModel): screenshotsgenerated: float = Field(..., alias='screenshotsGenerated') members: float = Field(..., alias='members') webhooks: float = Field(..., alias='webhooks') + wafrules: float = Field(..., alias='wafRules') projects: float = Field(..., alias='projects') platforms: float = Field(..., alias='platforms') users: float = Field(..., alias='users') diff --git a/appwrite/models/database.py b/appwrite/models/database.py index b491a89a..f83198fd 100644 --- a/appwrite/models/database.py +++ b/appwrite/models/database.py @@ -26,10 +26,12 @@ class Database(AppwriteModel): type : DatabaseType Database type. status : Optional[DatabaseStatus] - Database status. Possible values: `provisioning`, `ready` or `failed` - policies : List[BackupPolicy] + Dedicated database lifecycle status. Null when the database has no valid dedicated backing. + replicas : Optional[float] + Number of secondary high availability replicas, excluding the primary. Null when backing configuration is unavailable. + policies : Optional[List[BackupPolicy]] Database backup policies. - archives : List[BackupArchive] + archives : Optional[List[BackupArchive]] Database backup archives. """ id: str = Field(..., alias='$id') @@ -39,5 +41,6 @@ class Database(AppwriteModel): enabled: bool = Field(..., alias='enabled') type: DatabaseType = Field(..., alias='type') status: Optional[DatabaseStatus] = Field(default=None, alias='status') - policies: List[BackupPolicy] = Field(..., alias='policies') - archives: List[BackupArchive] = Field(..., alias='archives') + replicas: Optional[float] = Field(default=None, alias='replicas') + policies: Optional[List[BackupPolicy]] = Field(default=None, alias='policies') + archives: Optional[List[BackupArchive]] = Field(default=None, alias='archives') diff --git a/appwrite/models/database_status.py b/appwrite/models/database_status.py new file mode 100644 index 00000000..dd469917 --- /dev/null +++ b/appwrite/models/database_status.py @@ -0,0 +1,39 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .database_status_connections import DatabaseStatusConnections +from .database_status_replica import DatabaseStatusReplica +from .database_status_volume import DatabaseStatusVolume + +class DatabaseStatus(AppwriteModel): + """ + Status + + Attributes + ---------- + health : str + Overall health status: healthy, degraded, or unhealthy. + ready : bool + Whether the database is ready to accept connections. + engine : str + Database engine: postgresql, mysql, mariadb, or mongodb. + version : str + Database engine version. + uptime : float + Database uptime in seconds. + connections : DatabaseStatusConnections + Connection statistics. + replicas : List[DatabaseStatusReplica] + List of database replicas and their status. + volumes : List[DatabaseStatusVolume] + Storage volume information. + """ + health: str = Field(..., alias='health') + ready: bool = Field(..., alias='ready') + engine: str = Field(..., alias='engine') + version: str = Field(..., alias='version') + uptime: float = Field(..., alias='uptime') + connections: DatabaseStatusConnections = Field(..., alias='connections') + replicas: List[DatabaseStatusReplica] = Field(..., alias='replicas') + volumes: List[DatabaseStatusVolume] = Field(..., alias='volumes') diff --git a/appwrite/models/database_status_connections.py b/appwrite/models/database_status_connections.py new file mode 100644 index 00000000..fe335596 --- /dev/null +++ b/appwrite/models/database_status_connections.py @@ -0,0 +1,18 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class DatabaseStatusConnections(AppwriteModel): + """ + Connections + + Attributes + ---------- + current : float + Current number of active connections. + max : float + Maximum allowed connections. + """ + current: float = Field(..., alias='current') + max: float = Field(..., alias='max') diff --git a/appwrite/models/database_status_replica.py b/appwrite/models/database_status_replica.py new file mode 100644 index 00000000..73cf79e4 --- /dev/null +++ b/appwrite/models/database_status_replica.py @@ -0,0 +1,24 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class DatabaseStatusReplica(AppwriteModel): + """ + Replica + + Attributes + ---------- + index : float + StatefulSet pod index (0 = primary, 1+ = replicas). + role : str + Replica role: primary or replica. + healthy : bool + Whether the replica is healthy. + lagseconds : Optional[float] + Replication lag in seconds (null for primary). + """ + index: float = Field(..., alias='index') + role: str = Field(..., alias='role') + healthy: bool = Field(..., alias='healthy') + lagseconds: Optional[float] = Field(default=None, alias='lagSeconds') diff --git a/appwrite/models/database_status_volume.py b/appwrite/models/database_status_volume.py new file mode 100644 index 00000000..0b14ba8c --- /dev/null +++ b/appwrite/models/database_status_volume.py @@ -0,0 +1,24 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class DatabaseStatusVolume(AppwriteModel): + """ + Volume + + Attributes + ---------- + path : str + Mount path of the volume. + usedpercent : str + Percentage of storage used. + available : str + Available storage space. + mounted : bool + Whether the volume is mounted. + """ + path: str = Field(..., alias='path') + usedpercent: str = Field(..., alias='usedPercent') + available: str = Field(..., alias='available') + mounted: bool = Field(..., alias='mounted') diff --git a/appwrite/models/dedicated_database.py b/appwrite/models/dedicated_database.py new file mode 100644 index 00000000..9f943228 --- /dev/null +++ b/appwrite/models/dedicated_database.py @@ -0,0 +1,159 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class DedicatedDatabase(AppwriteModel): + """ + DedicatedDatabase + + Attributes + ---------- + id : str + Dedicated database ID. + createdat : str + Database creation time in ISO 8601 format. + updatedat : str + Database update date in ISO 8601 format. + projectid : str + Project ID that owns this database. + name : str + Database display name. + api : str + Product API that owns this database: tablesdb, documentsdb, vectorsdb, mysql, postgresql, or mongodb. + engine : str + Database engine: postgresql, mysql, mariadb, or mongodb. + version : str + Database engine version. + specification : str + Specification identifier. + backend : str + Database backend provider. Possible values: prisma, edge. + hostname : str + Database hostname for connections. + connectionport : float + Database port for connections. + connectionuser : str + Database username for connections. + connectionpassword : str + Database password for connections. + connectionstring : str + Full database connection string (URI format). + ssl : bool + Whether SSL/TLS is required for client connections. + status : str + Database status. Possible values: provisioning, ready, inactive, paused, failed, deleted, restoring, scaling. + containerstatus : str + Container status for lifecycle-managed database runtimes: active or inactive. + lastaccessedat : Optional[str] + Last activity timestamp in ISO 8601 format. + idleuntil : Optional[str] + Display-only timestamp when the database is expected to be considered idle (ISO 8601 format). Derived from last activity; lifecycle transitions are driven by lifecycleState. + lifecyclestate : str + Idle-lifecycle state of the database. Possible values: active, warm, cold, hibernated. + idletimeoutminutes : float + Minutes of inactivity before container scales to zero. + cpu : float + CPU allocated in millicores. + memory : float + Memory allocated in MB. + storage : float + Storage allocated in GB. + storageclass : str + Storage class. Currently always 'ssd'; DigitalOcean exposes a single block-storage class. + storagemaxgb : float + Maximum storage allowed in GB. 0 means use system default. + nodepool : str + Kubernetes node pool where the database is scheduled. + replicas : float + Number of high availability replicas. High availability is enabled when greater than 0. + syncmode : str + Replication sync mode: async, sync, or quorum. + crossregionreplicas : float + Number of cross-region replicas. Cross-region availability is enabled when greater than 0. + networkmaxconnections : float + Maximum concurrent connections. + networkidletimeoutseconds : float + Connection idle timeout in seconds. + networkipallowlist : List[Any] + IP addresses/CIDR ranges allowed to connect. + backupenabled : bool + Whether automatic backups are enabled. + pitr : bool + Whether point-in-time recovery is enabled. + pitrretentiondays : float + Number of days to retain PITR data. + storageautoscaling : bool + Whether automatic storage expansion is enabled. + storageautoscalingthresholdpercent : float + Storage usage percentage that triggers automatic expansion. + storageautoscalingmaxgb : float + Maximum storage size in GB for autoscaling. 0 means no limit. + maintenancewindowday : str + Day of the week for the maintenance window. Possible values: sun, mon, tue, wed, thu, fri, sat. + maintenancewindowhourutc : float + Hour in UTC (0-23) when the maintenance window starts. + metricsenabled : bool + Whether metrics collection is enabled. + sqlapienabled : bool + Whether the SQL API sidecar is enabled for this database. + sqlapiallowedstatements : List[Any] + Statement types accepted by the SQL API. Defaults to read/write DML only; DDL/DCL types (CREATE, ALTER, DROP, TRUNCATE, GRANT, REVOKE) are opt-in per database. Allowed values: SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, DROP, TRUNCATE, GRANT, REVOKE. + sqlapimaxrows : float + Maximum rows returned per SQL API execution. Results larger than this are truncated. + sqlapimaxbytes : float + Maximum serialised SQL API result payload in bytes. Results larger than this are truncated. + sqlapitimeoutseconds : float + Maximum server-side SQL API execution time in seconds before the query is cancelled. + error : str + Error message if status is failed. + """ + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + projectid: str = Field(..., alias='projectId') + name: str = Field(..., alias='name') + api: str = Field(..., alias='api') + engine: str = Field(..., alias='engine') + version: str = Field(..., alias='version') + specification: str = Field(..., alias='specification') + backend: str = Field(..., alias='backend') + hostname: str = Field(..., alias='hostname') + connectionport: float = Field(..., alias='connectionPort') + connectionuser: str = Field(..., alias='connectionUser') + connectionpassword: str = Field(..., alias='connectionPassword') + connectionstring: str = Field(..., alias='connectionString') + ssl: bool = Field(..., alias='ssl') + status: str = Field(..., alias='status') + containerstatus: str = Field(..., alias='containerStatus') + lastaccessedat: Optional[str] = Field(default=None, alias='lastAccessedAt') + idleuntil: Optional[str] = Field(default=None, alias='idleUntil') + lifecyclestate: str = Field(..., alias='lifecycleState') + idletimeoutminutes: float = Field(..., alias='idleTimeoutMinutes') + cpu: float = Field(..., alias='cpu') + memory: float = Field(..., alias='memory') + storage: float = Field(..., alias='storage') + storageclass: str = Field(..., alias='storageClass') + storagemaxgb: float = Field(..., alias='storageMaxGb') + nodepool: str = Field(..., alias='nodePool') + replicas: float = Field(..., alias='replicas') + syncmode: str = Field(..., alias='syncMode') + crossregionreplicas: float = Field(..., alias='crossRegionReplicas') + networkmaxconnections: float = Field(..., alias='networkMaxConnections') + networkidletimeoutseconds: float = Field(..., alias='networkIdleTimeoutSeconds') + networkipallowlist: List[Any] = Field(..., alias='networkIPAllowlist') + backupenabled: bool = Field(..., alias='backupEnabled') + pitr: bool = Field(..., alias='pitr') + pitrretentiondays: float = Field(..., alias='pitrRetentionDays') + storageautoscaling: bool = Field(..., alias='storageAutoscaling') + storageautoscalingthresholdpercent: float = Field(..., alias='storageAutoscalingThresholdPercent') + storageautoscalingmaxgb: float = Field(..., alias='storageAutoscalingMaxGb') + maintenancewindowday: str = Field(..., alias='maintenanceWindowDay') + maintenancewindowhourutc: float = Field(..., alias='maintenanceWindowHourUtc') + metricsenabled: bool = Field(..., alias='metricsEnabled') + sqlapienabled: bool = Field(..., alias='sqlApiEnabled') + sqlapiallowedstatements: List[Any] = Field(..., alias='sqlApiAllowedStatements') + sqlapimaxrows: float = Field(..., alias='sqlApiMaxRows') + sqlapimaxbytes: float = Field(..., alias='sqlApiMaxBytes') + sqlapitimeoutseconds: float = Field(..., alias='sqlApiTimeoutSeconds') + error: str = Field(..., alias='error') diff --git a/appwrite/models/dedicated_database_member.py b/appwrite/models/dedicated_database_member.py new file mode 100644 index 00000000..6cb4daa3 --- /dev/null +++ b/appwrite/models/dedicated_database_member.py @@ -0,0 +1,24 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class DedicatedDatabaseMember(AppwriteModel): + """ + Member + + Attributes + ---------- + id : str + Member identifier. + role : str + Member role. Possible values: primary (accepts reads and writes), replica (read-only follower). + status : str + Member pod status. Possible values: provisioning (pod missing or Pending), starting (Running but not Ready), active (Running and Ready), failed (Failed phase or CrashLoopBackOff container), or the lowercased pod phase reported by the cluster. + lagseconds : float + Replication lag in seconds. + """ + id: str = Field(..., alias='$id') + role: str = Field(..., alias='role') + status: str = Field(..., alias='status') + lagseconds: float = Field(..., alias='lagSeconds') diff --git a/appwrite/models/dedicated_database_replicas.py b/appwrite/models/dedicated_database_replicas.py new file mode 100644 index 00000000..fa19a87a --- /dev/null +++ b/appwrite/models/dedicated_database_replicas.py @@ -0,0 +1,22 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .dedicated_database_member import DedicatedDatabaseMember + +class DedicatedDatabaseReplicas(AppwriteModel): + """ + Replicas + + Attributes + ---------- + replicas : float + Number of configured replicas. Zero means high availability is disabled. + syncmode : str + Replication sync mode. Possible values: async (asynchronous, fastest), sync (synchronous, strong consistency), quorum (quorum-based, majority of replicas must confirm). + members : List[DedicatedDatabaseMember] + Per-pod statuses for the primary and every replica. + """ + replicas: float = Field(..., alias='replicas') + syncmode: str = Field(..., alias='syncMode') + members: List[DedicatedDatabaseMember] = Field(..., alias='members') diff --git a/appwrite/models/dedicated_database_specification.py b/appwrite/models/dedicated_database_specification.py new file mode 100644 index 00000000..d5f37295 --- /dev/null +++ b/appwrite/models/dedicated_database_specification.py @@ -0,0 +1,39 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class DedicatedDatabaseSpecification(AppwriteModel): + """ + Specification + + Attributes + ---------- + slug : str + Specification slug. Use this value when creating a dedicated database. + name : str + Human readable specification name. + price : float + Monthly price of the specification in USD. + cpu : float + Allocated CPU in millicores. + memory : float + Allocated memory in MB. + maxconnections : float + Maximum number of concurrent connections. + includedstorage : float + Included storage in GB before overage charges apply. + includedbandwidth : float + Included bandwidth in GB before overage charges apply. + enabled : bool + Whether the specification is available on the current plan. + """ + slug: str = Field(..., alias='slug') + name: str = Field(..., alias='name') + price: float = Field(..., alias='price') + cpu: float = Field(..., alias='cpu') + memory: float = Field(..., alias='memory') + maxconnections: float = Field(..., alias='maxConnections') + includedstorage: float = Field(..., alias='includedStorage') + includedbandwidth: float = Field(..., alias='includedBandwidth') + enabled: bool = Field(..., alias='enabled') diff --git a/appwrite/models/dedicated_database_specification_list.py b/appwrite/models/dedicated_database_specification_list.py new file mode 100644 index 00000000..8cbd905d --- /dev/null +++ b/appwrite/models/dedicated_database_specification_list.py @@ -0,0 +1,23 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .dedicated_database_specification import DedicatedDatabaseSpecification +from .dedicated_database_specification_pricing import DedicatedDatabaseSpecificationPricing + +class DedicatedDatabaseSpecificationList(AppwriteModel): + """ + SpecificationList + + Attributes + ---------- + specifications : List[DedicatedDatabaseSpecification] + List of dedicated database specifications. + total : float + Total number of specifications. + pricing : DedicatedDatabaseSpecificationPricing + Overage and add-on pricing shared across all specifications. + """ + specifications: List[DedicatedDatabaseSpecification] = Field(..., alias='specifications') + total: float = Field(..., alias='total') + pricing: DedicatedDatabaseSpecificationPricing = Field(..., alias='pricing') diff --git a/appwrite/models/dedicated_database_specification_pricing.py b/appwrite/models/dedicated_database_specification_pricing.py new file mode 100644 index 00000000..43d3b1e7 --- /dev/null +++ b/appwrite/models/dedicated_database_specification_pricing.py @@ -0,0 +1,27 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class DedicatedDatabaseSpecificationPricing(AppwriteModel): + """ + SpecificationPricing + + Attributes + ---------- + storageoveragerate : float + Price per GB of storage above the included amount, per month, in USD. + bandwidthoveragerate : float + Price per GB of bandwidth above the included amount, per month, in USD. + replicarate : float + High availability replica price as a fraction of the specification cost. + crossregionreplicarate : float + Cross-region replica price as a fraction of the specification cost. + pitrrate : float + Point-in-time recovery price as a fraction of the specification cost. + """ + storageoveragerate: float = Field(..., alias='storageOverageRate') + bandwidthoveragerate: float = Field(..., alias='bandwidthOverageRate') + replicarate: float = Field(..., alias='replicaRate') + crossregionreplicarate: float = Field(..., alias='crossRegionReplicaRate') + pitrrate: float = Field(..., alias='pitrRate') diff --git a/appwrite/models/oauth2_consent.py b/appwrite/models/oauth2_consent.py new file mode 100644 index 00000000..6675856b --- /dev/null +++ b/appwrite/models/oauth2_consent.py @@ -0,0 +1,42 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class Oauth2Consent(AppwriteModel): + """ + OAuth2 Consent + + Attributes + ---------- + id : str + Consent ID. + createdat : str + Consent creation time in ISO 8601 format. + updatedat : str + Consent update date in ISO 8601 format. + userid : str + ID of the user the consent belongs to. + appid : str + ID of the registered app the consent was given to. Empty for URL-form (CIMD) clients. + cimdurl : str + Client ID metadata document URL of the client the consent was given to. Empty for registered apps. + scopes : List[Any] + OAuth2 scopes the user consented to. + resources : List[Any] + RFC 8707 resource indicators the user consented to. + authorizationdetails : str + Authorization details the user consented to, as a JSON string. Each entry has a `type` plus project-defined fields. + expire : str + Consent expiration time in ISO 8601 format. Empty when the consent has no token-bound expiry yet. + """ + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + userid: str = Field(..., alias='userId') + appid: str = Field(..., alias='appId') + cimdurl: str = Field(..., alias='cimdUrl') + scopes: List[Any] = Field(..., alias='scopes') + resources: List[Any] = Field(..., alias='resources') + authorizationdetails: str = Field(..., alias='authorizationDetails') + expire: str = Field(..., alias='expire') diff --git a/appwrite/models/oauth2_consent_list.py b/appwrite/models/oauth2_consent_list.py new file mode 100644 index 00000000..610353b6 --- /dev/null +++ b/appwrite/models/oauth2_consent_list.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .oauth2_consent import Oauth2Consent + +class Oauth2ConsentList(AppwriteModel): + """ + OAuth2 consents list + + Attributes + ---------- + total : float + Total number of consents that matched your query. + consents : List[Oauth2Consent] + List of consents. + """ + total: float = Field(..., alias='total') + consents: List[Oauth2Consent] = Field(..., alias='consents') diff --git a/appwrite/models/oauth2_consent_token.py b/appwrite/models/oauth2_consent_token.py new file mode 100644 index 00000000..2fd26ca5 --- /dev/null +++ b/appwrite/models/oauth2_consent_token.py @@ -0,0 +1,45 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class Oauth2ConsentToken(AppwriteModel): + """ + OAuth2 Consent Token + + Attributes + ---------- + id : str + Token family ID. + createdat : str + Token creation time in ISO 8601 format. + updatedat : str + Token update date in ISO 8601 format. Refreshing the token family updates this. + consentid : str + ID of the consent the token family was issued under. + userid : str + ID of the user the token family belongs to. + appid : str + ID of the registered app the token family was issued to. Empty for URL-form (CIMD) clients. + cimdurl : str + Client ID metadata document URL of the client the token family was issued to. Empty for registered apps. + scopes : List[Any] + OAuth2 scopes granted on the token family. + resources : List[Any] + RFC 8707 resource indicators granted on the token family. + authorizationdetails : str + Authorization details granted on the token family, as a JSON string. Each entry has a `type` plus project-defined fields. + expire : str + Expiration time of the current access token of this family in ISO 8601 format. + """ + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + consentid: str = Field(..., alias='consentId') + userid: str = Field(..., alias='userId') + appid: str = Field(..., alias='appId') + cimdurl: str = Field(..., alias='cimdUrl') + scopes: List[Any] = Field(..., alias='scopes') + resources: List[Any] = Field(..., alias='resources') + authorizationdetails: str = Field(..., alias='authorizationDetails') + expire: str = Field(..., alias='expire') diff --git a/appwrite/models/oauth2_consent_token_list.py b/appwrite/models/oauth2_consent_token_list.py new file mode 100644 index 00000000..e4864cdb --- /dev/null +++ b/appwrite/models/oauth2_consent_token_list.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .oauth2_consent_token import Oauth2ConsentToken + +class Oauth2ConsentTokenList(AppwriteModel): + """ + OAuth2 consent tokens list + + Attributes + ---------- + total : float + Total number of tokens that matched your query. + tokens : List[Oauth2ConsentToken] + List of tokens. + """ + total: float = Field(..., alias='total') + tokens: List[Oauth2ConsentToken] = Field(..., alias='tokens') diff --git a/appwrite/models/project.py b/appwrite/models/project.py index 38e41404..3dd2ca01 100644 --- a/appwrite/models/project.py +++ b/appwrite/models/project.py @@ -69,6 +69,8 @@ class Project(AppwriteModel): Project blocks information consoleaccessedat : str Last time the project was accessed via console. Used with plan's projectInactivityDays to determine if project is paused. + wafenabled : bool + Whether WAF enforcement is enabled for the project. billinglimits : Optional[BillingLimits] Billing limits reached oauth2serverenabled : Optional[bool] @@ -129,6 +131,7 @@ class Project(AppwriteModel): protocols: List[ProjectProtocol] = Field(..., alias='protocols') blocks: List[Block] = Field(..., alias='blocks') consoleaccessedat: str = Field(..., alias='consoleAccessedAt') + wafenabled: bool = Field(..., alias='wafEnabled') billinglimits: Optional[BillingLimits] = Field(default=None, alias='billingLimits') oauth2serverenabled: Optional[bool] = Field(default=None, alias='oAuth2ServerEnabled') oauth2serverauthorizationurl: Optional[str] = Field(default=None, alias='oAuth2ServerAuthorizationUrl') diff --git a/appwrite/services/account.py b/appwrite/services/account.py index 5af533a9..492d9102 100644 --- a/appwrite/services/account.py +++ b/appwrite/services/account.py @@ -4,6 +4,10 @@ from ..exception import AppwriteException from appwrite.utils.deprecated import deprecated from ..models.user import User +from ..models.oauth2_consent_list import Oauth2ConsentList +from ..models.oauth2_consent import Oauth2Consent +from ..models.oauth2_consent_token_list import Oauth2ConsentTokenList +from ..models.oauth2_consent_token import Oauth2ConsentToken from ..models.identity_list import IdentityList from ..models.jwt import Jwt from ..models.log_list import LogList @@ -124,6 +128,269 @@ def create( return User.with_data(response, model_type) + def list_consents( + self, + queries: Optional[List[str]] = None, + total: Optional[bool] = None + ) -> Oauth2ConsentList: + """ + Get a list of the OAuth2 consents the current user has given to third-party apps. + + 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. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + + Returns + ------- + Oauth2ConsentList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/consents' + 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=Oauth2ConsentList) + + + def get_consent( + self, + consent_id: str + ) -> Oauth2Consent: + """ + Get an OAuth2 consent the current user has given to a third-party app by its unique ID. + + Parameters + ---------- + consent_id : str + Consent unique ID. + + Returns + ------- + Oauth2Consent + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/consents/{consentId}' + api_params = {} + if consent_id is None: + raise AppwriteException('Missing required parameter: "consent_id"') + + api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_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=Oauth2Consent) + + + def delete_consent( + self, + consent_id: str + ) -> Dict[str, Any]: + """ + Delete an OAuth2 consent by its unique ID. All token families issued under the consent are revoked, and the app must ask for consent again to regain access. + + Parameters + ---------- + consent_id : str + Consent unique ID. + + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/consents/{consentId}' + api_params = {} + if consent_id is None: + raise AppwriteException('Missing required parameter: "consent_id"') + + api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_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 + + + def list_consent_tokens( + self, + consent_id: str, + queries: Optional[List[str]] = None, + total: Optional[bool] = None + ) -> Oauth2ConsentTokenList: + """ + Get a list of the token families issued under an OAuth2 consent. Each entry represents one authorized device or session; the token secrets themselves are never returned. + + Parameters + ---------- + consent_id : str + Consent unique 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. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + + Returns + ------- + Oauth2ConsentTokenList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/consents/{consentId}/tokens' + api_params = {} + if consent_id is None: + raise AppwriteException('Missing required parameter: "consent_id"') + + api_path = api_path.replace('{consentId}', str(self._normalize_value(consent_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', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=Oauth2ConsentTokenList) + + + def get_consent_token( + self, + consent_id: str, + token_id: str + ) -> Oauth2ConsentToken: + """ + Get a token family issued under an OAuth2 consent by its unique ID. The token secrets themselves are never returned. + + Parameters + ---------- + consent_id : str + Consent unique ID. + token_id : str + Token unique ID. + + Returns + ------- + Oauth2ConsentToken + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/consents/{consentId}/tokens/{tokenId}' + api_params = {} + if consent_id is None: + raise AppwriteException('Missing required parameter: "consent_id"') + + if token_id is None: + 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))) + + + 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=Oauth2ConsentToken) + + + def delete_consent_token( + self, + consent_id: str, + token_id: str + ) -> Dict[str, Any]: + """ + Delete a token family issued under an OAuth2 consent by its unique ID. The access and refresh tokens of the family stop working immediately; other token families and the consent itself are unaffected. + + Parameters + ---------- + consent_id : str + Consent unique ID. + token_id : str + Token unique ID. + + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/account/consents/{consentId}/tokens/{tokenId}' + api_params = {} + if consent_id is None: + raise AppwriteException('Missing required parameter: "consent_id"') + + if token_id is None: + 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))) + + + 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_email( self, email: str, diff --git a/appwrite/services/apps.py b/appwrite/services/apps.py index ab3e9110..8b8e3285 100644 --- a/appwrite/services/apps.py +++ b/appwrite/services/apps.py @@ -5,6 +5,7 @@ from appwrite.utils.deprecated import deprecated from ..models.apps_list import AppsList from ..models.app import App +from ..models.app_scope_list import AppScopeList from ..models.app_secret_list import AppSecretList from ..models.app_secret_plaintext import AppSecretPlaintext from ..models.app_secret import AppSecret @@ -88,7 +89,7 @@ def create( name : str Application name. redirect_uris : List[str] - Redirect URIs (array of valid URLs). + Redirect URIs. Each must be an https URL, an http loopback URL (localhost, 127.0.0.1, [::1]), or a private-use scheme URI (e.g. com.example.app:/oauth), and must not contain a fragment. description : Optional[str] Application description shown to users during OAuth2 consent. client_uri : Optional[str] @@ -112,7 +113,7 @@ def create( data_deletion_url : Optional[str] Application data deletion URL shown to users during OAuth2 consent. post_logout_redirect_uris : Optional[List[str]] - Post-logout redirect URIs for OpenID Connect RP-Initiated Logout (array of valid URLs). After ending the user session, the logout endpoint only redirects to URIs in this list. + Post-logout redirect URIs for OpenID Connect RP-Initiated Logout. Each must be an https URL, an http loopback URL, or a private-use scheme URI, and must not contain a fragment. After ending the user session, the logout endpoint only redirects to URIs in this list. enabled : Optional[bool] Is application enabled? type : Optional[str] @@ -190,6 +191,34 @@ def create( return self._parse_response(response, model=App) + def list_o_auth2_scopes( + self + ) -> AppScopeList: + """ + List scopes an application can request during the OAuth2 flow. + + Returns + ------- + AppScopeList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/scopes/oauth2' + api_params = {} + + 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=AppScopeList) + + def get( self, app_id: str @@ -200,7 +229,7 @@ def get( Parameters ---------- app_id : str - Application unique ID. + Application unique ID or HTTPS client ID metadata document URL. Returns ------- @@ -284,9 +313,9 @@ def update( enabled : Optional[bool] Is application enabled? redirect_uris : Optional[List[str]] - Redirect URIs (array of valid URLs). + Redirect URIs. Each must be an https URL, an http loopback URL (localhost, 127.0.0.1, [::1]), or a private-use scheme URI (e.g. com.example.app:/oauth), and must not contain a fragment. post_logout_redirect_uris : Optional[List[str]] - Post-logout redirect URIs for OpenID Connect RP-Initiated Logout (array of valid URLs). After ending the user session, the logout endpoint only redirects to URIs in this list. + Post-logout redirect URIs for OpenID Connect RP-Initiated Logout. Each must be an https URL, an http loopback URL, or a private-use scheme URI, and must not contain a fragment. After ending the user session, the logout endpoint only redirects to URIs in this list. type : Optional[str] OAuth2 client type. Use `public` for SPAs, mobile, and native apps that cannot keep a `client_secret` — PKCE is then required at the token endpoint. Use `confidential` for server-side clients that present a `client_secret`. Defaults to `confidential`. device_flow : Optional[bool] diff --git a/appwrite/services/backups.py b/appwrite/services/backups.py index 5aac7329..c8b782c5 100644 --- a/appwrite/services/backups.py +++ b/appwrite/services/backups.py @@ -434,13 +434,18 @@ def create_restoration( archive_id: str, services: List[BackupServices], new_resource_id: Optional[str] = None, - new_resource_name: Optional[str] = None, - new_specification: Optional[str] = None + new_resource_name: Optional[str] = None ) -> BackupRestoration: """ Create and trigger a new restoration for a backup on a project. - When restoring a DocumentsDB or VectorsDB database to a new resource, pass `newSpecification` to provision the restored database on a different specification than the archived one (for example, restoring onto a larger or smaller dedicated database). Use `serverless` to restore onto the shared pool, or a dedicated specification slug to restore onto a dedicated database of that size. The specification must be permitted by the organization's plan. `newSpecification` is not supported for legacy/TablesDB databases or for bucket restores. + For a backup of one database, the restoration resolves its destination before it is queued. Pass `newResourceId` to restore into that database ID, including the archived database ID to overwrite it. When `newResourceId` is omitted, a new database ID is generated and returned in `options`. + + The restoration migration records the archived database in `resourceId` and `resourceType`, and the resolved database in `destinationResourceId` and `destinationResourceType`. Database types are stored canonically as `database`, `documentsdb`, or `vectorsdb`. Project-wide restorations leave these fields empty because they do not have a single source or destination database. + + To list every migration related to one database, use its canonical type in a nested `OR(AND(...), AND(...), AND(...))` across the root, parent, and destination relation pairs: `(resourceType, resourceId)`, `(parentResourceType, parentResourceId)`, and `(destinationResourceType, destinationResourceId)`. Legacy and TablesDB databases use `database`; the operational `resourceType` of a table migration is not rewritten to `tablesdb`. + + When restoring a DocumentsDB or VectorsDB database to a new resource from a dedicated source, the restore provisions a fresh dedicated backing database at the source database's own specification. Parameters @@ -450,11 +455,9 @@ def create_restoration( services : List[BackupServices] Array of services to restore new_resource_id : Optional[str] - Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. + Destination resource ID. Omit to generate a new ID, or pass the archived resource ID to overwrite it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars. new_resource_name : Optional[str] Database name. Max length: 128 chars. - new_specification : Optional[str] - Specification to provision the restored database on, when restoring a DocumentsDB or VectorsDB database to a new resource. Defaults to the archived database's specification. Use `serverless` for the shared pool or a dedicated specification slug. Returns ------- @@ -482,8 +485,6 @@ def create_restoration( api_params['newResourceId'] = self._normalize_value(new_resource_id) if new_resource_name is not None: api_params['newResourceName'] = self._normalize_value(new_resource_name) - if new_specification is not None: - api_params['newSpecification'] = self._normalize_value(new_specification) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/oauth2.py b/appwrite/services/oauth2.py index 21612207..cf7f0bc6 100644 --- a/appwrite/services/oauth2.py +++ b/appwrite/services/oauth2.py @@ -91,7 +91,7 @@ def authorize( Parameters ---------- client_id : Optional[str] - OAuth2 client ID. + OAuth2 client ID. Either a registered app ID or an HTTPS client ID metadata document URL. redirect_uri : Optional[str] Redirect URI where visitor will be redirected after authorization, whether successful or not. response_type : Optional[str] @@ -170,6 +170,109 @@ def authorize( return self._parse_response(response, model=Oauth2Authorize) + def authorize_post( + self, + client_id: Optional[str] = None, + redirect_uri: Optional[str] = None, + response_type: Optional[str] = None, + scope: Optional[str] = None, + state: Optional[str] = None, + nonce: Optional[str] = None, + code_challenge: Optional[str] = None, + code_challenge_method: Optional[str] = None, + prompt: Optional[str] = None, + max_age: Optional[float] = None, + authorization_details: Optional[str] = None, + resource: Optional[str] = None, + audience: Optional[str] = None, + request_uri: Optional[str] = None + ) -> Oauth2Authorize: + """ + Begin the OAuth2 authorization flow. When called without a session, the user is redirected to the consent screen without grant ID. When called with a session, the redirect URL includes param for grant ID. You can pass Accept header of `application/json` to receive a JSON response instead of a redirect. + + Parameters + ---------- + client_id : Optional[str] + OAuth2 client ID. Either a registered app ID or an HTTPS client ID metadata document URL. + redirect_uri : Optional[str] + Redirect URI where visitor will be redirected after authorization, whether successful or not. + response_type : Optional[str] + OAuth2 / OIDC response type. One of `code` (Authorization Code Flow), `id_token` (Implicit Flow, OIDC login only), or `code id_token` (Hybrid Flow). + scope : Optional[str] + Space-separated OAuth2 scopes. Can include project scopes, and built-in scopes: `openid`, `email`, `profile`, `phone`. + state : Optional[str] + OAuth2 state. You receive this back in the redirect URI. + nonce : Optional[str] + OIDC nonce parameter to prevent replay attacks. Required when response_type includes `id_token`. + code_challenge : Optional[str] + PKCE code challenge. Required when OAuth2 app is public. + code_challenge_method : Optional[str] + PKCE code challenge method. Required when OAuth2 app is public. + prompt : Optional[str] + OIDC prompt parameter for customization of consent screen. Space-separated list of: none, login, consent, select_account. + max_age : Optional[float] + OIDC max_age paraleter for customization of consent screen. Maximum allowable elapsed time in seconds since the user last authenticated. If exceeded, re-authentication is required. + authorization_details : Optional[str] + Rich authorization request. JSON array of objects, each with a `type` and project-defined fields + resource : Optional[str] + RFC 8707 resource indicator URI or URI list. Each value must be an absolute URI without a fragment. + audience : Optional[str] + Compatibility alias for a single OAuth2 resource indicator URI. + request_uri : Optional[str] + OAuth2 authorization request handle returned by the pushed authorization request endpoint. + + Returns + ------- + Oauth2Authorize + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/oauth2/{project_id}/authorize' + api_params = {} + api_path = api_path.replace('{project_id}', str(self._normalize_value(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: + api_params['redirect_uri'] = self._normalize_value(redirect_uri) + if response_type is not None: + api_params['response_type'] = self._normalize_value(response_type) + if scope is not None: + api_params['scope'] = self._normalize_value(scope) + if state is not None: + api_params['state'] = self._normalize_value(state) + if nonce is not None: + api_params['nonce'] = self._normalize_value(nonce) + if code_challenge is not None: + api_params['code_challenge'] = self._normalize_value(code_challenge) + if code_challenge_method is not None: + api_params['code_challenge_method'] = self._normalize_value(code_challenge_method) + if prompt is not None: + api_params['prompt'] = self._normalize_value(prompt) + if max_age is not None: + api_params['max_age'] = self._normalize_value(max_age) + if authorization_details is not None: + api_params['authorization_details'] = self._normalize_value(authorization_details) + if resource is not None: + api_params['resource'] = self._normalize_value(resource) + if audience is not None: + api_params['audience'] = self._normalize_value(audience) + if request_uri is not None: + api_params['request_uri'] = self._normalize_value(request_uri) + + response = self.client.call('post', api_path, { + 'content-type': 'application/json', + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=Oauth2Authorize) + + def create_device_authorization( self, client_id: Optional[str] = None, @@ -184,7 +287,7 @@ def create_device_authorization( Parameters ---------- client_id : Optional[str] - OAuth2 client ID. + OAuth2 client ID. Either a registered app ID or an HTTPS client ID metadata document URL. scope : Optional[str] Space-separated OAuth2 scopes. Can include project scopes, and built-in scopes: `openid`, `email`, `profile`. authorization_details : Optional[str] @@ -376,7 +479,7 @@ def create_par( Parameters ---------- client_id : str - OAuth2 client ID. + OAuth2 client ID. Either a registered app ID or an HTTPS client ID metadata document URL. redirect_uri : str Redirect URI where visitor will be redirected after authorization, whether successful or not. response_type : str @@ -562,7 +665,7 @@ def revoke( token_type_hint : Optional[str] Type of token to revoke (access_token or refresh_token). client_id : Optional[str] - OAuth2 client ID. + OAuth2 client ID. Either a registered app ID or an HTTPS client ID metadata document URL. client_secret : Optional[str] OAuth2 client secret. Required for confidential apps; omitted for public apps. @@ -627,7 +730,7 @@ def create_token( device_code : Optional[str] Device code obtained from the device authorization endpoint. Required for `urn:ietf:params:oauth:grant-type:device_code` grant type. client_id : Optional[str] - OAuth2 client ID. + OAuth2 client ID. Either a registered app ID or an HTTPS client ID metadata document URL. client_secret : Optional[str] OAuth2 client secret. Required for confidential apps. code_verifier : Optional[str] diff --git a/appwrite/services/sites.py b/appwrite/services/sites.py index 611dcb02..bcf0c1ad 100644 --- a/appwrite/services/sites.py +++ b/appwrite/services/sites.py @@ -988,7 +988,8 @@ def get_deployment_download( self, site_id: str, deployment_id: str, - type: Optional[DeploymentDownloadType] = None + type: Optional[DeploymentDownloadType] = None, + token: Optional[str] = None ) -> bytes: """ Get a site deployment content by its unique ID. The endpoint response return with a 'Content-Disposition: attachment' header that tells the browser to start downloading the file to user downloads directory. @@ -1001,6 +1002,8 @@ def get_deployment_download( Deployment ID. type : Optional[DeploymentDownloadType] Deployment file to download. Can be: "source", "output". + token : Optional[str] + Presigned source-download token for accessing this deployment without a session (jobs-service). Returns ------- @@ -1026,6 +1029,8 @@ def get_deployment_download( if type is not None: api_params['type'] = self._normalize_value(type) + if token is not None: + api_params['token'] = self._normalize_value(token) response = self.client.call('get', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), diff --git a/appwrite/services/tables_db.py b/appwrite/services/tables_db.py index 806bdc84..f84f9cca 100644 --- a/appwrite/services/tables_db.py +++ b/appwrite/services/tables_db.py @@ -5,8 +5,12 @@ from appwrite.utils.deprecated import deprecated from ..models.database_list import DatabaseList from ..models.database import Database +from ..models.dedicated_database_specification_list import DedicatedDatabaseSpecificationList from ..models.transaction_list import TransactionList from ..models.transaction import Transaction +from ..models.dedicated_database import DedicatedDatabase +from ..models.dedicated_database_replicas import DedicatedDatabaseReplicas +from ..models.database_status import DatabaseStatus from ..models.table_list import TableList from ..models.table import Table from ..models.column_list import ColumnList @@ -96,7 +100,8 @@ def create( database_id: str, name: str, enabled: Optional[bool] = None, - specification: Optional[str] = None + specification: Optional[str] = None, + replicas: Optional[float] = None ) -> Database: """ Create a new Database. @@ -112,6 +117,8 @@ def create( 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. 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] + Number of high availability replicas (0-5) for the dedicated database backing this database. Requires a dedicated `specification`; must be 0 for a serverless database. High availability is enabled when greater than 0. Returns ------- @@ -139,6 +146,8 @@ def create( api_params['enabled'] = self._normalize_value(enabled) if specification is not None: api_params['specification'] = self._normalize_value(specification) + if replicas is not None: + api_params['replicas'] = self._normalize_value(replicas) response = self.client.call('post', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -149,6 +158,34 @@ def create( return self._parse_response(response, model=Database) + def list_specifications( + self + ) -> DedicatedDatabaseSpecificationList: + """ + List the dedicated database specifications available on the current plan. Each specification reports its resource limits, pricing, and whether it is enabled for the organization. + + Returns + ------- + DedicatedDatabaseSpecificationList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/tablesdb/specifications' + api_params = {} + + 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=DedicatedDatabaseSpecificationList) + + def list_transactions( self, queries: Optional[List[str]] = None @@ -440,7 +477,8 @@ def update( self, database_id: str, name: Optional[str] = None, - enabled: Optional[bool] = None + enabled: Optional[bool] = None, + replicas: Optional[float] = None ) -> Database: """ Update a database by its unique ID. @@ -453,6 +491,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. + replicas : Optional[float] + Number of high availability replicas (0-5) for the dedicated database backing this database. Only valid when the database is backed by a dedicated specification. High availability is enabled when greater than 0. Returns ------- @@ -476,6 +516,8 @@ def update( api_params['name'] = self._normalize_value(name) if enabled is not None: api_params['enabled'] = self._normalize_value(enabled) + if replicas is not None: + api_params['replicas'] = self._normalize_value(replicas) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -525,6 +567,129 @@ def delete( return response + def create_failover( + self, + database_id: str, + target_replica_id: Optional[str] = None + ) -> DedicatedDatabase: + """ + Trigger a manual failover for a dedicated database with high availability enabled. Promotes a replica to primary. The failover runs asynchronously; poll the database document for status updates. + + Parameters + ---------- + database_id : str + Database ID. + target_replica_id : Optional[str] + Target replica ID to promote. If not specified, the healthiest replica is selected. + + Returns + ------- + DedicatedDatabase + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/tablesdb/{databaseId}/failovers' + api_params = {} + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_id))) + + if target_replica_id is not None: + api_params['targetReplicaId'] = self._normalize_value(target_replica_id) + + 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=DedicatedDatabase) + + + def get_replicas( + self, + database_id: str + ) -> DedicatedDatabaseReplicas: + """ + Get high availability status for a dedicated database. Returns replica statuses, replication lag, and sync mode. + + Parameters + ---------- + database_id : str + Database ID. + + Returns + ------- + DedicatedDatabaseReplicas + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/tablesdb/{databaseId}/replicas' + api_params = {} + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_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=DedicatedDatabaseReplicas) + + + def get_status( + self, + database_id: str + ) -> DatabaseStatus: + """ + Get real-time health and status information for a dedicated database. Returns health status, readiness, uptime, connection info, replica status, and volume information. + + Parameters + ---------- + database_id : str + Database ID. + + Returns + ------- + DatabaseStatus + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/tablesdb/{databaseId}/status' + api_params = {} + if database_id is None: + raise AppwriteException('Missing required parameter: "database_id"') + + api_path = api_path.replace('{databaseId}', str(self._normalize_value(database_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=DatabaseStatus) + + def list_tables( self, database_id: str, diff --git a/docs/examples/account/delete-consent-token.md b/docs/examples/account/delete-consent-token.md new file mode 100644 index 00000000..614b78ee --- /dev/null +++ b/docs/examples/account/delete-consent-token.md @@ -0,0 +1,16 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account + +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 = account.delete_consent_token( + consent_id = '', + token_id = '' +) +``` diff --git a/docs/examples/account/delete-consent.md b/docs/examples/account/delete-consent.md new file mode 100644 index 00000000..8c2c4113 --- /dev/null +++ b/docs/examples/account/delete-consent.md @@ -0,0 +1,15 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account + +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 = account.delete_consent( + consent_id = '' +) +``` diff --git a/docs/examples/account/get-consent-token.md b/docs/examples/account/get-consent-token.md new file mode 100644 index 00000000..a5edd0a7 --- /dev/null +++ b/docs/examples/account/get-consent-token.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account +from appwrite.models import Oauth2ConsentToken + +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: Oauth2ConsentToken = account.get_consent_token( + consent_id = '', + token_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/account/get-consent.md b/docs/examples/account/get-consent.md new file mode 100644 index 00000000..45ba0a82 --- /dev/null +++ b/docs/examples/account/get-consent.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account +from appwrite.models import Oauth2Consent + +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: Oauth2Consent = account.get_consent( + consent_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/account/list-consent-tokens.md b/docs/examples/account/list-consent-tokens.md new file mode 100644 index 00000000..3be9fbe3 --- /dev/null +++ b/docs/examples/account/list-consent-tokens.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account +from appwrite.models import Oauth2ConsentTokenList + +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: Oauth2ConsentTokenList = account.list_consent_tokens( + consent_id = '', + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/account/list-consents.md b/docs/examples/account/list-consents.md new file mode 100644 index 00000000..d2b5b69c --- /dev/null +++ b/docs/examples/account/list-consents.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.account import Account +from appwrite.models import Oauth2ConsentList + +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: Oauth2ConsentList = account.list_consents( + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/list-o-auth-2-scopes.md b/docs/examples/apps/list-o-auth-2-scopes.md new file mode 100644 index 00000000..8720e78c --- /dev/null +++ b/docs/examples/apps/list-o-auth-2-scopes.md @@ -0,0 +1,16 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import AppScopeList + +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 + +apps = Apps(client) + +result: AppScopeList = apps.list_o_auth2_scopes() + +print(result.model_dump()) +``` diff --git a/docs/examples/backups/create-restoration.md b/docs/examples/backups/create-restoration.md index 03f3f0a9..e2966caa 100644 --- a/docs/examples/backups/create-restoration.md +++ b/docs/examples/backups/create-restoration.md @@ -15,8 +15,7 @@ result: BackupRestoration = backups.create_restoration( archive_id = '', services = [BackupServices.DATABASES], new_resource_id = '', # optional - new_resource_name = '', # optional - new_specification = 'serverless' # optional + new_resource_name = '' # optional ) print(result.model_dump()) diff --git a/docs/examples/oauth2/authorize-post.md b/docs/examples/oauth2/authorize-post.md new file mode 100644 index 00000000..b6dd6449 --- /dev/null +++ b/docs/examples/oauth2/authorize-post.md @@ -0,0 +1,31 @@ +```python +from appwrite.client import Client +from appwrite.services.oauth2 import Oauth2 +from appwrite.models import Oauth2Authorize + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_session('') # The user session to authenticate with +client.set_project('') # Your project ID + +oauth2 = Oauth2(client) + +result: Oauth2Authorize = oauth2.authorize_post( + client_id = '', # optional + redirect_uri = 'https://example.com', # optional + response_type = '', # optional + scope = '', # optional + state = '', # optional + nonce = '', # optional + code_challenge = '', # optional + code_challenge_method = 's256', # optional + prompt = '', # optional + max_age = 0, # optional + authorization_details = '', # optional + resource = '', # optional + audience = '', # optional + request_uri = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/sites/get-deployment-download.md b/docs/examples/sites/get-deployment-download.md index eae2543c..7d6ae537 100644 --- a/docs/examples/sites/get-deployment-download.md +++ b/docs/examples/sites/get-deployment-download.md @@ -13,6 +13,7 @@ sites = Sites(client) result: bytes = sites.get_deployment_download( site_id = '', deployment_id = '', - type = DeploymentDownloadType.SOURCE # optional + type = DeploymentDownloadType.SOURCE, # optional + token = '' # optional ) ``` diff --git a/docs/examples/tablesdb/create-failover.md b/docs/examples/tablesdb/create-failover.md new file mode 100644 index 00000000..839f8baf --- /dev/null +++ b/docs/examples/tablesdb/create-failover.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import DedicatedDatabase + +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 + +tables_db = TablesDB(client) + +result: DedicatedDatabase = tables_db.create_failover( + database_id = '', + target_replica_id = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/tablesdb/create.md b/docs/examples/tablesdb/create.md index a816905f..0c5260cf 100644 --- a/docs/examples/tablesdb/create.md +++ b/docs/examples/tablesdb/create.md @@ -14,7 +14,8 @@ result: Database = tables_db.create( database_id = '', name = '', enabled = False, # optional - specification = 'serverless' # optional + specification = 'serverless', # optional + replicas = 0 # optional ) print(result.model_dump()) diff --git a/docs/examples/tablesdb/get-replicas.md b/docs/examples/tablesdb/get-replicas.md new file mode 100644 index 00000000..abd5df1b --- /dev/null +++ b/docs/examples/tablesdb/get-replicas.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import DedicatedDatabaseReplicas + +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 + +tables_db = TablesDB(client) + +result: DedicatedDatabaseReplicas = tables_db.get_replicas( + database_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/tablesdb/get-status.md b/docs/examples/tablesdb/get-status.md new file mode 100644 index 00000000..0695da1f --- /dev/null +++ b/docs/examples/tablesdb/get-status.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import DatabaseStatus + +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 + +tables_db = TablesDB(client) + +result: DatabaseStatus = tables_db.get_status( + database_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/tablesdb/list-specifications.md b/docs/examples/tablesdb/list-specifications.md new file mode 100644 index 00000000..c0fc0f8b --- /dev/null +++ b/docs/examples/tablesdb/list-specifications.md @@ -0,0 +1,16 @@ +```python +from appwrite.client import Client +from appwrite.services.tables_db import TablesDB +from appwrite.models import DedicatedDatabaseSpecificationList + +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 + +tables_db = TablesDB(client) + +result: DedicatedDatabaseSpecificationList = tables_db.list_specifications() + +print(result.model_dump()) +``` diff --git a/docs/examples/tablesdb/update.md b/docs/examples/tablesdb/update.md index aea54858..8609072e 100644 --- a/docs/examples/tablesdb/update.md +++ b/docs/examples/tablesdb/update.md @@ -13,7 +13,8 @@ tables_db = TablesDB(client) result: Database = tables_db.update( database_id = '', name = '', # optional - enabled = False # optional + enabled = False, # optional + replicas = 0 # optional ) print(result.model_dump()) diff --git a/pyproject.toml b/pyproject.toml index 10559426..0d439733 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "appwrite" -version = "22.0.0" +version = "22.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.9" diff --git a/setup.py b/setup.py index 9557066e..1baebf1e 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name = 'appwrite', packages = setuptools.find_packages(), - version = '22.0.0', + version = '22.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/22.0.0.tar.gz', + download_url='https://github.com/appwrite/sdk-for-python/archive/22.1.0.tar.gz', install_requires=[ 'requests', 'pydantic>=2,<3', diff --git a/test/services/test_account.py b/test/services/test_account.py index 4ab80f3b..c0bab652 100644 --- a/test/services/test_account.py +++ b/test/services/test_account.py @@ -72,6 +72,108 @@ def test_create(self, m): self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_list_consents(self, m): + data = { + "total": 5.0, + "consents": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.account.list_consents( + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_consent(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "userId": "5e5ea5c16897e", + "appId": "5e5ea5c16897e", + "cimdUrl": "https:\/\/example.com\/.well-known\/client-metadata.json", + "scopes": [], + "resources": [], + "authorizationDetails": "[{\"type\":\"calendar\",\"identifier\":\"primary\",\"actions\":[\"read_events\",\"create_event\"]}]", + "expire": "2020-10-15T06:38:00.000+00:00" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.account.get_consent( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete_consent(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.account.delete_consent( + '', + ) + + self.assertEqual(response, data) + + @requests_mock.Mocker() + def test_list_consent_tokens(self, m): + data = { + "total": 5.0, + "tokens": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.account.list_consent_tokens( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_consent_token(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "consentId": "5e5ea5c16897e", + "userId": "5e5ea5c16897e", + "appId": "5e5ea5c16897e", + "cimdUrl": "https:\/\/example.com\/.well-known\/client-metadata.json", + "scopes": [], + "resources": [], + "authorizationDetails": "[{\"type\":\"calendar\",\"identifier\":\"primary\",\"actions\":[\"read_events\",\"create_event\"]}]", + "expire": "2020-10-15T06:38:00.000+00:00" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.account.get_consent_token( + '', + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete_consent_token(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.account.delete_consent_token( + '', + '', + ) + + self.assertEqual(response, data) + @requests_mock.Mocker() def test_update_email(self, m): data = { diff --git a/test/services/test_activities.py b/test/services/test_activities.py index 9fcc5e4d..c4b8e550 100644 --- a/test/services/test_activities.py +++ b/test/services/test_activities.py @@ -44,10 +44,21 @@ def test_get_event(self, m): "ip": "127.0.0.1", "mode": "admin", "country": "US", + "continentCode": "NA", + "city": "Mountain View", + "subdivisions": "California", + "isp": "Google", + "autonomousSystemNumber": "15169", + "autonomousSystemOrganization": "GOOGLE", + "connectionType": "cable", + "connectionUsageType": "residential", + "connectionOrganization": "Google LLC", "time": "2020-10-15T06:38:00.000+00:00", "projectId": "610fc2f985ee0", "teamId": "610fc2f985ee0", - "hostname": "appwrite.io" + "hostname": "appwrite.io", + "sdk": "web", + "sdkVersion": "14.0.0" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) diff --git a/test/services/test_apps.py b/test/services/test_apps.py index 4520a42b..e457b572 100644 --- a/test/services/test_apps.py +++ b/test/services/test_apps.py @@ -66,6 +66,20 @@ def test_create(self, m): self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_list_o_auth2_scopes(self, m): + data = { + "total": 5.0, + "scopes": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.list_o_auth2_scopes( + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_get(self, m): data = { @@ -234,7 +248,7 @@ def test_get_secret(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "appId": "5e5ea5c16897e", - "secret": "$argon2i$v=19$m=16,t=2,p=1$MTIzMTIzMTIzMTIzMQ$3\/ZUl3IWERBO2RIm5rHltg", + "secret": "", "hint": "f5c6c7", "createdById": "5e5ea5c16897e", "createdByName": "Walter White" diff --git a/test/services/test_backups.py b/test/services/test_backups.py index dfa1f5a2..83c59835 100644 --- a/test/services/test_backups.py +++ b/test/services/test_backups.py @@ -196,7 +196,7 @@ def test_create_restoration(self, m): "migrationId": "did8jx6ws45jana098ab7", "services": [], "resources": [], - "options": "{databases.database[{oldId, newId, newName, newSpecification}]}" + "options": "{databases.database[{oldId, newId, newName}]}" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -235,7 +235,7 @@ def test_get_restoration(self, m): "migrationId": "did8jx6ws45jana098ab7", "services": [], "resources": [], - "options": "{databases.database[{oldId, newId, newName, newSpecification}]}" + "options": "{databases.database[{oldId, newId, newName}]}" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) diff --git a/test/services/test_databases.py b/test/services/test_databases.py index f6bc3112..11f4ecaf 100644 --- a/test/services/test_databases.py +++ b/test/services/test_databases.py @@ -35,9 +35,7 @@ def test_create(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, - "type": "legacy", - "policies": [], - "archives": [] + "type": "legacy" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -158,9 +156,7 @@ def test_get(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, - "type": "legacy", - "policies": [], - "archives": [] + "type": "legacy" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -179,9 +175,7 @@ def test_update(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, - "type": "legacy", - "policies": [], - "archives": [] + "type": "legacy" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) diff --git a/test/services/test_oauth2.py b/test/services/test_oauth2.py index 3b0bc38e..0f331808 100644 --- a/test/services/test_oauth2.py +++ b/test/services/test_oauth2.py @@ -41,6 +41,20 @@ def test_authorize(self, m): self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_authorize_post(self, m): + data = { + "grantId": "5e5ea5c16897e", + "redirectUrl": "https:\/\/example.com\/callback?code=abcde&state=fghij" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.oauth2.authorize_post( + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_create_device_authorization(self, m): data = { diff --git a/test/services/test_organization.py b/test/services/test_organization.py index 7cd67f61..399dd374 100644 --- a/test/services/test_organization.py +++ b/test/services/test_organization.py @@ -39,6 +39,7 @@ def test_get(self, m): "screenshotsGenerated": 50.0, "members": 25.0, "webhooks": 25.0, + "wafRules": 2.0, "projects": 2.0, "platforms": 3.0, "users": 25.0, @@ -264,6 +265,7 @@ def test_update(self, m): "screenshotsGenerated": 50.0, "members": 25.0, "webhooks": 25.0, + "wafRules": 2.0, "projects": 2.0, "platforms": 3.0, "users": 25.0, @@ -725,7 +727,8 @@ def test_create_project(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -766,7 +769,8 @@ def test_get_project(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -806,7 +810,8 @@ def test_update_project(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) diff --git a/test/services/test_project.py b/test/services/test_project.py index c3e87cf3..b4b9c640 100644 --- a/test/services/test_project.py +++ b/test/services/test_project.py @@ -42,7 +42,8 @@ def test_get(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -92,7 +93,8 @@ def test_update_auth_method(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -252,7 +254,8 @@ def test_update_labels(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -385,7 +388,8 @@ def test_update_o_auth2_server(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1417,7 +1421,8 @@ def test_update_deny_aliased_email_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1457,7 +1462,8 @@ def test_update_deny_corporate_email_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1497,7 +1503,8 @@ def test_update_deny_disposable_email_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1537,7 +1544,8 @@ def test_update_deny_free_email_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1577,7 +1585,8 @@ def test_update_membership_privacy_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1616,7 +1625,8 @@ def test_update_password_dictionary_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1656,7 +1666,8 @@ def test_update_password_history_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1696,7 +1707,8 @@ def test_update_password_personal_data_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1754,7 +1766,8 @@ def test_update_session_alert_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1794,7 +1807,8 @@ def test_update_session_duration_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1834,7 +1848,8 @@ def test_update_session_invalidation_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1874,7 +1889,8 @@ def test_update_session_limit_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1914,7 +1930,8 @@ def test_update_user_limit_policy(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -1969,7 +1986,8 @@ def test_update_protocol(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -2010,7 +2028,8 @@ def test_update_service(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -2051,7 +2070,8 @@ def test_update_smtp(self, m): "services": [], "protocols": [], "blocks": [], - "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00" + "consoleAccessedAt": "2020-10-15T06:38:00.000+00:00", + "wafEnabled": True } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) diff --git a/test/services/test_tables_db.py b/test/services/test_tables_db.py index 7f2ecabf..888be71f 100644 --- a/test/services/test_tables_db.py +++ b/test/services/test_tables_db.py @@ -35,9 +35,7 @@ def test_create(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, - "type": "legacy", - "policies": [], - "archives": [] + "type": "legacy" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -49,6 +47,27 @@ def test_create(self, m): self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_list_specifications(self, m): + data = { + "specifications": [], + "total": 9.0, + "pricing": { + "storageOverageRate": 0.125, + "bandwidthOverageRate": 0.08, + "replicaRate": 1, + "crossRegionReplicaRate": 1, + "pitrRate": 0.2 + } +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.tables_db.list_specifications( + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_list_transactions(self, m): data = { @@ -158,9 +177,7 @@ def test_get(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, - "type": "legacy", - "policies": [], - "archives": [] + "type": "legacy" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -179,9 +196,7 @@ def test_update(self, m): "$createdAt": "2020-10-15T06:38:00.000+00:00", "$updatedAt": "2020-10-15T06:38:00.000+00:00", "enabled": True, - "type": "legacy", - "policies": [], - "archives": [] + "type": "legacy" } headers = {'Content-Type': 'application/json'} m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) @@ -204,6 +219,106 @@ def test_delete(self, m): self.assertEqual(response, data) + @requests_mock.Mocker() + def test_create_failover(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "projectId": "5e5ea5c16897e", + "name": "My Production Database", + "api": "postgresql", + "engine": "postgresql", + "version": "16", + "specification": "s-2vcpu-2gb", + "backend": "edge", + "hostname": "db-myproject-mydb.fra.appwrite.center", + "connectionPort": 5432.0, + "connectionUser": "appwrite_user", + "connectionPassword": "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", + "connectionString": "postgresql:\/\/user:pass@db-myproject-mydb.fra.appwrite.center:5432\/postgres?sslmode=require", + "ssl": True, + "status": "ready", + "containerStatus": "active", + "lifecycleState": "active", + "idleTimeoutMinutes": 15.0, + "cpu": 2000.0, + "memory": 4096.0, + "storage": 100.0, + "storageClass": "ssd", + "storageMaxGb": 100.0, + "nodePool": "db-pool-4vcpu-8gb", + "replicas": 2.0, + "syncMode": "async", + "crossRegionReplicas": 1.0, + "networkMaxConnections": 500.0, + "networkIdleTimeoutSeconds": 900.0, + "networkIPAllowlist": [], + "backupEnabled": True, + "pitr": True, + "pitrRetentionDays": 14.0, + "storageAutoscaling": True, + "storageAutoscalingThresholdPercent": 85.0, + "storageAutoscalingMaxGb": 500.0, + "maintenanceWindowDay": "sun", + "maintenanceWindowHourUtc": 3.0, + "metricsEnabled": True, + "sqlApiEnabled": True, + "sqlApiAllowedStatements": [], + "sqlApiMaxRows": 10000.0, + "sqlApiMaxBytes": 10485760.0, + "sqlApiTimeoutSeconds": 30.0, + "error": "" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.tables_db.create_failover( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_replicas(self, m): + data = { + "replicas": 2.0, + "syncMode": "async", + "members": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.tables_db.get_replicas( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_status(self, m): + data = { + "health": "healthy", + "ready": True, + "engine": "postgresql", + "version": "17", + "uptime": 86400.0, + "connections": { + "current": 12.0, + "max": 100.0 + }, + "replicas": [], + "volumes": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.tables_db.get_status( + '', + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_list_tables(self, m): data = { From 6402b883973803040f71f50e2a4cdf1542f3e486 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 23 Jul 2026 16:17:34 +0530 Subject: [PATCH 4/5] chore: update Python SDK to 22.2.0 --- CHANGELOG.md | 9 + appwrite/client.py | 4 +- appwrite/enums/organization_key_scopes.py | 2 + appwrite/models/__init__.py | 8 + appwrite/models/app.py | 6 + appwrite/models/app_installation.py | 42 ++ appwrite/models/app_installation_list.py | 19 + appwrite/models/app_key.py | 39 ++ appwrite/models/app_key_list.py | 19 + appwrite/models/database.py | 6 + appwrite/models/project.py | 3 + appwrite/services/apps.py | 369 +++++++++++++++++- appwrite/services/organization.py | 213 ++++++++++ appwrite/services/project.py | 5 + appwrite/services/teams.py | 248 ++++++++++++ .../apps/create-installation-token.md | 19 + docs/examples/apps/create-key.md | 18 + docs/examples/apps/delete-key.md | 16 + docs/examples/apps/get-installation.md | 19 + docs/examples/apps/get-key.md | 19 + .../examples/apps/list-installation-scopes.md | 16 + docs/examples/apps/list-installations.md | 20 + docs/examples/apps/list-keys.md | 20 + docs/examples/apps/update.md | 4 +- .../organization/create-installation.md | 19 + .../organization/delete-installation.md | 15 + .../examples/organization/get-installation.md | 18 + .../organization/list-installations.md | 19 + .../organization/update-installation.md | 19 + .../project/update-o-auth-2-server.md | 1 + docs/examples/teams/create-installation.md | 20 + docs/examples/teams/delete-installation.md | 16 + docs/examples/teams/get-installation.md | 19 + docs/examples/teams/list-installations.md | 20 + docs/examples/teams/update-installation.md | 20 + pyproject.toml | 2 +- setup.py | 4 +- test/services/test_apps.py | 152 ++++++++ test/services/test_organization.py | 92 +++++ test/services/test_teams.py | 97 +++++ 40 files changed, 1669 insertions(+), 7 deletions(-) create mode 100644 appwrite/models/app_installation.py create mode 100644 appwrite/models/app_installation_list.py create mode 100644 appwrite/models/app_key.py create mode 100644 appwrite/models/app_key_list.py create mode 100644 docs/examples/apps/create-installation-token.md create mode 100644 docs/examples/apps/create-key.md create mode 100644 docs/examples/apps/delete-key.md create mode 100644 docs/examples/apps/get-installation.md create mode 100644 docs/examples/apps/get-key.md create mode 100644 docs/examples/apps/list-installation-scopes.md create mode 100644 docs/examples/apps/list-installations.md create mode 100644 docs/examples/apps/list-keys.md create mode 100644 docs/examples/organization/create-installation.md create mode 100644 docs/examples/organization/delete-installation.md create mode 100644 docs/examples/organization/get-installation.md create mode 100644 docs/examples/organization/list-installations.md create mode 100644 docs/examples/organization/update-installation.md create mode 100644 docs/examples/teams/create-installation.md create mode 100644 docs/examples/teams/delete-installation.md create mode 100644 docs/examples/teams/get-installation.md create mode 100644 docs/examples/teams/list-installations.md create mode 100644 docs/examples/teams/update-installation.md diff --git a/CHANGELOG.md b/CHANGELOG.md index d0f3fdfd..4eba1752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## 22.2.0 + +* Added: `apps` installation and key management methods (`list_installations`, `get_installation`, `create_installation_token`, `list_installation_scopes`, `list_keys`, `create_key`, `get_key`, `delete_key`) +* Added: `installation_scopes` and `installation_redirect_url` parameters to `apps.update` +* Added: app installation management methods to `organization` and `teams` services +* Added: `installation_access_token_duration` parameter to `project.update_o_auth2_server` +* Added: `installationScopes` and `installationRedirectUrl` fields to `App` model +* Added: `organization.installations.read`/`organization.installations.write` key scopes + ## 22.1.0 * Updated: removed `new_specification` parameter from `backups.create_restoration` diff --git a/appwrite/client.py b/appwrite/client.py index 3588e3a7..6e1d40e7 100644 --- a/appwrite/client.py +++ b/appwrite/client.py @@ -17,11 +17,11 @@ def __init__(self): self._endpoint = 'https://cloud.appwrite.io/v1' self._global_headers = { 'content-type': '', - 'user-agent' : f'AppwritePythonSDK/22.1.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})', + 'user-agent' : f'AppwritePythonSDK/22.2.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': '22.1.0', + 'x-sdk-version': '22.2.0', 'X-Appwrite-Response-Format' : '1.9.5', } self._config = {} diff --git a/appwrite/enums/organization_key_scopes.py b/appwrite/enums/organization_key_scopes.py index ab4e4526..bb2515df 100644 --- a/appwrite/enums/organization_key_scopes.py +++ b/appwrite/enums/organization_key_scopes.py @@ -7,6 +7,8 @@ class OrganizationKeyScopes(Enum): DEVKEYS_WRITE = "devKeys.write" ORGANIZATION_KEYS_READ = "organization.keys.read" ORGANIZATION_KEYS_WRITE = "organization.keys.write" + ORGANIZATION_INSTALLATIONS_READ = "organization.installations.read" + ORGANIZATION_INSTALLATIONS_WRITE = "organization.installations.write" ORGANIZATION_MEMBERSHIPS_READ = "organization.memberships.read" ORGANIZATION_MEMBERSHIPS_WRITE = "organization.memberships.write" ORGANIZATION_READ = "organization.read" diff --git a/appwrite/models/__init__.py b/appwrite/models/__init__.py index 07a73030..419db211 100644 --- a/appwrite/models/__init__.py +++ b/appwrite/models/__init__.py @@ -243,6 +243,8 @@ from .app_secret import AppSecret from .app_secret_plaintext import AppSecretPlaintext from .app_scope import AppScope +from .app_installation import AppInstallation +from .app_key import AppKey from .oauth2_authorize import Oauth2Authorize from .oauth2_approve import Oauth2Approve from .oauth2_reject import Oauth2Reject @@ -265,6 +267,8 @@ from .apps_list import AppsList from .app_secret_list import AppSecretList from .app_scope_list import AppScopeList +from .app_installation_list import AppInstallationList +from .app_key_list import AppKeyList __all__ = [ 'AppwriteModel', @@ -512,6 +516,8 @@ 'AppSecret', 'AppSecretPlaintext', 'AppScope', + 'AppInstallation', + 'AppKey', 'Oauth2Authorize', 'Oauth2Approve', 'Oauth2Reject', @@ -534,4 +540,6 @@ 'AppsList', 'AppSecretList', 'AppScopeList', + 'AppInstallationList', + 'AppKeyList', ] diff --git a/appwrite/models/app.py b/appwrite/models/app.py index f570a66c..76e93798 100644 --- a/appwrite/models/app.py +++ b/appwrite/models/app.py @@ -56,6 +56,10 @@ class App(AppwriteModel): ID of team that owns the application, if owned by team. Otherwise, user ID will be used. userid : str ID of user who owns the application, if owned by user. Otherwise, team ID will be used. + installationscopes : List[Any] + Scopes the application requests when installed on a team. Organization-level and project-level scopes only. + installationredirecturl : str + URL users are redirected to after creating or updating an installation of this application. Empty for no redirect. secrets : List[AppSecret] List of application secrets. """ @@ -82,4 +86,6 @@ class App(AppwriteModel): deviceflow: bool = Field(..., alias='deviceFlow') teamid: str = Field(..., alias='teamId') userid: str = Field(..., alias='userId') + installationscopes: List[Any] = Field(..., alias='installationScopes') + installationredirecturl: str = Field(..., alias='installationRedirectUrl') secrets: List[AppSecret] = Field(..., alias='secrets') diff --git a/appwrite/models/app_installation.py b/appwrite/models/app_installation.py new file mode 100644 index 00000000..3984389c --- /dev/null +++ b/appwrite/models/app_installation.py @@ -0,0 +1,42 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class AppInstallation(AppwriteModel): + """ + AppInstallation + + Attributes + ---------- + id : str + Installation ID. + createdat : str + Installation creation time in ISO 8601 format. + updatedat : str + Installation update time in ISO 8601 format. + appid : str + ID of the installed application. + teamid : str + ID of the team the application is installed on. + scopes : List[Any] + Scopes granted to the application. Snapshot of the application's installation scopes taken when the installation was created or last updated. + authorizationdetails : Dict[str, Any] + Authorization details granted to the application. Rich authorization request (RFC 9396) style entries; the Appwrite Console stores authorized project IDs here. + createdbyid : str + ID of the user who created the installation. + createdbyname : str + Name of the user who created the installation. + lastaccessedat : Optional[str] + Time an access token was last issued for the installation in ISO 8601 format. Null if never used. + """ + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + appid: str = Field(..., alias='appId') + teamid: str = Field(..., alias='teamId') + scopes: List[Any] = Field(..., alias='scopes') + authorizationdetails: Dict[str, Any] = Field(..., alias='authorizationDetails') + createdbyid: str = Field(..., alias='createdById') + createdbyname: str = Field(..., alias='createdByName') + lastaccessedat: Optional[str] = Field(default=None, alias='lastAccessedAt') diff --git a/appwrite/models/app_installation_list.py b/appwrite/models/app_installation_list.py new file mode 100644 index 00000000..0519077d --- /dev/null +++ b/appwrite/models/app_installation_list.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .app_installation import AppInstallation + +class AppInstallationList(AppwriteModel): + """ + App installations list + + Attributes + ---------- + total : float + Total number of installations that matched your query. + installations : List[AppInstallation] + List of installations. + """ + total: float = Field(..., alias='total') + installations: List[AppInstallation] = Field(..., alias='installations') diff --git a/appwrite/models/app_key.py b/appwrite/models/app_key.py new file mode 100644 index 00000000..88a319f2 --- /dev/null +++ b/appwrite/models/app_key.py @@ -0,0 +1,39 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel + +class AppKey(AppwriteModel): + """ + AppKey + + Attributes + ---------- + id : str + App key ID. + createdat : str + App key creation time in ISO 8601 format. + updatedat : str + App key update time in ISO 8601 format. + appid : str + Application ID this app key belongs to. + secret : str + App key secret. + hint : str + Last few characters of the app key secret, used to help identify it. + createdbyid : str + ID of the user who created the app key. + createdbyname : str + Name of the user who created the app key. + lastaccessedat : Optional[str] + Time the app key was last used for authentication in ISO 8601 format. Null if never used. + """ + id: str = Field(..., alias='$id') + createdat: str = Field(..., alias='$createdAt') + updatedat: str = Field(..., alias='$updatedAt') + appid: str = Field(..., alias='appId') + secret: str = Field(..., alias='secret') + hint: str = Field(..., alias='hint') + createdbyid: str = Field(..., alias='createdById') + createdbyname: str = Field(..., alias='createdByName') + lastaccessedat: Optional[str] = Field(default=None, alias='lastAccessedAt') diff --git a/appwrite/models/app_key_list.py b/appwrite/models/app_key_list.py new file mode 100644 index 00000000..2d4b7ff4 --- /dev/null +++ b/appwrite/models/app_key_list.py @@ -0,0 +1,19 @@ +from typing import Any, Dict, List, Optional, Union, cast +from pydantic import Field, PrivateAttr + +from .base_model import AppwriteModel +from .app_key import AppKey + +class AppKeyList(AppwriteModel): + """ + App keys list + + Attributes + ---------- + total : float + Total number of keys that matched your query. + keys : List[AppKey] + List of keys. + """ + total: float = Field(..., alias='total') + keys: List[AppKey] = Field(..., alias='keys') diff --git a/appwrite/models/database.py b/appwrite/models/database.py index f83198fd..c1852901 100644 --- a/appwrite/models/database.py +++ b/appwrite/models/database.py @@ -27,6 +27,10 @@ class Database(AppwriteModel): Database type. status : Optional[DatabaseStatus] Dedicated database lifecycle status. Null when the database has no valid dedicated backing. + engine : Optional[str] + Underlying engine of the dedicated backing: postgresql, mysql, mariadb, or mongodb. A managed product (tablesdb, documentsdb, vectorsdb) reports the engine it runs on, so its type and engine can differ. Null when the database has no dedicated backing. + specification : Optional[str] + Compute specification identifier of the dedicated backing, e.g. s-2vcpu-2gb. Null when the database has no dedicated backing. replicas : Optional[float] Number of secondary high availability replicas, excluding the primary. Null when backing configuration is unavailable. policies : Optional[List[BackupPolicy]] @@ -41,6 +45,8 @@ class Database(AppwriteModel): enabled: bool = Field(..., alias='enabled') type: DatabaseType = Field(..., alias='type') status: Optional[DatabaseStatus] = Field(default=None, alias='status') + engine: Optional[str] = Field(default=None, alias='engine') + specification: Optional[str] = Field(default=None, alias='specification') replicas: Optional[float] = Field(default=None, alias='replicas') policies: Optional[List[BackupPolicy]] = Field(default=None, alias='policies') archives: Optional[List[BackupArchive]] = Field(default=None, alias='archives') diff --git a/appwrite/models/project.py b/appwrite/models/project.py index 3dd2ca01..76822354 100644 --- a/appwrite/models/project.py +++ b/appwrite/models/project.py @@ -91,6 +91,8 @@ class Project(AppwriteModel): OAuth2 server access token duration in seconds for public clients (SPAs, mobile, native) oauth2serverpublicrefreshtokenduration : Optional[float] OAuth2 server refresh token duration in seconds for public clients (SPAs, mobile, native) + oauth2serverinstallationaccesstokenduration : Optional[float] + OAuth2 server access token duration in seconds for app installation access tokens oauth2serverconfidentialpkce : Optional[bool] When enabled, PKCE is required for confidential clients (server-side flows using client_secret). PKCE is always required for public clients regardless of this setting. oauth2serververificationurl : Optional[str] @@ -142,6 +144,7 @@ class Project(AppwriteModel): oauth2serverrefreshtokenduration: Optional[float] = Field(default=None, alias='oAuth2ServerRefreshTokenDuration') oauth2serverpublicaccesstokenduration: Optional[float] = Field(default=None, alias='oAuth2ServerPublicAccessTokenDuration') oauth2serverpublicrefreshtokenduration: Optional[float] = Field(default=None, alias='oAuth2ServerPublicRefreshTokenDuration') + oauth2serverinstallationaccesstokenduration: Optional[float] = Field(default=None, alias='oAuth2ServerInstallationAccessTokenDuration') oauth2serverconfidentialpkce: Optional[bool] = Field(default=None, alias='oAuth2ServerConfidentialPkce') oauth2serververificationurl: Optional[str] = Field(default=None, alias='oAuth2ServerVerificationUrl') oauth2serverusercodelength: Optional[float] = Field(default=None, alias='oAuth2ServerUserCodeLength') diff --git a/appwrite/services/apps.py b/appwrite/services/apps.py index 8b8e3285..d4a3f681 100644 --- a/appwrite/services/apps.py +++ b/appwrite/services/apps.py @@ -6,6 +6,11 @@ from ..models.apps_list import AppsList from ..models.app import App from ..models.app_scope_list import AppScopeList +from ..models.app_installation_list import AppInstallationList +from ..models.app_installation import AppInstallation +from ..models.oauth2_token import Oauth2Token +from ..models.app_key_list import AppKeyList +from ..models.app_key import AppKey from ..models.app_secret_list import AppSecretList from ..models.app_secret_plaintext import AppSecretPlaintext from ..models.app_secret import AppSecret @@ -191,6 +196,34 @@ def create( return self._parse_response(response, model=App) + def list_installation_scopes( + self + ) -> AppScopeList: + """ + List scopes an application can request when installed on a team. + + Returns + ------- + AppScopeList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/scopes/installations' + api_params = {} + + 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=AppScopeList) + + def list_o_auth2_scopes( self ) -> AppScopeList: @@ -277,7 +310,9 @@ def update( redirect_uris: Optional[List[str]] = None, post_logout_redirect_uris: Optional[List[str]] = None, type: Optional[str] = None, - device_flow: Optional[bool] = None + device_flow: Optional[bool] = None, + installation_scopes: Optional[List[str]] = None, + installation_redirect_url: Optional[str] = None ) -> App: """ Update an application by its unique ID. @@ -320,6 +355,10 @@ def update( OAuth2 client type. Use `public` for SPAs, mobile, and native apps that cannot keep a `client_secret` — PKCE is then required at the token endpoint. Use `confidential` for server-side clients that present a `client_secret`. Defaults to `confidential`. device_flow : Optional[bool] Allow this client to use the OAuth2 Device Authorization Grant (RFC 8628) for input-constrained devices such as TVs and CLIs. Defaults to false. + installation_scopes : Optional[List[str]] + Scopes the application requests when installed on a team. Organization-level and project-level scopes only; use the list scopes endpoint with `type=installation` to discover available values. Maximum of 100 scopes are allowed. + installation_redirect_url : Optional[str] + URL users are redirected to after creating or updating an installation of this application. Must be an https URL, an http loopback URL (localhost, 127.0.0.1, [::1]), or a private-use scheme URI, and must not contain a fragment. Leave empty for no redirect. Returns ------- @@ -375,6 +414,10 @@ def update( api_params['type'] = self._normalize_value(type) if device_flow is not None: api_params['deviceFlow'] = self._normalize_value(device_flow) + if installation_scopes is not None: + api_params['installationScopes'] = self._normalize_value(installation_scopes) + if installation_redirect_url is not None: + api_params['installationRedirectUrl'] = self._normalize_value(installation_redirect_url) response = self.client.call('put', api_path, { 'X-Appwrite-Project': self.client.get_config('project'), @@ -425,6 +468,330 @@ def delete( return response + def list_installations( + self, + app_id: str, + queries: Optional[List[str]] = None, + total: Optional[bool] = None + ) -> AppInstallationList: + """ + List installations of an application. Requires an app key sent in the `X-Appwrite-Key` header alongside the `X-Appwrite-App` header. + + Parameters + ---------- + app_id : str + Application unique 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. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + + Returns + ------- + AppInstallationList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/installations' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + api_path = api_path.replace('{appId}', str(self._normalize_value(app_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', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=AppInstallationList) + + + def get_installation( + self, + app_id: str, + installation_id: str + ) -> AppInstallation: + """ + Get an installation of an application by its unique ID. Requires an app key sent in the `X-Appwrite-Key` header alongside the `X-Appwrite-App` header. + + Parameters + ---------- + app_id : str + Application unique ID. + installation_id : str + Installation unique ID. + + Returns + ------- + AppInstallation + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/installations/{installationId}' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + if installation_id is None: + 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))) + + + 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=AppInstallation) + + + def create_installation_token( + self, + app_id: str, + installation_id: str + ) -> Oauth2Token: + """ + Create a token for an installation of an application. Requires an app key sent in the `X-Appwrite-Key` header alongside the `X-Appwrite-App` header. The returned token carries the scopes and authorization details granted to the installation, and can be used as an `Authorization: Bearer` header everywhere OAuth2 access tokens are accepted. Multiple tokens can be active for the same installation at once; each token stays valid until it expires or the installation is updated or deleted. + + Parameters + ---------- + app_id : str + Application unique ID. + installation_id : str + Installation unique ID. + + Returns + ------- + Oauth2Token + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/installations/{installationId}/tokens' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + if installation_id is None: + 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))) + + + 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=Oauth2Token) + + + def list_keys( + self, + app_id: str, + queries: Optional[List[str]] = None, + total: Optional[bool] = None + ) -> AppKeyList: + """ + List app keys for an application. + + Parameters + ---------- + app_id : str + Application unique 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. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + + Returns + ------- + AppKeyList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/keys' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + api_path = api_path.replace('{appId}', str(self._normalize_value(app_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', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=AppKeyList) + + + def create_key( + self, + app_id: str + ) -> AppKey: + """ + Create a new app key for an application. App keys carry no scopes; send one in the `X-Appwrite-Key` header alongside the `X-Appwrite-App` header to list the application's installations and create installation access tokens. + + Parameters + ---------- + app_id : str + Application unique ID. + + Returns + ------- + AppKey + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/keys' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + api_path = api_path.replace('{appId}', str(self._normalize_value(app_id))) + + + 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=AppKey) + + + def get_key( + self, + app_id: str, + key_id: str + ) -> AppKey: + """ + Get an app key by its unique ID. + + Parameters + ---------- + app_id : str + Application unique ID. + key_id : str + App key unique ID. + + Returns + ------- + AppKey + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/keys/{keyId}' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + if key_id is None: + 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))) + + + 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=AppKey) + + + def delete_key( + self, + app_id: str, + key_id: str + ) -> Dict[str, Any]: + """ + Delete an app key by its unique ID. + + Parameters + ---------- + app_id : str + Application unique ID. + key_id : str + App key unique ID. + + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/apps/{appId}/keys/{keyId}' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + if key_id is None: + 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))) + + + 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_labels( self, app_id: str, diff --git a/appwrite/services/organization.py b/appwrite/services/organization.py index e3f11e50..6ea8aa89 100644 --- a/appwrite/services/organization.py +++ b/appwrite/services/organization.py @@ -4,6 +4,8 @@ from ..exception import AppwriteException from appwrite.utils.deprecated import deprecated from ..models.organization import Organization as OrganizationModel +from ..models.app_installation_list import AppInstallationList +from ..models.app_installation import AppInstallation from ..models.key_list import KeyList from ..enums.organization_key_scopes import OrganizationKeyScopes from ..models.key import Key @@ -127,6 +129,217 @@ def delete( return response + def list_installations( + self, + queries: Optional[List[str]] = None, + total: Optional[bool] = None + ) -> AppInstallationList: + """ + List app installations on the organization. Any organization member can read installations. + + 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. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + + Returns + ------- + AppInstallationList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/installations' + 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=AppInstallationList) + + + def create_installation( + self, + app_id: str, + authorization_details: Optional[str] = None + ) -> AppInstallation: + """ + Install an app on the organization. Only organization members with the owner role can install apps. The installation is granted the scopes the app currently requests. + + Parameters + ---------- + app_id : str + Application unique ID. + authorization_details : Optional[str] + Authorization details granted to the installation as a JSON array of objects, each with a `type` and app-defined fields. The Appwrite Console stores authorized project IDs here. + + Returns + ------- + AppInstallation + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/installations' + api_params = {} + if app_id is None: + raise AppwriteException('Missing required parameter: "app_id"') + + + api_params['appId'] = self._normalize_value(app_id) + if authorization_details is not None: + api_params['authorizationDetails'] = self._normalize_value(authorization_details) + + 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=AppInstallation) + + + def get_installation( + self, + installation_id: str + ) -> AppInstallation: + """ + Get an app installation on the organization by its unique ID. Any organization member can read installations. + + Parameters + ---------- + installation_id : str + Installation unique ID. + + Returns + ------- + AppInstallation + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/installations/{installationId}' + api_params = {} + if installation_id is None: + raise AppwriteException('Missing required parameter: "installation_id"') + + api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_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=AppInstallation) + + + def update_installation( + self, + installation_id: str, + authorization_details: Optional[str] = None + ) -> AppInstallation: + """ + Update an app installation on the organization. Only organization members with the owner role can update installations. The installation's granted scopes are refreshed to the scopes the app currently requests; previously issued installation access tokens are revoked. + + Parameters + ---------- + installation_id : str + Installation unique ID. + authorization_details : Optional[str] + Authorization details granted to the installation as a JSON array of objects, each with a `type` and app-defined fields. Omit to keep the current value. + + Returns + ------- + AppInstallation + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/installations/{installationId}' + api_params = {} + if installation_id is None: + raise AppwriteException('Missing required parameter: "installation_id"') + + api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_id))) + + if authorization_details is not None: + api_params['authorizationDetails'] = self._normalize_value(authorization_details) + + response = self.client.call('put', 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=AppInstallation) + + + def delete_installation( + self, + installation_id: str + ) -> Dict[str, Any]: + """ + Uninstall an app from the organization by its installation ID. Only organization members with the owner role can remove installations. Previously issued installation access tokens are revoked. + + Parameters + ---------- + installation_id : str + Installation unique ID. + + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/organization/installations/{installationId}' + api_params = {} + if installation_id is None: + raise AppwriteException('Missing required parameter: "installation_id"') + + api_path = api_path.replace('{installationId}', str(self._normalize_value(installation_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 + + def list_keys( self, queries: Optional[List[str]] = None, diff --git a/appwrite/services/project.py b/appwrite/services/project.py index 573abdae..38af962f 100644 --- a/appwrite/services/project.py +++ b/appwrite/services/project.py @@ -790,6 +790,7 @@ def update_o_auth2_server( refresh_token_duration: Optional[float] = None, public_access_token_duration: Optional[float] = None, public_refresh_token_duration: Optional[float] = None, + installation_access_token_duration: Optional[float] = None, confidential_pkce: Optional[bool] = None, verification_url: Optional[str] = None, user_code_length: Optional[float] = None, @@ -818,6 +819,8 @@ def update_o_auth2_server( Access token duration in seconds for public clients (SPAs, mobile, and native apps that cannot keep a client secret). Leave empty to use default 1 hour. public_refresh_token_duration : Optional[float] Refresh token duration in seconds for public clients (SPAs, mobile, and native apps that cannot keep a client secret). Leave empty to use default 30 days. + installation_access_token_duration : Optional[float] + Access token duration in seconds for app installation access tokens. Leave empty to use default 1 hour. confidential_pkce : Optional[bool] When enabled, PKCE is required for confidential clients (server-side flows using client_secret). PKCE is always required for public clients regardless of this setting. verification_url : Optional[str] @@ -865,6 +868,8 @@ def update_o_auth2_server( api_params['publicAccessTokenDuration'] = self._normalize_value(public_access_token_duration) if public_refresh_token_duration is not None: api_params['publicRefreshTokenDuration'] = self._normalize_value(public_refresh_token_duration) + if installation_access_token_duration is not None: + api_params['installationAccessTokenDuration'] = self._normalize_value(installation_access_token_duration) if confidential_pkce is not None: api_params['confidentialPkce'] = self._normalize_value(confidential_pkce) if verification_url is not None: diff --git a/appwrite/services/teams.py b/appwrite/services/teams.py index 6b8a6fc9..45594204 100644 --- a/appwrite/services/teams.py +++ b/appwrite/services/teams.py @@ -5,6 +5,8 @@ from appwrite.utils.deprecated import deprecated from ..models.team_list import TeamList from ..models.team import Team +from ..models.app_installation_list import AppInstallationList +from ..models.app_installation import AppInstallation from ..models.membership_list import MembershipList from ..models.membership import Membership from ..models.preferences import Preferences @@ -256,6 +258,252 @@ def delete( return response + def list_installations( + self, + team_id: str, + queries: Optional[List[str]] = None, + total: Optional[bool] = None + ) -> AppInstallationList: + """ + List app installations on a team. Any team member can read installations. + + Parameters + ---------- + team_id : str + Team 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. + total : Optional[bool] + When set to false, the total count returned will be 0 and will not be calculated. + + Returns + ------- + AppInstallationList + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/teams/{teamId}/installations' + api_params = {} + if team_id is None: + raise AppwriteException('Missing required parameter: "team_id"') + + api_path = api_path.replace('{teamId}', str(self._normalize_value(team_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', api_path, { + 'X-Appwrite-Project': self.client.get_config('project'), + 'accept': 'application/json', + }, api_params) + + return self._parse_response(response, model=AppInstallationList) + + + def create_installation( + self, + team_id: str, + app_id: str, + authorization_details: Optional[str] = None + ) -> AppInstallation: + """ + Install an app on a team. When authenticated as a user, only team members with the owner role can install apps. Requests using an API key or in admin mode can install apps on any team. The installation is granted the scopes the app currently requests. + + Parameters + ---------- + team_id : str + Team ID. + app_id : str + Application unique ID. + authorization_details : Optional[str] + Authorization details granted to the installation as a JSON array of objects, each with a `type` and app-defined fields. The Appwrite Console stores authorized project IDs here. + + Returns + ------- + AppInstallation + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/teams/{teamId}/installations' + api_params = {} + if team_id is None: + 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_params['appId'] = self._normalize_value(app_id) + if authorization_details is not None: + api_params['authorizationDetails'] = self._normalize_value(authorization_details) + + 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=AppInstallation) + + + def get_installation( + self, + team_id: str, + installation_id: str + ) -> AppInstallation: + """ + Get an app installation on a team by its unique ID. Any team member can read installations. + + Parameters + ---------- + team_id : str + Team ID. + installation_id : str + Installation unique ID. + + Returns + ------- + AppInstallation + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/teams/{teamId}/installations/{installationId}' + api_params = {} + if team_id is None: + raise AppwriteException('Missing required parameter: "team_id"') + + if installation_id is None: + 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))) + + + 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=AppInstallation) + + + def update_installation( + self, + team_id: str, + installation_id: str, + authorization_details: Optional[str] = None + ) -> AppInstallation: + """ + Update an app installation on a team. Only team members with the owner role can update installations. The installation's granted scopes are refreshed to the scopes the app currently requests; previously issued installation access tokens are revoked. + + Parameters + ---------- + team_id : str + Team ID. + installation_id : str + Installation unique ID. + authorization_details : Optional[str] + Authorization details granted to the installation as a JSON array of objects, each with a `type` and app-defined fields. Omit to keep the current value. + + Returns + ------- + AppInstallation + API response as a typed Pydantic model + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/teams/{teamId}/installations/{installationId}' + api_params = {} + if team_id is None: + raise AppwriteException('Missing required parameter: "team_id"') + + if installation_id is None: + 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))) + + if authorization_details is not None: + api_params['authorizationDetails'] = self._normalize_value(authorization_details) + + response = self.client.call('put', 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=AppInstallation) + + + def delete_installation( + self, + team_id: str, + installation_id: str + ) -> Dict[str, Any]: + """ + Uninstall an app from a team by its installation ID. Only team members with the owner role can remove installations. Previously issued installation access tokens are revoked. + + Parameters + ---------- + team_id : str + Team ID. + installation_id : str + Installation unique ID. + + Returns + ------- + Dict[str, Any] + API response as a dictionary + + Raises + ------ + AppwriteException + If API request fails + """ + + api_path = '/teams/{teamId}/installations/{installationId}' + api_params = {} + if team_id is None: + raise AppwriteException('Missing required parameter: "team_id"') + + if installation_id is None: + 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))) + + + 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 list_memberships( self, team_id: str, diff --git a/docs/examples/apps/create-installation-token.md b/docs/examples/apps/create-installation-token.md new file mode 100644 index 00000000..62a62ca6 --- /dev/null +++ b/docs/examples/apps/create-installation-token.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import Oauth2Token + +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 + +apps = Apps(client) + +result: Oauth2Token = apps.create_installation_token( + app_id = '', + installation_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/create-key.md b/docs/examples/apps/create-key.md new file mode 100644 index 00000000..2821ba65 --- /dev/null +++ b/docs/examples/apps/create-key.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import AppKey + +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 + +apps = Apps(client) + +result: AppKey = apps.create_key( + app_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/delete-key.md b/docs/examples/apps/delete-key.md new file mode 100644 index 00000000..dca52e39 --- /dev/null +++ b/docs/examples/apps/delete-key.md @@ -0,0 +1,16 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps + +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 + +apps = Apps(client) + +result = apps.delete_key( + app_id = '', + key_id = '' +) +``` diff --git a/docs/examples/apps/get-installation.md b/docs/examples/apps/get-installation.md new file mode 100644 index 00000000..f6a52721 --- /dev/null +++ b/docs/examples/apps/get-installation.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import AppInstallation + +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 + +apps = Apps(client) + +result: AppInstallation = apps.get_installation( + app_id = '', + installation_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/get-key.md b/docs/examples/apps/get-key.md new file mode 100644 index 00000000..4df3d424 --- /dev/null +++ b/docs/examples/apps/get-key.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import AppKey + +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 + +apps = Apps(client) + +result: AppKey = apps.get_key( + app_id = '', + key_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/list-installation-scopes.md b/docs/examples/apps/list-installation-scopes.md new file mode 100644 index 00000000..4705cf7b --- /dev/null +++ b/docs/examples/apps/list-installation-scopes.md @@ -0,0 +1,16 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import AppScopeList + +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 + +apps = Apps(client) + +result: AppScopeList = apps.list_installation_scopes() + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/list-installations.md b/docs/examples/apps/list-installations.md new file mode 100644 index 00000000..f5e292b2 --- /dev/null +++ b/docs/examples/apps/list-installations.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import AppInstallationList + +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 + +apps = Apps(client) + +result: AppInstallationList = apps.list_installations( + app_id = '', + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/list-keys.md b/docs/examples/apps/list-keys.md new file mode 100644 index 00000000..7ed78543 --- /dev/null +++ b/docs/examples/apps/list-keys.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.apps import Apps +from appwrite.models import AppKeyList + +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 + +apps = Apps(client) + +result: AppKeyList = apps.list_keys( + app_id = '', + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/apps/update.md b/docs/examples/apps/update.md index 49c85efb..c9006df6 100644 --- a/docs/examples/apps/update.md +++ b/docs/examples/apps/update.md @@ -28,7 +28,9 @@ result: App = apps.update( redirect_uris = [], # optional post_logout_redirect_uris = [], # optional type = 'public', # optional - device_flow = False # optional + device_flow = False, # optional + installation_scopes = [], # optional + installation_redirect_url = 'https://example.com' # optional ) print(result.model_dump()) diff --git a/docs/examples/organization/create-installation.md b/docs/examples/organization/create-installation.md new file mode 100644 index 00000000..c7af602a --- /dev/null +++ b/docs/examples/organization/create-installation.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.organization import Organization +from appwrite.models import AppInstallation + +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 + +organization = Organization(client) + +result: AppInstallation = organization.create_installation( + app_id = '', + authorization_details = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/organization/delete-installation.md b/docs/examples/organization/delete-installation.md new file mode 100644 index 00000000..82790282 --- /dev/null +++ b/docs/examples/organization/delete-installation.md @@ -0,0 +1,15 @@ +```python +from appwrite.client import Client +from appwrite.services.organization import Organization + +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 + +organization = Organization(client) + +result = organization.delete_installation( + installation_id = '' +) +``` diff --git a/docs/examples/organization/get-installation.md b/docs/examples/organization/get-installation.md new file mode 100644 index 00000000..0bd78a30 --- /dev/null +++ b/docs/examples/organization/get-installation.md @@ -0,0 +1,18 @@ +```python +from appwrite.client import Client +from appwrite.services.organization import Organization +from appwrite.models import AppInstallation + +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 + +organization = Organization(client) + +result: AppInstallation = organization.get_installation( + installation_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/organization/list-installations.md b/docs/examples/organization/list-installations.md new file mode 100644 index 00000000..c2ebe4e8 --- /dev/null +++ b/docs/examples/organization/list-installations.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.organization import Organization +from appwrite.models import AppInstallationList + +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 + +organization = Organization(client) + +result: AppInstallationList = organization.list_installations( + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/organization/update-installation.md b/docs/examples/organization/update-installation.md new file mode 100644 index 00000000..ab8301b2 --- /dev/null +++ b/docs/examples/organization/update-installation.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.organization import Organization +from appwrite.models import AppInstallation + +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 + +organization = Organization(client) + +result: AppInstallation = organization.update_installation( + installation_id = '', + authorization_details = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/project/update-o-auth-2-server.md b/docs/examples/project/update-o-auth-2-server.md index 6be24c54..bce0876e 100644 --- a/docs/examples/project/update-o-auth-2-server.md +++ b/docs/examples/project/update-o-auth-2-server.md @@ -19,6 +19,7 @@ result: ProjectModel = project.update_o_auth2_server( refresh_token_duration = 60, # optional public_access_token_duration = 60, # optional public_refresh_token_duration = 60, # optional + installation_access_token_duration = 60, # optional confidential_pkce = False, # optional verification_url = 'https://example.com', # optional user_code_length = 6, # optional diff --git a/docs/examples/teams/create-installation.md b/docs/examples/teams/create-installation.md new file mode 100644 index 00000000..185e379f --- /dev/null +++ b/docs/examples/teams/create-installation.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.teams import Teams +from appwrite.models import AppInstallation + +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 + +teams = Teams(client) + +result: AppInstallation = teams.create_installation( + team_id = '', + app_id = '', + authorization_details = '' # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/teams/delete-installation.md b/docs/examples/teams/delete-installation.md new file mode 100644 index 00000000..912195d3 --- /dev/null +++ b/docs/examples/teams/delete-installation.md @@ -0,0 +1,16 @@ +```python +from appwrite.client import Client +from appwrite.services.teams import Teams + +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 + +teams = Teams(client) + +result = teams.delete_installation( + team_id = '', + installation_id = '' +) +``` diff --git a/docs/examples/teams/get-installation.md b/docs/examples/teams/get-installation.md new file mode 100644 index 00000000..06ad5f01 --- /dev/null +++ b/docs/examples/teams/get-installation.md @@ -0,0 +1,19 @@ +```python +from appwrite.client import Client +from appwrite.services.teams import Teams +from appwrite.models import AppInstallation + +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 + +teams = Teams(client) + +result: AppInstallation = teams.get_installation( + team_id = '', + installation_id = '' +) + +print(result.model_dump()) +``` diff --git a/docs/examples/teams/list-installations.md b/docs/examples/teams/list-installations.md new file mode 100644 index 00000000..fbc9485b --- /dev/null +++ b/docs/examples/teams/list-installations.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.teams import Teams +from appwrite.models import AppInstallationList + +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 + +teams = Teams(client) + +result: AppInstallationList = teams.list_installations( + team_id = '', + queries = [], # optional + total = False # optional +) + +print(result.model_dump()) +``` diff --git a/docs/examples/teams/update-installation.md b/docs/examples/teams/update-installation.md new file mode 100644 index 00000000..a5df1213 --- /dev/null +++ b/docs/examples/teams/update-installation.md @@ -0,0 +1,20 @@ +```python +from appwrite.client import Client +from appwrite.services.teams import Teams +from appwrite.models import AppInstallation + +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 + +teams = Teams(client) + +result: AppInstallation = teams.update_installation( + team_id = '', + installation_id = '', + authorization_details = '' # optional +) + +print(result.model_dump()) +``` diff --git a/pyproject.toml b/pyproject.toml index 0d439733..b71d5337 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "appwrite" -version = "22.1.0" +version = "22.2.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" diff --git a/setup.py b/setup.py index 1baebf1e..3d51ebff 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name = 'appwrite', packages = setuptools.find_packages(), - version = '22.1.0', + version = '22.2.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/22.1.0.tar.gz', + download_url='https://github.com/appwrite/sdk-for-python/archive/22.2.0.tar.gz', install_requires=[ 'requests', 'pydantic>=2,<3', diff --git a/test/services/test_apps.py b/test/services/test_apps.py index e457b572..e923f2e5 100644 --- a/test/services/test_apps.py +++ b/test/services/test_apps.py @@ -53,6 +53,8 @@ def test_create(self, m): "deviceFlow": True, "teamId": "5e5ea5c16897e", "userId": "5e5ea5c16897e", + "installationScopes": [], + "installationRedirectUrl": "https:\/\/example.com\/setup", "secrets": [] } headers = {'Content-Type': 'application/json'} @@ -66,6 +68,20 @@ def test_create(self, m): self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() + def test_list_installation_scopes(self, m): + data = { + "total": 5.0, + "scopes": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.list_installation_scopes( + ) + + self.assertEqual(response.to_dict(), data) + @requests_mock.Mocker() def test_list_o_auth2_scopes(self, m): data = { @@ -106,6 +122,8 @@ def test_get(self, m): "deviceFlow": True, "teamId": "5e5ea5c16897e", "userId": "5e5ea5c16897e", + "installationScopes": [], + "installationRedirectUrl": "https:\/\/example.com\/setup", "secrets": [] } headers = {'Content-Type': 'application/json'} @@ -143,6 +161,8 @@ def test_update(self, m): "deviceFlow": True, "teamId": "5e5ea5c16897e", "userId": "5e5ea5c16897e", + "installationScopes": [], + "installationRedirectUrl": "https:\/\/example.com\/setup", "secrets": [] } headers = {'Content-Type': 'application/json'} @@ -167,6 +187,134 @@ def test_delete(self, m): self.assertEqual(response, data) + @requests_mock.Mocker() + def test_list_installations(self, m): + data = { + "total": 5.0, + "installations": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.list_installations( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_installation(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "teamId": "5e5ea5c16897e", + "scopes": [], + "authorizationDetails": {}, + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.get_installation( + '', + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_installation_token(self, m): + data = { + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...", + "token_type": "Bearer", + "expires_in": 3600.0, + "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", + "scope": "openid email profile" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.create_installation_token( + '', + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_list_keys(self, m): + data = { + "total": 5.0, + "keys": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.list_keys( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_key(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "secret": "5f3c8d2a1b9e4f7a6c8b2d1e9f4a7b3c5d8e1f2a9b4c7d6e3f5a8b1c4d7e2f9a", + "hint": "f5c6c7", + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.create_key( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_key(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "secret": "5f3c8d2a1b9e4f7a6c8b2d1e9f4a7b3c5d8e1f2a9b4c7d6e3f5a8b1c4d7e2f9a", + "hint": "f5c6c7", + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.get_key( + '', + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete_key(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.apps.delete_key( + '', + '', + ) + + self.assertEqual(response, data) + @requests_mock.Mocker() def test_update_labels(self, m): data = { @@ -193,6 +341,8 @@ def test_update_labels(self, m): "deviceFlow": True, "teamId": "5e5ea5c16897e", "userId": "5e5ea5c16897e", + "installationScopes": [], + "installationRedirectUrl": "https:\/\/example.com\/setup", "secrets": [] } headers = {'Content-Type': 'application/json'} @@ -302,6 +452,8 @@ def test_update_team(self, m): "deviceFlow": True, "teamId": "5e5ea5c16897e", "userId": "5e5ea5c16897e", + "installationScopes": [], + "installationRedirectUrl": "https:\/\/example.com\/setup", "secrets": [] } headers = {'Content-Type': 'application/json'} diff --git a/test/services/test_organization.py b/test/services/test_organization.py index 399dd374..544d647d 100644 --- a/test/services/test_organization.py +++ b/test/services/test_organization.py @@ -477,6 +477,98 @@ def test_delete(self, m): self.assertEqual(response, data) + @requests_mock.Mocker() + def test_list_installations(self, m): + data = { + "total": 5.0, + "installations": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.list_installations( + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_installation(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "teamId": "5e5ea5c16897e", + "scopes": [], + "authorizationDetails": {}, + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.create_installation( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_installation(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "teamId": "5e5ea5c16897e", + "scopes": [], + "authorizationDetails": {}, + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.get_installation( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_update_installation(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "teamId": "5e5ea5c16897e", + "scopes": [], + "authorizationDetails": {}, + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.update_installation( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete_installation(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.organization.delete_installation( + '', + ) + + self.assertEqual(response, data) + @requests_mock.Mocker() def test_list_keys(self, m): data = { diff --git a/test/services/test_teams.py b/test/services/test_teams.py index 84688663..684d00f8 100644 --- a/test/services/test_teams.py +++ b/test/services/test_teams.py @@ -98,6 +98,103 @@ def test_delete(self, m): self.assertEqual(response, data) + @requests_mock.Mocker() + def test_list_installations(self, m): + data = { + "total": 5.0, + "installations": [] +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.teams.list_installations( + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_create_installation(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "teamId": "5e5ea5c16897e", + "scopes": [], + "authorizationDetails": {}, + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.teams.create_installation( + '', + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_get_installation(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "teamId": "5e5ea5c16897e", + "scopes": [], + "authorizationDetails": {}, + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.teams.get_installation( + '', + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_update_installation(self, m): + data = { + "$id": "5e5ea5c16897e", + "$createdAt": "2020-10-15T06:38:00.000+00:00", + "$updatedAt": "2020-10-15T06:38:00.000+00:00", + "appId": "5e5ea5c16897e", + "teamId": "5e5ea5c16897e", + "scopes": [], + "authorizationDetails": {}, + "createdById": "5e5ea5c16897e", + "createdByName": "Walter White" +} + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.teams.update_installation( + '', + '', + ) + + self.assertEqual(response.to_dict(), data) + + @requests_mock.Mocker() + def test_delete_installation(self, m): + data = '' + headers = {'Content-Type': 'application/json'} + m.request(requests_mock.ANY, requests_mock.ANY, text=json.dumps(data), headers=headers) + + response = self.teams.delete_installation( + '', + '', + ) + + self.assertEqual(response, data) + @requests_mock.Mocker() def test_list_memberships(self, m): data = { From 253f92583fa46b13199b11fc92a5b7334b6f2216 Mon Sep 17 00:00:00 2001 From: Chirag Aggarwal Date: Thu, 23 Jul 2026 17:01:53 +0530 Subject: [PATCH 5/5] chore: update Python SDK to 22.2.0 --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f0e30cb5..53679c3b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,10 +9,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: '3.9'