Skip to content
File

Blob: src/worker/objects/card-dav/addressbooks.ts

typescript107 lines
1import { addressbookHref } from "@/worker/carddav/paths";
2import type { CardDavDoDb } from "@/worker/db/card-dav-do/client";
3import type { AddressbookRow } from "@/worker/db/card-dav-do/schema";
4import {
5 addressSyncToken,
6 addressbookById,
7 addressbookByName,
8 createAddressbook as createAddressbookRow,
9 deleteAddressbook as deleteAddressbookRow,
10 ensureDefaultAddressbook,
11 listAddressbooks as listAddressbookRows,
12 updateAddressbook as updateAddressbookRow,
13 validAddressbookName,
14} from "@/worker/db/card-dav-do/repository";
15import {
16 cardDavError as err,
17 type CardDavError,
18 type AddressbookCreateInput,
19 type AddressbookDeleteInput,
20 type AddressbookGetInput,
21 type AddressbookListInput,
22 type AddressbookRecord,
23 type AddressbookUpdateInput,
24} from "@/worker/objects/card-dav/types";
25import {
26 createCollectionRecord,
27 deleteCollectionRecord,
28 getCollectionRecord,
29 listCollectionRecords,
30 updateCollectionRecord,
31 type CollectionCrudSpec,
32} from "@/worker/objects/common/collection-crud";
33 
34function recordAddressbook(row: AddressbookRow): AddressbookRecord {
35 return {
36 id: row.id,
37 name: row.name,
38 displayName: row.displayName,
39 description: row.description,
40 href: addressbookHref(row.name),
41 createdAtMs: row.createdAtMs,
42 modifiedAtMs: row.modifiedAtMs,
43 syncToken: addressSyncToken(row.syncSeq),
44 };
45}
46 
47export function listAddressbooks(db: CardDavDoDb, input: AddressbookListInput) {
48 const result = listCollectionRecords(db, input, addressbookCrudSpec);
49 return { ok: true as const, addressbooks: result.records };
50}
51 
52export function getAddressbook(db: CardDavDoDb, input: AddressbookGetInput) {
53 const result = getCollectionRecord(db, input, addressbookCrudSpec);
54 return result.ok ? { ok: true as const, addressbook: result.record } : result;
55}
56 
57export function createAddressbook(db: CardDavDoDb, input: AddressbookCreateInput) {
58 const result = createCollectionRecord(db, input, addressbookCrudSpec);
59 return result.ok ? { ok: true as const, addressbook: result.record } : result;
60}
61 
62export function updateAddressbook(db: CardDavDoDb, input: AddressbookUpdateInput) {
63 const result = updateCollectionRecord(db, input, addressbookCrudSpec);
64 return result.ok ? { ok: true as const, addressbook: result.record } : result;
65}
66 
67export function deleteAddressbook(db: CardDavDoDb, input: AddressbookDeleteInput) {
68 return deleteCollectionRecord(db, input, addressbookCrudSpec);
69}
70 
71const addressbookCrudSpec = {
72 ensureDefaultCollection: ensureDefaultAddressbook,
73 listCollections: listAddressbookRows,
74 collectionById: addressbookById,
75 collectionByName: addressbookByName,
76 validName: validAddressbookName,
77 deleteCollection: deleteAddressbookRow,
78 record: recordAddressbook,
79 createCollection: (db, input: AddressbookCreateInput, displayName) =>
80 createAddressbookRow(db, {
81 subjectId: input.subjectId,
82 name: input.name,
83 displayName,
84 description: input.description ?? null,
85 nowMs: input.nowMs,
86 }),
87 updateCollection: (db, input: AddressbookUpdateInput, displayName) =>
88 updateAddressbookRow(db, {
89 id: input.id,
90 displayName,
91 description: input.description,
92 nowMs: input.nowMs,
93 }),
94 invalidName: () => err(400, "bad_request", "Address book name must be a non-empty DAV path segment."),
95 nameConflict: () => err(409, "conflict", "Address book name already exists"),
96 collectionNotFound: () => err(404, "not_found", "Address book not found"),
97 emptyDisplayName: () => err(400, "bad_request", "Address book display name cannot be empty."),
98 defaultDeleteForbidden: () => err(403, "forbidden", "Default address book cannot be deleted."),
99} satisfies CollectionCrudSpec<CardDavDoDb, AddressbookRow, AddressbookRecord, CardDavError> & {
100 createCollection(db: CardDavDoDb, input: AddressbookCreateInput, displayName: string): AddressbookRow;
101 updateCollection(
102 db: CardDavDoDb,
103 input: AddressbookUpdateInput,
104 displayName: string | undefined,
105 ): AddressbookRow | undefined;
106};