511 lines
16 KiB
Python
511 lines
16 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, SecretStr
|
|
|
|
|
|
AddressBookScope = Literal["user", "group", "tenant", "system"]
|
|
AddressSyncDirection = Literal["read_only", "import", "export", "two_way"]
|
|
AddressSyncStatus = Literal["idle", "running", "succeeded", "failed", "conflict", "disabled"]
|
|
AddressSyncDiagnosticSeverity = Literal["debug", "info", "warning", "error"]
|
|
AddressSyncConflictStatus = Literal["open", "resolved", "ignored"]
|
|
AddressSyncConflictResolution = Literal["keep_local", "use_remote", "merge", "manual", "ignored"]
|
|
AddressCardDavAuthType = Literal["none", "basic", "bearer"]
|
|
AddressSyncPlanAction = Literal["create", "update", "delete", "remote_create", "remote_update", "remote_delete", "conflict", "unchanged", "error"]
|
|
|
|
|
|
class ContactEmailPayload(BaseModel):
|
|
label: str | None = Field(default=None, max_length=80)
|
|
email: str = Field(max_length=320)
|
|
is_primary: bool = False
|
|
|
|
|
|
class ContactPhonePayload(BaseModel):
|
|
label: str | None = Field(default=None, max_length=80)
|
|
phone: str = Field(max_length=100)
|
|
is_primary: bool = False
|
|
|
|
|
|
class ContactPostalAddressPayload(BaseModel):
|
|
label: str | None = Field(default=None, max_length=80)
|
|
street: str | None = Field(default=None, max_length=500)
|
|
postal_code: str | None = Field(default=None, max_length=40)
|
|
locality: str | None = Field(default=None, max_length=255)
|
|
region: str | None = Field(default=None, max_length=255)
|
|
country: str | None = Field(default=None, max_length=255)
|
|
is_primary: bool = False
|
|
|
|
|
|
class AddressBookCreateRequest(BaseModel):
|
|
scope_type: AddressBookScope = "user"
|
|
group_id: str | None = Field(default=None, max_length=36)
|
|
name: str = Field(min_length=1, max_length=255)
|
|
description: str | None = None
|
|
|
|
|
|
class AddressBookUpdateRequest(BaseModel):
|
|
name: str | None = Field(default=None, min_length=1, max_length=255)
|
|
description: str | None = None
|
|
|
|
|
|
class AddressBookResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
scope_type: AddressBookScope
|
|
scope_id: str | None = None
|
|
name: str
|
|
description: str | None = None
|
|
source_kind: str
|
|
source_ref: str | None = None
|
|
read_only: bool
|
|
sync_status: str | None = None
|
|
sync_error: str | None = None
|
|
contact_count: int = 0
|
|
deleted_at: datetime | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class AddressBookListResponse(BaseModel):
|
|
address_books: list[AddressBookResponse]
|
|
|
|
|
|
class AddressListCreateRequest(BaseModel):
|
|
name: str = Field(min_length=1, max_length=255)
|
|
description: str | None = None
|
|
|
|
|
|
class AddressListUpdateRequest(BaseModel):
|
|
name: str | None = Field(default=None, min_length=1, max_length=255)
|
|
description: str | None = None
|
|
|
|
|
|
class AddressListEntryCreateRequest(BaseModel):
|
|
contact_id: str = Field(max_length=36)
|
|
contact_email_id: str | None = Field(default=None, max_length=36)
|
|
contact_postal_address_id: str | None = Field(default=None, max_length=36)
|
|
label: str | None = Field(default=None, max_length=255)
|
|
|
|
|
|
class AddressListResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
address_book_id: str
|
|
name: str
|
|
description: str | None = None
|
|
source_kind: str
|
|
source_ref: str | None = None
|
|
read_only: bool
|
|
entry_count: int = 0
|
|
deleted_at: datetime | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class AddressListListResponse(BaseModel):
|
|
address_lists: list[AddressListResponse]
|
|
|
|
|
|
class AddressListEntryResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
address_list_id: str
|
|
contact_id: str
|
|
contact_email_id: str | None = None
|
|
contact_postal_address_id: str | None = None
|
|
target_kind: str
|
|
label: str | None = None
|
|
order_index: int
|
|
contact_display_name: str
|
|
email: str | None = None
|
|
postal_address: str | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class AddressListEntryListResponse(BaseModel):
|
|
entries: list[AddressListEntryResponse]
|
|
|
|
|
|
class ContactCreateRequest(BaseModel):
|
|
display_name: str | None = Field(default=None, max_length=255)
|
|
given_name: str | None = Field(default=None, max_length=255)
|
|
family_name: str | None = Field(default=None, max_length=255)
|
|
organization: str | None = Field(default=None, max_length=255)
|
|
role_title: str | None = Field(default=None, max_length=255)
|
|
note: str | None = None
|
|
tags: list[str] = Field(default_factory=list)
|
|
emails: list[ContactEmailPayload] = Field(default_factory=list)
|
|
phones: list[ContactPhonePayload] = Field(default_factory=list)
|
|
postal_addresses: list[ContactPostalAddressPayload] = Field(default_factory=list)
|
|
provenance: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class ContactUpdateRequest(BaseModel):
|
|
display_name: str | None = Field(default=None, max_length=255)
|
|
given_name: str | None = Field(default=None, max_length=255)
|
|
family_name: str | None = Field(default=None, max_length=255)
|
|
organization: str | None = Field(default=None, max_length=255)
|
|
role_title: str | None = Field(default=None, max_length=255)
|
|
note: str | None = None
|
|
tags: list[str] | None = None
|
|
emails: list[ContactEmailPayload] | None = None
|
|
phones: list[ContactPhonePayload] | None = None
|
|
postal_addresses: list[ContactPostalAddressPayload] | None = None
|
|
provenance: dict[str, Any] | None = None
|
|
|
|
|
|
class ContactEmailResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
label: str | None = None
|
|
email: str
|
|
is_primary: bool
|
|
|
|
|
|
class ContactPhoneResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
label: str | None = None
|
|
phone: str
|
|
is_primary: bool
|
|
|
|
|
|
class ContactPostalAddressResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
label: str | None = None
|
|
street: str | None = None
|
|
postal_code: str | None = None
|
|
locality: str | None = None
|
|
region: str | None = None
|
|
country: str | None = None
|
|
is_primary: bool
|
|
|
|
|
|
class ContactResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
address_book_id: str
|
|
display_name: str
|
|
given_name: str | None = None
|
|
family_name: str | None = None
|
|
organization: str | None = None
|
|
role_title: str | None = None
|
|
note: str | None = None
|
|
tags: list[str]
|
|
source_kind: str
|
|
source_ref: str | None = None
|
|
source_payload_kind: str | None = None
|
|
source_revision: str | None = None
|
|
provenance: dict[str, Any]
|
|
emails: list[ContactEmailResponse]
|
|
phones: list[ContactPhoneResponse]
|
|
postal_addresses: list[ContactPostalAddressResponse]
|
|
deleted_at: datetime | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ContactListResponse(BaseModel):
|
|
contacts: list[ContactResponse]
|
|
|
|
|
|
class AddressLookupResponse(BaseModel):
|
|
contacts: list[ContactResponse]
|
|
|
|
|
|
class AddressBookWriteDecisionResponse(BaseModel):
|
|
address_book_id: str
|
|
address_book_label: str | None = None
|
|
operation: str
|
|
allowed: bool
|
|
reason: str
|
|
message: str
|
|
scope_type: AddressBookScope | None = None
|
|
scope_id: str | None = None
|
|
tenant_id: str | None = None
|
|
source_kind: str | None = None
|
|
read_only: bool = False
|
|
required_scopes: list[str] = Field(default_factory=list)
|
|
provenance: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AddressBookWriteTargetsResponse(BaseModel):
|
|
targets: list[AddressBookWriteDecisionResponse] = Field(default_factory=list)
|
|
|
|
|
|
class AddressSyncSourceCreateRequest(BaseModel):
|
|
connector_type: str = Field(min_length=1, max_length=60)
|
|
display_name: str = Field(min_length=1, max_length=255)
|
|
external_account_ref: str | None = Field(default=None, max_length=1000)
|
|
external_address_book_ref: str | None = Field(default=None, max_length=1000)
|
|
sync_direction: AddressSyncDirection = "read_only"
|
|
read_only: bool | None = None
|
|
enabled: bool = True
|
|
sync_token: str | None = None
|
|
etag: str | None = Field(default=None, max_length=1000)
|
|
remote_revision: str | None = Field(default=None, max_length=1000)
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AddressSyncSourceUpdateRequest(BaseModel):
|
|
display_name: str | None = Field(default=None, min_length=1, max_length=255)
|
|
external_account_ref: str | None = Field(default=None, max_length=1000)
|
|
external_address_book_ref: str | None = Field(default=None, max_length=1000)
|
|
sync_direction: AddressSyncDirection | None = None
|
|
read_only: bool | None = None
|
|
enabled: bool | None = None
|
|
sync_token: str | None = None
|
|
etag: str | None = Field(default=None, max_length=1000)
|
|
remote_revision: str | None = Field(default=None, max_length=1000)
|
|
metadata: dict[str, Any] | None = None
|
|
|
|
|
|
class AddressSyncAttemptFinishRequest(BaseModel):
|
|
status: Literal["succeeded", "failed", "conflict"]
|
|
sync_token: str | None = None
|
|
etag: str | None = Field(default=None, max_length=1000)
|
|
remote_revision: str | None = Field(default=None, max_length=1000)
|
|
error: str | None = None
|
|
diagnostic: dict[str, Any] | None = None
|
|
|
|
|
|
class AddressSyncSourceResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
address_book_id: str
|
|
connector_type: str
|
|
display_name: str
|
|
external_account_ref: str | None = None
|
|
external_address_book_ref: str | None = None
|
|
sync_direction: str
|
|
read_only: bool
|
|
enabled: bool
|
|
status: str
|
|
sync_token: str | None = None
|
|
etag: str | None = None
|
|
remote_revision: str | None = None
|
|
last_attempted_at: datetime | None = None
|
|
last_success_at: datetime | None = None
|
|
last_error: str | None = None
|
|
last_diagnostic: dict[str, Any] | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class AddressSyncSourceListResponse(BaseModel):
|
|
sync_sources: list[AddressSyncSourceResponse]
|
|
|
|
|
|
class AddressSyncDiagnosticCreateRequest(BaseModel):
|
|
severity: AddressSyncDiagnosticSeverity = "info"
|
|
code: str = Field(min_length=1, max_length=120)
|
|
message: str = Field(min_length=1)
|
|
details: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AddressSyncDiagnosticResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
sync_source_id: str
|
|
severity: str
|
|
code: str
|
|
message: str
|
|
details: dict[str, Any] = Field(default_factory=dict)
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class AddressSyncDiagnosticListResponse(BaseModel):
|
|
diagnostics: list[AddressSyncDiagnosticResponse]
|
|
|
|
|
|
class AddressSyncTombstoneCreateRequest(BaseModel):
|
|
contact_id: str | None = Field(default=None, max_length=36)
|
|
remote_uid: str | None = Field(default=None, max_length=1000)
|
|
resource_href: str | None = Field(default=None, max_length=1000)
|
|
local_deleted_at: datetime | None = None
|
|
remote_deleted_at: datetime | None = None
|
|
synced_at: datetime | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AddressSyncTombstoneResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
sync_source_id: str
|
|
address_book_id: str
|
|
contact_id: str | None = None
|
|
remote_uid: str | None = None
|
|
resource_href: str | None = None
|
|
local_deleted_at: datetime | None = None
|
|
remote_deleted_at: datetime | None = None
|
|
synced_at: datetime | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class AddressSyncTombstoneListResponse(BaseModel):
|
|
tombstones: list[AddressSyncTombstoneResponse]
|
|
|
|
|
|
class AddressSyncConflictCreateRequest(BaseModel):
|
|
contact_id: str | None = Field(default=None, max_length=36)
|
|
remote_uid: str | None = Field(default=None, max_length=1000)
|
|
resource_href: str | None = Field(default=None, max_length=1000)
|
|
field_path: str = Field(min_length=1, max_length=500)
|
|
local_value: dict[str, Any] | None = None
|
|
remote_value: dict[str, Any] | None = None
|
|
local_updated_at: datetime | None = None
|
|
remote_updated_at: datetime | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class AddressSyncConflictResolveRequest(BaseModel):
|
|
resolution: AddressSyncConflictResolution
|
|
status: Literal["resolved", "ignored"] = "resolved"
|
|
merged_payload: ContactCreateRequest | None = None
|
|
|
|
|
|
class AddressSyncConflictResponse(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: str
|
|
tenant_id: str | None = None
|
|
sync_source_id: str
|
|
address_book_id: str
|
|
contact_id: str | None = None
|
|
remote_uid: str | None = None
|
|
resource_href: str | None = None
|
|
field_path: str
|
|
local_value: dict[str, Any] | None = None
|
|
remote_value: dict[str, Any] | None = None
|
|
local_updated_at: datetime | None = None
|
|
remote_updated_at: datetime | None = None
|
|
status: str
|
|
resolution: str | None = None
|
|
resolved_at: datetime | None = None
|
|
resolved_by_account_id: str | None = None
|
|
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class AddressSyncConflictListResponse(BaseModel):
|
|
conflicts: list[AddressSyncConflictResponse]
|
|
|
|
|
|
class AddressCardDavDiscoveryRequest(BaseModel):
|
|
url: str = Field(min_length=1, max_length=2000)
|
|
auth_type: AddressCardDavAuthType = "none"
|
|
username: str | None = Field(default=None, max_length=320)
|
|
password: SecretStr | None = None
|
|
bearer_token: SecretStr | None = None
|
|
credential_ref: str | None = Field(default=None, max_length=1000)
|
|
source_id: str | None = Field(default=None, max_length=36)
|
|
|
|
|
|
class AddressCardDavAddressBookResponse(BaseModel):
|
|
collection_url: str
|
|
href: str
|
|
display_name: str | None = None
|
|
description: str | None = None
|
|
ctag: str | None = None
|
|
sync_token: str | None = None
|
|
|
|
|
|
class AddressCardDavDiscoveryResponse(BaseModel):
|
|
address_books: list[AddressCardDavAddressBookResponse]
|
|
|
|
|
|
class AddressCardDavSourceCreateRequest(BaseModel):
|
|
collection_url: str = Field(min_length=1, max_length=2000)
|
|
display_name: str | None = Field(default=None, max_length=255)
|
|
auth_type: AddressCardDavAuthType = "none"
|
|
username: str | None = Field(default=None, max_length=320)
|
|
password: SecretStr | None = None
|
|
bearer_token: SecretStr | None = None
|
|
credential_ref: str | None = Field(default=None, max_length=1000)
|
|
sync_direction: AddressSyncDirection = "read_only"
|
|
read_only: bool | None = None
|
|
sync_token: str | None = None
|
|
etag: str | None = Field(default=None, max_length=1000)
|
|
remote_revision: str | None = Field(default=None, max_length=1000)
|
|
|
|
|
|
class AddressSyncRunRequest(BaseModel):
|
|
force_full: bool = False
|
|
password: SecretStr | None = None
|
|
bearer_token: SecretStr | None = None
|
|
|
|
|
|
class AddressSyncPlanStats(BaseModel):
|
|
created: int = 0
|
|
updated: int = 0
|
|
deleted: int = 0
|
|
conflicts: int = 0
|
|
unchanged: int = 0
|
|
errors: int = 0
|
|
fetched: int = 0
|
|
full_sync: bool = False
|
|
used_sync_token: bool = False
|
|
sync_token: str | None = None
|
|
etag: str | None = None
|
|
remote_revision: str | None = None
|
|
|
|
|
|
class AddressSyncPlanItemResponse(BaseModel):
|
|
action: AddressSyncPlanAction
|
|
href: str | None = None
|
|
remote_uid: str | None = None
|
|
contact_id: str | None = None
|
|
display_name: str | None = None
|
|
etag: str | None = None
|
|
message: str | None = None
|
|
|
|
|
|
class AddressSyncPlanResponse(BaseModel):
|
|
sync_source: AddressSyncSourceResponse
|
|
stats: AddressSyncPlanStats
|
|
items: list[AddressSyncPlanItemResponse]
|
|
|
|
|
|
class VCardImportRequest(BaseModel):
|
|
content: str = Field(min_length=1)
|
|
|
|
|
|
class VCardImportIssue(BaseModel):
|
|
index: int
|
|
message: str
|
|
severity: Literal["warning", "error"] = "error"
|
|
field: str | None = None
|
|
line: int | None = None
|
|
|
|
|
|
class VCardImportResponse(BaseModel):
|
|
imported: int
|
|
skipped: int
|
|
contacts: list[ContactResponse]
|
|
issues: list[VCardImportIssue] = Field(default_factory=list)
|