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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Change Log

## 20.1.0

* Added: `createSesProvider` and `updateSesProvider` to `messaging`
* Added: `updateOAuth2Server` to `project` for OAuth2 server settings
* Added: `updatePasswordStrengthPolicy` and `PolicyPasswordStrength` to `project`
* Added: `getAuditsDB` health check to `health`
* Added: `password-strength` to `ProjectPolicyId`
* Added: `apps.read` and `apps.write` to `ProjectKeyScopes`


## 20.0.0

* Breaking: Removed `githubImagine` and `googleImagine` from `ProjectOAuthProviderId`
Expand All @@ -8,7 +18,7 @@
* Added: `Organization` service for managing projects and API keys
* Added: `PolicyDenyAliasedEmail`, `PolicyDenyDisposableEmail`, and `PolicyDenyFreeEmail` policy models
* Added: `deny-aliased-email`, `deny-disposable-email`, and `deny-free-email` to `ProjectPolicyId`
* Added: `BrowserTheme`, `HealthQueueName`, `OrganizationKeyScopes`, and `Region` enums
* Replaced: `BrowserTheme`, `HealthQueueName`, `OrganizationKeyScopes`, and `Region` enums
* Added: `dart-3.12` and `flutter-3.44` runtimes
* Added: `ProjectList` model and new attributes on `Function`, `Site`, and `UsageGauge`
* Updated: `functions`, `sites`, `usage`, `health`, and `avatars` services
Expand Down
20 changes: 17 additions & 3 deletions appwrite/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ def __init__(self):
self._endpoint = 'https://cloud.appwrite.io/v1'
self._global_headers = {
'content-type': '',
'user-agent' : f'AppwritePythonSDK/20.0.0 ({platform.uname().system}; {platform.uname().version}; {platform.uname().machine})',
'user-agent' : f'AppwritePythonSDK/20.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': '20.0.0',
'x-sdk-version': '20.1.0',
'X-Appwrite-Response-Format' : '1.9.5',
}
self._config = {}

def set_self_signed(self, status=True):
self._self_signed = status
Expand All @@ -43,68 +44,81 @@ def add_header(self, key, value):
def get_headers(self):
return dict(self._global_headers)

def get_config(self, key):
return self._config.get(key, '')

def set_project(self, value):
"""Your project ID"""

self._global_headers['x-appwrite-project'] = value
self._config['project'] = value
return self

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

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

def set_jwt(self, value):
Comment on lines 59 to 63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 set_project no longer writes to global headers

set_project previously called self._global_headers['x-appwrite-project'] = value, so the project ID was automatically included in every request via the {**self._global_headers, **headers} merge in call(). After this change it only stores the value in self._config. Any call made through client.call() directly — bypassing a service class — will now silently omit the X-Appwrite-Project header even after set_project was called. All generated service methods were updated to inject the header explicitly, so the normal SDK path works correctly, but existing code that calls client.call() directly will regress.

"""Your secret JSON Web Token"""

self._global_headers['x-appwrite-jwt'] = value
self._config['jwt'] = value
return self

def set_locale(self, value):
self._global_headers['x-appwrite-locale'] = value
self._config['locale'] = value
return self

def set_session(self, value):
"""The user session to authenticate with"""

self._global_headers['x-appwrite-session'] = value
self._config['session'] = value
return self

def set_forwarded_user_agent(self, value):
"""The user agent string of the client that made the request"""

