Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
# Change Log

## 25.0.0

* Breaking: `account.list_logs`, `users.list_logs`, `Log` and `LogList` removed
* Breaking: `organization` key methods renamed to `create_ephemeral_project_key`, `get_project_key`, `list_project_keys`, `update_project_key` and `delete_project_key`; `OrganizationKeyScopes` removed
* Breaking: `set_dev_key` client method, `DevKey` model and `Project.devKeys` removed
* Breaking: `projectName`, `region`, `organizationId`, `organizationName`, `billingPlan` and `reason` removed from `Block`
* Breaking: `DedicatedDatabaseSpecificationList.pricing` and `DedicatedDatabaseSpecificationPricing` removed; rates now live on `DedicatedDatabaseSpecification`
* Breaking: `BillingPlanGroup` `starter` replaced by `free` and `start`
* Breaking: `resource` is now an array on `oauth2.authorize`, `oauth2.create_par`, `oauth2.create_token` and `oauth2.create_device_authorization`
* Breaking: `BackupRestoration.options` is now a key-value object instead of a string
* Breaking: optional parameters reordered in `messaging.update_email`, `messaging.update_push` and `messaging.update_sms`; new ones inserted mid-list in `messaging.create_email`, `teams.create_membership` and `project` OAuth2 provider updates; use named arguments
* Added: passkey methods `account.list_passkeys`, `account.create_passkey`, `account.get_passkey`, `account.update_passkey`, `account.delete_passkey`, `account.update_passkey_verification`, `account.create_passkey_token` and `account.update_passkey_token`
* Added: `users.list_passkeys`, `users.get_passkey`, `users.update_passkey`, `users.delete_passkey`, plus `project.update_passkey_policy`
* Added: `Passkey`, `PasskeyList`, `PasskeyChallenge`, `PolicyPasskey` and `TokenSecret` models
* Added: `fingerprints` on Android platforms for passkeys
* Added: `domains` service for domains, DNS records, email presets, prices and transfers
* Added: `waf` service for bypass, challenge, deny, rate-limit and redirect rules
* Added: `account.create_id_token_session` for native Apple and Google sign-in, with native client IDs on those providers
* Added: email verification and recovery OTP methods on `account`
* Added: `oauth2.introspect` for RFC 7662 token introspection, returning `Oauth2Introspection`
* Added: `avatars.update_photo` and `avatars.delete_photo`
* Added: `messaging.create_appwrite_provider` and `messaging.update_appwrite_provider`
* Added: `project.update_password_pwned_policy`, `PolicyPasswordPwned` model and `User.passwordPwned`
* Added: Kakao, TikTok and Webflow OAuth2 providers
* Added: `prompt` on Auth0, Discord, GitHub, Microsoft, Okta, Salesforce and Zoho providers, with prompt enums
* Added: `Topic` and `ResolvedTopic` helpers for building MQTT push topics
* Added: reply-to on email messages, `channelId` on push messages, `qos` and `expiry` on topics
* Added: `membershipId` on `teams.create_membership`, and `total` on locale and other list endpoints
* Added: `storageStatus` and `storageTargetGb` on `DedicatedDatabase`; `eligibleCountries` on `BillingPlan`
* Added: `auto` image gravity, `jaspr` framework, `dart-3.13` and `flutter-3.47` runtimes
* Added: `project.update_password_rotation_policy` and the `PolicyPasswordRotation` model
* Fixed: response models fill missing fields from spec defaults instead of throwing
* Fixed: multipart requests without a file are sent as multipart instead of URL-encoded
* Updated: several model fields are now optional and may be `None`, e.g. `DedicatedDatabase.engine`, `Organization.billingStartDate`, `BillingPlan.usage`, `BackupArchive.size`
* Updated: requests send `X-Appwrite-Response-Format` `2.4.0`

## 24.2.0

* Added: optional `interval` parameter on `functions.create` and `functions.update` to run a function every N minutes
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Appwrite Python SDK

