Skip to content
Merged
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
  •  
  •  
  •  
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Change Log

## 25.0.0

* Breaking: requests send `X-Appwrite-Response-Format: 2.5.0`; responses follow the 2.5 shapes
* Breaking: removed `account.list_logs`, `users.list_logs` and the `Log`/`LogList` models
* Breaking: removed `organization.create_key`, `get_key`, `list_keys`, `update_key` and `delete_key`, and `OrganizationKeyScopes`; use `organization.list_project_keys`, `get_project_key`, `update_project_key` and `delete_project_key` instead
* Breaking: removed `Client.set_dev_key`, the `DevKey` model and `Project.dev_keys`
* Breaking: removed `BillingPlanGroup.STARTER`; use `BillingPlanGroup.FREE`
* Breaking: `list_specifications` returns each specification's prices and overage rates on the row itself. Removed the list-level `pricing` field and the `DedicatedDatabaseSpecificationPricing` model
* Breaking: removed `Block` fields `reason`, `project_name`, `region`, `organization_name`, `organization_id` and `billing_plan`
* Breaking: OAuth2 `resource` params now take `List[str]`
* Breaking: `project.update_o_auth2_kakao` takes `enabled` before `prompt`
* Breaking: `BackupRestoration.options` is now a dict
* Breaking: some response fields became `Optional`, such as `BillingPlan.usage` and `DedicatedDatabase.engine`
* Added: `Topic` helper to build MQTT topics for push messages
* Added: `mongo`, `mysql` and `postgresql` `promote_restoration`, `restore_mode`, `target_name` and `auto_promote` on `create_restoration`, and `DedicatedDatabaseRestoration.restore_mode`
* Added: `proxy.create_smtp_rule`, the `ProxyRuleProtocol` enum and `ProxyRule.protocol`
* Added: `email_enabled` on `functions.update` and `Function`, and `ExecutionTrigger.EMAIL`
* Added: `Query` aggregates (`count`, `count_distinct`, `sum`, `avg`, `min`, `max` and more), `group_by`, `having`, `distinct`, joins, `relationship`, `covers`, `spatial_equals` and `page`
* Added: `Query.builder()` to chain queries; `build()` returns the query list
* Updated: missing response fields fall back to their documented defaults
* Fixed: map fields missing from a response default to `{}` instead of `[]`

## 24.4.0

* Added: `domains` service to manage domains, DNS records, email presets, prices, transfer status and zones
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.5.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.5.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-python/releases) when APIs differ.

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
49 changes: 29 additions & 20 deletions appwrite/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .input_file import InputFile
from .exception import AppwriteException
from .encoders.value_class_encoder import ValueClassEncoder
from .query import Query


class Client:
Expand All @@ -18,12 +19,12 @@ def __init__(self):
self._endpoint = 'https://cloud.appwrite.io/v1'
self._global_headers = {
'content-type': '',
'user-agent': f'AppwritePythonSDK/24.4.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.4.0',
'X-Appwrite-Response-Format': '2.0.0',
'x-sdk-version': '25.0.0',
'X-Appwrite-Response-Format': '2.5.0',
}
self._config = {}

Expand Down Expand Up @@ -101,13 +102,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 @@ -143,6 +137,8 @@ def call(self, method, path='', headers=None, params=None, response_type='json')
if params is None:
params = {}

params = {key: Query._flatten(value) for key, value in params.items()}

data = {}
files = {}
stringify = False
Expand All @@ -164,6 +160,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 Down Expand Up @@ -217,8 +222,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 @@ -228,13 +237,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(method, path, headers, params)
return self.call(method, path, headers, params, response_type=response_type)

offset = 0
counter = 0
Expand Down Expand Up @@ -281,6 +290,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 @@ -304,11 +315,12 @@ def upload_chunk(chunk, current_upload_id):
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 @@ -317,7 +329,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 Expand Up @@ -354,13 +366,10 @@ def upload_remaining_chunk(chunk):

def flatten(self, data, prefix='', stringify=False):
output = {}
i = 0
entries = data.items() if isinstance(data, dict) else enumerate(data)

for key in data:
value = data[key] if isinstance(data, dict) else key
finalKey = prefix + '[' + key + ']' if prefix else key
finalKey = prefix + '[' + str(i) + ']' if isinstance(data, list) else finalKey
i += 1
for key, value in entries:
finalKey = f'{prefix}[{key}]' if prefix or isinstance(data, list) else key

