@remit/data-ports 0.0.74 → 0.0.76

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/data-ports",
3
- "version": "0.0.74",
3
+ "version": "0.0.76",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -78,5 +78,27 @@ export function accountRepositoryConformance(
78
78
  AccountService.Calendar,
79
79
  ]);
80
80
  });
81
+
82
+ test("the mail sources of a config leave out an account with mail off", async () => {
83
+ const accountConfigId = harness.makeId();
84
+ const mailOnly = await repo.create(accountInput(accountConfigId));
85
+ const both = await repo.create({
86
+ ...accountInput(accountConfigId),
87
+ syncedServices: [AccountService.Mail, AccountService.Calendar],
88
+ });
89
+ await repo.create({
90
+ ...accountInput(accountConfigId),
91
+ syncedServices: [AccountService.Calendar],
92
+ });
93
+ await repo.create(accountInput(harness.makeId()));
94
+
95
+ const sources =
96
+ await repo.listMailSourcesByAccountConfig(accountConfigId);
97
+
98
+ assert.deepEqual(
99
+ sources.map((account) => account.accountId).sort(),
100
+ [mailOnly.accountId, both.accountId].sort(),
101
+ );
102
+ });
81
103
  });
82
104
  }
@@ -231,6 +231,98 @@ export function calendarCollectionRepositoryConformance(
231
231
  );
232
232
  });
233
233
 
