Files
govoplan-addresses/src/govoplan_addresses/backend/schemas.py
T

907 lines
29 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"]
AddressDistributionChannel = Literal["email", "postal", "internal_mail", "portal"]
AddressContactPointChannel = Literal[
"email",
"phone",
"postal",
"internal_mail",
"portal",
]
AddressChannelDecision = Literal[
"allowed",
"opted_in",
"preferred",
"opted_out",
"suppressed",
"invalid",
"returned",
"temporarily_unavailable",
]
AddressDistributionOutcome = Literal[
"usable",
"unresolved",
"invalid",
"suppressed",
"ambiguous",
"duplicate",
"policy_blocked",
"provider_unavailable",
"stale",
]
AddressContactPointFallbackRule = Literal["none", "primary", "any"]
AddressPostalFormat = Literal["domestic", "international"]
ContactPointQualityState = Literal[
"valid",
"invalid",
"returned",
"stale",
"undeliverable",
]
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 ContactFieldProvenanceResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
contact_id: str
field_path: str
value: Any = None
source_kind: str
source_ref: str | None = None
source_revision: str | None = None
precedence: int
selected: bool
reason_code: str
explanation: str | None = None
visibility: str
merge_record_id: str | None = None
created_by_account_id: str | None = None
metadata: dict[str, Any] = Field(default_factory=dict, validation_alias="metadata_")
created_at: datetime
class ContactPointQualityDecisionCreateRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
channel: AddressContactPointChannel
contact_point_id: str | None = Field(default=None, max_length=36)
state: ContactPointQualityState
reason_code: str | None = Field(default=None, max_length=120)
reason: str | None = None
evidence_ref: str | None = Field(default=None, max_length=1000)
effective_from: datetime | None = None
metadata: dict[str, Any] = Field(default_factory=dict)
class ContactPointQualityDecisionResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
tenant_id: str | None = None
contact_id: str
channel: AddressContactPointChannel
contact_point_id: str | None = None
state: ContactPointQualityState
reason_code: str
reason: str | None = None
evidence_ref: str | None = None
effective_from: datetime
effective_until: datetime | None = None
created_by_account_id: str | None = None
metadata: dict[str, Any] = Field(default_factory=dict, validation_alias="metadata_")
created_at: datetime
updated_at: datetime
class ContactPointQualityDecisionListResponse(BaseModel):
decisions: list[ContactPointQualityDecisionResponse] = Field(default_factory=list)
class ContactEmailResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
label: str | None = None
email: str
original_email: str = ""
normalized_email: str = ""
provenance: dict[str, Any] = Field(default_factory=dict)
quality_state: ContactPointQualityState = "valid"
quality_reason_code: str | None = None
is_primary: bool
class ContactPhoneResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
label: str | None = None
phone: str
original_phone: str = ""
normalized_phone: str = ""
provenance: dict[str, Any] = Field(default_factory=dict)
quality_state: ContactPointQualityState = "valid"
quality_reason_code: str | None = None
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
original_value: dict[str, Any] = Field(default_factory=dict)
normalized_value: dict[str, Any] = Field(default_factory=dict)
provenance: dict[str, Any] = Field(default_factory=dict)
quality_state: ContactPointQualityState = "valid"
quality_reason_code: 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]
field_provenance: list[ContactFieldProvenanceResponse] = Field(default_factory=list)
deleted_at: datetime | None = None
created_at: datetime
updated_at: datetime
class ContactListResponse(BaseModel):
contacts: list[ContactResponse]
total: int
offset: int
limit: int
has_more: bool
class ContactDuplicateFeatureResponse(BaseModel):
code: str
label: str
weight: int
value: str
class ContactDuplicateSuggestionResponse(BaseModel):
left: ContactResponse
right: ContactResponse
score: int
confidence: Literal["possible", "likely", "strong"]
features: list[ContactDuplicateFeatureResponse]
class ContactDuplicateSuggestionListResponse(BaseModel):
suggestions: list[ContactDuplicateSuggestionResponse] = Field(default_factory=list)
scanned_contacts: int
candidate_pairs: int
truncated: bool
class ContactMergeRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
winner_contact_id: str = Field(max_length=36)
duplicate_contact_ids: list[str] = Field(min_length=1, max_length=20)
reason: str = Field(min_length=3)
field_sources: dict[str, str] = Field(default_factory=dict)
contact_point_strategy: Literal["union", "winner_only"] = "union"
source_precedence: list[str] = Field(default_factory=list, max_length=20)
class ContactMergeRecoveryRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
reason: str = Field(min_length=3)
expected_after_hash: str = Field(min_length=64, max_length=64)
class ContactMergeRecordResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
tenant_id: str | None = None
address_book_id: str
winner_contact_id: str
loser_contact_ids: list[str]
status: str
reason: str
survivorship: dict[str, Any]
decisions: list[dict[str, Any]]
before_hash: str
after_hash: str
created_by_account_id: str | None = None
recovered_at: datetime | None = None
recovered_by_account_id: str | None = None
recovery_action: str | None = None
recovery_reason: str | None = None
provenance: dict[str, Any]
created_at: datetime
updated_at: datetime
class ContactMergeRecordListResponse(BaseModel):
merges: list[ContactMergeRecordResponse] = Field(default_factory=list)
class ContactRedirectResponse(BaseModel):
requested_contact_id: str
resolved_contact_id: str
redirected: bool
redirect_chain: list[str] = Field(default_factory=list)
merge_record_ids: list[str] = Field(default_factory=list)
class AddressQualityCorrectionResponse(BaseModel):
contact_id: str
display_name: str
channel: AddressContactPointChannel
contact_point_id: str | None = None
state: ContactPointQualityState
reason_code: str
reason: str | None = None
effective_from: datetime
class AddressQualitySummaryResponse(BaseModel):
contact_count: int
contact_point_count: int
quality_counts: dict[str, int] = Field(default_factory=dict)
duplicate_suggestion_count: int
correction_count: int
corrections: list[AddressQualityCorrectionResponse] = Field(default_factory=list)
truncated: bool = False
class ContactChannelRuleCreateRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
channel: AddressDistributionChannel
purpose: str | None = Field(default=None, max_length=120)
contact_point_id: str | None = Field(default=None, max_length=36)
decision: AddressChannelDecision
legal_basis: str | None = Field(default=None, max_length=255)
evidence_ref: str | None = Field(default=None, max_length=1000)
reason: str | None = None
preference_rank: int | None = Field(default=None, ge=0, le=10000)
locale: str | None = Field(default=None, max_length=20)
effective_from: datetime | None = None
effective_until: datetime | None = None
metadata: dict[str, Any] = Field(default_factory=dict)
class ContactChannelRuleResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
tenant_id: str | None = None
contact_id: str
channel: AddressDistributionChannel
purpose: str | None = None
contact_point_id: str | None = None
decision: AddressChannelDecision
legal_basis: str | None = None
evidence_ref: str | None = None
reason: str | None = None
preference_rank: int | None = None
locale: str | None = None
effective_from: datetime | None = None
effective_until: datetime | None = None
metadata: dict[str, Any] = Field(default_factory=dict, validation_alias="metadata_")
created_by_account_id: str | None = None
created_at: datetime
updated_at: datetime
class ContactChannelRuleListResponse(BaseModel):
rules: list[ContactChannelRuleResponse] = Field(default_factory=list)
class AddressSourceReferencePayload(BaseModel):
provider: str = Field(min_length=1, max_length=120)
resource_type: str = Field(min_length=1, max_length=120)
resource_id: str = Field(min_length=1, max_length=1000)
revision: str | None = Field(default=None, max_length=1000)
fingerprint: str | None = Field(default=None, max_length=255)
label: str | None = Field(default=None, max_length=500)
metadata: dict[str, Any] = Field(default_factory=dict)
class ContactPointResolveRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
subject: AddressSourceReferencePayload
effective_at: datetime
purpose: str | None = Field(default=None, max_length=120)
requested_channels: list[AddressDistributionChannel] = Field(default_factory=list)
address_purpose: str | None = Field(default=None, max_length=80)
fallback_rule: AddressContactPointFallbackRule = "primary"
locale: str | None = Field(default=None, max_length=20)
postal_format: AddressPostalFormat = "domestic"
context: dict[str, Any] = Field(default_factory=dict)
class ContactPointSourceRequestPayload(BaseModel):
model_config = ConfigDict(extra="forbid")
source_id: str = Field(min_length=1, max_length=1000)
effective_at: datetime
purpose: str | None = Field(default=None, max_length=120)
requested_channels: list[AddressDistributionChannel] = Field(default_factory=list)
address_purpose: str | None = Field(default=None, max_length=80)
fallback_rule: AddressContactPointFallbackRule = "primary"
locale: str | None = Field(default=None, max_length=20)
postal_format: AddressPostalFormat = "domestic"
max_items: int = Field(default=5000, ge=1, le=20000)
context: dict[str, Any] = Field(default_factory=dict)
class ContactPointSourceRequestResponse(ContactPointSourceRequestPayload):
tenant_id: str
class ContactPointCandidateResponse(BaseModel):
channel: AddressDistributionChannel
target: str
target_key: str
status: AddressDistributionOutcome
contact_point_id: str | None = None
address_purpose: str | None = None
locale: str | None = None
preferred: bool = False
preference_rank: int | None = None
reason_code: str | None = None
explanation: str | None = None
source: AddressSourceReferencePayload | None = None
source_revision: str | None = None
preference_revision: str | None = None
consent_revision: str | None = None
value: dict[str, Any] = Field(default_factory=dict)
provenance: dict[str, Any] = Field(default_factory=dict)
class DistributionExplanationResponse(BaseModel):
code: str
message: str
severity: Literal["info", "warning", "error"]
provider: str | None = None
source: AddressSourceReferencePayload | None = None
provenance: dict[str, Any] = Field(default_factory=dict)
class ContactPointResolutionResponse(BaseModel):
contract_version: str
subject: AddressSourceReferencePayload
status: AddressDistributionOutcome
contact_id: str | None = None
display_name: str | None = None
candidates: list[ContactPointCandidateResponse] = Field(default_factory=list)
excluded: list[ContactPointCandidateResponse] = Field(default_factory=list)
explanations: list[DistributionExplanationResponse] = Field(default_factory=list)
source_revision: str | None = None
source_fingerprint: str | None = None
provenance: dict[str, Any] = Field(default_factory=dict)
class ContactPointSourcePreviewResponse(BaseModel):
contract_version: str
source: AddressSourceReferencePayload
request: ContactPointSourceRequestResponse
resolutions: list[ContactPointResolutionResponse]
total_count: int
usable_count: int
excluded_count: int
offset: int
limit: int
has_more: bool
source_revision: str
source_fingerprint: str
generated_at: datetime
provenance: dict[str, Any] = Field(default_factory=dict)
class ContactPointSnapshotResponse(BaseModel):
id: str
tenant_id: str
contract_version: str
source: AddressSourceReferencePayload
request: ContactPointSourceRequestResponse
resolutions: list[ContactPointResolutionResponse]
recipient_count: int
excluded_count: int
source_revision: str
source_fingerprint: str
snapshot_hash: str
generated_at: datetime
provenance: dict[str, Any] = Field(default_factory=dict)
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 AddressCredentialEnvelopeResponse(BaseModel):
id: str
scope_type: str
scope_id: str | None = None
name: str
description: str | None = None
credential_kind: str
public_data: dict[str, Any] = Field(default_factory=dict)
secret_keys: list[str] = Field(default_factory=list)
secret_configured: bool = False
allowed_modules: list[str] = Field(default_factory=list)
inherit_to_lower_scopes: bool = False
is_active: bool = True
revision: str
class AddressCredentialEnvelopeListResponse(BaseModel):
credentials: list[AddressCredentialEnvelopeResponse] = Field(default_factory=list)
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)