Add governed contact channel facts
This commit is contained in:
@@ -19,6 +19,7 @@ from govoplan_addresses.backend.db.models import (
|
||||
AddressSyncSource,
|
||||
AddressSyncTombstone,
|
||||
Contact,
|
||||
ContactChannelRule,
|
||||
ContactPostalAddress,
|
||||
)
|
||||
from govoplan_addresses.backend.capabilities import AddressesContactWriterCapability
|
||||
@@ -62,6 +63,9 @@ from govoplan_addresses.backend.schemas import (
|
||||
AddressSyncTombstoneListResponse,
|
||||
AddressSyncTombstoneResponse,
|
||||
ContactCreateRequest,
|
||||
ContactChannelRuleCreateRequest,
|
||||
ContactChannelRuleListResponse,
|
||||
ContactChannelRuleResponse,
|
||||
ContactListResponse,
|
||||
ContactResponse,
|
||||
ContactUpdateRequest,
|
||||
@@ -79,6 +83,7 @@ from govoplan_addresses.backend.service import (
|
||||
create_address_list_entry,
|
||||
create_carddav_sync_source,
|
||||
create_contact,
|
||||
create_contact_channel_rule,
|
||||
create_sync_source,
|
||||
count_contacts,
|
||||
delete_address_book,
|
||||
@@ -89,12 +94,14 @@ from govoplan_addresses.backend.service import (
|
||||
discover_carddav_address_books,
|
||||
export_address_book_vcard,
|
||||
export_contact_vcard,
|
||||
end_contact_channel_rule,
|
||||
finish_sync_attempt,
|
||||
import_vcards,
|
||||
list_address_list_entries,
|
||||
list_address_lists,
|
||||
list_address_books,
|
||||
list_contacts,
|
||||
list_contact_channel_rules,
|
||||
list_sync_conflicts,
|
||||
list_sync_diagnostics,
|
||||
list_sync_sources,
|
||||
@@ -156,6 +163,10 @@ def _contact_response(contact: Contact) -> ContactResponse:
|
||||
return ContactResponse.model_validate(contact)
|
||||
|
||||
|
||||
def _channel_rule_response(rule: ContactChannelRule) -> ContactChannelRuleResponse:
|
||||
return ContactChannelRuleResponse.model_validate(rule)
|
||||
|
||||
|
||||
def _address_list_response(address_list: AddressList, *, entry_count: int = 0) -> AddressListResponse:
|
||||
return AddressListResponse.model_validate(
|
||||
{
|
||||
@@ -1081,6 +1092,97 @@ def api_update_contact(
|
||||
raise _error(exc) from exc
|
||||
|
||||
|
||||
@router.get("/contacts/{contact_id}/channel-rules", response_model=ContactChannelRuleListResponse)
|
||||
def api_list_contact_channel_rules(
|
||||
contact_id: str,
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_scope(principal, "addresses:governance:read")
|
||||
try:
|
||||
return ContactChannelRuleListResponse(
|
||||
rules=[
|
||||
_channel_rule_response(rule)
|
||||
for rule in list_contact_channel_rules(session, principal, contact_id)
|
||||
]
|
||||
)
|
||||
except AddressBookError as exc:
|
||||
raise _error(exc) from exc
|
||||
|
||||
|
||||
@router.post(
|
||||
"/contacts/{contact_id}/channel-rules",
|
||||
response_model=ContactChannelRuleResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
)
|
||||
def api_create_contact_channel_rule(
|
||||
contact_id: str,
|
||||
payload: ContactChannelRuleCreateRequest,
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_scope(principal, "addresses:governance:write")
|
||||
try:
|
||||
rule = create_contact_channel_rule(session, principal, contact_id, payload)
|
||||
_audit_address_sync(
|
||||
session,
|
||||
principal,
|
||||
action="addresses.channel_rule_created",
|
||||
object_type="address_contact_channel_rule",
|
||||
object_id=rule.id,
|
||||
details={
|
||||
"contact_id": contact_id,
|
||||
"channel": rule.channel,
|
||||
"purpose": rule.purpose,
|
||||
"contact_point_id": rule.contact_point_id,
|
||||
"decision": rule.decision,
|
||||
"effective_from": rule.effective_from.isoformat() if rule.effective_from else None,
|
||||
"effective_until": rule.effective_until.isoformat() if rule.effective_until else None,
|
||||
"evidence_ref": rule.evidence_ref,
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
session.refresh(rule)
|
||||
return _channel_rule_response(rule)
|
||||
except AddressBookError as exc:
|
||||
session.rollback()
|
||||
raise _error(exc) from exc
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/contact-channel-rules/{rule_id}",
|
||||
response_model=ContactChannelRuleResponse,
|
||||
)
|
||||
def api_end_contact_channel_rule(
|
||||
rule_id: str,
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
_require_scope(principal, "addresses:governance:write")
|
||||
try:
|
||||
rule = end_contact_channel_rule(session, principal, rule_id)
|
||||
_audit_address_sync(
|
||||
session,
|
||||
principal,
|
||||
action="addresses.channel_rule_ended",
|
||||
object_type="address_contact_channel_rule",
|
||||
object_id=rule.id,
|
||||
details={
|
||||
"contact_id": rule.contact_id,
|
||||
"channel": rule.channel,
|
||||
"purpose": rule.purpose,
|
||||
"decision": rule.decision,
|
||||
"effective_until": rule.effective_until.isoformat() if rule.effective_until else None,
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
session.refresh(rule)
|
||||
return _channel_rule_response(rule)
|
||||
except AddressBookError as exc:
|
||||
session.rollback()
|
||||
raise _error(exc) from exc
|
||||
|
||||
|
||||
@router.delete("/contacts/{contact_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||
def api_delete_contact(
|
||||
contact_id: str,
|
||||
|
||||
Reference in New Issue
Block a user