![License](https://img.shields.io/github/license/appwrite/sdk-for-python.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.0.0-blue.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.4.0-blue.svg?style=flat-square)
[![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator)
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord)

**This SDK targets Appwrite server version 1.9.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-python/releases) when APIs differ.
**This SDK targets Appwrite server version 2.4.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-python/releases) when APIs differ.

Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Python SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

Expand Down
43 changes: 28 additions & 15 deletions appwrite/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ def __init__(self):
self._endpoint = 'https://cloud.appwrite.io/v1'
self._global_headers = {
'content-type': '',
'user-agent': f'AppwritePythonSDK/24.2.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})',
'user-agent': f'AppwritePythonSDK/25.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': '24.2.0',
'X-Appwrite-Response-Format': '2.0.0',
'x-sdk-version': '25.0.0',
'X-Appwrite-Response-Format': '2.4.0',
}
self._config = {}

Expand Down Expand Up @@ -101,13 +101,6 @@ def set_forwarded_user_agent(self, value):
self._config['forwardeduseragent'] = value
return self

def set_dev_key(self, value):
"""Your secret dev API key"""

self._global_headers['x-appwrite-dev-key'] = value
self._config['devkey'] = value
return self

def set_cookie(self, value):
"""The user cookie to authenticate with. Used by SDKs that forward an incoming Cookie header in server-side runtimes."""

Expand Down Expand Up @@ -164,6 +157,15 @@ def call(self, method, path='', headers=None, params=None, response_type='json')
files[key] = (data[key].filename, data[key].data)
del data[key]
data = self.flatten(data, stringify=stringify)
if not files and data:
# Without a file, requests would fall back to a URL-encoded body.
files = {key: (None, value) for key, value in data.items()}
data = {}
elif not files:
# With no parts at all, requests would send a bare body, so close an empty multipart one.
boundary = os.urandom(16).hex()
headers['content-type'] = f'multipart/form-data; boundary={boundary}'
data = f'--{boundary}--\r\n'

response = None
try:
Expand All @@ -190,6 +192,9 @@ def call(self, method, path='', headers=None, params=None, response_type='json')
if response_type == 'location':
return response.headers.get('Location')

if response_type == 'text':
return response.content.decode('utf-8')

if content_type.startswith('application/json'):
return response.json()

Expand All @@ -214,7 +219,12 @@ def chunked_upload(
param_name='',
on_progress=None,
upload_id='',
response_type='json',
method='post',
):
if params.get(param_name) is None:
return self.call(method, path, headers, params, response_type=response_type)

input_file = params[param_name]

