@dv4resi/dvss-backend-module-calendar-im 0.0.9 → 0.0.11

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/dist/index.d.ts CHANGED
@@ -1750,10 +1750,10 @@ interface IEventSyncMetadata {
1750
1750
  createdBy?: 'DVSS' | 'PROVIDER';
1751
1751
  syncVersion?: number;
1752
1752
  lastSyncTimestamp?: string;
1753
- externalEventId?: string;
1753
+ organizerEventId?: string;
1754
1754
  syncType?: 'SINGLE' | 'RECURRING_SERIES' | 'CUSTOM_DATES';
1755
1755
  externalSeriesId?: string;
1756
- externalEventIds?: {
1756
+ organizerEventIds?: {
1757
1757
  date: string;
1758
1758
  eventId: string;
1759
1759
  }[];
@@ -1835,6 +1835,8 @@ interface IBookingEventInstance {
1835
1835
  startDateLabel: string;
1836
1836
  seriesId?: string;
1837
1837
  iCalUId?: string;
1838
+ uid?: string;
1839
+ instanceKind?: EVENT_INSTANCE_KIND;
1838
1840
  transactionId?: string;
1839
1841
  subject?: string;
1840
1842
  }
@@ -2423,6 +2425,7 @@ interface IMicrosoftGraphEvent {
2423
2425
  seriesMasterId?: string;
2424
2426
  transactionId?: string;
2425
2427
  iCalUId?: string;
2428
+ uid?: string;
2426
2429
  recurrence?: IMicrosoftGraphRecurrence;
2427
2430
  attendees?: IMicrosoftGraphAttendee[];
2428
2431
  organizer?: {
@@ -2565,6 +2568,7 @@ declare class MicrosoftCalendarBookingService extends BaseCalendarBookingManagem
2565
2568
  private deleteGraphEvent;
2566
2569
  private resolveSkipDatesIfAny;
2567
2570
  private findOccurrenceInstanceId;
2571
+ private listAllEvents;
2568
2572
  private pickMailbox;
2569
2573
  private organizerUserMailbox;
2570
2574
  private assertMailbox;
package/dist/index.js CHANGED
@@ -4023,18 +4023,25 @@ function scheduleToGraphRecurrence(schedule, window) {
4023
4023
  }
4024
4024
  __name(scheduleToGraphRecurrence, "scheduleToGraphRecurrence");
4025
4025
  function graphEventToRef(graph, options) {
4026
- return {
4026
+ const result = {
4027
4027
  id: graph.id,
4028
4028
  seriesId: graph.seriesMasterId,
4029
4029
  instanceKind: graph.type ? GRAPH_TYPE_TO_INSTANCE_KIND[graph.type] : void 0,
4030
4030
  syncMetadata: omitUndefined({
4031
4031
  ...options?.syncMetadata,
4032
- externalEventId: graph.id ?? options?.syncMetadata?.externalEventId,
4032
+ organizerEventId: graph.id ?? options?.syncMetadata?.organizerEventId,
4033
4033
  organizerUserEmail: options?.organizerUserEmail ?? options?.syncMetadata?.organizerUserEmail,
4034
4034
  resourceId: options?.resourceId ?? options?.syncMetadata?.resourceId,
4035
4035
  excludedDateResults: options?.excludedDateResults ?? options?.syncMetadata?.excludedDateResults
4036
4036
  })
4037
4037
  };
4038
+ if (graph.iCalUId !== void 0) {
4039
+ result.iCalUId = graph.iCalUId;
4040
+ result.providerPayload = {
4041
+ iCalUId: graph.iCalUId
4042
+ };
4043
+ }
4044
+ return result;
4038
4045
  }
4039
4046
  __name(graphEventToRef, "graphEventToRef");
4040
4047
  function graphEventsToInstances(response) {
@@ -4043,11 +4050,43 @@ function graphEventsToInstances(response) {
4043
4050
  startDateLabel: graph.start.dateTime.slice(0, 10),
4044
4051
  seriesId: graph.seriesMasterId,
4045
4052
  iCalUId: graph.iCalUId,
4053
+ uid: graph.uid,
4054
+ instanceKind: graph.type ? GRAPH_TYPE_TO_INSTANCE_KIND[graph.type] : void 0,
4046
4055
  transactionId: graph.transactionId,
4047
4056
  subject: graph.subject
4048
4057
  }));
4049
4058
  }
4050
4059
  __name(graphEventsToInstances, "graphEventsToInstances");
4060
+ function toGraphWindowDateTime(dateLabel, timeLabel, timeZone) {
4061
+ const local = `${dateLabel}T${timeLabel}`;
4062
+ const offset = timeZone ? timeZoneOffset(local, timeZone) : void 0;
4063
+ return offset ? `${local}${offset}` : local;
4064
+ }
4065
+ __name(toGraphWindowDateTime, "toGraphWindowDateTime");
4066
+ function timeZoneOffset(local, timeZone) {
4067
+ try {
4068
+ const zoneName = new Intl.DateTimeFormat("en-US", {
4069
+ timeZone,
4070
+ timeZoneName: "longOffset"
4071
+ }).formatToParts(/* @__PURE__ */ new Date(`${local}Z`)).find((part) => part.type === "timeZoneName")?.value;
4072
+ const match = zoneName ? /^GMT(?<offset>[+-]\d{2}:\d{2})?$/.exec(zoneName) : void 0;
4073
+ if (!match) {
4074
+ return void 0;
4075
+ }
4076
+ return match.groups?.offset ?? "+00:00";
4077
+ } catch {
4078
+ return void 0;
4079
+ }
4080
+ }
4081
+ __name(timeZoneOffset, "timeZoneOffset");
4082
+ function toGraphRelativeUrl(link) {
4083
+ if (!link) {
4084
+ return void 0;
4085
+ }
4086
+ const parsed = new URL(link);
4087
+ return `${parsed.pathname.replace(/^\/(?:v1\.0|beta)(?=\/)/, "")}${parsed.search}`;
4088
+ }
4089
+ __name(toGraphRelativeUrl, "toGraphRelativeUrl");
4051
4090
  function toGraphDateTime(dateLabel, timeLabel, isFullDay) {
4052
4091
  if (isFullDay || !timeLabel) {
4053
4092
  return `${dateLabel}T00:00:00`;
@@ -4077,6 +4116,10 @@ function _ts_metadata23(k, v) {
4077
4116
  }
4078
4117
  __name(_ts_metadata23, "_ts_metadata");
4079
4118
  var GRAPH_BATCH_MAX_SIZE = 20;
4119
+ var GRAPH_LIST_PAGE_SIZE = 100;
4120
+ var GRAPH_LIST_MAX_PAGES = 50;
4121
+ var WINDOW_START_TIME = "00:00:00";
4122
+ var WINDOW_END_TIME = "23:59:59";
4080
4123
  exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService extends BaseCalendarBookingManagement {
4081
4124
  static {
4082
4125
  __name(this, "MicrosoftCalendarBookingService");
@@ -4105,7 +4148,7 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4105
4148
  targetMailbox: mailboxEmail
4106
4149
  })
4107
4150
  }, integration, loggedInUserId);
4108
- const excludedDateResults = await this.resolveSkipDatesIfAny(graphEvent.id, request.event.schedule?.skipDates, mailboxEmail, this.organizerUserMailbox(organizerEmail), integration, loggedInUserId);
4151
+ const excludedDateResults = await this.resolveSkipDatesIfAny(graphEvent.id, request.event.schedule?.skipDates, mailboxEmail, this.organizerUserMailbox(organizerEmail), request.event.timeZone, integration, loggedInUserId);
4109
4152
  const ref = graphEventToRef(graphEvent, {
4110
4153
  organizerUserEmail: this.organizerUserMailbox(organizerEmail),
4111
4154
  resourceId: roomEmail,
@@ -4139,7 +4182,7 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4139
4182
  targetMailbox: mailboxEmail
4140
4183
  })
4141
4184
  }, integration, loggedInUserId);
4142
- const excludedDateResults = await this.resolveSkipDatesIfAny(graphEvent.id ?? request.eventId, request.event.schedule?.skipDates, mailboxEmail, this.organizerUserMailbox(request.organizerUserEmail), integration, loggedInUserId);
4185
+ const excludedDateResults = await this.resolveSkipDatesIfAny(graphEvent.id ?? request.eventId, request.event.schedule?.skipDates, mailboxEmail, this.organizerUserMailbox(request.organizerUserEmail), request.event.timeZone, integration, loggedInUserId);
4143
4186
  const ref = graphEventToRef(graphEvent, {
4144
4187
  organizerUserEmail: this.organizerUserMailbox(request.organizerUserEmail),
4145
4188
  resourceId: request.resourceEmail,
@@ -4261,18 +4304,15 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4261
4304
  try {
4262
4305
  const mailboxEmail = this.pickMailbox(request.organizerUserEmail, request.resourceEmail);
4263
4306
  this.assertMailbox(mailboxEmail);
4264
- const hasWindow = Boolean(request.startDateLabel && request.endDateLabel);
4265
- const url = hasWindow ? MICROSOFT_GRAPH_PATHS.userCalendarView(mailboxEmail, `${request.startDateLabel}T00:00:00`, `${request.endDateLabel}T23:59:59`) : MICROSOFT_GRAPH_PATHS.userCalendarEvents(mailboxEmail);
4307
+ const url = request.startDateLabel && request.endDateLabel ? MICROSOFT_GRAPH_PATHS.userCalendarView(mailboxEmail, toGraphWindowDateTime(request.startDateLabel, WINDOW_START_TIME, request.timeZone), toGraphWindowDateTime(request.endDateLabel, WINDOW_END_TIME, request.timeZone)) : MICROSOFT_GRAPH_PATHS.userCalendarEvents(mailboxEmail);
4266
4308
  this.logger.info(loggedInUserId, this.getEvents.name, this.fileName, "List Microsoft Graph calendar events", {
4267
4309
  mailboxEmail,
4268
4310
  path: url,
4269
- hasTimeWindow: hasWindow
4311
+ hasTimeWindow: Boolean(request.startDateLabel && request.endDateLabel)
4312
+ });
4313
+ const events = graphEventsToInstances({
4314
+ value: await this.listAllEvents(url, request.timeZone, integration, loggedInUserId)
4270
4315
  });
4271
- const response = await this.microsoftApiClient.apiCall({
4272
- apiMethod: HTTP_METHOD.GET,
4273
- url
4274
- }, integration, loggedInUserId);
4275
- const events = graphEventsToInstances(response);
4276
4316
  this.logger.info(loggedInUserId, this.getEvents.name, this.fileName, "Listed Microsoft Graph calendar events", {
4277
4317
  mailboxEmail,
4278
4318
  count: events.length
@@ -4295,17 +4335,15 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4295
4335
  const mailboxEmail = this.pickMailbox(request.organizerUserEmail, request.resourceEmail);
4296
4336
  this.assertMailbox(mailboxEmail);
4297
4337
  this.assertEventId(request.seriesEventId);
4298
- const url = MICROSOFT_GRAPH_PATHS.userCalendarEventInstances(mailboxEmail, request.seriesEventId, `${request.startDateLabel}T00:00:00`, `${request.endDateLabel}T23:59:59`);
4338
+ const url = MICROSOFT_GRAPH_PATHS.userCalendarEventInstances(mailboxEmail, request.seriesEventId, toGraphWindowDateTime(request.startDateLabel, WINDOW_START_TIME, request.timeZone), toGraphWindowDateTime(request.endDateLabel, WINDOW_END_TIME, request.timeZone));
4299
4339
  this.logger.info(loggedInUserId, this.getEventInstances.name, this.fileName, "List Microsoft Graph series instances", {
4300
4340
  mailboxEmail,
4301
4341
  seriesEventId: request.seriesEventId,
4302
4342
  path: url
4303
4343
  });
4304
- const response = await this.microsoftApiClient.apiCall({
4305
- apiMethod: HTTP_METHOD.GET,
4306
- url
4307
- }, integration, loggedInUserId);
4308
- const events = graphEventsToInstances(response);
4344
+ const events = graphEventsToInstances({
4345
+ value: await this.listAllEvents(url, request.timeZone, integration, loggedInUserId)
4346
+ });
4309
4347
  this.logger.info(loggedInUserId, this.getEventInstances.name, this.fileName, "Listed Microsoft Graph series instances", {
4310
4348
  mailboxEmail,
4311
4349
  seriesEventId: request.seriesEventId,
@@ -4410,14 +4448,14 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4410
4448
  * created/updated and cancels it via the existing DELETE path. Best-effort per date — a
4411
4449
  * date that can't be resolved is reported `excluded: false`, not thrown.
4412
4450
  */
4413
- async resolveSkipDatesIfAny(seriesId, dates, mailboxEmail, organizerUserEmail, integration, loggedInUserId) {
4451
+ async resolveSkipDatesIfAny(seriesId, dates, mailboxEmail, organizerUserEmail, timeZone, integration, loggedInUserId) {
4414
4452
  if (!dates?.length) {
4415
4453
  return void 0;
4416
4454
  }
4417
4455
  const results = [];
4418
4456
  for (const date of dates) {
4419
4457
  try {
4420
- const instanceId = await this.findOccurrenceInstanceId(seriesId, date, mailboxEmail, integration, loggedInUserId);
4458
+ const instanceId = await this.findOccurrenceInstanceId(seriesId, date, mailboxEmail, timeZone, integration, loggedInUserId);
4421
4459
  if (!instanceId) {
4422
4460
  this.logger.warn(loggedInUserId, this.resolveSkipDatesIfAny.name, this.fileName, "Could not resolve skip-date occurrence", {
4423
4461
  seriesId,
@@ -4455,12 +4493,54 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4455
4493
  }
4456
4494
  return results;
4457
4495
  }
4458
- async findOccurrenceInstanceId(seriesId, date, mailboxEmail, integration, loggedInUserId) {
4459
- const response = await this.microsoftApiClient.apiCall({
4460
- apiMethod: HTTP_METHOD.GET,
4461
- url: MICROSOFT_GRAPH_PATHS.userCalendarView(mailboxEmail, `${date}T00:00:00`, `${date}T23:59:59`)
4462
- }, integration, loggedInUserId);
4463
- return (response.value ?? []).find((occurrence) => occurrence.seriesMasterId === seriesId)?.id;
4496
+ /**
4497
+ * The series' own instance on `date`. Lists the series' instances rather than the mailbox's
4498
+ * whole calendarView, so a busy day can't push the occurrence off the returned page.
4499
+ */
4500
+ async findOccurrenceInstanceId(seriesId, date, mailboxEmail, timeZone, integration, loggedInUserId) {
4501
+ if (!seriesId) {
4502
+ return void 0;
4503
+ }
4504
+ const instances = await this.listAllEvents(MICROSOFT_GRAPH_PATHS.userCalendarEventInstances(mailboxEmail, seriesId, toGraphWindowDateTime(date, WINDOW_START_TIME, timeZone), toGraphWindowDateTime(date, WINDOW_END_TIME, timeZone)), timeZone, integration, loggedInUserId);
4505
+ return instances.find((occurrence) => occurrence.seriesMasterId === seriesId)?.id;
4506
+ }
4507
+ /**
4508
+ * GETs a Graph event listing and follows `@odata.nextLink` until the last page.
4509
+ *
4510
+ * Graph pages calendarView/instances listings (10 events by default), so reading only the first
4511
+ * page silently drops every event past it — on a busy organizer mailbox that is the very series
4512
+ * being looked up. `Prefer: outlook.timezone` makes Graph return `start`/`end` in the property's
4513
+ * zone, so the date labels cut from them are local dates rather than UTC ones.
4514
+ * @param url - The first page's path (calendarView / instances / events)
4515
+ * @param timeZone - IANA zone to return event times in; UTC when absent
4516
+ */
4517
+ async listAllEvents(url, timeZone, integration, loggedInUserId) {
4518
+ const headers = timeZone ? {
4519
+ Prefer: `outlook.timezone="${timeZone}"`
4520
+ } : void 0;
4521
+ const events = [];
4522
+ let nextUrl = `${url}${url.includes("?") ? "&" : "?"}$top=${GRAPH_LIST_PAGE_SIZE}`;
4523
+ let pageCount = 0;
4524
+ while (nextUrl && pageCount < GRAPH_LIST_MAX_PAGES) {
4525
+ const response = await this.microsoftApiClient.apiCall({
4526
+ apiMethod: HTTP_METHOD.GET,
4527
+ url: nextUrl,
4528
+ ...headers ? {
4529
+ headers
4530
+ } : {}
4531
+ }, integration, loggedInUserId);
4532
+ events.push(...response.value ?? []);
4533
+ nextUrl = toGraphRelativeUrl(response["@odata.nextLink"]);
4534
+ pageCount += 1;
4535
+ }
4536
+ if (nextUrl) {
4537
+ this.logger.warn(loggedInUserId, this.listAllEvents.name, this.fileName, "Stopped following Graph pages at the page limit; listing truncated", {
4538
+ url,
4539
+ pageCount,
4540
+ eventCount: events.length
4541
+ });
4542
+ }
4543
+ return events;
4464
4544
  }
4465
4545
  /** Mailbox the Graph call targets: the organizing user if known, else the room. */
4466
4546
  pickMailbox(organizerEmail, roomEmail) {