@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
|
@@ -5,13 +5,14 @@ import type {
|
|
|
5
5
|
ResultList,
|
|
6
6
|
UpdateOutboxMessageInput,
|
|
7
7
|
} from "@remit/data-ports";
|
|
8
|
-
import { and, desc, eq,
|
|
9
|
-
import type {
|
|
8
|
+
import { and, desc, eq, lt, or } from "drizzle-orm";
|
|
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
|
+
import { inList } from "./in-list.js";
|
|
13
14
|
|
|
14
|
-
type DB =
|
|
15
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
15
16
|
|
|
16
17
|
function rowToOutboxMessage(
|
|
17
18
|
row: typeof outboxMessageTable.$inferSelect,
|
|
@@ -99,7 +100,7 @@ export class OutboxMessageRepo implements IOutboxMessageRepository {
|
|
|
99
100
|
.where(
|
|
100
101
|
and(
|
|
101
102
|
eq(outboxMessageTable.accountConfigId, accountConfigId),
|
|
102
|
-
|
|
103
|
+
inList(outboxMessageTable.outboxMessageId, outboxMessageId),
|
|
103
104
|
),
|
|
104
105
|
);
|
|
105
106
|
return rows.map(rowToOutboxMessage);
|
|
@@ -264,7 +265,7 @@ export class OutboxMessageRepo implements IOutboxMessageRepository {
|
|
|
264
265
|
.where(
|
|
265
266
|
and(
|
|
266
267
|
eq(outboxMessageTable.accountConfigId, accountConfigId),
|
|
267
|
-
|
|
268
|
+
inList(outboxMessageTable.outboxMessageId, outboxMessageIds),
|
|
268
269
|
),
|
|
269
270
|
);
|
|
270
271
|
}
|
|
@@ -298,7 +299,7 @@ export class OutboxMessageRepo implements IOutboxMessageRepository {
|
|
|
298
299
|
.from(outboxMessageTable)
|
|
299
300
|
.where(
|
|
300
301
|
and(
|
|
301
|
-
|
|
302
|
+
inList(outboxMessageTable.accountId, accountIds),
|
|
302
303
|
after
|
|
303
304
|
? or(
|
|
304
305
|
lt(outboxMessageTable.createdAt, after.createdAt),
|
|
@@ -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,61 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { after, before, describe, test } from "node:test";
|
|
3
|
+
import { calendarTable } from "../schema.js";
|
|
4
|
+
import { createSqliteTestDb, type SqliteTestDb } from "../test-db-sqlite.js";
|
|
5
|
+
import { inList } from "./in-list.js";
|
|
6
|
+
|
|
7
|
+
const SCHEMA = { calendars: calendarTable };
|
|
8
|
+
|
|
9
|
+
const calendar = (calendarId: string): typeof calendarTable.$inferInsert => ({
|
|
10
|
+
calendarId,
|
|
11
|
+
accountConfigId: "account-config-in-list",
|
|
12
|
+
urlSegment: calendarId,
|
|
13
|
+
displayName: calendarId,
|
|
14
|
+
color: "Cal1",
|
|
15
|
+
componentSet: "VeventOnly",
|
|
16
|
+
source: "UserCreated",
|
|
17
|
+
timezone: "",
|
|
18
|
+
subscriptionUrl: "",
|
|
19
|
+
subscriptionEnabled: false,
|
|
20
|
+
syncSequence: 0,
|
|
21
|
+
createdAt: 0,
|
|
22
|
+
updatedAt: 0,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("inList on better-sqlite3", () => {
|
|
26
|
+
let db: SqliteTestDb<typeof SCHEMA>;
|
|
27
|
+
let close: () => Promise<void>;
|
|
28
|
+
const allIds = Array.from({ length: 150 }, (_, index) => `calendar-${index}`);
|
|
29
|
+
|
|
30
|
+
before(async () => {
|
|
31
|
+
({ db, close } = await createSqliteTestDb(SCHEMA));
|
|
32
|
+
await db.insert(calendarTable).values(allIds.map(calendar));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
after(() => close());
|
|
36
|
+
|
|
37
|
+
test("binds a single parameter for 150 ids", () => {
|
|
38
|
+
const query = db
|
|
39
|
+
.select({ calendarId: calendarTable.calendarId })
|
|
40
|
+
.from(calendarTable)
|
|
41
|
+
.where(inList(calendarTable.calendarId, allIds))
|
|
42
|
+
.toSQL();
|
|
43
|
+
assert.equal(query.params.length, 1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("matches only the listed ids", async () => {
|
|
47
|
+
const rows = await db
|
|
48
|
+
.select({ calendarId: calendarTable.calendarId })
|
|
49
|
+
.from(calendarTable)
|
|
50
|
+
.where(inList(calendarTable.calendarId, ["calendar-3", "missing"]));
|
|
51
|
+
assert.deepEqual(rows, [{ calendarId: "calendar-3" }]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("matches nothing for an empty list", async () => {
|
|
55
|
+
const rows = await db
|
|
56
|
+
.select({ calendarId: calendarTable.calendarId })
|
|
57
|
+
.from(calendarTable)
|
|
58
|
+
.where(inList(calendarTable.calendarId, []));
|
|
59
|
+
assert.deepEqual(rows, []);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Column, type SQL, sql } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
export const inList = <T extends Column>(
|
|
4
|
+
column: T,
|
|
5
|
+
values: readonly T["_"]["data"][],
|
|
6
|
+
): SQL =>
|
|
7
|
+
sql`${column} in (select value from json_each(${JSON.stringify(values)}))`;
|
|
8
|
+
|
|
9
|
+
export const pairInList = <A extends Column, B extends Column>(
|
|
10
|
+
first: A,
|
|
11
|
+
second: B,
|
|
12
|
+
pairs: readonly (readonly [A["_"]["data"], B["_"]["data"]])[],
|
|
13
|
+
): SQL =>
|
|
14
|
+
sql`(${first}, ${second}) in (select value->>0, value->>1 from json_each(${JSON.stringify(pairs)}))`;
|
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
|
|
|
@@ -4,15 +4,13 @@ import type {
|
|
|
4
4
|
IMessageFlagRepository,
|
|
5
5
|
MessageFlagItem,
|
|
6
6
|
} from "@remit/data-ports";
|
|
7
|
-
import { and, eq
|
|
8
|
-
import type {
|
|
7
|
+
import { and, eq } from "drizzle-orm";
|
|
8
|
+
import type { RepositoryDb } from "../db.js";
|
|
9
9
|
import { NotFoundError } from "../error.js";
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
messageFlagTable,
|
|
13
|
-
} from "../schema/message-data.js";
|
|
10
|
+
import { messageFlagTable } from "../schema/message-data.js";
|
|
11
|
+
import { inList } from "./in-list.js";
|
|
14
12
|
|
|
15
|
-
type DB =
|
|
13
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
16
14
|
|
|
17
15
|
function rowToMessageFlag(
|
|
18
16
|
row: typeof messageFlagTable.$inferSelect,
|
|
@@ -58,7 +56,7 @@ export class DrizzleMessageFlagRepository implements IMessageFlagRepository {
|
|
|
58
56
|
const rows = await this.db
|
|
59
57
|
.select()
|
|
60
58
|
.from(messageFlagTable)
|
|
61
|
-
.where(
|
|
59
|
+
.where(inList(messageFlagTable.messageFlagId, idOrIds));
|
|
62
60
|
return rows.map(rowToMessageFlag);
|
|
63
61
|
}
|
|
64
62
|
const [row] = await this.db
|
|
@@ -79,7 +77,7 @@ export class DrizzleMessageFlagRepository implements IMessageFlagRepository {
|
|
|
79
77
|
if (messageFlagIds.length === 0) return;
|
|
80
78
|
await this.db
|
|
81
79
|
.delete(messageFlagTable)
|
|
82
|
-
.where(
|
|
80
|
+
.where(inList(messageFlagTable.messageFlagId, messageFlagIds));
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
async getFlags(messageId: string): Promise<MessageFlagItem[]> {
|
|
@@ -155,7 +153,7 @@ export class DrizzleMessageFlagRepository implements IMessageFlagRepository {
|
|
|
155
153
|
.where(
|
|
156
154
|
and(
|
|
157
155
|
eq(messageFlagTable.messageId, messageId),
|
|
158
|
-
|
|
156
|
+
inList(messageFlagTable.flagName, flagNames),
|
|
159
157
|
),
|
|
160
158
|
);
|
|
161
159
|
}
|
|
@@ -3,12 +3,13 @@ import type {
|
|
|
3
3
|
IMessageLabelRepository,
|
|
4
4
|
MessageLabelItem,
|
|
5
5
|
} from "@remit/data-ports";
|
|
6
|
-
import { and, desc, eq
|
|
7
|
-
import type {
|
|
6
|
+
import { and, desc, eq } from "drizzle-orm";
|
|
7
|
+
import type { RepositoryDb } from "../db.js";
|
|
8
8
|
import { deterministicBase36Id } from "../id.js";
|
|
9
9
|
import { messageLabelTable } from "../schema.js";
|
|
10
|
+
import { inList } from "./in-list.js";
|
|
10
11
|
|
|
11
|
-
type DB =
|
|
12
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
12
13
|
|
|
13
14
|
function rowToMessageLabel(
|
|
14
15
|
row: typeof messageLabelTable.$inferSelect,
|
|
@@ -82,7 +83,7 @@ export class MessageLabelRepo implements IMessageLabelRepository {
|
|
|
82
83
|
const rows = await this.db
|
|
83
84
|
.select()
|
|
84
85
|
.from(messageLabelTable)
|
|
85
|
-
.where(
|
|
86
|
+
.where(inList(messageLabelTable.messageId, messageIds));
|
|
86
87
|
return rows.map(rowToMessageLabel);
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
} from "../id.js";
|
|
7
7
|
import { messageDataSchema } from "../schema/message-data.js";
|
|
8
8
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
9
|
-
import {
|
|
9
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
10
10
|
import { createSqliteTestDb, type SqliteTestDb } from "../test-db-sqlite.js";
|
|
11
|
-
import { DrizzleMessageRepository } from "./message.js";
|
|
11
|
+
import type { DrizzleMessageRepository } from "./message.js";
|
|
12
12
|
|
|
13
13
|
const SCHEMA = { ...messageDataSchema, threadMessages: threadMessageTable };
|
|
14
14
|
|
|
@@ -43,7 +43,7 @@ describe("DrizzleMessageRepository (sqlite)", () => {
|
|
|
43
43
|
|
|
44
44
|
before(async () => {
|
|
45
45
|
({ db, close } = await createSqliteTestDb(SCHEMA));
|
|
46
|
-
repo =
|
|
46
|
+
repo = createSqliteRepositories({ app: db, mail: db, search: db }).message;
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
after(async () => {
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
envelopeId as deriveEnvelopeId,
|
|
5
5
|
rootBodyPartId as deriveRootBodyPartId,
|
|
6
6
|
} from "../id.js";
|
|
7
|
-
import {
|
|
8
|
-
import { DrizzleEnvelopeRepository } from "./envelope.js";
|
|
9
|
-
import { DrizzleMessageRepository } from "./message.js";
|
|
7
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
8
|
+
import type { DrizzleEnvelopeRepository } from "./envelope.js";
|
|
9
|
+
import type { DrizzleMessageRepository } from "./message.js";
|
|
10
10
|
import { createTestDb, type TestDb } from "./test-helpers.js";
|
|
11
11
|
|
|
12
12
|
describe("DrizzleMessageRepository", () => {
|
|
@@ -37,8 +37,9 @@ describe("DrizzleMessageRepository", () => {
|
|
|
37
37
|
|
|
38
38
|
before(async () => {
|
|
39
39
|
({ db, stop } = await createTestDb());
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const repos = createSqliteRepositories({ app: db, mail: db, search: db });
|
|
41
|
+
messageRepo = repos.message;
|
|
42
|
+
envelopeRepo = repos.envelope;
|
|
42
43
|
});
|
|
43
44
|
|
|
44
45
|
after(async () => {
|
package/src/repos/message.ts
CHANGED
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
or,
|
|
19
19
|
type SQL,
|
|
20
20
|
} from "drizzle-orm";
|
|
21
|
-
import {
|
|
22
|
-
import type {
|
|
21
|
+
import type { SqliteBatchItem, WriteBatch } from "../batch.js";
|
|
22
|
+
import type { RepositoryDb, SqliteStore } from "../db.js";
|
|
23
23
|
import {
|
|
24
24
|
CreateFailedConflictError,
|
|
25
25
|
isUniqueViolation,
|
|
@@ -37,7 +37,6 @@ import {
|
|
|
37
37
|
bodyStructureRefetchTable,
|
|
38
38
|
envelopeAddressTable,
|
|
39
39
|
envelopeTable,
|
|
40
|
-
type MessageDataSchema,
|
|
41
40
|
messageFlagTable,
|
|
42
41
|
messageReferenceTable,
|
|
43
42
|
messageTable,
|
|
@@ -45,6 +44,7 @@ import {
|
|
|
45
44
|
} from "../schema/message-data.js";
|
|
46
45
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
47
46
|
import type { SearchIndex } from "../search/thread-message-search.js";
|
|
47
|
+
import { inList } from "./in-list.js";
|
|
48
48
|
import {
|
|
49
49
|
toBodyPartItem,
|
|
50
50
|
toBodyPartParameterItem,
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
toRawMessageStorageItem,
|
|
56
56
|
} from "./mappers.js";
|
|
57
57
|
|
|
58
|
-
type DB =
|
|
58
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
59
59
|
|
|
60
60
|
function toMessageItem(row: typeof messageTable.$inferSelect): MessageItem {
|
|
61
61
|
return {
|
|
@@ -111,7 +111,7 @@ function toMessageItem(row: typeof messageTable.$inferSelect): MessageItem {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
export type SubtreeDb = Pick<
|
|
114
|
+
export type SubtreeDb = Pick<RepositoryDb<Record<string, unknown>>, "delete">;
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
117
|
* The statements that delete a message and its whole per-message subtree by
|
|
@@ -129,36 +129,36 @@ export function deleteMessageSubtree(
|
|
|
129
129
|
return [
|
|
130
130
|
db
|
|
131
131
|
.delete(messageFlagTable)
|
|
132
|
-
.where(and(
|
|
132
|
+
.where(and(inList(messageFlagTable.messageId, messageIds), guard)),
|
|
133
133
|
db
|
|
134
134
|
.delete(messageReferenceTable)
|
|
135
|
-
.where(and(
|
|
135
|
+
.where(and(inList(messageReferenceTable.messageId, messageIds), guard)),
|
|
136
136
|
db
|
|
137
137
|
.delete(envelopeAddressTable)
|
|
138
|
-
.where(and(
|
|
138
|
+
.where(and(inList(envelopeAddressTable.messageId, messageIds), guard)),
|
|
139
139
|
db
|
|
140
140
|
.delete(bodyPartParameterTable)
|
|
141
|
-
.where(and(
|
|
141
|
+
.where(and(inList(bodyPartParameterTable.messageId, messageIds), guard)),
|
|
142
142
|
db
|
|
143
143
|
.delete(bodyPartStorageTable)
|
|
144
|
-
.where(and(
|
|
144
|
+
.where(and(inList(bodyPartStorageTable.messageId, messageIds), guard)),
|
|
145
145
|
db
|
|
146
146
|
.delete(rawMessageStorageTable)
|
|
147
|
-
.where(and(
|
|
147
|
+
.where(and(inList(rawMessageStorageTable.messageId, messageIds), guard)),
|
|
148
148
|
db
|
|
149
149
|
.delete(bodyPartTable)
|
|
150
|
-
.where(and(
|
|
150
|
+
.where(and(inList(bodyPartTable.messageId, messageIds), guard)),
|
|
151
151
|
db
|
|
152
152
|
.delete(bodyStructureRefetchTable)
|
|
153
153
|
.where(
|
|
154
|
-
and(
|
|
154
|
+
and(inList(bodyStructureRefetchTable.messageId, messageIds), guard),
|
|
155
155
|
),
|
|
156
156
|
db
|
|
157
157
|
.delete(envelopeTable)
|
|
158
|
-
.where(and(
|
|
158
|
+
.where(and(inList(envelopeTable.messageId, messageIds), guard)),
|
|
159
159
|
db
|
|
160
160
|
.delete(messageTable)
|
|
161
|
-
.where(and(
|
|
161
|
+
.where(and(inList(messageTable.messageId, messageIds), guard))
|
|
162
162
|
.returning({ messageId: messageTable.messageId }),
|
|
163
163
|
];
|
|
164
164
|
}
|
|
@@ -192,10 +192,16 @@ const placementTerms = (expected: PlacementPredicate): SQL[] => {
|
|
|
192
192
|
};
|
|
193
193
|
|
|
194
194
|
export class DrizzleMessageRepository implements IMessageRepository {
|
|
195
|
+
private readonly db: DB;
|
|
196
|
+
private readonly batch: WriteBatch;
|
|
197
|
+
|
|
195
198
|
constructor(
|
|
196
|
-
|
|
199
|
+
store: SqliteStore<Record<string, unknown>>,
|
|
197
200
|
private search: SearchIndex,
|
|
198
|
-
) {
|
|
201
|
+
) {
|
|
202
|
+
this.db = store.db;
|
|
203
|
+
this.batch = store.batch;
|
|
204
|
+
}
|
|
199
205
|
|
|
200
206
|
private rowOf(input: CreateMessageInput) {
|
|
201
207
|
const now = Date.now();
|
|
@@ -275,7 +281,7 @@ export class DrizzleMessageRepository implements IMessageRepository {
|
|
|
275
281
|
const rows = await this.db
|
|
276
282
|
.select()
|
|
277
283
|
.from(messageTable)
|
|
278
|
-
.where(
|
|
284
|
+
.where(inList(messageTable.messageId, messageId));
|
|
279
285
|
return rows.map(toMessageItem);
|
|
280
286
|
}
|
|
281
287
|
|
|
@@ -461,7 +467,7 @@ export class DrizzleMessageRepository implements IMessageRepository {
|
|
|
461
467
|
.select({ threadMessageId: threadMessageTable.threadMessageId })
|
|
462
468
|
.from(threadMessageTable)
|
|
463
469
|
.where(eq(threadMessageTable.messageId, messageId));
|
|
464
|
-
const results = await
|
|
470
|
+
const results = await this.batch([
|
|
465
471
|
this.db
|
|
466
472
|
.delete(threadMessageTable)
|
|
467
473
|
.where(and(eq(threadMessageTable.messageId, messageId), placed)),
|
|
@@ -474,7 +480,7 @@ export class DrizzleMessageRepository implements IMessageRepository {
|
|
|
474
480
|
|
|
475
481
|
async deleteMany(messageIds: string[]): Promise<void> {
|
|
476
482
|
if (messageIds.length === 0) return;
|
|
477
|
-
await
|
|
483
|
+
await this.batch(deleteMessageSubtree(this.db, messageIds));
|
|
478
484
|
}
|
|
479
485
|
|
|
480
486
|
async listByMailbox(
|
|
@@ -547,18 +553,18 @@ export class DrizzleMessageRepository implements IMessageRepository {
|
|
|
547
553
|
or(
|
|
548
554
|
and(
|
|
549
555
|
eq(messageTable.status, "moving"),
|
|
550
|
-
|
|
556
|
+
inList(messageTable.originalMailboxId, ids),
|
|
551
557
|
isNotNull(messageTable.originalUid),
|
|
552
558
|
),
|
|
553
559
|
and(
|
|
554
560
|
eq(messageTable.status, "deleting"),
|
|
555
|
-
|
|
561
|
+
inList(messageTable.mailboxId, ids),
|
|
556
562
|
),
|
|
557
563
|
and(
|
|
558
564
|
eq(messageTable.status, "moving"),
|
|
559
565
|
eq(messageTable.uid, 0),
|
|
560
566
|
isNull(messageTable.originalUid),
|
|
561
|
-
|
|
567
|
+
inList(messageTable.mailboxId, ids),
|
|
562
568
|
),
|
|
563
569
|
),
|
|
564
570
|
),
|
|
@@ -622,7 +628,7 @@ export class DrizzleMessageRepository implements IMessageRepository {
|
|
|
622
628
|
.select({ threadMessageId: threadMessageTable.threadMessageId })
|
|
623
629
|
.from(threadMessageTable)
|
|
624
630
|
.where(eq(threadMessageTable.messageId, messageId));
|
|
625
|
-
const [moved] = await
|
|
631
|
+
const [moved] = await this.batch([
|
|
626
632
|
this.db
|
|
627
633
|
.update(messageTable)
|
|
628
634
|
.set({
|
package/src/repos/quarantine.ts
CHANGED
|
@@ -6,10 +6,10 @@ import type {
|
|
|
6
6
|
} from "@remit/data-ports";
|
|
7
7
|
import { deriveQuarantineId } from "@remit/data-ports/id";
|
|
8
8
|
import { desc, eq } from "drizzle-orm";
|
|
9
|
-
import type {
|
|
9
|
+
import type { RepositoryDb } from "../db.js";
|
|
10
10
|
import { quarantineTable } from "../schema/quarantine.js";
|
|
11
11
|
|
|
12
|
-
type DB =
|
|
12
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
13
13
|
|
|
14
14
|
function rowToItem(row: typeof quarantineTable.$inferSelect): QuarantineItem {
|
|
15
15
|
return {
|
|
@@ -5,11 +5,12 @@ import type {
|
|
|
5
5
|
SyncWarningKey,
|
|
6
6
|
} from "@remit/data-ports";
|
|
7
7
|
import { deriveSyncWarningId } from "@remit/data-ports/id";
|
|
8
|
-
import { and, count, desc, eq, gte,
|
|
9
|
-
import type {
|
|
8
|
+
import { and, count, desc, eq, gte, sql } from "drizzle-orm";
|
|
9
|
+
import type { RepositoryDb } from "../db.js";
|
|
10
10
|
import { syncWarningTable } from "../schema/sync-warning.js";
|
|
11
|
+
import { inList } from "./in-list.js";
|
|
11
12
|
|
|
12
|
-
type DB =
|
|
13
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
13
14
|
|
|
14
15
|
export class SyncWarningRepo implements ISyncWarningRepository {
|
|
15
16
|
constructor(private db: DB) {}
|
|
@@ -71,6 +72,6 @@ export class SyncWarningRepo implements ISyncWarningRepository {
|
|
|
71
72
|
if (syncWarningIds.length === 0) return;
|
|
72
73
|
await this.db
|
|
73
74
|
.delete(syncWarningTable)
|
|
74
|
-
.where(
|
|
75
|
+
.where(inList(syncWarningTable.syncWarningId, syncWarningIds));
|
|
75
76
|
};
|
|
76
77
|
}
|
|
@@ -12,10 +12,10 @@ import { after, before, describe, test } from "node:test";
|
|
|
12
12
|
import type { CreateThreadMessageInput } from "@remit/data-ports";
|
|
13
13
|
import { addressTable } from "../schema/i4-address.js";
|
|
14
14
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
15
|
-
import {
|
|
15
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
16
16
|
import { createSqliteTestDb } from "../test-db-sqlite.js";
|
|
17
17
|
import { shippedIndexDdl } from "../test-shipped-sqlite-schema.js";
|
|
18
|
-
import { DrizzleThreadMessageRepository } from "./thread-message.js";
|
|
18
|
+
import type { DrizzleThreadMessageRepository } from "./thread-message.js";
|
|
19
19
|
|
|
20
20
|
// The index the mail baseline creates for this predicate, read from the one
|
|
21
21
|
// committed source so the plan assertion below is about the real object.
|
|
@@ -61,7 +61,11 @@ describe("thread-message body and muted-sender predicates (sqlite)", () => {
|
|
|
61
61
|
{ searchIndex: true },
|
|
62
62
|
));
|
|
63
63
|
sqlite.exec(mutedIndexDdl);
|
|
64
|
-
repo =
|
|
64
|
+
repo = createSqliteRepositories({
|
|
65
|
+
app: db,
|
|
66
|
+
mail: db,
|
|
67
|
+
search: db,
|
|
68
|
+
}).threadMessage;
|
|
65
69
|
});
|
|
66
70
|
|
|
67
71
|
after(async () => {
|
|
@@ -2,10 +2,10 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { after, before, describe, test } from "node:test";
|
|
3
3
|
import type { CreateThreadMessageInput } from "@remit/data-ports";
|
|
4
4
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
6
6
|
import { createSqliteTestDb } from "../test-db-sqlite.js";
|
|
7
7
|
import {
|
|
8
|
-
DrizzleThreadMessageRepository,
|
|
8
|
+
type DrizzleThreadMessageRepository,
|
|
9
9
|
THREAD_SEARCH_MAX_LIMIT,
|
|
10
10
|
} from "./thread-message.js";
|
|
11
11
|
|
|
@@ -55,10 +55,11 @@ describe("DrizzleThreadMessageRepository.listByFieldTerms (sqlite, #459)", () =>
|
|
|
55
55
|
{ searchIndex: true },
|
|
56
56
|
);
|
|
57
57
|
close = created.close;
|
|
58
|
-
repo =
|
|
59
|
-
created.db,
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
repo = createSqliteRepositories({
|
|
59
|
+
app: created.db,
|
|
60
|
+
mail: created.db,
|
|
61
|
+
search: created.db,
|
|
62
|
+
}).threadMessage;
|
|
62
63
|
|
|
63
64
|
await repo.create(
|
|
64
65
|
makeInput({
|
|
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import { after, before, describe, test } from "node:test";
|
|
3
3
|
import type { CreateThreadMessageInput } from "@remit/data-ports";
|
|
4
4
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
5
|
-
import {
|
|
5
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
6
6
|
import { createSqliteTestDb } from "../test-db-sqlite.js";
|
|
7
7
|
import {
|
|
8
8
|
BASE_DATE,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
totalRows,
|
|
13
13
|
} from "./category-fixture.js";
|
|
14
14
|
import {
|
|
15
|
-
DrizzleThreadMessageRepository,
|
|
15
|
+
type DrizzleThreadMessageRepository,
|
|
16
16
|
THREAD_SEARCH_MAX_LIMIT,
|
|
17
17
|
} from "./thread-message.js";
|
|
18
18
|
|
|
@@ -58,7 +58,11 @@ describe("DrizzleThreadMessageRepository (sqlite)", () => {
|
|
|
58
58
|
},
|
|
59
59
|
{ searchIndex: true },
|
|
60
60
|
));
|
|
61
|
-
repo =
|
|
61
|
+
repo = createSqliteRepositories({
|
|
62
|
+
app: db,
|
|
63
|
+
mail: db,
|
|
64
|
+
search: db,
|
|
65
|
+
}).threadMessage;
|
|
62
66
|
});
|
|
63
67
|
|
|
64
68
|
after(async () => {
|
|
@@ -3,11 +3,11 @@ import { after, before, describe, test } from "node:test";
|
|
|
3
3
|
import type { CreateThreadMessageInput } from "@remit/data-ports";
|
|
4
4
|
import shortUuid from "short-uuid";
|
|
5
5
|
import { threadMessageTable } from "../schema/thread-message.js";
|
|
6
|
-
import {
|
|
6
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
7
7
|
import { createSqliteTestDb } from "../test-db-sqlite.js";
|
|
8
8
|
import {
|
|
9
9
|
clampThreadSearchLimit,
|
|
10
|
-
DrizzleThreadMessageRepository,
|
|
10
|
+
type DrizzleThreadMessageRepository,
|
|
11
11
|
deriveThreadMessageId,
|
|
12
12
|
THREAD_SEARCH_MAX_LIMIT,
|
|
13
13
|
} from "./thread-message.js";
|
|
@@ -87,10 +87,11 @@ describe("DrizzleThreadMessageRepository.searchByMailboxWindow / countByMailbox"
|
|
|
87
87
|
before(async () => {
|
|
88
88
|
const harness = await setupDb();
|
|
89
89
|
close = harness.close;
|
|
90
|
-
repo =
|
|
91
|
-
harness.db,
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
repo = createSqliteRepositories({
|
|
91
|
+
app: harness.db,
|
|
92
|
+
mail: harness.db,
|
|
93
|
+
search: harness.db,
|
|
94
|
+
}).threadMessage;
|
|
94
95
|
});
|
|
95
96
|
|
|
96
97
|
after(async () => {
|
|
@@ -391,10 +392,11 @@ describe("DrizzleThreadMessageRepository.searchByDate", () => {
|
|
|
391
392
|
before(async () => {
|
|
392
393
|
const harness = await setupDb();
|
|
393
394
|
close = harness.close;
|
|
394
|
-
repo =
|
|
395
|
-
harness.db,
|
|
396
|
-
|
|
397
|
-
|
|
395
|
+
repo = createSqliteRepositories({
|
|
396
|
+
app: harness.db,
|
|
397
|
+
mail: harness.db,
|
|
398
|
+
search: harness.db,
|
|
399
|
+
}).threadMessage;
|
|
398
400
|
});
|
|
399
401
|
|
|
400
402
|
after(async () => {
|
|
@@ -921,10 +923,11 @@ describe("DrizzleThreadMessageRepository — native text search", () => {
|
|
|
921
923
|
before(async () => {
|
|
922
924
|
const harness = await setupDb();
|
|
923
925
|
close = harness.close;
|
|
924
|
-
repo =
|
|
925
|
-
harness.db,
|
|
926
|
-
|
|
927
|
-
|
|
926
|
+
repo = createSqliteRepositories({
|
|
927
|
+
app: harness.db,
|
|
928
|
+
mail: harness.db,
|
|
929
|
+
search: harness.db,
|
|
930
|
+
}).threadMessage;
|
|
928
931
|
});
|
|
929
932
|
|
|
930
933
|
after(async () => {
|
|
@@ -1096,10 +1099,11 @@ describe("DrizzleThreadMessageRepository — core CRUD", () => {
|
|
|
1096
1099
|
before(async () => {
|
|
1097
1100
|
const harness = await setupDb();
|
|
1098
1101
|
close = harness.close;
|
|
1099
|
-
repo =
|
|
1100
|
-
harness.db,
|
|
1101
|
-
|
|
1102
|
-
|
|
1102
|
+
repo = createSqliteRepositories({
|
|
1103
|
+
app: harness.db,
|
|
1104
|
+
mail: harness.db,
|
|
1105
|
+
search: harness.db,
|
|
1106
|
+
}).threadMessage;
|
|
1103
1107
|
});
|
|
1104
1108
|
|
|
1105
1109
|
after(async () => {
|