if input_file.source_type == 'path':
Expand All @@ -224,13 +234,13 @@ def chunked_upload(
size = len(input_file.data)
input = input_file.data

if size < self._chunk_size:
if size <= self._chunk_size or response_type == 'text':
if input_file.source_type == 'path':
with open(input_file.path, 'rb') as input:
input_file.data = input.read()

params[param_name] = input_file
return self.call('post', path, headers, params)
return self.call(method, path, headers, params, response_type=response_type)

offset = 0
counter = 0
Expand Down Expand Up @@ -277,6 +287,8 @@ def read_chunk(start, end):
final_result = None

def is_upload_complete(chunk_result):
if not isinstance(chunk_result, dict):
return False
chunks_uploaded = chunk_result.get('chunksUploaded')
if chunks_uploaded is None:
return False
Expand All @@ -296,15 +308,16 @@ def upload_chunk(chunk, current_upload_id):
chunk_headers["x-appwrite-id"] = current_upload_id

return self.call(
'post',
method,
path,
chunk_headers,
chunk_params,
response_type=response_type,
)

result = upload_chunk(chunks[0], upload_id_header)
last_result = result
if "$id" in result:
if isinstance(result, dict) and "$id" in result:
upload_id_header = result["$id"]

completed_count = chunks[0]['index'] + 1
Expand All @@ -313,7 +326,7 @@ def upload_chunk(chunk, current_upload_id):
if on_progress is not None:
on_progress(
{
"$id": result.get("$id"),
"$id": result.get("$id") if isinstance(result, dict) else None,
"progress": uploaded_size / size * 100,
"sizeUploaded": uploaded_size,
"chunksTotal": total_chunks,
Expand Down
84 changes: 80 additions & 4 deletions appwrite/encoders/value_class_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from ..models.base_model import AppwriteModel
from ..enums.authenticator_type import AuthenticatorType
from ..enums.authentication_factor import AuthenticationFactor
from ..enums.id_token_provider import IdTokenProvider
from ..enums.o_auth_provider import OAuthProvider
from ..enums.browser import Browser
from ..enums.credit_card import CreditCard
Expand All @@ -16,6 +17,7 @@
from ..enums.databases_index_type import DatabasesIndexType
from ..enums.order_by import OrderBy
from ..enums.documents_db_index_type import DocumentsDBIndexType
from ..enums.domain_registration_type import DomainRegistrationType
from ..enums.embedding_model import EmbeddingModel
from ..enums.runtime import Runtime
from ..enums.project_key_scopes import ProjectKeyScopes
Expand All @@ -25,11 +27,18 @@
from ..enums.execution_method import ExecutionMethod
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_auth0_prompt import ProjectOAuth2Auth0Prompt
from ..enums.project_o_auth2_discord_prompt import ProjectOAuth2DiscordPrompt
from ..enums.project_o_auth2_git_hub_prompt import ProjectOAuth2GitHubPrompt
from ..enums.project_o_auth2_google_prompt import ProjectOAuth2GooglePrompt
from ..enums.project_o_auth2_kakao_prompt import ProjectOAuth2KakaoPrompt
from ..enums.project_o_auth2_microsoft_prompt import ProjectOAuth2MicrosoftPrompt
from ..enums.project_o_auth2_oidc_prompt import ProjectOAuth2OidcPrompt
from ..enums.project_o_auth2_okta_prompt import ProjectOAuth2OktaPrompt
from ..enums.project_o_auth2_salesforce_prompt import ProjectOAuth2SalesforcePrompt
from ..enums.project_o_auth2_zoho_prompt import ProjectOAuth2ZohoPrompt
from ..enums.project_o_auth_provider_id import ProjectOAuthProviderId
from ..enums.project_policy_id import ProjectPolicyId
from ..enums.project_protocol_id import ProjectProtocolId
Expand Down Expand Up @@ -58,13 +67,23 @@
from ..enums.execution_resource_type import ExecutionResourceType
from ..enums.execution_trigger import ExecutionTrigger
from ..enums.execution_status import ExecutionStatus
from ..enums.o_auth2_github_prompt import OAuth2GithubPrompt
from ..enums.o_auth2_discord_prompt import OAuth2DiscordPrompt
from ..enums.o_auth2_google_prompt import OAuth2GooglePrompt
from ..enums.o_auth2_zoho_prompt import OAuth2ZohoPrompt
from ..enums.o_auth2_salesforce_prompt import OAuth2SalesforcePrompt
from ..enums.o_auth2_auth0_prompt import OAuth2Auth0Prompt
from ..enums.o_auth2_oidc_prompt import OAuth2OidcPrompt
from ..enums.o_auth2_okta_prompt import OAuth2OktaPrompt
from ..enums.o_auth2_microsoft_prompt import OAuth2MicrosoftPrompt
from ..enums.o_auth2_kakao_prompt import OAuth2KakaoPrompt
from ..enums.platform_type import PlatformType
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
from ..enums.domain_transfer_status_enum import DomainTransferStatusEnum
from ..enums.waf_rule_action import WafRuleAction


class ValueClassEncoder(json.JSONEncoder):
Expand All @@ -78,6 +97,9 @@ def default(self, o):
if isinstance(o, AuthenticationFactor):
return o.value

if isinstance(o, IdTokenProvider):
return o.value

if isinstance(o, OAuthProvider):
return o.value

Expand Down Expand Up @@ -120,6 +142,9 @@ def default(self, o):
if isinstance(o, DocumentsDBIndexType):
return o.value

if isinstance(o, DomainRegistrationType):
return o.value

if isinstance(o, EmbeddingModel):
return o.value

Expand Down Expand Up @@ -147,21 +172,42 @@ def default(self, o):
if isinstance(o, SmtpEncryption):
return o.value

if isinstance(o, OrganizationKeyScopes):
return o.value

if isinstance(o, Region):
return o.value

if isinstance(o, ProjectAuthMethodId):
return o.value

if isinstance(o, ProjectOAuth2Auth0Prompt):
return o.value

if isinstance(o, ProjectOAuth2DiscordPrompt):
return o.value

if isinstance(o, ProjectOAuth2GitHubPrompt):
return o.value

if isinstance(o, ProjectOAuth2GooglePrompt):
return o.value

if isinstance(o, ProjectOAuth2KakaoPrompt):
return o.value

if isinstance(o, ProjectOAuth2MicrosoftPrompt):
return o.value

if isinstance(o, ProjectOAuth2OidcPrompt):
return o.value

if isinstance(o, ProjectOAuth2OktaPrompt):
return o.value

if isinstance(o, ProjectOAuth2SalesforcePrompt):
return o.value

if isinstance(o, ProjectOAuth2ZohoPrompt):
return o.value

if isinstance(o, ProjectOAuthProviderId):
return o.value

Expand Down Expand Up @@ -246,12 +292,36 @@ def default(self, o):
if isinstance(o, ExecutionStatus):
return o.value

if isinstance(o, OAuth2GithubPrompt):
return o.value

if isinstance(o, OAuth2DiscordPrompt):
return o.value

if isinstance(o, OAuth2GooglePrompt):
return o.value

if isinstance(o, OAuth2ZohoPrompt):
return o.value

if isinstance(o, OAuth2SalesforcePrompt):
return o.value

if isinstance(o, OAuth2Auth0Prompt):
return o.value

if isinstance(o, OAuth2OidcPrompt):
return o.value

if isinstance(o, OAuth2OktaPrompt):
return o.value

if isinstance(o, OAuth2MicrosoftPrompt):
return o.value

if isinstance(o, OAuth2KakaoPrompt):
return o.value

if isinstance(o, PlatformType):
return o.value

Expand All @@ -267,4 +337,10 @@ def default(self, o):
if isinstance(o, BillingPlanGroup):
return o.value

if isinstance(o, DomainTransferStatusEnum):
return o.value

if isinstance(o, WafRuleAction):
return o.value

return super().default(o)
3 changes: 2 additions & 1 deletion appwrite/enums/billing_plan_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


class BillingPlanGroup(Enum):
STARTER = "starter"
FREE = "free"
START = "start"
PRO = "pro"
SCALE = "scale"
2 changes: 2 additions & 0 deletions appwrite/enums/build_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class BuildRuntime(Enum):
DART_3_10 = "dart-3.10"
DART_3_11 = "dart-3.11"
DART_3_12 = "dart-3.12"
DART_3_13 = "dart-3.13"
DOTNET_6_0 = "dotnet-6.0"
DOTNET_7_0 = "dotnet-7.0"
DOTNET_8_0 = "dotnet-8.0"
Expand Down Expand Up @@ -95,3 +96,4 @@ class BuildRuntime(Enum):
FLUTTER_3_38 = "flutter-3.38"
FLUTTER_3_41 = "flutter-3.41"
FLUTTER_3_44 = "flutter-3.44"
FLUTTER_3_47 = "flutter-3.47"
8 changes: 8 additions & 0 deletions appwrite/enums/domain_registration_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from enum import Enum


class DomainRegistrationType(Enum):
NEW = "new"
TRANSFER = "transfer"
RENEWAL = "renewal"
TRADE = "trade"
12 changes: 12 additions & 0 deletions appwrite/enums/domain_transfer_status_enum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from enum import Enum


class DomainTransferStatusEnum(Enum):
TRANSFERRABLE = "transferrable"
NOT_TRANSFERRABLE = "not_transferrable"
PENDING_OWNER = "pending_owner"
PENDING_ADMIN = "pending_admin"
PENDING_REGISTRY = "pending_registry"
COMPLETED = "completed"
CANCELLED = "cancelled"
SERVICE_UNAVAILABLE = "service_unavailable"
Loading
Loading