if isinstance(value, list) or isinstance(value, dict):
output = {**output, **self.flatten(value, finalKey, stringify)}
Expand Down
8 changes: 4 additions & 4 deletions appwrite/encoders/value_class_encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
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
Expand Down Expand Up @@ -81,6 +80,7 @@
from ..enums.platform_type import PlatformType
from ..enums.proxy_rule_deployment_resource_type import ProxyRuleDeploymentResourceType
from ..enums.proxy_rule_status import ProxyRuleStatus
from ..enums.proxy_rule_protocol import ProxyRuleProtocol
from ..enums.message_status import MessageStatus
from ..enums.billing_plan_group import BillingPlanGroup
from ..enums.domain_transfer_status_enum import DomainTransferStatusEnum
Expand Down Expand Up @@ -173,9 +173,6 @@ 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

Expand Down Expand Up @@ -335,6 +332,9 @@ def default(self, o):
if isinstance(o, ProxyRuleStatus):
return o.value

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

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

Expand Down
1 change: 0 additions & 1 deletion appwrite/enums/billing_plan_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@


class BillingPlanGroup(Enum):
STARTER = "starter"
FREE = "free"
START = "start"
PRO = "pro"
Expand Down
1 change: 1 addition & 0 deletions appwrite/enums/execution_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ class ExecutionTrigger(Enum):
HTTP = "http"
SCHEDULE = "schedule"
EVENT = "event"
EMAIL = "email"
20 changes: 0 additions & 20 deletions appwrite/enums/organization_key_scopes.py

This file was deleted.

6 changes: 6 additions & 0 deletions appwrite/enums/proxy_rule_protocol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum


class ProxyRuleProtocol(Enum):
HTTP = "http"
SMTP = "smtp"
24 changes: 8 additions & 16 deletions appwrite/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from .user_list import UserList
from .session_list import SessionList
from .identity_list import IdentityList
from .log_list import LogList
from .file_list import FileList
from .bucket_list import BucketList
from .resource_token_list import ResourceTokenList
Expand Down Expand Up @@ -97,7 +96,6 @@
from .row import Row
from .document import Document
from .presence import Presence
from .log import Log
from .user import User
from .algo_md5 import AlgoMd5
from .algo_sha import AlgoSha
Expand Down Expand Up @@ -133,7 +131,6 @@
from .webhook import Webhook
from .key import Key
from .ephemeral_key import EphemeralKey
from .dev_key import DevKey
from .mock_number import MockNumber
from .o_auth2_github import OAuth2Github
from .o_auth2_discord import OAuth2Discord
Expand Down Expand Up @@ -197,6 +194,10 @@
from .policy_membership_privacy import PolicyMembershipPrivacy
from .policy_mfa_factors import PolicyMfaFactors
from .policy_passkey import PolicyPasskey
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 .platform_web import PlatformWeb
from .platform_apple import PlatformApple
from .platform_android import PlatformAndroid
Expand Down Expand Up @@ -266,18 +267,13 @@
from .organization import Organization
from .dedicated_database_pitr_windows import DedicatedDatabasePITRWindows
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 .dedicated_database_pooler import DedicatedDatabasePooler
from .postgres_extension import PostgresExtension
from .program import Program
from .backup_restoration import BackupRestoration
from .dedicated_database_restoration_list import DedicatedDatabaseRestorationList
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
Expand Down Expand Up @@ -338,7 +334,6 @@
'UserList',
'SessionList',
'IdentityList',
'LogList',
'FileList',
'BucketList',
'ResourceTokenList',
Expand Down Expand Up @@ -425,7 +420,6 @@
'Row',
'Document',
'Presence',
'Log',
'User',
'AlgoMd5',
'AlgoSha',
Expand Down Expand Up @@ -461,7 +455,6 @@
'Webhook',
'Key',
'EphemeralKey',
'DevKey',
'MockNumber',
'OAuth2Github',
'OAuth2Discord',
Expand Down Expand Up @@ -525,6 +518,10 @@
'PolicyMembershipPrivacy',
'PolicyMfaFactors',
'PolicyPasskey',
'PolicyDenyAliasedEmail',
'PolicyDenyDisposableEmail',
'PolicyDenyFreeEmail',
'PolicyDenyCorporateEmail',
'PlatformWeb',
'PlatformApple',
'PlatformAndroid',
Expand Down Expand Up @@ -594,18 +591,13 @@
'Organization',
'DedicatedDatabasePITRWindows',
'BackupPolicy',
'PolicyDenyAliasedEmail',
'PolicyDenyDisposableEmail',
'PolicyDenyFreeEmail',
'PolicyDenyCorporateEmail',
'DedicatedDatabasePooler',
'PostgresExtension',
'Program',
'BackupRestoration',
'DedicatedDatabaseRestorationList',
'DedicatedDatabaseSpecification',
'DedicatedDatabaseSpecificationList',
'DedicatedDatabaseSpecificationPricing',
'DatabaseStatusConnections',
'DatabaseStatusReplica',
'DatabaseStatusVolume',
Expand Down
Loading