self._global_headers['x-forwarded-user-agent'] = 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."""

self._global_headers['cookie'] = value
self._config['cookie'] = value
return self

def set_impersonate_user_id(self, value):
"""Impersonate a user by ID on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data."""

self._global_headers['x-appwrite-impersonate-user-id'] = value
self._config['impersonateuserid'] = value
return self

def set_impersonate_user_email(self, value):
"""Impersonate a user by email on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data."""

self._global_headers['x-appwrite-impersonate-user-email'] = value
self._config['impersonateuseremail'] = value
return self

def set_impersonate_user_phone(self, value):
"""Impersonate a user by phone on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data."""

self._global_headers['x-appwrite-impersonate-user-phone'] = value
self._config['impersonateuserphone'] = value
return self

def call(self, method, path='', headers=None, params=None, response_type='json'):
Expand Down
2 changes: 2 additions & 0 deletions appwrite/enums/project_key_scopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,6 @@ class ProjectKeyScopes(Enum):
DOMAINS_READ = "domains.read"
DOMAINS_WRITE = "domains.write"
EVENTS_READ = "events.read"
APPS_READ = "apps.read"
APPS_WRITE = "apps.write"
USAGE_READ = "usage.read"
1 change: 1 addition & 0 deletions appwrite/enums/project_policy_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
class ProjectPolicyId(Enum):
PASSWORD_DICTIONARY = "password-dictionary"
PASSWORD_HISTORY = "password-history"
PASSWORD_STRENGTH = "password-strength"
PASSWORD_PERSONAL_DATA = "password-personal-data"
SESSION_ALERT = "session-alert"
SESSION_DURATION = "session-duration"
Expand Down
2 changes: 2 additions & 0 deletions appwrite/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
from .o_auth2_provider_list import OAuth2ProviderList
from .policy_password_dictionary import PolicyPasswordDictionary
from .policy_password_history import PolicyPasswordHistory
from .policy_password_strength import PolicyPasswordStrength
from .policy_password_personal_data import PolicyPasswordPersonalData
from .policy_session_alert import PolicySessionAlert
from .policy_session_duration import PolicySessionDuration
Expand Down Expand Up @@ -406,6 +407,7 @@
'OAuth2ProviderList',
'PolicyPasswordDictionary',
'PolicyPasswordHistory',
'PolicyPasswordStrength',
'PolicyPasswordPersonalData',
'PolicySessionAlert',
'PolicySessionDuration',
Expand Down
5 changes: 3 additions & 2 deletions appwrite/models/policy_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .base_model import AppwriteModel
from .policy_password_dictionary import PolicyPasswordDictionary
from .policy_password_history import PolicyPasswordHistory
from .policy_password_strength import PolicyPasswordStrength
from .policy_password_personal_data import PolicyPasswordPersonalData
from .policy_session_alert import PolicySessionAlert
from .policy_session_duration import PolicySessionDuration
Expand All @@ -23,8 +24,8 @@ class PolicyList(AppwriteModel):
----------
total : float
Total number of policies in the given project.
policies : List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordPersonalData, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail]]
policies : List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail]]
List of policies.
"""
total: float = Field(..., alias='total')
policies: List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordPersonalData, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail]] = Field(..., alias='policies')
policies: List[Union[PolicyPasswordDictionary, PolicyPasswordHistory, PolicyPasswordStrength, PolicyPasswordPersonalData, PolicySessionAlert, PolicySessionDuration, PolicySessionInvalidation, PolicySessionLimit, PolicyUserLimit, PolicyMembershipPrivacy, PolicyDenyAliasedEmail, PolicyDenyDisposableEmail, PolicyDenyFreeEmail]] = Field(..., alias='policies')
30 changes: 30 additions & 0 deletions appwrite/models/policy_password_strength.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from typing import Any, Dict, List, Optional, Union, cast
from pydantic import Field, PrivateAttr

from .base_model import AppwriteModel

class PolicyPasswordStrength(AppwriteModel):
"""
Policy Password Strength

Attributes
----------
id : str
Policy ID.
min : float
Minimum password length required for user passwords.
uppercase : bool
Whether passwords must include at least one uppercase letter.
lowercase : bool
Whether passwords must include at least one lowercase letter.
number : bool
Whether passwords must include at least one number.
symbols : bool
Whether passwords must include at least one symbol.
"""
id: str = Field(..., alias='$id')
min: float = Field(..., alias='min')
uppercase: bool = Field(..., alias='uppercase')
lowercase: bool = Field(..., alias='lowercase')
number: bool = Field(..., alias='number')
symbols: bool = Field(..., alias='symbols')
41 changes: 34 additions & 7 deletions appwrite/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from .project_auth_method import ProjectAuthMethod
from .project_service import ProjectService
from .project_protocol import ProjectProtocol
from .billing_limits import BillingLimits
from .block import Block
from .billing_limits import BillingLimits

