This commit is contained in:
Trey t
2026-01-20 12:25:00 -06:00
parent fbfdf136ae
commit c49206bb7c
6 changed files with 359 additions and 17 deletions

View File

@@ -28,6 +28,8 @@ from ..config import (
CLOUDKIT_CONTAINER_ID,
CLOUDKIT_ENVIRONMENT,
CLOUDKIT_BATCH_SIZE,
CLOUDKIT_KEY_ID,
CLOUDKIT_PRIVATE_KEY_PATH,
)
from ..utils.logging import get_logger
@@ -86,14 +88,15 @@ class CloudKitRecord:
def _format_field_value(self, value: Any) -> dict:
"""Format a single field value for CloudKit API."""
if isinstance(value, str):
# Check bool BEFORE int (bool is a subclass of int in Python)
if isinstance(value, bool):
return {"value": 1 if value else 0, "type": "INT64"}
elif isinstance(value, str):
return {"value": value, "type": "STRING"}
elif isinstance(value, int):
return {"value": value, "type": "INT64"}
elif isinstance(value, float):
return {"value": value, "type": "DOUBLE"}
elif isinstance(value, bool):
return {"value": 1 if value else 0, "type": "INT64"}
elif isinstance(value, datetime):
# CloudKit expects milliseconds since epoch
timestamp_ms = int(value.timestamp() * 1000)
@@ -182,8 +185,8 @@ class CloudKitClient:
self.environment = environment
self.logger = get_logger()
# Load authentication credentials
self.key_id = key_id or os.environ.get("CLOUDKIT_KEY_ID")
# Load authentication credentials (config defaults > env vars > None)
self.key_id = key_id or os.environ.get("CLOUDKIT_KEY_ID") or CLOUDKIT_KEY_ID
if private_key:
self._private_key_pem = private_key
@@ -193,6 +196,8 @@ class CloudKitClient:
self._private_key_pem = os.environ["CLOUDKIT_PRIVATE_KEY"]
elif os.environ.get("CLOUDKIT_PRIVATE_KEY_PATH"):
self._private_key_pem = Path(os.environ["CLOUDKIT_PRIVATE_KEY_PATH"]).read_text()
elif CLOUDKIT_PRIVATE_KEY_PATH.exists():
self._private_key_pem = CLOUDKIT_PRIVATE_KEY_PATH.read_text()
else:
self._private_key_pem = None
@@ -494,13 +499,13 @@ class CloudKitClient:
def delete_records(
self,
record_type: RecordType,
record_names: list[str],
records: list[dict],
) -> BatchResult:
"""Delete records from CloudKit.
Args:
record_type: Type of records to delete
record_names: List of record names to delete
records: List of record dicts (must have recordName and recordChangeTag)
Returns:
BatchResult with success/failure details
@@ -508,16 +513,16 @@ class CloudKitClient:
result = BatchResult()
# Process in batches
for i in range(0, len(record_names), CLOUDKIT_BATCH_SIZE):
batch = record_names[i:i + CLOUDKIT_BATCH_SIZE]
for i in range(0, len(records), CLOUDKIT_BATCH_SIZE):
batch = records[i:i + CLOUDKIT_BATCH_SIZE]
operations = []
for name in batch:
for record in batch:
operations.append({
"operationType": "delete",
"record": {
"recordName": name,
"recordType": record_type.value,
"recordName": record["recordName"],
"recordChangeTag": record.get("recordChangeTag"),
},
})
@@ -526,9 +531,9 @@ class CloudKitClient:
try:
response = self._request("POST", "records/modify", body)
except CloudKitError as e:
for name in batch:
for record in batch:
result.failed.append(OperationResult(
record_name=name,
record_name=record["recordName"],
success=False,
error_message=str(e),
))