@dv4resi/dvss-backend-module-calendar-im 0.0.10 → 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
@@ -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
@@ -4050,11 +4050,43 @@ function graphEventsToInstances(response) {
4050
4050
  startDateLabel: graph.start.dateTime.slice(0, 10),
4051
4051
  seriesId: graph.seriesMasterId,
4052
4052
  iCalUId: graph.iCalUId,
4053
+ uid: graph.uid,
4054
+ instanceKind: graph.type ? GRAPH_TYPE_TO_INSTANCE_KIND[graph.type] : void 0,
4053
4055
  transactionId: graph.transactionId,
4054
4056
  subject: graph.subject
4055
4057
  }));
4056
4058
  }
4057
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");
4058
4090
  function toGraphDateTime(dateLabel, timeLabel, isFullDay) {
4059
4091
  if (isFullDay || !timeLabel) {
4060
4092
  return `${dateLabel}T00:00:00`;
@@ -4084,6 +4116,10 @@ function _ts_metadata23(k, v) {
4084
4116
  }
4085
4117
  __name(_ts_metadata23, "_ts_metadata");
4086
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";
4087
4123
  exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService extends BaseCalendarBookingManagement {
4088
4124
  static {
4089
4125
  __name(this, "MicrosoftCalendarBookingService");
@@ -4112,7 +4148,7 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4112
4148
  targetMailbox: mailboxEmail
4113
4149
  })
4114
4150
  }, integration, loggedInUserId);
4115
- 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);
4116
4152
  const ref = graphEventToRef(graphEvent, {
4117
4153
  organizerUserEmail: this.organizerUserMailbox(organizerEmail),
4118
4154
  resourceId: roomEmail,
@@ -4146,7 +4182,7 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4146
4182
  targetMailbox: mailboxEmail
4147
4183
  })
4148
4184
  }, integration, loggedInUserId);
4149
- 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);
4150
4186
  const ref = graphEventToRef(graphEvent, {
4151
4187
  organizerUserEmail: this.organizerUserMailbox(request.organizerUserEmail),
4152
4188
  resourceId: request.resourceEmail,
@@ -4268,18 +4304,15 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4268
4304
  try {
4269
4305
  const mailboxEmail = this.pickMailbox(request.organizerUserEmail, request.resourceEmail);
4270
4306
  this.assertMailbox(mailboxEmail);
4271
- const hasWindow = Boolean(request.startDateLabel && request.endDateLabel);
4272
- 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);
4273
4308
  this.logger.info(loggedInUserId, this.getEvents.name, this.fileName, "List Microsoft Graph calendar events", {
4274
4309
  mailboxEmail,
4275
4310
  path: url,
4276
- hasTimeWindow: hasWindow
4311
+ hasTimeWindow: Boolean(request.startDateLabel && request.endDateLabel)
4312
+ });
4313
+ const events = graphEventsToInstances({
4314
+ value: await this.listAllEvents(url, request.timeZone, integration, loggedInUserId)
4277
4315
  });
4278
- const response = await this.microsoftApiClient.apiCall({
4279
- apiMethod: HTTP_METHOD.GET,
4280
- url
4281
- }, integration, loggedInUserId);
4282
- const events = graphEventsToInstances(response);
4283
4316
  this.logger.info(loggedInUserId, this.getEvents.name, this.fileName, "Listed Microsoft Graph calendar events", {
4284
4317
  mailboxEmail,
4285
4318
  count: events.length
@@ -4302,17 +4335,15 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4302
4335
  const mailboxEmail = this.pickMailbox(request.organizerUserEmail, request.resourceEmail);
4303
4336
  this.assertMailbox(mailboxEmail);
4304
4337
  this.assertEventId(request.seriesEventId);
4305
- 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));
4306
4339
  this.logger.info(loggedInUserId, this.getEventInstances.name, this.fileName, "List Microsoft Graph series instances", {
4307
4340
  mailboxEmail,
4308
4341
  seriesEventId: request.seriesEventId,
4309
4342
  path: url
4310
4343
  });
4311
- const response = await this.microsoftApiClient.apiCall({
4312
- apiMethod: HTTP_METHOD.GET,
4313
- url
4314
- }, integration, loggedInUserId);
4315
- const events = graphEventsToInstances(response);
4344
+ const events = graphEventsToInstances({
4345
+ value: await this.listAllEvents(url, request.timeZone, integration, loggedInUserId)
4346
+ });
4316
4347
  this.logger.info(loggedInUserId, this.getEventInstances.name, this.fileName, "Listed Microsoft Graph series instances", {
4317
4348
  mailboxEmail,
4318
4349
  seriesEventId: request.seriesEventId,
@@ -4417,14 +4448,14 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4417
4448
  * created/updated and cancels it via the existing DELETE path. Best-effort per date — a
4418
4449
  * date that can't be resolved is reported `excluded: false`, not thrown.
4419
4450
  */
4420
- async resolveSkipDatesIfAny(seriesId, dates, mailboxEmail, organizerUserEmail, integration, loggedInUserId) {
4451
+ async resolveSkipDatesIfAny(seriesId, dates, mailboxEmail, organizerUserEmail, timeZone, integration, loggedInUserId) {
4421
4452
  if (!dates?.length) {
4422
4453
  return void 0;
4423
4454
  }
4424
4455
  const results = [];
4425
4456
  for (const date of dates) {
4426
4457
  try {
4427
- const instanceId = await this.findOccurrenceInstanceId(seriesId, date, mailboxEmail, integration, loggedInUserId);
4458
+ const instanceId = await this.findOccurrenceInstanceId(seriesId, date, mailboxEmail, timeZone, integration, loggedInUserId);
4428
4459
  if (!instanceId) {
4429
4460
  this.logger.warn(loggedInUserId, this.resolveSkipDatesIfAny.name, this.fileName, "Could not resolve skip-date occurrence", {
4430
4461
  seriesId,
@@ -4462,12 +4493,54 @@ exports.MicrosoftCalendarBookingService = class MicrosoftCalendarBookingService
4462
4493
  }
4463
4494
  return results;
4464
4495
  }
4465
- async findOccurrenceInstanceId(seriesId, date, mailboxEmail, integration, loggedInUserId) {
4466
- const response = await this.microsoftApiClient.apiCall({
4467
- apiMethod: HTTP_METHOD.GET,
4468
- url: MICROSOFT_GRAPH_PATHS.userCalendarView(mailboxEmail, `${date}T00:00:00`, `${date}T23:59:59`)
4469
- }, integration, loggedInUserId);
4470
- 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;
4471
4544
  }
4472
4545
  /** Mailbox the Graph call targets: the organizing user if known, else the room. */
4473
4546
  pickMailbox(organizerEmail, roomEmail) {