@remit/drizzle-service 0.0.102 → 0.0.104
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 +4 -3
- 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.d1.test.ts +20 -0
- package/src/repos/account.conformance.sqlite.test.ts +6 -2
- package/src/repos/calendar-collection.conformance.d1.test.ts +20 -0
- package/src/repos/calendar-collection.ts +2 -2
- package/src/repos/calendar-default.d1.test.ts +4 -0
- package/src/repos/calendar-default.sqlite.test.ts +3 -72
- package/src/repos/calendar-default.suite.ts +68 -0
- package/src/repos/calendar-event-index.conformance.d1.test.ts +20 -0
- package/src/repos/calendar-event-index.ts +2 -2
- package/src/repos/calendar-feed-token.conformance.d1.test.ts +20 -0
- package/src/repos/calendar-feed-token.ts +2 -2
- package/src/repos/calendar-object.conformance.d1.test.ts +20 -0
- package/src/repos/calendar-object.ts +2 -2
- package/src/repos/calendar-put.d1.test.ts +4 -0
- package/src/repos/calendar-put.sqlite.test.ts +3 -205
- package/src/repos/calendar-put.suite.ts +210 -0
- package/src/repos/calendar-store.ts +36 -40
- package/src/repos/calendar-suggestion.ts +2 -2
- package/src/repos/cascade-delete.test.ts +3 -2
- package/src/repos/cascade-delete.ts +12 -8
- package/src/repos/envelope.sqlite.test.ts +3 -2
- package/src/repos/envelope.test.ts +3 -2
- package/src/repos/envelope.ts +14 -9
- package/src/repos/filter.d1.test.ts +4 -0
- package/src/repos/filter.suite.ts +476 -0
- package/src/repos/filter.test.ts +3 -472
- package/src/repos/filter.ts +2 -2
- package/src/repos/i4-account-config.d1.test.ts +4 -0
- package/src/repos/i4-account-config.suite.ts +131 -0
- package/src/repos/i4-account-config.test.ts +3 -123
- package/src/repos/i4-account-config.ts +8 -10
- package/src/repos/i4-account-export-request.d1.test.ts +4 -0
- package/src/repos/i4-account-export-request.suite.ts +44 -0
- package/src/repos/i4-account-export-request.test.ts +3 -40
- package/src/repos/i4-account-export-request.ts +2 -2
- package/src/repos/i4-account-setting.d1.test.ts +4 -0
- package/src/repos/i4-account-setting.suite.ts +100 -0
- package/src/repos/i4-account-setting.test.ts +3 -93
- package/src/repos/i4-account-setting.ts +2 -2
- package/src/repos/i4-account.test.ts +12 -8
- package/src/repos/i4-account.ts +6 -10
- package/src/repos/i4-address-junk-move.sqlite.test.ts +7 -2
- package/src/repos/i4-address.ts +12 -2
- 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 +29 -40
- 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 +13 -7
- 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 +11 -5
- package/src/repos/i4-outbox-message.ts +2 -2
- package/src/repos/i4-sender-signer-standing.ts +2 -2
- package/src/repos/label.conformance.d1.test.ts +20 -0
- package/src/repos/label.ts +2 -2
- package/src/repos/message-flag.ts +3 -6
- package/src/repos/message-label.ts +2 -2
- package/src/repos/message.sqlite.test.ts +57 -3
- package/src/repos/message.test.ts +6 -5
- package/src/repos/message.ts +81 -10
- package/src/repos/quarantine.ts +2 -2
- package/src/repos/sync-warning.ts +2 -2
- package/src/repos/thread-message-body-muted.sqlite.test.ts +30 -22
- package/src/repos/thread-message-field-terms.sqlite.test.ts +16 -11
- package/src/repos/thread-message.sqlite.test.ts +13 -7
- package/src/repos/thread-message.test.ts +43 -27
- package/src/repos/thread-message.ts +26 -14
- package/src/repos/thread-search-predicates.ts +36 -63
- package/src/schema/message-data.ts +3 -0
- package/src/schema-sets/mail.ts +1 -0
- package/src/search/thread-message-search.sqlite.test.ts +57 -11
- package/src/search/thread-message-search.ts +26 -12
- package/src/sqlite-repositories.ts +48 -32
- package/src/test-d1.ts +40 -0
- package/src/test-db-sqlite.ts +1 -1
- package/src/test-shipped-sqlite-schema.ts +8 -6
- package/src/test-stores.ts +30 -0
|
@@ -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,
|
|
@@ -15,14 +18,21 @@ import {
|
|
|
15
18
|
} from "@remit/data-ports/folder-role";
|
|
16
19
|
import { CanonicalMailboxRole } from "@remit/domain-enums";
|
|
17
20
|
import { eq } from "drizzle-orm";
|
|
18
|
-
import type {
|
|
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 { AccountSettingRepo } from "./i4-account-setting.js";
|
|
23
24
|
import { inList } from "./in-list.js";
|
|
24
25
|
|
|
25
|
-
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">;
|
|
26
36
|
|
|
27
37
|
const JUNK_ROLES: readonly CanonicalMailboxRoleValue[] = [
|
|
28
38
|
CanonicalMailboxRole.Junk,
|
|
@@ -89,14 +99,11 @@ function rowToSpecialUse(
|
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
92
|
-
private readonly accountSetting: AccountSettingRepo;
|
|
93
|
-
|
|
94
102
|
constructor(
|
|
95
|
-
private app: DB,
|
|
96
103
|
private db: DB,
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
104
|
+
private accounts: AccountReader,
|
|
105
|
+
private accountSetting: AppointmentReader,
|
|
106
|
+
) {}
|
|
100
107
|
|
|
101
108
|
async create(
|
|
102
109
|
mailboxId: string,
|
|
@@ -203,11 +210,9 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
203
210
|
async resolveJunkRolesForConfig(
|
|
204
211
|
accountConfigId: string,
|
|
205
212
|
): Promise<JunkRoleMailboxes> {
|
|
206
|
-
|
|
207
|
-
.
|
|
208
|
-
|
|
209
|
-
.where(eq(accountTable.accountConfigId, accountConfigId));
|
|
210
|
-
return this.resolveJunkRoles(accounts.map((row) => row.accountId));
|
|
213
|
+
return this.resolveJunkRoles(
|
|
214
|
+
await this.accounts.listAllByAccountConfig(accountConfigId),
|
|
215
|
+
);
|
|
211
216
|
}
|
|
212
217
|
|
|
213
218
|
/**
|
|
@@ -217,21 +222,19 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
217
222
|
* run one pass over the address table instead of one per config.
|
|
218
223
|
*/
|
|
219
224
|
async resolveJunkRolesForInstance(): Promise<JunkRoleMailboxes> {
|
|
220
|
-
|
|
221
|
-
.select({ accountId: accountTable.accountId })
|
|
222
|
-
.from(accountTable);
|
|
223
|
-
return this.resolveJunkRoles(accounts.map((row) => row.accountId));
|
|
225
|
+
return this.resolveJunkRoles(await this.accounts.listAll());
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
private async resolveJunkRoles(
|
|
227
|
-
|
|
229
|
+
accounts: readonly AppointedAccount[],
|
|
228
230
|
): Promise<JunkRoleMailboxes> {
|
|
229
|
-
if (
|
|
231
|
+
if (accounts.length === 0) {
|
|
230
232
|
return { junkMailboxIds: [], trashMailboxIds: [] };
|
|
231
233
|
}
|
|
234
|
+
const accountIds = accounts.map((account) => account.accountId);
|
|
232
235
|
const [candidates, appointments] = await Promise.all([
|
|
233
236
|
this.roleCandidatesFor(accountIds),
|
|
234
|
-
this.appointedMailboxIds(
|
|
237
|
+
this.appointedMailboxIds(accounts, JUNK_ROLES),
|
|
235
238
|
]);
|
|
236
239
|
|
|
237
240
|
const junkMailboxIds: string[] = [];
|
|
@@ -255,24 +258,13 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
/**
|
|
258
|
-
* Each account's appointment for each role, in
|
|
259
|
-
* input as `appointedMailboxId`, batched
|
|
260
|
-
* racing a delete, and leaves the account with no appointment rather than
|
|
261
|
-
* failing the lookup.
|
|
261
|
+
* Each account's appointment for each role, in one read. Same precedence
|
|
262
|
+
* input as `appointedMailboxId`, batched.
|
|
262
263
|
*/
|
|
263
264
|
private async appointedMailboxIds(
|
|
264
|
-
|
|
265
|
+
accounts: readonly AppointedAccount[],
|
|
265
266
|
roles: readonly CanonicalMailboxRoleValue[],
|
|
266
267
|
): Promise<Map<string, string>> {
|
|
267
|
-
const accounts = await this.app
|
|
268
|
-
.select({
|
|
269
|
-
accountId: accountTable.accountId,
|
|
270
|
-
accountConfigId: accountTable.accountConfigId,
|
|
271
|
-
})
|
|
272
|
-
.from(accountTable)
|
|
273
|
-
.where(inList(accountTable.accountId, accountIds));
|
|
274
|
-
if (accounts.length === 0) return new Map();
|
|
275
|
-
|
|
276
268
|
const wanted = new Map<string, string>();
|
|
277
269
|
for (const account of accounts) {
|
|
278
270
|
for (const role of roles) {
|
|
@@ -367,10 +359,7 @@ export class MailboxSpecialUseRepo implements IMailboxSpecialUseRepository {
|
|
|
367
359
|
accountId: string,
|
|
368
360
|
role: CanonicalMailboxRoleValue,
|
|
369
361
|
): Promise<string | undefined> {
|
|
370
|
-
const [account] = await this.
|
|
371
|
-
.select({ accountConfigId: accountTable.accountConfigId })
|
|
372
|
-
.from(accountTable)
|
|
373
|
-
.where(eq(accountTable.accountId, accountId));
|
|
362
|
+
const [account] = await this.accounts.get([accountId]);
|
|
374
363
|
if (!account) return undefined;
|
|
375
364
|
|
|
376
365
|
const setting = await this.accountSetting.get(
|
|
@@ -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 {
|
|
@@ -52,7 +52,7 @@ const base36Translator = shortUuid.createTranslator(
|
|
|
52
52
|
);
|
|
53
53
|
const generateMailboxId = () => base36Translator.fromUUID(randomUUID());
|
|
54
54
|
|
|
55
|
-
type DB =
|
|
55
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* Message subtrees removed per batch by {@link MailboxRepo.deleteMailboxWithMail},
|
|
@@ -149,10 +149,16 @@ export function rowToMailbox(
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
export class MailboxRepo implements IMailboxRepository {
|
|
152
|
+
private readonly db: DB;
|
|
153
|
+
private readonly batch: WriteBatch;
|
|
154
|
+
|
|
152
155
|
constructor(
|
|
153
|
-
|
|
156
|
+
store: SqliteStore<Record<string, unknown>>,
|
|
154
157
|
private search: SearchIndex,
|
|
155
|
-
) {
|
|
158
|
+
) {
|
|
159
|
+
this.db = store.db;
|
|
160
|
+
this.batch = store.batch;
|
|
161
|
+
}
|
|
156
162
|
|
|
157
163
|
async create(input: CreateMailboxInput): Promise<CreateMailboxResult> {
|
|
158
164
|
const now = Date.now();
|
|
@@ -379,7 +385,7 @@ export class MailboxRepo implements IMailboxRepository {
|
|
|
379
385
|
|
|
380
386
|
async deleteMany(accountId: string, mailboxIds: string[]): Promise<void> {
|
|
381
387
|
if (mailboxIds.length === 0) return;
|
|
382
|
-
await
|
|
388
|
+
await this.batch([
|
|
383
389
|
this.db
|
|
384
390
|
.delete(syncWarningTable)
|
|
385
391
|
.where(
|
|
@@ -556,7 +562,7 @@ export class MailboxRepo implements IMailboxRepository {
|
|
|
556
562
|
|
|
557
563
|
// The primitive the rest of the codebase deletes mail with: nine
|
|
558
564
|
// per-message child tables. A bespoke table list would orphan them.
|
|
559
|
-
await
|
|
565
|
+
await this.batch([
|
|
560
566
|
...deleteMessageSubtree(this.db, messageIds),
|
|
561
567
|
this.db
|
|
562
568
|
.delete(threadMessageTable)
|
|
@@ -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 () => {
|
|
@@ -7,13 +7,13 @@ import type {
|
|
|
7
7
|
} from "@remit/data-ports";
|
|
8
8
|
import { holdsRoom } from "@remit/data-ports";
|
|
9
9
|
import { and, eq, gt, gte, lt, or, sum } from "drizzle-orm";
|
|
10
|
-
import {
|
|
11
|
-
import type {
|
|
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
14
|
import { inList } from "./in-list.js";
|
|
15
15
|
|
|
16
|
-
type DB =
|
|
16
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
17
17
|
|
|
18
18
|
function rowToOutboxAttachment(
|
|
19
19
|
row: typeof outboxAttachmentTable.$inferSelect,
|
|
@@ -35,7 +35,13 @@ function rowToOutboxAttachment(
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export class OutboxAttachmentRepo implements IOutboxAttachmentRepository {
|
|
38
|
-
|
|
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
|
+
}
|
|
39
45
|
|
|
40
46
|
/**
|
|
41
47
|
* The per-message cap. The insert, the read of the draft's live rows and a
|
|
@@ -60,7 +66,7 @@ export class OutboxAttachmentRepo implements IOutboxAttachmentRepository {
|
|
|
60
66
|
gte(outboxAttachmentTable.reservationExpiresAt, cap.nowSeconds),
|
|
61
67
|
),
|
|
62
68
|
);
|
|
63
|
-
const [inserted, rows, removed] = await
|
|
69
|
+
const [inserted, rows, removed] = await this.batch([
|
|
64
70
|
this.db
|
|
65
71
|
.insert(outboxAttachmentTable)
|
|
66
72
|
.values({
|
|
@@ -6,13 +6,13 @@ import type {
|
|
|
6
6
|
UpdateOutboxMessageInput,
|
|
7
7
|
} from "@remit/data-ports";
|
|
8
8
|
import { and, desc, eq, lt, or } from "drizzle-orm";
|
|
9
|
-
import type {
|
|
9
|
+
import type { RepositoryDb } from "../db.js";
|
|
10
10
|
import { ForbiddenError, NotFoundError } from "../error.js";
|
|
11
11
|
import { decodeToken, resultList } from "../pagination.js";
|
|
12
12
|
import { outboxMessageTable } from "../schema/i4-outbox-message.js";
|
|
13
13
|
import { inList } from "./in-list.js";
|
|
14
14
|
|
|
15
|
-
type DB =
|
|
15
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
16
16
|
|
|
17
17
|
function rowToOutboxMessage(
|
|
18
18
|
row: typeof outboxMessageTable.$inferSelect,
|
|
@@ -4,11 +4,11 @@ import type {
|
|
|
4
4
|
SenderSignerStandingItem,
|
|
5
5
|
} from "@remit/data-ports";
|
|
6
6
|
import { and, eq, sql } from "drizzle-orm";
|
|
7
|
-
import type {
|
|
7
|
+
import type { RepositoryDb } from "../db.js";
|
|
8
8
|
import { NotFoundError } from "../error.js";
|
|
9
9
|
import { senderSignerStandingTable } from "../schema.js";
|
|
10
10
|
|
|
11
|
-
type DB =
|
|
11
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
12
12
|
|
|
13
13
|
function rowToStanding(
|
|
14
14
|
row: typeof senderSignerStandingTable.$inferSelect,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { labelRepositoryConformance } from "@remit/data-ports/conformance";
|
|
2
|
+
import { NotFoundError } from "../error.js";
|
|
3
|
+
import { randomId } from "../id.js";
|
|
4
|
+
import { openD1TestStores } from "../test-d1.js";
|
|
5
|
+
import { LabelRepo } from "./label.js";
|
|
6
|
+
|
|
7
|
+
let close: (() => Promise<void>) | undefined;
|
|
8
|
+
|
|
9
|
+
labelRepositoryConformance({
|
|
10
|
+
async createRepository() {
|
|
11
|
+
const opened = await openD1TestStores();
|
|
12
|
+
close = opened.close;
|
|
13
|
+
return new LabelRepo(opened.stores.app.db);
|
|
14
|
+
},
|
|
15
|
+
teardown: async () => {
|
|
16
|
+
await close?.();
|
|
17
|
+
},
|
|
18
|
+
makeId: () => randomId(),
|
|
19
|
+
isNotFoundError: (error) => error instanceof NotFoundError,
|
|
20
|
+
});
|
package/src/repos/label.ts
CHANGED
|
@@ -6,13 +6,13 @@ import type {
|
|
|
6
6
|
UpdateLabelInput,
|
|
7
7
|
} from "@remit/data-ports";
|
|
8
8
|
import { and, asc, eq, gt, or } 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 { labelTable } from "../schema.js";
|
|
14
14
|
|
|
15
|
-
type DB =
|
|
15
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
16
16
|
|
|
17
17
|
const normalize = (name: string): string => name.trim().toLowerCase();
|
|
18
18
|
|
|
@@ -5,15 +5,12 @@ import type {
|
|
|
5
5
|
MessageFlagItem,
|
|
6
6
|
} from "@remit/data-ports";
|
|
7
7
|
import { and, eq } from "drizzle-orm";
|
|
8
|
-
import type {
|
|
8
|
+
import type { RepositoryDb } from "../db.js";
|
|
9
9
|
import { NotFoundError } from "../error.js";
|
|
10
|
-
import {
|
|
11
|
-
type MessageDataSchema,
|
|
12
|
-
messageFlagTable,
|
|
13
|
-
} from "../schema/message-data.js";
|
|
10
|
+
import { messageFlagTable } from "../schema/message-data.js";
|
|
14
11
|
import { inList } from "./in-list.js";
|
|
15
12
|
|
|
16
|
-
type DB =
|
|
13
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
17
14
|
|
|
18
15
|
function rowToMessageFlag(
|
|
19
16
|
row: typeof messageFlagTable.$inferSelect,
|
|
@@ -4,12 +4,12 @@ import type {
|
|
|
4
4
|
MessageLabelItem,
|
|
5
5
|
} from "@remit/data-ports";
|
|
6
6
|
import { and, desc, eq } from "drizzle-orm";
|
|
7
|
-
import type {
|
|
7
|
+
import type { RepositoryDb } from "../db.js";
|
|
8
8
|
import { deterministicBase36Id } from "../id.js";
|
|
9
9
|
import { messageLabelTable } from "../schema.js";
|
|
10
10
|
import { inList } from "./in-list.js";
|
|
11
11
|
|
|
12
|
-
type DB =
|
|
12
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
13
13
|
|
|
14
14
|
function rowToMessageLabel(
|
|
15
15
|
row: typeof messageLabelTable.$inferSelect,
|