234
+ test("a collection starts with no subscription and empty fetch bookkeeping", async () => {
235
+ const calendar = await repo.create({
236
+ accountConfigId: harness.makeId(),
237
+ urlSegment: "plain",
238
+ displayName: "Plain",
239
+ });
240
+
241
+ assert.equal(calendar.subscriptionUrl, "");
242
+ assert.equal(calendar.subscriptionEnabled, false);
243
+ assert.equal(calendar.subscriptionCheckedAt, 0);
244
+ assert.equal(calendar.subscriptionFetchedAt, 0);
245
+ assert.equal(calendar.subscriptionError, "");
246
+ });
247
+
248
+ test("listEnabledSubscriptions returns enabled subscriptions across account configs and nothing else", async () => {
249
+ const first = harness.makeId();
250
+ const second = harness.makeId();
251
+ const subscribed = await repo.create({
252
+ accountConfigId: first,
253
+ urlSegment: "feed",
254
+ displayName: "Feed",
255
+ source: CalendarSource.Subscribed,
256
+ subscriptionUrl: "https://calendar.example/feed.ics",
257
+ subscriptionEnabled: true,
258
+ });
259
+ const elsewhere = await repo.create({
260
+ accountConfigId: second,
261
+ urlSegment: "feed",
262
+ displayName: "Feed",
263
+ source: CalendarSource.Subscribed,
264
+ subscriptionUrl: "https://calendar.example/other.ics",
265
+ subscriptionEnabled: true,
266
+ });
267
+ const paused = await repo.create({
268
+ accountConfigId: first,
269
+ urlSegment: "paused",
270
+ displayName: "Paused",
271
+ source: CalendarSource.Subscribed,
272
+ subscriptionUrl: "https://calendar.example/paused.ics",
273
+ subscriptionEnabled: true,
274
+ });
275
+ await repo.update(first, paused.calendarId, {
276
+ subscriptionEnabled: false,
277
+ });
278
+ await repo.create({
279
+ accountConfigId: first,
280
+ urlSegment: "own",
281
+ displayName: "Own",
282
+ });
283
+
284
+ const ids = (await repo.listEnabledSubscriptions())
285
+ .filter(
286
+ (calendar) =>
287
+ calendar.accountConfigId === first ||
288
+ calendar.accountConfigId === second,
289
+ )
290
+ .map((calendar) => calendar.calendarId)
291
+ .sort();
292
+
293
+ assert.deepEqual(
294
+ ids,
295
+ [subscribed.calendarId, elsewhere.calendarId].sort(),
296
+ );
297
+ });
298
+
299
+ test("update records a fetch outcome on the collection", async () => {
300
+ const accountConfigId = harness.makeId();
301
+ const calendar = await repo.create({
302
+ accountConfigId,
303
+ urlSegment: "fetched",
304
+ displayName: "Fetched",
305
+ source: CalendarSource.Subscribed,
306
+ subscriptionUrl: "https://calendar.example/feed.ics",
307
+ subscriptionEnabled: true,
308
+ });
309
+
310
+ const updated = await repo.update(accountConfigId, calendar.calendarId, {
311
+ subscriptionCheckedAt: 2_000,
312
+ subscriptionFetchedAt: 1_000,
313
+ subscriptionError: "the feed answered 404",
314
+ });
315
+
316
+ assert.equal(updated.subscriptionCheckedAt, 2_000);
317
+ assert.equal(updated.subscriptionFetchedAt, 1_000);
318
+ assert.equal(updated.subscriptionError, "the feed answered 404");
319
+ assert.equal(
320
+ updated.subscriptionUrl,
321
+ "https://calendar.example/feed.ics",
322
+ );
323
+ assert.equal(updated.subscriptionEnabled, true);
324
+ });
325
+
234
326
  test("delete removes the row", async () => {
235
327
  const accountConfigId = harness.makeId();
236
328
  const calendar = await repo.create({
package/src/errors.ts CHANGED
@@ -212,3 +212,15 @@ export class InternalServerError extends UnhandledError {
212
212
  */
213
213
  export const isNotFoundError = (error: unknown): boolean =>
214
214
  (error as { name?: string })?.name === "NotFoundError";
215
+
216
+ export class MailSyncOffError extends BadRequestError {
217
+ name = "MailSyncOffError";
218
+
219
+ constructor(message: string, accountId: string) {
220
+ super(message);
221
+ this.publicApiError = {
222
+ code: "mail_sync_off",
223
+ details: { accountId },
224
+ };
225
+ }
226
+ }
@@ -24,6 +24,9 @@ export interface IAccountRepository {
24
24
  options?: { limit?: number; continuationToken?: string },
25
25
  ): Promise<ResultList<AccountItem>>;
26
26
  listAllByAccountConfig(accountConfigId: string): Promise<AccountItem[]>;
27
+ listMailSourcesByAccountConfig(
28
+ accountConfigId: string,
29
+ ): Promise<AccountItem[]>;
27
30
  describe(accountId: string): Promise<AccountDescription>;
28
31
  listAll(): Promise<AccountItem[]>;
29
32
  incrementMailboxSynced(accountId: string): Promise<AccountItem>;
@@ -37,6 +37,12 @@ export interface ICalendarCollectionRepository {
37
37
  listByAccountConfig(
38
38
  accountConfigId: string,
39
39
  ): Promise<CalendarCollectionItem[]>;
40
+ /**
41
+ * Every `Subscribed` collection whose refresh is on, across all account
42
+ * configs. The scheduled refresh walks this; a disabled subscription is
43
+ * absent, which is what stops its refresh.
44
+ */
45
+ listEnabledSubscriptions(): Promise<CalendarCollectionItem[]>;
40
46
  findByUrlSegment(
41
47
  accountConfigId: string,
42
48
  urlSegment: string,
package/src/types.ts CHANGED
@@ -701,7 +701,8 @@ export type CalendarEventIndexItem = z.infer<typeof CalendarEventIndexSchema>;
701
701
 
702
702
  // `calendarId` is derived from accountConfigId + urlSegment, and syncSequence
703
703
  // starts at zero and only ever moves through the collection's own bump — so
704
- // neither is ever supplied here.
704
+ // neither is ever supplied here. The fetch bookkeeping starts empty and only
705
+ // moves through an update after a fetch.
705
706
  export type CreateCalendarCollectionInput = Omit<
706
707
  CalendarCollectionItem,
707
708
  | "calendarId"
@@ -712,15 +713,31 @@ export type CreateCalendarCollectionInput = Omit<
712
713
  | "componentSet"
713
714
  | "source"
714
715
  | "timezone"
716
+ | "subscriptionUrl"
717
+ | "subscriptionEnabled"
718
+ | "subscriptionCheckedAt"
719
+ | "subscriptionFetchedAt"
720
+ | "subscriptionError"
715
721
  > & {
716
722
  color?: CalendarCollectionItem["color"];
717
723
  componentSet?: CalendarCollectionItem["componentSet"];
718
724
  source?: CalendarCollectionItem["source"];
719
725
  timezone?: CalendarCollectionItem["timezone"];
726
+ subscriptionUrl?: CalendarCollectionItem["subscriptionUrl"];
727
+ subscriptionEnabled?: CalendarCollectionItem["subscriptionEnabled"];
720
728
  };
721
729
 
722
730
  export type UpdateCalendarCollectionInput = Partial<
723
- Pick<CalendarCollectionItem, "displayName" | "color" | "timezone">
731
+ Pick<
732
+ CalendarCollectionItem,
733
+ | "displayName"
734
+ | "color"
735
+ | "timezone"
736
+ | "subscriptionEnabled"
737
+ | "subscriptionCheckedAt"
738
+ | "subscriptionFetchedAt"
739
+ | "subscriptionError"
740
+ >
724
741
  >;
725
742
 
726
743
  export type CalendarFeedTokenItem = z.infer<typeof CalendarFeedTokenSchema>;