@remit/drizzle-service 0.0.102 → 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 +2 -2
- package/src/repos/cascade-delete.test.ts +3 -2
- package/src/repos/cascade-delete.ts +11 -7
- 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.ts +2 -2
- package/src/repos/i4-account-config.test.ts +10 -6
- package/src/repos/i4-account-config.ts +8 -10
- package/src/repos/i4-account-export-request.ts +2 -2
- 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.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 +3 -3
- package/src/repos/message.test.ts +6 -5
- package/src/repos/message.ts +15 -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 +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 +13 -5
- 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/envelope.ts
CHANGED
|
@@ -9,8 +9,8 @@ import type {
|
|
|
9
9
|
UpdateEnvelopeInput,
|
|
10
10
|
} from "@remit/data-ports";
|
|
11
11
|
import { eq } from "drizzle-orm";
|
|
12
|
-
import {
|
|
13
|
-
import type {
|
|
12
|
+
import type { WriteBatch } from "../batch.js";
|
|
13
|
+
import type { RepositoryDb, SqliteStore } from "../db.js";
|
|
14
14
|
import {
|
|
15
15
|
CreateFailedConflictError,
|
|
16
16
|
isUniqueViolation,
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
bodyStructureRefetchTable,
|
|
30
30
|
envelopeAddressTable,
|
|
31
31
|
envelopeTable,
|
|
32
|
-
type MessageDataSchema,
|
|
33
32
|
messageReferenceTable,
|
|
34
33
|
messageTable,
|
|
35
34
|
rawMessageStorageTable,
|
|
@@ -45,7 +44,7 @@ import {
|
|
|
45
44
|
toRawMessageStorageItem,
|
|
46
45
|
} from "./mappers.js";
|
|
47
46
|
|
|
48
|
-
type DB =
|
|
47
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
49
48
|
|
|
50
49
|
function dedupeById<T>(rows: T[], getId: (row: T) => string): T[] {
|
|
51
50
|
const byId = new Map<string, T>();
|
|
@@ -56,7 +55,13 @@ function dedupeById<T>(rows: T[], getId: (row: T) => string): T[] {
|
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
export class DrizzleEnvelopeRepository implements IEnvelopeRepository {
|
|
59
|
-
|
|
58
|
+
private readonly db: DB;
|
|
59
|
+
private readonly batch: WriteBatch;
|
|
60
|
+
|
|
61
|
+
constructor(store: SqliteStore<Record<string, unknown>>) {
|
|
62
|
+
this.db = store.db;
|
|
63
|
+
this.batch = store.batch;
|
|
64
|
+
}
|
|
60
65
|
|
|
61
66
|
async getMessageData(messageId: string): Promise<MessageData> {
|
|
62
67
|
const [
|
|
@@ -183,10 +188,10 @@ export class DrizzleEnvelopeRepository implements IEnvelopeRepository {
|
|
|
183
188
|
set: { updatedAt: now },
|
|
184
189
|
});
|
|
185
190
|
if (dedupedParameterRows.length === 0) {
|
|
186
|
-
await
|
|
191
|
+
await this.batch([insertParts]);
|
|
187
192
|
return;
|
|
188
193
|
}
|
|
189
|
-
await
|
|
194
|
+
await this.batch([
|
|
190
195
|
insertParts,
|
|
191
196
|
this.db
|
|
192
197
|
.insert(bodyPartParameterTable)
|
|
@@ -211,10 +216,10 @@ export class DrizzleEnvelopeRepository implements IEnvelopeRepository {
|
|
|
211
216
|
.delete(messageReferenceTable)
|
|
212
217
|
.where(eq(messageReferenceTable.messageId, messageId));
|
|
213
218
|
if (references.length === 0) {
|
|
214
|
-
await
|
|
219
|
+
await this.batch([clear]);
|
|
215
220
|
return;
|
|
216
221
|
}
|
|
217
|
-
await
|
|
222
|
+
await this.batch([
|
|
218
223
|
clear,
|
|
219
224
|
this.db.insert(messageReferenceTable).values(
|
|
220
225
|
references.map((reference) => ({
|
package/src/repos/filter.ts
CHANGED
|
@@ -12,13 +12,13 @@ import {
|
|
|
12
12
|
FilterState,
|
|
13
13
|
} from "@remit/domain-enums";
|
|
14
14
|
import { and, asc, eq, gt, or } from "drizzle-orm";
|
|
15
|
-
import type {
|
|
15
|
+
import type { RepositoryDb } from "../db.js";
|
|
16
16
|
import { NotFoundError } from "../error.js";
|
|
17
17
|
import { randomId } from "../id.js";
|
|
18
18
|
import { decodeToken, resultList } from "../pagination.js";
|
|
19
19
|
import { filterTable } from "../schema.js";
|
|
20
20
|
|
|
21
|
-
type DB =
|
|
21
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
22
22
|
|
|
23
23
|
const RULE_ASSERTION_FIELDS = [
|
|
24
24
|
"matchOperator",
|
|
@@ -1,9 +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, type TestDb } from "../test-db.js";
|
|
4
|
-
import {
|
|
5
|
-
import { AccountConfigRepo } from "./i4-account-config.js";
|
|
6
|
-
import { AddressRepo } from "./i4-address.js";
|
|
5
|
+
import type { AccountConfigRepo } from "./i4-account-config.js";
|
|
7
6
|
|
|
8
7
|
describe("AccountConfigRepo", () => {
|
|
9
8
|
let db: TestDb;
|
|
@@ -12,7 +11,11 @@ describe("AccountConfigRepo", () => {
|
|
|
12
11
|
|
|
13
12
|
before(async () => {
|
|
14
13
|
({ db, close } = await createTestDb());
|
|
15
|
-
repo =
|
|
14
|
+
repo = createSqliteRepositories({
|
|
15
|
+
app: db,
|
|
16
|
+
mail: db,
|
|
17
|
+
search: db,
|
|
18
|
+
}).accountConfig;
|
|
16
19
|
});
|
|
17
20
|
|
|
18
21
|
after(async () => {
|
|
@@ -58,8 +61,9 @@ describe("AccountConfigRepo", () => {
|
|
|
58
61
|
const userId = randomId();
|
|
59
62
|
const cfg = await repo.create({ userId });
|
|
60
63
|
|
|
61
|
-
const
|
|
62
|
-
const
|
|
64
|
+
const repos = createSqliteRepositories({ app: db, mail: db, search: db });
|
|
65
|
+
const accountRepo = repos.account;
|
|
66
|
+
const addrRepo = repos.address;
|
|
63
67
|
|
|
64
68
|
await accountRepo.create({
|
|
65
69
|
accountConfigId: cfg.accountConfigId,
|
|
@@ -3,11 +3,12 @@ import type {
|
|
|
3
3
|
AccountConfigItem,
|
|
4
4
|
CreateAccountConfigInput,
|
|
5
5
|
IAccountConfigRepository,
|
|
6
|
+
IAddressRepository,
|
|
6
7
|
ResultList,
|
|
7
8
|
UpdateAccountConfigInput,
|
|
8
9
|
} from "@remit/data-ports";
|
|
9
10
|
import { and, asc, eq, gt, or } from "drizzle-orm";
|
|
10
|
-
import type {
|
|
11
|
+
import type { RepositoryDb } from "../db.js";
|
|
11
12
|
import { NotFoundError } from "../error.js";
|
|
12
13
|
import { randomId } from "../id.js";
|
|
13
14
|
import { decodeToken, resultList } from "../pagination.js";
|
|
@@ -15,12 +16,12 @@ import {
|
|
|
15
16
|
accountConfigTable,
|
|
16
17
|
accountTable,
|
|
17
18
|
} from "../schema/i4-account-config.js";
|
|
18
|
-
import { addressTable } from "../schema/i4-address.js";
|
|
19
19
|
import { rowToAccount } from "./i4-account.js";
|
|
20
|
-
import { rowToAddress } from "./i4-address.js";
|
|
21
20
|
import { inList } from "./in-list.js";
|
|
22
21
|
|
|
23
|
-
type DB =
|
|
22
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
23
|
+
|
|
24
|
+
type AddressReader = Pick<IAddressRepository, "listAllByAccountConfig">;
|
|
24
25
|
|
|
25
26
|
function rowToAccountConfig(
|
|
26
27
|
row: typeof accountConfigTable.$inferSelect,
|
|
@@ -40,7 +41,7 @@ function rowToAccountConfig(
|
|
|
40
41
|
export class AccountConfigRepo implements IAccountConfigRepository {
|
|
41
42
|
constructor(
|
|
42
43
|
private db: DB,
|
|
43
|
-
private
|
|
44
|
+
private addresses: AddressReader,
|
|
44
45
|
) {}
|
|
45
46
|
|
|
46
47
|
async create(input: CreateAccountConfigInput): Promise<AccountConfigItem> {
|
|
@@ -183,10 +184,7 @@ export class AccountConfigRepo implements IAccountConfigRepository {
|
|
|
183
184
|
.select()
|
|
184
185
|
.from(accountTable)
|
|
185
186
|
.where(eq(accountTable.accountConfigId, accountConfigId)),
|
|
186
|
-
this.
|
|
187
|
-
.select()
|
|
188
|
-
.from(addressTable)
|
|
189
|
-
.where(eq(addressTable.accountConfigId, accountConfigId)),
|
|
187
|
+
this.addresses.listAllByAccountConfig(accountConfigId),
|
|
190
188
|
]);
|
|
191
189
|
if (configs.length === 0) {
|
|
192
190
|
throw new NotFoundError(`AccountConfig not found: ${accountConfigId}`);
|
|
@@ -194,7 +192,7 @@ export class AccountConfigRepo implements IAccountConfigRepository {
|
|
|
194
192
|
return {
|
|
195
193
|
accountConfig: configs.map(rowToAccountConfig),
|
|
196
194
|
account: accounts.map(rowToAccount),
|
|
197
|
-
address: addresses
|
|
195
|
+
address: addresses,
|
|
198
196
|
};
|
|
199
197
|
}
|
|
200
198
|
|
|
@@ -6,13 +6,13 @@ import type {
|
|
|
6
6
|
UpdateAccountExportRequestInput,
|
|
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 { accountExportRequestTable } from "../schema/i4-account-export-request.js";
|
|
14
14
|
|
|
15
|
-
type DB =
|
|
15
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
16
16
|
|
|
17
17
|
function rowToItem(
|
|
18
18
|
row: typeof accountExportRequestTable.$inferSelect,
|
|
@@ -6,11 +6,11 @@ import type {
|
|
|
6
6
|
UpsertAccountSettingInput,
|
|
7
7
|
} from "@remit/data-ports";
|
|
8
8
|
import { and, eq } from "drizzle-orm";
|
|
9
|
-
import type {
|
|
9
|
+
import type { RepositoryDb } from "../db.js";
|
|
10
10
|
import { accountSettingTable } from "../schema/i4-account-setting.js";
|
|
11
11
|
import { inList } from "./in-list.js";
|
|
12
12
|
|
|
13
|
-
type DB =
|
|
13
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
14
14
|
|
|
15
15
|
// The PG store derives its own deterministic id from the composite key. It is
|
|
16
16
|
// stable within this backend but not byte-identical to the DDB base36 UUIDv5
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
2
|
import { after, before, describe, test } from "node:test";
|
|
3
|
-
import {
|
|
3
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
4
4
|
import { createTestDb, randomId, type TestDb } from "../test-db.js";
|
|
5
|
-
import { AccountRepo } from "./i4-account.js";
|
|
6
|
-
import { MailboxRepo } from "./i4-mailbox.js";
|
|
5
|
+
import type { AccountRepo } from "./i4-account.js";
|
|
7
6
|
import { mailboxCreated } from "./test-helpers.js";
|
|
8
7
|
|
|
9
8
|
function makeAccountInput(accountConfigId: string) {
|
|
@@ -27,7 +26,11 @@ describe("AccountRepo", () => {
|
|
|
27
26
|
|
|
28
27
|
before(async () => {
|
|
29
28
|
({ db, close } = await createTestDb());
|
|
30
|
-
repo =
|
|
29
|
+
repo = createSqliteRepositories({
|
|
30
|
+
app: db,
|
|
31
|
+
mail: db,
|
|
32
|
+
search: db,
|
|
33
|
+
}).account;
|
|
31
34
|
});
|
|
32
35
|
|
|
33
36
|
after(async () => {
|
|
@@ -161,10 +164,11 @@ describe("AccountRepo", () => {
|
|
|
161
164
|
const accountConfigId = randomId();
|
|
162
165
|
const account = await repo.create(makeAccountInput(accountConfigId));
|
|
163
166
|
|
|
164
|
-
const mailboxRepo =
|
|
165
|
-
db
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
const mailboxRepo = createSqliteRepositories({
|
|
168
|
+
app: db,
|
|
169
|
+
mail: db,
|
|
170
|
+
search: db,
|
|
171
|
+
}).mailbox;
|
|
168
172
|
mailboxCreated(
|
|
169
173
|
await mailboxRepo.create({
|
|
170
174
|
accountId: account.accountId,
|
package/src/repos/i4-account.ts
CHANGED
|
@@ -4,22 +4,21 @@ import type {
|
|
|
4
4
|
AccountSchedulerPage,
|
|
5
5
|
CreateAccountInput,
|
|
6
6
|
IAccountRepository,
|
|
7
|
+
IMailboxRepository,
|
|
7
8
|
RemovableAccountField,
|
|
8
9
|
ResultList,
|
|
9
10
|
UpdateAccountInput,
|
|
10
11
|
} from "@remit/data-ports";
|
|
11
12
|
import { AccountService, SyncPhase } from "@remit/domain-enums";
|
|
12
13
|
import { and, asc, eq, gt, like, or, sql } from "drizzle-orm";
|
|
13
|
-
import type {
|
|
14
|
+
import type { RepositoryDb } from "../db.js";
|
|
14
15
|
import { NotFoundError } from "../error.js";
|
|
15
16
|
import { randomId } from "../id.js";
|
|
16
17
|
import { decodeToken, encodeToken, resultList } from "../pagination.js";
|
|
17
18
|
import { accountTable } from "../schema/i4-account-config.js";
|
|
18
|
-
import { mailboxTable } from "../schema/i4-mailbox.js";
|
|
19
|
-
import { rowToMailbox } from "./i4-mailbox.js";
|
|
20
19
|
import { inList } from "./in-list.js";
|
|
21
20
|
|
|
22
|
-
type DB =
|
|
21
|
+
type DB = RepositoryDb<Record<string, unknown>>;
|
|
23
22
|
|
|
24
23
|
/**
|
|
25
24
|
* A stored failure is a sentence the account card shows, not a transcript.
|
|
@@ -74,7 +73,7 @@ export function rowToAccount(
|
|
|
74
73
|
export class AccountRepo implements IAccountRepository {
|
|
75
74
|
constructor(
|
|
76
75
|
private db: DB,
|
|
77
|
-
private
|
|
76
|
+
private mailboxes: Pick<IMailboxRepository, "listAllByAccount">,
|
|
78
77
|
) {}
|
|
79
78
|
|
|
80
79
|
async create(input: CreateAccountInput): Promise<AccountItem> {
|
|
@@ -269,17 +268,14 @@ export class AccountRepo implements IAccountRepository {
|
|
|
269
268
|
.select()
|
|
270
269
|
.from(accountTable)
|
|
271
270
|
.where(eq(accountTable.accountId, accountId)),
|
|
272
|
-
this.
|
|
273
|
-
.select()
|
|
274
|
-
.from(mailboxTable)
|
|
275
|
-
.where(eq(mailboxTable.accountId, accountId)),
|
|
271
|
+
this.mailboxes.listAllByAccount(accountId),
|
|
276
272
|
]);
|
|
277
273
|
if (accounts.length === 0) {
|
|
278
274
|
throw new NotFoundError(`Account not found: ${accountId}`);
|
|
279
275
|
}
|
|
280
276
|
return {
|
|
281
277
|
account: accounts.map(rowToAccount),
|
|
282
|
-
mailbox: mailboxes
|
|
278
|
+
mailbox: mailboxes,
|
|
283
279
|
};
|
|
284
280
|
}
|
|
285
281
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { after, before, beforeEach, describe, test } from "node:test";
|
|
3
3
|
import type Database from "better-sqlite3";
|
|
4
|
+
import { createSqliteRepositories } from "../sqlite-repositories.js";
|
|
4
5
|
import { createTestDb, type TestDb } from "../test-db.js";
|
|
5
6
|
import { AddressRepo } from "./i4-address.js";
|
|
6
|
-
import { MailboxSpecialUseRepo } from "./i4-mailbox-special-use.js";
|
|
7
|
+
import type { MailboxSpecialUseRepo } from "./i4-mailbox-special-use.js";
|
|
7
8
|
|
|
8
9
|
const CONFIG = "cfg-1";
|
|
9
10
|
|
|
@@ -124,7 +125,11 @@ describe("reconciling one message's addresses at the moment it moves", () => {
|
|
|
124
125
|
before(async () => {
|
|
125
126
|
({ db, sqlite, close } = await createTestDb());
|
|
126
127
|
repo = new AddressRepo(db as never);
|
|
127
|
-
specialUse =
|
|
128
|
+
specialUse = createSqliteRepositories({
|
|
129
|
+
app: db,
|
|
130
|
+
mail: db,
|
|
131
|
+
search: db,
|
|
132
|
+
}).mailboxSpecialUse;
|
|
128
133
|
});
|
|
129
134
|
|
|
130
135
|
/**
|
package/src/repos/i4-address.ts
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
type SQL,
|
|
23
23
|
sql,
|
|
24
24
|
} from "drizzle-orm";
|
|
25
|
-
import type {
|
|
25
|
+
import type { RepositoryDb } from "../db.js";
|
|
26
26
|
import { NotFoundError } from "../error.js";
|
|
27
27
|
import { envelopeAddressId } from "../id.js";
|
|
28
28
|
import { decodeToken, resultList } from "../pagination.js";
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
} from "./address-search-predicates.js";
|
|
45
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]:
|
|
@@ -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> {
|
|
@@ -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
|
};
|