intermittent commit
This commit is contained in:
596
webui/src/api/addresses.ts
Normal file
596
webui/src/api/addresses.ts
Normal file
@@ -0,0 +1,596 @@
|
||||
import { apiFetch, type ApiSettings } from "@govoplan/core-webui";
|
||||
|
||||
export type AddressBookScope = "user" | "group" | "tenant" | "system";
|
||||
|
||||
export type AddressBook = {
|
||||
id: string;
|
||||
tenant_id?: string | null;
|
||||
scope_type: AddressBookScope;
|
||||
scope_id?: string | null;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
source_kind: string;
|
||||
source_ref?: string | null;
|
||||
read_only: boolean;
|
||||
sync_status?: string | null;
|
||||
sync_error?: string | null;
|
||||
contact_count: number;
|
||||
deleted_at?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type AddressList = {
|
||||
id: string;
|
||||
tenant_id?: string | null;
|
||||
address_book_id: string;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
source_kind: string;
|
||||
source_ref?: string | null;
|
||||
read_only: boolean;
|
||||
entry_count: number;
|
||||
deleted_at?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type ContactEmail = {
|
||||
id?: string;
|
||||
label?: string | null;
|
||||
email: string;
|
||||
is_primary: boolean;
|
||||
};
|
||||
|
||||
export type ContactPhone = {
|
||||
id?: string;
|
||||
label?: string | null;
|
||||
phone: string;
|
||||
is_primary: boolean;
|
||||
};
|
||||
|
||||
export type ContactPostalAddress = {
|
||||
id?: string;
|
||||
label?: string | null;
|
||||
street?: string | null;
|
||||
postal_code?: string | null;
|
||||
locality?: string | null;
|
||||
region?: string | null;
|
||||
country?: string | null;
|
||||
is_primary: boolean;
|
||||
};
|
||||
|
||||
export type Contact = {
|
||||
id: string;
|
||||
tenant_id?: string | null;
|
||||
address_book_id: string;
|
||||
display_name: string;
|
||||
given_name?: string | null;
|
||||
family_name?: string | null;
|
||||
organization?: string | null;
|
||||
role_title?: string | null;
|
||||
note?: string | null;
|
||||
tags: string[];
|
||||
source_kind: string;
|
||||
source_ref?: string | null;
|
||||
source_payload_kind?: string | null;
|
||||
source_revision?: string | null;
|
||||
provenance: Record<string, unknown>;
|
||||
emails: ContactEmail[];
|
||||
phones: ContactPhone[];
|
||||
postal_addresses: ContactPostalAddress[];
|
||||
deleted_at?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type AddressBookCreatePayload = {
|
||||
scope_type: AddressBookScope;
|
||||
group_id?: string | null;
|
||||
name: string;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type AddressBookUpdatePayload = {
|
||||
name?: string | null;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type AddressListPayload = {
|
||||
name: string;
|
||||
description?: string | null;
|
||||
};
|
||||
|
||||
export type AddressListEntryPayload = {
|
||||
contact_id: string;
|
||||
contact_email_id?: string | null;
|
||||
contact_postal_address_id?: string | null;
|
||||
label?: string | null;
|
||||
};
|
||||
|
||||
export type AddressListEntry = {
|
||||
id: string;
|
||||
address_list_id: string;
|
||||
contact_id: string;
|
||||
contact_email_id?: string | null;
|
||||
contact_postal_address_id?: string | null;
|
||||
target_kind: string;
|
||||
label?: string | null;
|
||||
order_index: number;
|
||||
contact_display_name: string;
|
||||
email?: string | null;
|
||||
postal_address?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type ContactPayload = {
|
||||
display_name?: string | null;
|
||||
given_name?: string | null;
|
||||
family_name?: string | null;
|
||||
organization?: string | null;
|
||||
role_title?: string | null;
|
||||
note?: string | null;
|
||||
tags?: string[];
|
||||
emails?: Array<Omit<ContactEmail, "id">>;
|
||||
phones?: Array<Omit<ContactPhone, "id">>;
|
||||
postal_addresses?: Array<Omit<ContactPostalAddress, "id">>;
|
||||
provenance?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type AddressBookWriteOperation = "create_contact" | "update_contact" | "delete_contact";
|
||||
|
||||
export type AddressBookWriteDecision = {
|
||||
address_book_id: string;
|
||||
address_book_label?: string | null;
|
||||
operation: string;
|
||||
allowed: boolean;
|
||||
reason: string;
|
||||
message: string;
|
||||
scope_type?: AddressBookScope | null;
|
||||
scope_id?: string | null;
|
||||
tenant_id?: string | null;
|
||||
source_kind?: string | null;
|
||||
read_only: boolean;
|
||||
required_scopes: string[];
|
||||
provenance: Record<string, unknown>;
|
||||
};
|
||||
|
||||
type AddressBookListResponse = {
|
||||
address_books: AddressBook[];
|
||||
};
|
||||
|
||||
type AddressListListResponse = {
|
||||
address_lists: AddressList[];
|
||||
};
|
||||
|
||||
type AddressListEntryListResponse = {
|
||||
entries: AddressListEntry[];
|
||||
};
|
||||
|
||||
type ContactListResponse = {
|
||||
contacts: Contact[];
|
||||
};
|
||||
|
||||
type AddressBookWriteTargetsResponse = {
|
||||
targets: AddressBookWriteDecision[];
|
||||
};
|
||||
|
||||
export type VCardImportResult = {
|
||||
imported: number;
|
||||
skipped: number;
|
||||
contacts: Contact[];
|
||||
issues: Array<{ index: number; message: string; severity: "warning" | "error"; field?: string | null; line?: number | null }>;
|
||||
};
|
||||
|
||||
export type AddressSyncSource = {
|
||||
id: string;
|
||||
tenant_id?: string | null;
|
||||
address_book_id: string;
|
||||
connector_type: string;
|
||||
display_name: string;
|
||||
external_account_ref?: string | null;
|
||||
external_address_book_ref?: string | null;
|
||||
sync_direction: string;
|
||||
read_only: boolean;
|
||||
enabled: boolean;
|
||||
status: string;
|
||||
sync_token?: string | null;
|
||||
etag?: string | null;
|
||||
remote_revision?: string | null;
|
||||
last_attempted_at?: string | null;
|
||||
last_success_at?: string | null;
|
||||
last_error?: string | null;
|
||||
last_diagnostic?: Record<string, unknown> | null;
|
||||
metadata: Record<string, unknown>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type AddressCardDavAddressBook = {
|
||||
collection_url: string;
|
||||
href: string;
|
||||
display_name?: string | null;
|
||||
description?: string | null;
|
||||
ctag?: string | null;
|
||||
sync_token?: string | null;
|
||||
};
|
||||
|
||||
export type AddressSyncPlanStats = {
|
||||
created: number;
|
||||
updated: number;
|
||||
deleted: number;
|
||||
conflicts: number;
|
||||
unchanged: number;
|
||||
errors: number;
|
||||
fetched: number;
|
||||
full_sync: boolean;
|
||||
used_sync_token: boolean;
|
||||
sync_token?: string | null;
|
||||
etag?: string | null;
|
||||
remote_revision?: string | null;
|
||||
};
|
||||
|
||||
export type AddressSyncPlanItem = {
|
||||
action: "create" | "update" | "delete" | "remote_create" | "remote_update" | "remote_delete" | "conflict" | "unchanged" | "error";
|
||||
href?: string | null;
|
||||
remote_uid?: string | null;
|
||||
contact_id?: string | null;
|
||||
display_name?: string | null;
|
||||
etag?: string | null;
|
||||
message?: string | null;
|
||||
};
|
||||
|
||||
export type AddressSyncPlan = {
|
||||
sync_source: AddressSyncSource;
|
||||
stats: AddressSyncPlanStats;
|
||||
items: AddressSyncPlanItem[];
|
||||
};
|
||||
|
||||
export type AddressSyncDiagnostic = {
|
||||
id: string;
|
||||
tenant_id?: string | null;
|
||||
sync_source_id: string;
|
||||
severity: string;
|
||||
code: string;
|
||||
message: string;
|
||||
details: Record<string, unknown>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type AddressSyncTombstone = {
|
||||
id: string;
|
||||
tenant_id?: string | null;
|
||||
sync_source_id: string;
|
||||
address_book_id: string;
|
||||
contact_id?: string | null;
|
||||
remote_uid?: string | null;
|
||||
resource_href?: string | null;
|
||||
local_deleted_at?: string | null;
|
||||
remote_deleted_at?: string | null;
|
||||
synced_at?: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type AddressSyncConflict = {
|
||||
id: string;
|
||||
tenant_id?: string | null;
|
||||
sync_source_id: string;
|
||||
address_book_id: string;
|
||||
contact_id?: string | null;
|
||||
remote_uid?: string | null;
|
||||
resource_href?: string | null;
|
||||
field_path: string;
|
||||
local_value?: Record<string, unknown> | null;
|
||||
remote_value?: Record<string, unknown> | null;
|
||||
local_updated_at?: string | null;
|
||||
remote_updated_at?: string | null;
|
||||
status: string;
|
||||
resolution?: string | null;
|
||||
resolved_at?: string | null;
|
||||
resolved_by_account_id?: string | null;
|
||||
metadata: Record<string, unknown>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
type AddressSyncSourceListResponse = {
|
||||
sync_sources: AddressSyncSource[];
|
||||
};
|
||||
|
||||
type AddressCardDavDiscoveryResponse = {
|
||||
address_books: AddressCardDavAddressBook[];
|
||||
};
|
||||
|
||||
type AddressSyncDiagnosticListResponse = {
|
||||
diagnostics: AddressSyncDiagnostic[];
|
||||
};
|
||||
|
||||
type AddressSyncTombstoneListResponse = {
|
||||
tombstones: AddressSyncTombstone[];
|
||||
};
|
||||
|
||||
type AddressSyncConflictListResponse = {
|
||||
conflicts: AddressSyncConflict[];
|
||||
};
|
||||
|
||||
function queryString(params: Record<string, string | number | null | undefined>): string {
|
||||
const search = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value !== null && value !== undefined && value !== "") search.set(key, String(value));
|
||||
}
|
||||
const serialized = search.toString();
|
||||
return serialized ? `?${serialized}` : "";
|
||||
}
|
||||
|
||||
export async function listAddressBooks(settings: ApiSettings, options: {includeDeleted?: boolean;} = {}): Promise<AddressBook[]> {
|
||||
const response = await apiFetch<AddressBookListResponse>(
|
||||
settings,
|
||||
`/api/v1/addresses/address-books${queryString({ include_deleted: options.includeDeleted ? "true" : null })}`
|
||||
);
|
||||
return response.address_books;
|
||||
}
|
||||
|
||||
export function createAddressBook(settings: ApiSettings, payload: AddressBookCreatePayload): Promise<AddressBook> {
|
||||
return apiFetch<AddressBook>(settings, "/api/v1/addresses/address-books", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateAddressBook(settings: ApiSettings, bookId: string, payload: AddressBookUpdatePayload): Promise<AddressBook> {
|
||||
return apiFetch<AddressBook>(settings, `/api/v1/addresses/address-books/${bookId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteAddressBook(settings: ApiSettings, bookId: string): Promise<void> {
|
||||
return apiFetch<void>(settings, `/api/v1/addresses/address-books/${bookId}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function restoreAddressBook(settings: ApiSettings, bookId: string): Promise<AddressBook> {
|
||||
return apiFetch<AddressBook>(settings, `/api/v1/addresses/address-books/${bookId}/restore`, { method: "POST" });
|
||||
}
|
||||
|
||||
export async function listAddressLists(
|
||||
settings: ApiSettings,
|
||||
options: { addressBookId?: string | null; includeDeleted?: boolean } = {}
|
||||
): Promise<AddressList[]> {
|
||||
const response = await apiFetch<AddressListListResponse>(
|
||||
settings,
|
||||
`/api/v1/addresses/address-lists${queryString({
|
||||
address_book_id: options.addressBookId,
|
||||
include_deleted: options.includeDeleted ? "true" : null
|
||||
})}`
|
||||
);
|
||||
return response.address_lists;
|
||||
}
|
||||
|
||||
export function createAddressList(settings: ApiSettings, addressBookId: string, payload: AddressListPayload): Promise<AddressList> {
|
||||
return apiFetch<AddressList>(settings, `/api/v1/addresses/address-books/${addressBookId}/address-lists`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateAddressList(settings: ApiSettings, addressListId: string, payload: Partial<AddressListPayload>): Promise<AddressList> {
|
||||
return apiFetch<AddressList>(settings, `/api/v1/addresses/address-lists/${addressListId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteAddressList(settings: ApiSettings, addressListId: string): Promise<void> {
|
||||
return apiFetch<void>(settings, `/api/v1/addresses/address-lists/${addressListId}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function restoreAddressList(settings: ApiSettings, addressListId: string): Promise<AddressList> {
|
||||
return apiFetch<AddressList>(settings, `/api/v1/addresses/address-lists/${addressListId}/restore`, { method: "POST" });
|
||||
}
|
||||
|
||||
export async function listAddressListEntries(settings: ApiSettings, addressListId: string): Promise<AddressListEntry[]> {
|
||||
const response = await apiFetch<AddressListEntryListResponse>(settings, `/api/v1/addresses/address-lists/${addressListId}/entries`);
|
||||
return response.entries;
|
||||
}
|
||||
|
||||
export function createAddressListEntry(
|
||||
settings: ApiSettings,
|
||||
addressListId: string,
|
||||
payload: AddressListEntryPayload
|
||||
): Promise<AddressListEntry> {
|
||||
return apiFetch<AddressListEntry>(settings, `/api/v1/addresses/address-lists/${addressListId}/entries`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteAddressListEntry(settings: ApiSettings, entryId: string): Promise<void> {
|
||||
return apiFetch<void>(settings, `/api/v1/addresses/address-list-entries/${entryId}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export async function listAddressBookWriteTargets(
|
||||
settings: ApiSettings,
|
||||
operation: AddressBookWriteOperation = "create_contact"
|
||||
): Promise<AddressBookWriteDecision[]> {
|
||||
const response = await apiFetch<AddressBookWriteTargetsResponse>(
|
||||
settings,
|
||||
`/api/v1/addresses/write-targets${queryString({ operation })}`
|
||||
);
|
||||
return response.targets;
|
||||
}
|
||||
|
||||
export function getAddressBookWriteDecision(
|
||||
settings: ApiSettings,
|
||||
addressBookId: string,
|
||||
operation: AddressBookWriteOperation = "create_contact"
|
||||
): Promise<AddressBookWriteDecision> {
|
||||
return apiFetch<AddressBookWriteDecision>(
|
||||
settings,
|
||||
`/api/v1/addresses/address-books/${addressBookId}/write-decision${queryString({ operation })}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function listAddressSyncSources(
|
||||
settings: ApiSettings,
|
||||
options: { addressBookId?: string | null; includeDisabled?: boolean } = {}
|
||||
): Promise<AddressSyncSource[]> {
|
||||
const response = await apiFetch<AddressSyncSourceListResponse>(
|
||||
settings,
|
||||
`/api/v1/addresses/sync-sources${queryString({
|
||||
address_book_id: options.addressBookId,
|
||||
include_disabled: options.includeDisabled ? "true" : null
|
||||
})}`
|
||||
);
|
||||
return response.sync_sources;
|
||||
}
|
||||
|
||||
export function discoverCardDavAddressBooks(
|
||||
settings: ApiSettings,
|
||||
payload: { url: string; auth_type: "none" | "basic" | "bearer"; username?: string | null; password?: string | null; bearer_token?: string | null; credential_ref?: string | null }
|
||||
): Promise<AddressCardDavAddressBook[]> {
|
||||
return apiFetch<AddressCardDavDiscoveryResponse>(settings, "/api/v1/addresses/carddav/discover", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
}).then((response) => response.address_books);
|
||||
}
|
||||
|
||||
export function createCardDavSyncSource(
|
||||
settings: ApiSettings,
|
||||
addressBookId: string,
|
||||
payload: {
|
||||
collection_url: string;
|
||||
display_name?: string | null;
|
||||
auth_type: "none" | "basic" | "bearer";
|
||||
username?: string | null;
|
||||
password?: string | null;
|
||||
bearer_token?: string | null;
|
||||
credential_ref?: string | null;
|
||||
sync_direction: "read_only" | "import" | "export" | "two_way";
|
||||
read_only?: boolean | null;
|
||||
sync_token?: string | null;
|
||||
etag?: string | null;
|
||||
remote_revision?: string | null;
|
||||
}
|
||||
): Promise<AddressSyncSource> {
|
||||
return apiFetch<AddressSyncSource>(settings, `/api/v1/addresses/address-books/${addressBookId}/carddav/sources`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateAddressSyncSource(
|
||||
settings: ApiSettings,
|
||||
syncSourceId: string,
|
||||
payload: Partial<{
|
||||
display_name: string | null;
|
||||
external_account_ref: string | null;
|
||||
external_address_book_ref: string | null;
|
||||
sync_direction: "read_only" | "import" | "export" | "two_way";
|
||||
read_only: boolean | null;
|
||||
enabled: boolean | null;
|
||||
sync_token: string | null;
|
||||
etag: string | null;
|
||||
remote_revision: string | null;
|
||||
metadata: Record<string, unknown> | null;
|
||||
}>
|
||||
): Promise<AddressSyncSource> {
|
||||
return apiFetch<AddressSyncSource>(settings, `/api/v1/addresses/sync-sources/${syncSourceId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteAddressSyncSource(settings: ApiSettings, syncSourceId: string): Promise<void> {
|
||||
return apiFetch<void>(settings, `/api/v1/addresses/sync-sources/${syncSourceId}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function previewAddressSyncSource(settings: ApiSettings, syncSourceId: string, options: { forceFull?: boolean } = {}): Promise<AddressSyncPlan> {
|
||||
return apiFetch<AddressSyncPlan>(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/preview`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ force_full: Boolean(options.forceFull) })
|
||||
});
|
||||
}
|
||||
|
||||
export function runAddressSyncSource(settings: ApiSettings, syncSourceId: string, options: { forceFull?: boolean } = {}): Promise<AddressSyncPlan> {
|
||||
return apiFetch<AddressSyncPlan>(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/run`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ force_full: Boolean(options.forceFull) })
|
||||
});
|
||||
}
|
||||
|
||||
export async function listAddressSyncDiagnostics(settings: ApiSettings, syncSourceId: string): Promise<AddressSyncDiagnostic[]> {
|
||||
const response = await apiFetch<AddressSyncDiagnosticListResponse>(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/diagnostics`);
|
||||
return response.diagnostics;
|
||||
}
|
||||
|
||||
export async function listAddressSyncTombstones(settings: ApiSettings, syncSourceId: string): Promise<AddressSyncTombstone[]> {
|
||||
const response = await apiFetch<AddressSyncTombstoneListResponse>(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/tombstones`);
|
||||
return response.tombstones;
|
||||
}
|
||||
|
||||
export async function listAddressSyncConflicts(settings: ApiSettings, syncSourceId: string, status = "open"): Promise<AddressSyncConflict[]> {
|
||||
const response = await apiFetch<AddressSyncConflictListResponse>(settings, `/api/v1/addresses/sync-sources/${syncSourceId}/conflicts${queryString({ status })}`);
|
||||
return response.conflicts;
|
||||
}
|
||||
|
||||
export function resolveAddressSyncConflict(
|
||||
settings: ApiSettings,
|
||||
conflictId: string,
|
||||
resolution = "manual",
|
||||
status = "resolved",
|
||||
options: { mergedPayload?: Record<string, unknown> | null } = {}
|
||||
): Promise<AddressSyncConflict> {
|
||||
return apiFetch<AddressSyncConflict>(settings, `/api/v1/addresses/sync-conflicts/${conflictId}/resolve`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ resolution, status, merged_payload: options.mergedPayload ?? null })
|
||||
});
|
||||
}
|
||||
|
||||
export async function listContacts(settings: ApiSettings, options: {addressBookId?: string | null;query?: string | null;limit?: number;includeDeleted?: boolean;} = {}): Promise<Contact[]> {
|
||||
const response = await apiFetch<ContactListResponse>(
|
||||
settings,
|
||||
`/api/v1/addresses/contacts${queryString({ address_book_id: options.addressBookId, query: options.query, limit: options.limit, include_deleted: options.includeDeleted ? "true" : null })}`
|
||||
);
|
||||
return response.contacts;
|
||||
}
|
||||
|
||||
export function createContact(settings: ApiSettings, addressBookId: string, payload: ContactPayload): Promise<Contact> {
|
||||
return apiFetch<Contact>(settings, `/api/v1/addresses/address-books/${addressBookId}/contacts`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function updateContact(settings: ApiSettings, contactId: string, payload: ContactPayload): Promise<Contact> {
|
||||
return apiFetch<Contact>(settings, `/api/v1/addresses/contacts/${contactId}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteContact(settings: ApiSettings, contactId: string): Promise<void> {
|
||||
return apiFetch<void>(settings, `/api/v1/addresses/contacts/${contactId}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function restoreContact(settings: ApiSettings, contactId: string): Promise<Contact> {
|
||||
return apiFetch<Contact>(settings, `/api/v1/addresses/contacts/${contactId}/restore`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function importAddressBookVcards(settings: ApiSettings, addressBookId: string, content: string): Promise<VCardImportResult> {
|
||||
return apiFetch<VCardImportResult>(settings, `/api/v1/addresses/address-books/${addressBookId}/vcards/import`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ content })
|
||||
});
|
||||
}
|
||||
|
||||
export function exportAddressBookVcards(settings: ApiSettings, addressBookId: string): Promise<string> {
|
||||
return apiFetch<string>(settings, `/api/v1/addresses/address-books/${addressBookId}/vcards/export`);
|
||||
}
|
||||
|
||||
export function exportContactVcard(settings: ApiSettings, contactId: string): Promise<string> {
|
||||
return apiFetch<string>(settings, `/api/v1/addresses/contacts/${contactId}/vcard`);
|
||||
}
|
||||
Reference in New Issue
Block a user