class Project(AppwriteModel):
"""
Expand All @@ -25,6 +25,8 @@ class Project(AppwriteModel):
Project name.
teamid : str
Project team ID.
region : str
Project region
devkeys : List[DevKey]
Deprecated since 1.9.5: List of dev keys.
smtpenabled : bool
Expand Down Expand Up @@ -61,20 +63,37 @@ class Project(AppwriteModel):
List of services.
protocols : List[ProjectProtocol]
List of protocols.
region : str
Project region
billinglimits : Optional[BillingLimits]
Billing limits reached
blocks : List[Block]
Project blocks information
consoleaccessedat : str
Last time the project was accessed via console. Used with plan's projectInactivityDays to determine if project is paused.
billinglimits : Optional[BillingLimits]
Billing limits reached
oauth2serverenabled : bool
OAuth2 server status
oauth2serverauthorizationurl : str
OAuth2 server authorization URL
oauth2serverscopes : List[Any]
OAuth2 server allowed scopes
oauth2serveraccesstokenduration : float
OAuth2 server access token duration in seconds for confidential clients
oauth2serverrefreshtokenduration : float
OAuth2 server refresh token duration in seconds for confidential clients
oauth2serverpublicaccesstokenduration : float
OAuth2 server access token duration in seconds for public clients (SPAs, mobile, native)
oauth2serverpublicrefreshtokenduration : float
OAuth2 server refresh token duration in seconds for public clients (SPAs, mobile, native)
oauth2serverconfidentialpkce : 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.
oauth2serverdiscoveryurl : str
OAuth2 server discovery URL
"""
id: str = Field(..., alias='$id')
createdat: str = Field(..., alias='$createdAt')
updatedat: str = Field(..., alias='$updatedAt')
name: str = Field(..., alias='name')
teamid: str = Field(..., alias='teamId')
region: str = Field(..., alias='region')
devkeys: List[DevKey] = Field(..., alias='devKeys')
smtpenabled: bool = Field(..., alias='smtpEnabled')
smtpsendername: str = Field(..., alias='smtpSenderName')
Expand All @@ -93,7 +112,15 @@ class Project(AppwriteModel):
authmethods: List[ProjectAuthMethod] = Field(..., alias='authMethods')
services: List[ProjectService] = Field(..., alias='services')
protocols: List[ProjectProtocol] = Field(..., alias='protocols')
region: str = Field(..., alias='region')
billinglimits: Optional[BillingLimits] = Field(default=None, alias='billingLimits')
blocks: List[Block] = Field(..., alias='blocks')
consoleaccessedat: str = Field(..., alias='consoleAccessedAt')
billinglimits: Optional[BillingLimits] = Field(default=None, alias='billingLimits')
oauth2serverenabled: bool = Field(..., alias='oAuth2ServerEnabled')
oauth2serverauthorizationurl: str = Field(..., alias='oAuth2ServerAuthorizationUrl')
oauth2serverscopes: List[Any] = Field(..., alias='oAuth2ServerScopes')
oauth2serveraccesstokenduration: float = Field(..., alias='oAuth2ServerAccessTokenDuration')
oauth2serverrefreshtokenduration: float = Field(..., alias='oAuth2ServerRefreshTokenDuration')
oauth2serverpublicaccesstokenduration: float = Field(..., alias='oAuth2ServerPublicAccessTokenDuration')
oauth2serverpublicrefreshtokenduration: float = Field(..., alias='oAuth2ServerPublicRefreshTokenDuration')
oauth2serverconfidentialpkce: bool = Field(..., alias='oAuth2ServerConfidentialPkce')
oauth2serverdiscoveryurl: str = Field(..., alias='oAuth2ServerDiscoveryUrl')
Loading
Loading