@remit/drizzle-service 0.0.101 → 0.0.103
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/batch.sqlite.test.ts +4 -4
- package/src/batch.ts +47 -21
- package/src/databases.ts +24 -6
- package/src/db.ts +20 -5
- package/src/index.ts +12 -0
- package/src/repair/junk-only-address.sqlite.test.ts +7 -2
- package/src/repos/account.conformance.sqlite.test.ts +6 -2
- package/src/repos/calendar-collection.ts +2 -2
- package/src/repos/calendar-default.sqlite.test.ts +6 -2
- package/src/repos/calendar-event-index.ts +2 -2
- package/src/repos/calendar-feed-token.ts +2 -2
- package/src/repos/calendar-object.ts +2 -2
- package/src/repos/calendar-put.sqlite.test.ts +6 -2
- package/src/repos/calendar-store.ts +36 -40
- package/src/repos/calendar-suggestion.ts +5 -4
- package/src/repos/cascade-delete.test.ts +3 -2
- package/src/repos/cascade-delete.ts +38 -35
- package/src/repos/envelope.sqlite.test.ts +3 -2
- package/src/repos/envelope.test.ts +3 -2
- package/src/repos/envelope.ts +19 -13
- package/src/repos/filter.ts +2 -2
- package/src/repos/i4-account-config.test.ts +10 -6
- package/src/repos/i4-account-config.ts +12 -13
- package/src/repos/i4-account-export-request.ts +2 -2
- package/src/repos/i4-account-setting.ts +6 -5
- package/src/repos/i4-account.test.ts +12 -8
- package/src/repos/i4-account.ts +10 -13
- package/src/repos/i4-address-junk-move.sqlite.test.ts +7 -2
- package/src/repos/i4-address.ts +17 -7
- package/src/repos/i4-config-import.ts +2 -2
- package/src/repos/i4-mailbox-lock.ts +2 -2
- package/src/repos/i4-mailbox-special-use.sqlite.test.ts +12 -10
- package/src/repos/i4-mailbox-special-use.ts +34 -44
- package/src/repos/i4-mailbox.sqlite.test.ts +12 -10
- package/src/repos/i4-mailbox.test.ts +15 -14
- package/src/repos/i4-mailbox.ts +19 -12
- package/src/repos/i4-message-flag-push.ts +2 -2
- package/src/repos/i4-message-placement-move.ts +2 -2
- package/src/repos/i4-organize-job-request.ts +2 -2
- package/src/repos/i4-outbox-attachment.test.ts +7 -2
- package/src/repos/i4-outbox-attachment.ts +14 -10
- package/src/repos/i4-outbox-message.ts +7 -6
- package/src/repos/i4-sender-signer-standing.ts +2 -2
- package/src/repos/in-list.sqlite.test.ts +61 -0
- package/src/repos/in-list.ts +14 -0
- package/src/repos/label.ts +2 -2
- package/src/repos/message-flag.ts +8 -10
- package/src/repos/message-label.ts +5 -4
- package/src/repos/message.sqlite.test.ts +3 -3
- package/src/repos/message.test.ts +6 -5
- package/src/repos/message.ts +30 -24
- package/src/repos/quarantine.ts +2 -2
- package/src/repos/sync-warning.ts +5 -4
- package/src/repos/thread-message-body-muted.sqlite.test.ts +7 -3
- package/src/repos/thread-message-field-terms.sqlite.test.ts +7 -6
- package/src/repos/thread-message.sqlite.test.ts +7 -3
- package/src/repos/thread-message.test.ts +22 -18
- package/src/repos/thread-message.ts +16 -7
- package/src/search/thread-message-search.sqlite.test.ts +8 -3
- package/src/search/thread-message-search.ts +13 -8
- package/src/sqlite-repositories.ts +48 -32
package/src/repos/i4-address.ts
CHANGED
|
@@ -19,11 +19,10 @@ import {
|
|
|
19
19
|
eq,
|
|
20
20
|
getTableColumns,
|
|
21
21
|
gt,
|
|
22
|
-
inArray,
|
|
23
22
|
type SQL,
|
|
24
23
|
sql,
|
|
25
24
|
} from "drizzle-orm";
|
|
26
|
-
import type {
|
|
25
|
+
import type { RepositoryDb } from "../db.js";
|
|
27
26
|
import { NotFoundError } from "../error.js";
|
|
28
27
|
import { envelopeAddressId } from "../id.js";
|
|
29
28
|
import { decodeToken, resultList } from "../pagination.js";
|
|
@@ -43,8 +42,9 @@ import {
|
|
|
43
42
|
addressRecency,
|
|
44
43
|
addressSearchMatch,
|
|
45
44
|
} from "./address-search-predicates.js";
|
|
45
|
+
import { inList } from "./in-list.js";
|
|
46
46
|
|
|
47
|
-
type DB =
|
|
47
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
48
48
|
|
|
49
49
|
type AddressUpdate = Partial<{
|
|
50
50
|
[K in keyof typeof addressTable.$inferInsert]:
|
|
@@ -400,7 +400,7 @@ export class AddressRepo implements IAddressRepository {
|
|
|
400
400
|
.where(
|
|
401
401
|
and(
|
|
402
402
|
eq(addressTable.accountConfigId, accountConfigId),
|
|
403
|
-
|
|
403
|
+
inList(addressTable.addressId, addressId),
|
|
404
404
|
),
|
|
405
405
|
);
|
|
406
406
|
return rows.map(rowToAddress);
|
|
@@ -666,7 +666,7 @@ export class AddressRepo implements IAddressRepository {
|
|
|
666
666
|
.where(
|
|
667
667
|
and(
|
|
668
668
|
eq(addressTable.accountConfigId, accountConfigId),
|
|
669
|
-
|
|
669
|
+
inList(addressTable.addressId, addressIds),
|
|
670
670
|
),
|
|
671
671
|
);
|
|
672
672
|
}
|
|
@@ -786,6 +786,16 @@ export class AddressRepo implements IAddressRepository {
|
|
|
786
786
|
);
|
|
787
787
|
}
|
|
788
788
|
|
|
789
|
+
async listAllByAccountConfig(
|
|
790
|
+
accountConfigId: string,
|
|
791
|
+
): Promise<AddressItem[]> {
|
|
792
|
+
const rows = await this.db
|
|
793
|
+
.select()
|
|
794
|
+
.from(addressTable)
|
|
795
|
+
.where(eq(addressTable.accountConfigId, accountConfigId));
|
|
796
|
+
return rows.map(rowToAddress);
|
|
797
|
+
}
|
|
798
|
+
|
|
789
799
|
async createEnvelopeAddress(
|
|
790
800
|
input: CreateEnvelopeAddressInput,
|
|
791
801
|
): Promise<EnvelopeAddressItem> {
|
|
@@ -856,7 +866,7 @@ export class AddressRepo implements IAddressRepository {
|
|
|
856
866
|
.select()
|
|
857
867
|
.from(envelopeAddressTable)
|
|
858
868
|
.where(
|
|
859
|
-
|
|
869
|
+
inList(envelopeAddressTable.envelopeAddressId, envelopeAddressId),
|
|
860
870
|
);
|
|
861
871
|
return rows.map(rowToEnvelopeAddress);
|
|
862
872
|
}
|
|
@@ -884,7 +894,7 @@ export class AddressRepo implements IAddressRepository {
|
|
|
884
894
|
await this.db
|
|
885
895
|
.delete(envelopeAddressTable)
|
|
886
896
|
.where(
|
|
887
|
-
|
|
897
|
+
inList(envelopeAddressTable.envelopeAddressId, envelopeAddressIds),
|
|
888
898
|
);
|
|
889
899
|
}
|
|
890
900
|
}
|
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
type UpdateConfigImportInput,
|
|
8
8
|
} from "@remit/data-ports";
|
|
9
9
|
import { desc, eq } from "drizzle-orm";
|
|
10
|
-
import type {
|
|
10
|
+
import type { RepositoryDb } from "../db.js";
|
|
11
11
|
import { NotFoundError } from "../error.js";
|
|
12
12
|
import { randomId } from "../id.js";
|
|
13
13
|
import { configImportTable } from "../schema/i4-config-import.js";
|
|
14
14
|
|
|
15
|
-
type DB =
|
|
15
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
16
16
|
|
|
17
17
|
type StoredRef = Omit<ConfigImportUnresolvedRefItem, "mailboxId"> & {
|
|
18
18
|
mailboxId?: string;
|
|
@@ -4,11 +4,11 @@ import type {
|
|
|
4
4
|
WithMailboxLockResult,
|
|
5
5
|
} from "@remit/data-ports";
|
|
6
6
|
import { and, eq, lt } from "drizzle-orm";
|
|
7
|
-
import type {
|
|
7
|
+
import type { RepositoryDb } from "../db.js";
|
|
8
8
|
import { randomId } from "../id.js";
|
|
9
9
|
import { mailboxLockTable } from "../schema/i4-mailbox-lock.js";
|
|
10
10
|
|
|
11
|
-
type DB =
|
|
11
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
12
12
|
|
|
13
13
|
const TTL_SECONDS = 3600;
|
|
14
14
|
|
|
@@ -12,11 +12,11 @@ import {
|
|
|
12
12
|
mailboxSpecialUseTable,
|
|
13
13
|
mailboxTable,
|
|
14
14
|
} from "../schema.js";
|
|
15
|
-
import {
|
|
15
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
16
16
|
import { createSqliteTestDb } from "../test-db-sqlite.js";
|
|
17
|
-
import { AccountSettingRepo } from "./i4-account-setting.js";
|
|
18
|
-
import { MailboxRepo } from "./i4-mailbox.js";
|
|
19
|
-
import { MailboxSpecialUseRepo } from "./i4-mailbox-special-use.js";
|
|
17
|
+
import type { AccountSettingRepo } from "./i4-account-setting.js";
|
|
18
|
+
import type { MailboxRepo } from "./i4-mailbox.js";
|
|
19
|
+
import type { MailboxSpecialUseRepo } from "./i4-mailbox-special-use.js";
|
|
20
20
|
import { mailboxCreated } from "./test-helpers.js";
|
|
21
21
|
|
|
22
22
|
const makeMailboxInput = (
|
|
@@ -57,12 +57,14 @@ describe("MailboxSpecialUseRepo role lookups (sqlite)", () => {
|
|
|
57
57
|
});
|
|
58
58
|
close = testDb.close;
|
|
59
59
|
db = testDb.db;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
testDb.db
|
|
63
|
-
|
|
64
|
-
);
|
|
65
|
-
|
|
60
|
+
const repos = createSqliteRepositories({
|
|
61
|
+
app: testDb.db,
|
|
62
|
+
mail: testDb.db,
|
|
63
|
+
search: testDb.db,
|
|
64
|
+
});
|
|
65
|
+
repo = repos.mailboxSpecialUse;
|
|
66
|
+
mailboxes = repos.mailbox;
|
|
67
|
+
accountSettings = repos.accountSetting;
|
|
66
68
|
});
|
|
67
69
|
|
|
68
70
|
after(async () => {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
AccountItem,
|
|
3
|
+
IAccountRepository,
|
|
4
|
+
IAccountSettingRepository,
|
|
2
5
|
IMailboxSpecialUseRepository,
|
|
3
6
|
MailboxSpecialUseItem,
|
|
4
7
|
MailboxSpecialUseValue,
|
|
@@ -14,14 +17,22 @@ import {
|
|
|
14
17
|
type UnappointedRoleResolution,
|
|
15
18
|
} from "@remit/data-ports/folder-role";
|
|
16
19
|
import { CanonicalMailboxRole } from "@remit/domain-enums";
|
|
17
|
-
import { eq
|
|
18
|
-
import type {
|
|
20
|
+
import { eq } from "drizzle-orm";
|
|
21
|
+
import type { RepositoryDb } from "../db.js";
|
|
19
22
|
import { randomId } from "../id.js";
|
|
20
|
-
import { accountTable } from "../schema/i4-account-config.js";
|
|
21
23
|
import { mailboxSpecialUseTable, mailboxTable } from "../schema/i4-mailbox.js";
|
|
22
|
-
import {
|
|
24
|
+
import { inList } from "./in-list.js";
|
|
23
25
|
|
|
24
|
-
type DB =
|
|
26
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
27
|
+
|
|
28
|
+
type AccountReader = Pick<
|
|
29
|
+
IAccountRepository,
|
|
30
|
+
"get" | "listAllByAccountConfig" | "listAll"
|
|
31
|
+
>;
|
|
32
|
+
|
|
33
|
+
type AppointmentReader = Pick<IAccountSettingRepository, "get" | "getMany">;
|
|
34
|
+
|
|
35
|
+
type AppointedAccount = Pick<AccountItem, "accountId" | "accountConfigId">;
|
|
25
36
|
|
|
26
37
|
const JUNK_ROLES: readonly CanonicalMailboxRoleValue[] = [
|
|
27
38
|
CanonicalMailboxRole.Junk,
|
|
@@ -88,14 +99,11 @@ function rowToSpecialUse(
|
|
|
88
99
|
}
|
|
89
100
|
|
|
90
101
|
export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
91
|
-
private readonly accountSetting: AccountSettingRepo;
|
|
92
|
-
|
|
93
102
|
constructor(
|
|
94
|
-
private app: DB,
|
|
95
103
|
private db: DB,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
104
|
+
private accounts: AccountReader,
|
|
105
|
+
private accountSetting: AppointmentReader,
|
|
106
|
+
) {}
|
|
99
107
|
|
|
100
108
|
async create(
|
|
101
109
|
mailboxId: string,
|
|
@@ -202,11 +210,9 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
202
210
|
async resolveJunkRolesForConfig(
|
|
203
211
|
accountConfigId: string,
|
|
204
212
|
): Promise<JunkRoleMailboxes> {
|
|
205
|
-
|
|
206
|
-
.
|
|
207
|
-
|
|
208
|
-
.where(eq(accountTable.accountConfigId, accountConfigId));
|
|
209
|
-
return this.resolveJunkRoles(accounts.map((row) => row.accountId));
|
|
213
|
+
return this.resolveJunkRoles(
|
|
214
|
+
await this.accounts.listAllByAccountConfig(accountConfigId),
|
|
215
|
+
);
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
/**
|
|
@@ -216,21 +222,19 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
216
222
|
* run one pass over the address table instead of one per config.
|
|
217
223
|
*/
|
|
218
224
|
async resolveJunkRolesForInstance(): Promise<JunkRoleMailboxes> {
|
|
219
|
-
|
|
220
|
-
.select({ accountId: accountTable.accountId })
|
|
221
|
-
.from(accountTable);
|
|
222
|
-
return this.resolveJunkRoles(accounts.map((row) => row.accountId));
|
|
225
|
+
return this.resolveJunkRoles(await this.accounts.listAll());
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
private async resolveJunkRoles(
|
|
226
|
-
|
|
229
|
+
accounts: readonly AppointedAccount[],
|
|
227
230
|
): Promise<JunkRoleMailboxes> {
|
|
228
|
-
if (
|
|
231
|
+
if (accounts.length === 0) {
|
|
229
232
|
return { junkMailboxIds: [], trashMailboxIds: [] };
|
|
230
233
|
}
|
|
234
|
+
const accountIds = accounts.map((account) => account.accountId);
|
|
231
235
|
const [candidates, appointments] = await Promise.all([
|
|
232
236
|
this.roleCandidatesFor(accountIds),
|
|
233
|
-
this.appointedMailboxIds(
|
|
237
|
+
this.appointedMailboxIds(accounts, JUNK_ROLES),
|
|
234
238
|
]);
|
|
235
239
|
|
|
236
240
|
const junkMailboxIds: string[] = [];
|
|
@@ -254,24 +258,13 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
254
258
|
}
|
|
255
259
|
|
|
256
260
|
/**
|
|
257
|
-
* Each account's appointment for each role, in
|
|
258
|
-
* input as `appointedMailboxId`, batched
|
|
259
|
-
* racing a delete, and leaves the account with no appointment rather than
|
|
260
|
-
* failing the lookup.
|
|
261
|
+
* Each account's appointment for each role, in one read. Same precedence
|
|
262
|
+
* input as `appointedMailboxId`, batched.
|
|
261
263
|
*/
|
|
262
264
|
private async appointedMailboxIds(
|
|
263
|
-
|
|
265
|
+
accounts: readonly AppointedAccount[],
|
|
264
266
|
roles: readonly CanonicalMailboxRoleValue[],
|
|
265
267
|
): Promise<Map<string, string>> {
|
|
266
|
-
const accounts = await this.app
|
|
267
|
-
.select({
|
|
268
|
-
accountId: accountTable.accountId,
|
|
269
|
-
accountConfigId: accountTable.accountConfigId,
|
|
270
|
-
})
|
|
271
|
-
.from(accountTable)
|
|
272
|
-
.where(inArray(accountTable.accountId, [...accountIds]));
|
|
273
|
-
if (accounts.length === 0) return new Map();
|
|
274
|
-
|
|
275
268
|
const wanted = new Map<string, string>();
|
|
276
269
|
for (const account of accounts) {
|
|
277
270
|
for (const role of roles) {
|
|
@@ -303,14 +296,14 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
303
296
|
const rows = await this.db
|
|
304
297
|
.select()
|
|
305
298
|
.from(mailboxTable)
|
|
306
|
-
.where(
|
|
299
|
+
.where(inList(mailboxTable.accountId, accountIds));
|
|
307
300
|
if (rows.length === 0) return new Map();
|
|
308
301
|
|
|
309
302
|
const entries = await this.db
|
|
310
303
|
.select()
|
|
311
304
|
.from(mailboxSpecialUseTable)
|
|
312
305
|
.where(
|
|
313
|
-
|
|
306
|
+
inList(
|
|
314
307
|
mailboxSpecialUseTable.mailboxId,
|
|
315
308
|
rows.map((row) => row.mailboxId),
|
|
316
309
|
),
|
|
@@ -366,10 +359,7 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
366
359
|
accountId: string,
|
|
367
360
|
role: CanonicalMailboxRoleValue,
|
|
368
361
|
): Promise<string | undefined> {
|
|
369
|
-
const [account] = await this.
|
|
370
|
-
.select({ accountConfigId: accountTable.accountConfigId })
|
|
371
|
-
.from(accountTable)
|
|
372
|
-
.where(eq(accountTable.accountId, accountId));
|
|
362
|
+
const [account] = await this.accounts.get([accountId]);
|
|
373
363
|
if (!account) return undefined;
|
|
374
364
|
|
|
375
365
|
const setting = await this.accountSetting.get(
|
|
@@ -397,7 +387,7 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
397
387
|
.select()
|
|
398
388
|
.from(mailboxSpecialUseTable)
|
|
399
389
|
.where(
|
|
400
|
-
|
|
390
|
+
inList(
|
|
401
391
|
mailboxSpecialUseTable.mailboxId,
|
|
402
392
|
rows.map((row) => row.mailboxId),
|
|
403
393
|
),
|
|
@@ -5,13 +5,13 @@ import { MailboxSyncStatus } from "@remit/domain-enums";
|
|
|
5
5
|
import Database from "better-sqlite3";
|
|
6
6
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
7
7
|
import { mailboxTable } from "../schema.js";
|
|
8
|
-
import {
|
|
8
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
9
9
|
import { createSqliteTestDb } from "../test-db-sqlite.js";
|
|
10
10
|
import {
|
|
11
11
|
applyMigrationSet,
|
|
12
12
|
shippedTableDdl,
|
|
13
13
|
} from "../test-shipped-sqlite-schema.js";
|
|
14
|
-
import { MailboxRepo } from "./i4-mailbox.js";
|
|
14
|
+
import type { MailboxRepo } from "./i4-mailbox.js";
|
|
15
15
|
import { mailboxCreated } from "./test-helpers.js";
|
|
16
16
|
|
|
17
17
|
function makeMailboxInput(accountId: string, fullPath = "INBOX") {
|
|
@@ -41,10 +41,11 @@ describe("MailboxRepo (sqlite)", () => {
|
|
|
41
41
|
before(async () => {
|
|
42
42
|
const testDb = await createSqliteTestDb({ mailbox: mailboxTable });
|
|
43
43
|
close = testDb.close;
|
|
44
|
-
repo =
|
|
45
|
-
testDb.db
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
repo = createSqliteRepositories({
|
|
45
|
+
app: testDb.db,
|
|
46
|
+
mail: testDb.db,
|
|
47
|
+
search: testDb.db,
|
|
48
|
+
}).mailbox;
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
after(async () => {
|
|
@@ -210,10 +211,11 @@ describe("MailboxRepo (sqlite, shipped migrations)", () => {
|
|
|
210
211
|
const sqlite = new Database(":memory:");
|
|
211
212
|
applyMigrationSet(sqlite, "mail");
|
|
212
213
|
const db = drizzle(sqlite, { schema: { mailbox: mailboxTable } });
|
|
213
|
-
repo =
|
|
214
|
-
db
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
repo = createSqliteRepositories({
|
|
215
|
+
app: db,
|
|
216
|
+
mail: db,
|
|
217
|
+
search: db,
|
|
218
|
+
}).mailbox;
|
|
217
219
|
close = async () => {
|
|
218
220
|
sqlite.close();
|
|
219
221
|
};
|
|
@@ -2,7 +2,7 @@ import assert from "node:assert";
|
|
|
2
2
|
import { after, before, describe, test } from "node:test";
|
|
3
3
|
import { MailboxSyncStatus } from "@remit/domain-enums";
|
|
4
4
|
import { eq } from "drizzle-orm";
|
|
5
|
-
import {
|
|
5
|
+
import { syncBatch } from "../batch.js";
|
|
6
6
|
import {
|
|
7
7
|
envelopeId as deriveEnvelopeId,
|
|
8
8
|
rootBodyPartId as deriveRootBodyPartId,
|
|
@@ -10,14 +10,13 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
filterTable,
|
|
12
12
|
mailboxSpecialUseTable,
|
|
13
|
-
mailboxTable,
|
|
14
13
|
messageTable,
|
|
15
14
|
threadMessageTable,
|
|
16
15
|
} from "../schema.js";
|
|
17
|
-
import {
|
|
16
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
18
17
|
import { createTestDb, randomId, type TestDb } from "../test-db.js";
|
|
19
|
-
import { MailboxRepo } from "./i4-mailbox.js";
|
|
20
|
-
import {
|
|
18
|
+
import type { MailboxRepo } from "./i4-mailbox.js";
|
|
19
|
+
import { deleteMessageSubtree } from "./message.js";
|
|
21
20
|
import { mailboxCreated } from "./test-helpers.js";
|
|
22
21
|
|
|
23
22
|
/** Every state a row can carry, for a transition that decides against none. */
|
|
@@ -55,10 +54,11 @@ describe("MailboxRepo", () => {
|
|
|
55
54
|
|
|
56
55
|
before(async () => {
|
|
57
56
|
({ db, close } = await createTestDb());
|
|
58
|
-
repo =
|
|
59
|
-
db
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
repo = createSqliteRepositories({
|
|
58
|
+
app: db,
|
|
59
|
+
mail: db,
|
|
60
|
+
search: db,
|
|
61
|
+
}).mailbox;
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
after(async () => {
|
|
@@ -543,10 +543,11 @@ describe("MailboxRepo", () => {
|
|
|
543
543
|
describe("deleteMailboxWithMail — the folder's mail goes with it (D8)", () => {
|
|
544
544
|
const seedMessage = async (mailboxId: string, uid: number) => {
|
|
545
545
|
const messageId = randomId();
|
|
546
|
-
await
|
|
547
|
-
db
|
|
548
|
-
|
|
549
|
-
|
|
546
|
+
await createSqliteRepositories({
|
|
547
|
+
app: db,
|
|
548
|
+
mail: db,
|
|
549
|
+
search: db,
|
|
550
|
+
}).message.create({
|
|
550
551
|
messageId,
|
|
551
552
|
mailboxId,
|
|
552
553
|
uid,
|
|
@@ -674,7 +675,7 @@ describe("MailboxRepo", () => {
|
|
|
674
675
|
|
|
675
676
|
// The shape an interruption leaves: one message's rows already gone,
|
|
676
677
|
// the mailbox row still there and still `deleting`.
|
|
677
|
-
await
|
|
678
|
+
await syncBatch(db)(deleteMessageSubtree(db, [first]));
|
|
678
679
|
await repo.transition(accountId, mailbox.mailboxId, {
|
|
679
680
|
from: EVERY_STATE,
|
|
680
681
|
to: MailboxSyncStatus.deleting,
|
package/src/repos/i4-mailbox.ts
CHANGED
|
@@ -27,8 +27,8 @@ import {
|
|
|
27
27
|
sql,
|
|
28
28
|
} from "drizzle-orm";
|
|
29
29
|
import shortUuid from "short-uuid";
|
|
30
|
-
import {
|
|
31
|
-
import type {
|
|
30
|
+
import type { WriteBatch } from "../batch.js";
|
|
31
|
+
import type { RepositoryDb, SqliteStore } from "../db.js";
|
|
32
32
|
import { NotFoundError } from "../error.js";
|
|
33
33
|
import { decodeToken, resultList } from "../pagination.js";
|
|
34
34
|
import {
|
|
@@ -44,6 +44,7 @@ import { messageTable } from "../schema/message-data.js";
|
|
|
44
44
|
import { syncWarningTable } from "../schema/sync-warning.js";
|
|
45
45
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
46
46
|
import type { SearchIndex } from "../search/thread-message-search.js";
|
|
47
|
+
import { inList } from "./in-list.js";
|
|
47
48
|
import { deleteMessageSubtree } from "./message.js";
|
|
48
49
|
|
|
49
50
|
const base36Translator = shortUuid.createTranslator(
|
|
@@ -51,7 +52,7 @@ const base36Translator = shortUuid.createTranslator(
|
|
|
51
52
|
);
|
|
52
53
|
const generateMailboxId = () => base36Translator.fromUUID(randomUUID());
|
|
53
54
|
|
|
54
|
-
type DB =
|
|
55
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
58
|
* Message subtrees removed per batch by {@link MailboxRepo.deleteMailboxWithMail},
|
|
@@ -148,10 +149,16 @@ export function rowToMailbox(
|
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
export class MailboxRepo implements IMailboxRepository {
|
|
152
|
+
private readonly db: DB;
|
|
153
|
+
private readonly batch: WriteBatch;
|
|
154
|
+
|
|
151
155
|
constructor(
|
|
152
|
-
|
|
156
|
+
store: SqliteStore<Record<string, unknown>>,
|
|
153
157
|
private search: SearchIndex,
|
|
154
|
-
) {
|
|
158
|
+
) {
|
|
159
|
+
this.db = store.db;
|
|
160
|
+
this.batch = store.batch;
|
|
161
|
+
}
|
|
155
162
|
|
|
156
163
|
async create(input: CreateMailboxInput): Promise<CreateMailboxResult> {
|
|
157
164
|
const now = Date.now();
|
|
@@ -207,7 +214,7 @@ export class MailboxRepo implements IMailboxRepository {
|
|
|
207
214
|
.where(
|
|
208
215
|
and(
|
|
209
216
|
eq(mailboxTable.accountId, accountId),
|
|
210
|
-
|
|
217
|
+
inList(mailboxTable.mailboxId, mailboxId),
|
|
211
218
|
),
|
|
212
219
|
);
|
|
213
220
|
return rows.map(rowToMailbox);
|
|
@@ -378,13 +385,13 @@ export class MailboxRepo implements IMailboxRepository {
|
|
|
378
385
|
|
|
379
386
|
async deleteMany(accountId: string, mailboxIds: string[]): Promise<void> {
|
|
380
387
|
if (mailboxIds.length === 0) return;
|
|
381
|
-
await
|
|
388
|
+
await this.batch([
|
|
382
389
|
this.db
|
|
383
390
|
.delete(syncWarningTable)
|
|
384
391
|
.where(
|
|
385
392
|
and(
|
|
386
393
|
eq(syncWarningTable.accountId, accountId),
|
|
387
|
-
|
|
394
|
+
inList(syncWarningTable.mailboxId, mailboxIds),
|
|
388
395
|
),
|
|
389
396
|
),
|
|
390
397
|
this.db
|
|
@@ -392,7 +399,7 @@ export class MailboxRepo implements IMailboxRepository {
|
|
|
392
399
|
.where(
|
|
393
400
|
and(
|
|
394
401
|
eq(mailboxTable.accountId, accountId),
|
|
395
|
-
|
|
402
|
+
inList(mailboxTable.mailboxId, mailboxIds),
|
|
396
403
|
),
|
|
397
404
|
),
|
|
398
405
|
]);
|
|
@@ -551,15 +558,15 @@ export class MailboxRepo implements IMailboxRepository {
|
|
|
551
558
|
const listings = await this.db
|
|
552
559
|
.select({ threadMessageId: threadMessageTable.threadMessageId })
|
|
553
560
|
.from(threadMessageTable)
|
|
554
|
-
.where(
|
|
561
|
+
.where(inList(threadMessageTable.messageId, messageIds));
|
|
555
562
|
|
|
556
563
|
// The primitive the rest of the codebase deletes mail with: nine
|
|
557
564
|
// per-message child tables. A bespoke table list would orphan them.
|
|
558
|
-
await
|
|
565
|
+
await this.batch([
|
|
559
566
|
...deleteMessageSubtree(this.db, messageIds),
|
|
560
567
|
this.db
|
|
561
568
|
.delete(threadMessageTable)
|
|
562
|
-
.where(
|
|
569
|
+
.where(inList(threadMessageTable.messageId, messageIds)),
|
|
563
570
|
]);
|
|
564
571
|
await this.search.sync(listings.map((row) => row.threadMessageId));
|
|
565
572
|
}
|
|
@@ -3,10 +3,10 @@ import type {
|
|
|
3
3
|
PutMessageFlagPushInput,
|
|
4
4
|
} from "@remit/data-ports";
|
|
5
5
|
import { and, eq } from "drizzle-orm";
|
|
6
|
-
import type {
|
|
6
|
+
import type { RepositoryDb } from "../db.js";
|
|
7
7
|
import { messageFlagPushTable } from "../schema/i4-message-flag-push.js";
|
|
8
8
|
|
|
9
|
-
type DB =
|
|
9
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
10
10
|
|
|
11
11
|
export type { MessageFlagPushItem, PutMessageFlagPushInput };
|
|
12
12
|
export type FlagPushOperation = MessageFlagPushItem["operation"];
|
|
@@ -3,10 +3,10 @@ import type {
|
|
|
3
3
|
PutMessagePlacementMoveInput,
|
|
4
4
|
} from "@remit/data-ports";
|
|
5
5
|
import { eq } from "drizzle-orm";
|
|
6
|
-
import type {
|
|
6
|
+
import type { RepositoryDb } from "../db.js";
|
|
7
7
|
import { messagePlacementMoveTable } from "../schema/i4-message-placement-move.js";
|
|
8
8
|
|
|
9
|
-
type DB =
|
|
9
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
10
10
|
|
|
11
11
|
export type { MessagePlacementMoveItem, PutMessagePlacementMoveInput };
|
|
12
12
|
export type MessagePlacementMoveState = MessagePlacementMoveItem["state"];
|
|
@@ -6,13 +6,13 @@ import type {
|
|
|
6
6
|
UpdateOrganizeJobRequestInput,
|
|
7
7
|
} from "@remit/data-ports";
|
|
8
8
|
import { eq } from "drizzle-orm";
|
|
9
|
-
import type {
|
|
9
|
+
import type { RepositoryDb } from "../db.js";
|
|
10
10
|
import { NotFoundError } from "../error.js";
|
|
11
11
|
import { randomId } from "../id.js";
|
|
12
12
|
import { decodeToken, resultList } from "../pagination.js";
|
|
13
13
|
import { organizeJobRequestTable } from "../schema/i4-organize-job-request.js";
|
|
14
14
|
|
|
15
|
-
type DB =
|
|
15
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
16
16
|
|
|
17
17
|
function rowToItem(
|
|
18
18
|
row: typeof organizeJobRequestTable.$inferSelect,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { after, before, describe, test } from "node:test";
|
|
3
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
3
4
|
import { createTestDb, randomId } from "../test-db.js";
|
|
4
|
-
import { OutboxAttachmentRepo } from "./i4-outbox-attachment.js";
|
|
5
|
+
import type { OutboxAttachmentRepo } from "./i4-outbox-attachment.js";
|
|
5
6
|
|
|
6
7
|
const CAP = { maxTotalBytes: 1000, maxCount: 3, nowSeconds: 1_000_000 };
|
|
7
8
|
|
|
@@ -27,7 +28,11 @@ const input = (
|
|
|
27
28
|
|
|
28
29
|
before(async () => {
|
|
29
30
|
harness = await createTestDb();
|
|
30
|
-
repo =
|
|
31
|
+
repo = createSqliteRepositories({
|
|
32
|
+
app: harness.db,
|
|
33
|
+
mail: harness.db,
|
|
34
|
+
search: harness.db,
|
|
35
|
+
}).outboxAttachment;
|
|
31
36
|
});
|
|
32
37
|
|
|
33
38
|
after(async () => {
|
|
@@ -6,13 +6,14 @@ import type {
|
|
|
6
6
|
ReserveOutboxAttachmentResult,
|
|
7
7
|
} from "@remit/data-ports";
|
|
8
8
|
import { holdsRoom } from "@remit/data-ports";
|
|
9
|
-
import { and, eq, gt, gte,
|
|
10
|
-
import {
|
|
11
|
-
import type {
|
|
9
|
+
import { and, eq, gt, gte, lt, or, sum } from "drizzle-orm";
|
|
10
|
+
import type { WriteBatch } from "../batch.js";
|
|
11
|
+
import type { RepositoryDb, SqliteStore } from "../db.js";
|
|
12
12
|
import { NotFoundError } from "../error.js";
|
|
13
13
|
import { outboxAttachmentTable } from "../schema/i4-outbox-attachment.js";
|
|
14
|
+
import { inList } from "./in-list.js";
|
|
14
15
|
|
|
15
|
-
type DB =
|
|
16
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
16
17
|
|
|
17
18
|
function rowToOutboxAttachment(
|
|
18
19
|
row: typeof outboxAttachmentTable.$inferSelect,
|
|
@@ -34,7 +35,13 @@ function rowToOutboxAttachment(
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export class OutboxAttachmentRepo implements IOutboxAttachmentRepository {
|
|
37
|
-
|
|
38
|
+
private readonly db: DB;
|
|
39
|
+
private readonly batch: WriteBatch;
|
|
40
|
+
|
|
41
|
+
constructor(store: SqliteStore<Record<string, unknown>>) {
|
|
42
|
+
this.db = store.db;
|
|
43
|
+
this.batch = store.batch;
|
|
44
|
+
}
|
|
38
45
|
|
|
39
46
|
/**
|
|
40
47
|
* The per-message cap. The insert, the read of the draft's live rows and a
|
|
@@ -59,7 +66,7 @@ export class OutboxAttachmentRepo implements IOutboxAttachmentRepository {
|
|
|
59
66
|
gte(outboxAttachmentTable.reservationExpiresAt, cap.nowSeconds),
|
|
60
67
|
),
|
|
61
68
|
);
|
|
62
|
-
const [inserted, rows, removed] = await
|
|
69
|
+
const [inserted, rows, removed] = await this.batch([
|
|
63
70
|
this.db
|
|
64
71
|
.insert(outboxAttachmentTable)
|
|
65
72
|
.values({
|
|
@@ -218,10 +225,7 @@ export class OutboxAttachmentRepo implements IOutboxAttachmentRepository {
|
|
|
218
225
|
.where(
|
|
219
226
|
and(
|
|
220
227
|
eq(outboxAttachmentTable.accountConfigId, accountConfigId),
|
|
221
|
-
|
|
222
|
-
outboxAttachmentTable.outboxAttachmentId,
|
|
223
|
-
outboxAttachmentIds,
|
|
224
|
-
),
|
|
228
|
+
inList(outboxAttachmentTable.outboxAttachmentId, outboxAttachmentIds),
|
|
225
229
|
),
|
|
226
230
|
);
|
|
227
231
|
}
|