@agent-native/scheduling 0.2.0 → 0.2.1-nightly-20260926144920
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/actions/cancel-booking.d.ts +1 -0
- package/dist/actions/cancel-booking.js +29 -10
- package/dist/actions/cancel-booking.js.map +1 -1
- package/dist/actions/reschedule-booking.d.ts +1 -0
- package/dist/actions/reschedule-booking.js +31 -12
- package/dist/actions/reschedule-booking.js.map +1 -1
- package/dist/server/booking-service.d.ts +9 -0
- package/dist/server/booking-service.d.ts.map +1 -1
- package/dist/server/booking-service.js +68 -11
- package/dist/server/booking-service.js.map +1 -1
- package/dist/server/providers/teams.d.ts.map +1 -1
- package/dist/server/providers/teams.js +3 -2
- package/dist/server/providers/teams.js.map +1 -1
- package/dist/server/providers/zoom.d.ts.map +1 -1
- package/dist/server/providers/zoom.js +5 -2
- package/dist/server/providers/zoom.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/booking-authz.spec.ts +28 -0
- package/src/actions/cancel-booking.ts +33 -10
- package/src/actions/reschedule-booking.ts +35 -12
- package/src/server/booking-service.spec.ts +213 -4
- package/src/server/booking-service.ts +113 -10
- package/src/server/providers/teams.test.ts +7 -0
- package/src/server/providers/teams.ts +3 -1
- package/src/server/providers/zoom.test.ts +72 -0
- package/src/server/providers/zoom.ts +5 -2
|
@@ -2,6 +2,7 @@ declare const _default: import("@agent-native/core").ActionDefinition<{
|
|
|
2
2
|
uid: string;
|
|
3
3
|
reason?: string | undefined;
|
|
4
4
|
cancelledBy?: "attendee" | "host" | undefined;
|
|
5
|
+
zoomMeetingResolved?: boolean | undefined;
|
|
5
6
|
token?: string | undefined;
|
|
6
7
|
}, {
|
|
7
8
|
booking: import("../index.js").Booking;
|
|
@@ -1,19 +1,37 @@
|
|
|
1
|
-
import { defineAction } from "@agent-native/core/action";
|
|
1
|
+
import { defineAction, fail } from "@agent-native/core/action";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { cancelBooking } from "../server/booking-service.js";
|
|
3
|
+
import { BookingLifecycleError, cancelBooking, } from "../server/booking-service.js";
|
|
4
4
|
import { getBookingByUid } from "../server/bookings-repo.js";
|
|
5
5
|
import { currentUserEmailOrNull } from "./_helpers.js";
|
|
6
6
|
export default defineAction({
|
|
7
|
-
description: "Cancel a booking",
|
|
7
|
+
description: "Cancel a booking and its linked video meetings. If a Zoom booking has no recorded meeting, the host must review and resolve it first.",
|
|
8
8
|
schema: z.object({
|
|
9
|
-
uid: z.string(),
|
|
10
|
-
reason: z.string().optional(),
|
|
11
|
-
cancelledBy: z
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
uid: z.string().describe("Booking uid"),
|
|
10
|
+
reason: z.string().optional().describe("Reason for canceling the booking"),
|
|
11
|
+
cancelledBy: z
|
|
12
|
+
.enum(["attendee", "host"])
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Who canceled the booking: "attendee" or "host"'),
|
|
15
|
+
zoomMeetingResolved: z
|
|
16
|
+
.boolean()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe("Host confirms the Zoom meeting was checked and resolved"),
|
|
19
|
+
token: z.string().optional().describe("Booking cancellation token"),
|
|
16
20
|
}),
|
|
21
|
+
run: async (args) => {
|
|
22
|
+
try {
|
|
23
|
+
return { booking: await cancelBookingAfterAccessCheck(args) };
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error instanceof BookingLifecycleError) {
|
|
27
|
+
fail(error.message, {
|
|
28
|
+
statusCode: error.statusCode,
|
|
29
|
+
errorCode: error.errorCode,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
17
35
|
});
|
|
18
36
|
async function cancelBookingAfterAccessCheck(args) {
|
|
19
37
|
const booking = await getBookingByUid(args.uid);
|
|
@@ -29,6 +47,7 @@ async function cancelBookingAfterAccessCheck(args) {
|
|
|
29
47
|
uid: args.uid,
|
|
30
48
|
reason: args.reason,
|
|
31
49
|
cancelledBy: args.cancelledBy,
|
|
50
|
+
zoomMeetingResolved: isHost && args.zoomMeetingResolved,
|
|
32
51
|
});
|
|
33
52
|
}
|
|
34
53
|
//# sourceMappingURL=cancel-booking.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancel-booking.js","sourceRoot":"","sources":["../../src/actions/cancel-booking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"cancel-booking.js","sourceRoot":"","sources":["../../src/actions/cancel-booking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,qBAAqB,EACrB,aAAa,GACd,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEvD,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,uIAAuI;IACzI,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;QACvC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC1E,WAAW,EAAE,CAAC;aACX,IAAI,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;aAC1B,QAAQ,EAAE;aACV,QAAQ,CAAC,gDAAgD,CAAC;QAC7D,mBAAmB,EAAE,CAAC;aACnB,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,yDAAyD,CAAC;QACtE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;KACpE,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,IAAI,CAAC;YACH,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC,IAAI,CAAC,EAAE,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,qBAAqB,EAAE,CAAC;gBAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;oBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;iBAC3B,CAAC,CAAC;YACL,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAEH,KAAK,UAAU,6BAA6B,CAAC,IAM5C;IACC,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,sBAAsB,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,IAAI,SAAS,KAAK,OAAO,CAAC,SAAS,CAAC;IAC9D,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,OAAO,CAAC,WAAW,CAAC;IACpE,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,aAAa,CAAC;QACnB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,mBAAmB,EAAE,MAAM,IAAI,IAAI,CAAC,mBAAmB;KACxD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -4,6 +4,7 @@ declare const _default: import("@agent-native/core").ActionDefinition<{
|
|
|
4
4
|
newEndTime: string;
|
|
5
5
|
reason?: string | undefined;
|
|
6
6
|
rescheduledBy?: "attendee" | "host" | undefined;
|
|
7
|
+
zoomMeetingResolved?: boolean | undefined;
|
|
7
8
|
token?: string | undefined;
|
|
8
9
|
}, {
|
|
9
10
|
booking: import("../index.js").Booking;
|
|
@@ -1,21 +1,39 @@
|
|
|
1
|
-
import { defineAction } from "@agent-native/core/action";
|
|
1
|
+
import { defineAction, fail } from "@agent-native/core/action";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { rescheduleBooking } from "../server/booking-service.js";
|
|
3
|
+
import { BookingLifecycleError, rescheduleBooking, } from "../server/booking-service.js";
|
|
4
4
|
import { getBookingByUid } from "../server/bookings-repo.js";
|
|
5
5
|
import { currentUserEmailOrNull } from "./_helpers.js";
|
|
6
6
|
export default defineAction({
|
|
7
|
-
description: "Reschedule a booking
|
|
7
|
+
description: "Reschedule a booking, deleting its existing video meeting after the replacement is ready. If a Zoom booking has no recorded meeting, the host must review and resolve it first.",
|
|
8
8
|
schema: z.object({
|
|
9
|
-
uid: z.string(),
|
|
10
|
-
newStartTime: z.string(),
|
|
11
|
-
newEndTime: z.string(),
|
|
12
|
-
reason: z.string().optional(),
|
|
13
|
-
rescheduledBy: z
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
uid: z.string().describe("Booking uid"),
|
|
10
|
+
newStartTime: z.string().describe("New start time in ISO 8601 format"),
|
|
11
|
+
newEndTime: z.string().describe("New end time in ISO 8601 format"),
|
|
12
|
+
reason: z.string().optional().describe("Reason for rescheduling"),
|
|
13
|
+
rescheduledBy: z
|
|
14
|
+
.enum(["attendee", "host"])
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Who requested the reschedule: "attendee" or "host"'),
|
|
17
|
+
zoomMeetingResolved: z
|
|
18
|
+
.boolean()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe("Host confirms the Zoom meeting was checked and resolved"),
|
|
21
|
+
token: z.string().optional().describe("Booking reschedule token"),
|
|
18
22
|
}),
|
|
23
|
+
run: async (args) => {
|
|
24
|
+
try {
|
|
25
|
+
return { booking: await rescheduleBookingAfterAccessCheck(args) };
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error instanceof BookingLifecycleError) {
|
|
29
|
+
fail(error.message, {
|
|
30
|
+
statusCode: error.statusCode,
|
|
31
|
+
errorCode: error.errorCode,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
19
37
|
});
|
|
20
38
|
async function rescheduleBookingAfterAccessCheck(args) {
|
|
21
39
|
const booking = await getBookingByUid(args.uid);
|
|
@@ -33,6 +51,7 @@ async function rescheduleBookingAfterAccessCheck(args) {
|
|
|
33
51
|
newEndTime: args.newEndTime,
|
|
34
52
|
reason: args.reason,
|
|
35
53
|
rescheduledBy: args.rescheduledBy,
|
|
54
|
+
zoomMeetingResolved: isHost && args.zoomMeetingResolved,
|
|
36
55
|
});
|
|
37
56
|
}
|
|
38
57
|
//# sourceMappingURL=reschedule-booking.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reschedule-booking.js","sourceRoot":"","sources":["../../src/actions/reschedule-booking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"reschedule-booking.js","sourceRoot":"","sources":["../../src/actions/reschedule-booking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEvD,eAAe,YAAY,CAAC;IAC1B,WAAW,EACT,iLAAiL;IACnL,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;QACvC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC;QACtE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;QAClE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;QACjE,aAAa,EAAE,CAAC;aACb,IAAI,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;aAC1B,QAAQ,EAAE;aACV,QAAQ,CAAC,oDAAoD,CAAC;QACjE,mBAAmB,EAAE,CAAC;aACnB,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,yDAAyD,CAAC;QACtE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;KAClE,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,IAAI,CAAC;YACH,OAAO,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC,IAAI,CAAC,EAAE,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,qBAAqB,EAAE,CAAC;gBAC3C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;oBAClB,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;iBAC3B,CAAC,CAAC;YACL,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAC,CAAC;AAEH,KAAK,UAAU,iCAAiC,CAAC,IAQhD;IACC,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;IAC/D,MAAM,SAAS,GAAG,sBAAsB,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,IAAI,SAAS,KAAK,OAAO,CAAC,SAAS,CAAC;IAC9D,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,KAAK,OAAO,CAAC,eAAe,CAAC;IACxE,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,iBAAiB,CAAC;QACvB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,mBAAmB,EAAE,MAAM,IAAI,IAAI,CAAC,mBAAmB;KACxD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -28,6 +28,13 @@ export interface CreateBookingInput {
|
|
|
28
28
|
orgId?: string;
|
|
29
29
|
/** If set, we're rescheduling from this booking uid */
|
|
30
30
|
fromReschedule?: string;
|
|
31
|
+
/** Keep Zoom reschedules uncommitted until the replacement meeting is usable. */
|
|
32
|
+
requireZoomMeeting?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare class BookingLifecycleError extends Error {
|
|
35
|
+
readonly statusCode: 409 | 502;
|
|
36
|
+
readonly errorCode: "zoom_meeting_review_required" | "video_meeting_cleanup_failed" | "video_meeting_creation_failed";
|
|
37
|
+
constructor(message: string, statusCode: 409 | 502, errorCode: "zoom_meeting_review_required" | "video_meeting_cleanup_failed" | "video_meeting_creation_failed");
|
|
31
38
|
}
|
|
32
39
|
export declare function createBooking(input: CreateBookingInput): Promise<Booking>;
|
|
33
40
|
export declare function rescheduleBooking(input: {
|
|
@@ -36,11 +43,13 @@ export declare function rescheduleBooking(input: {
|
|
|
36
43
|
newEndTime: string;
|
|
37
44
|
reason?: string;
|
|
38
45
|
rescheduledBy?: "attendee" | "host";
|
|
46
|
+
zoomMeetingResolved?: boolean;
|
|
39
47
|
}): Promise<Booking>;
|
|
40
48
|
export declare function cancelBooking(input: {
|
|
41
49
|
uid: string;
|
|
42
50
|
reason?: string;
|
|
43
51
|
cancelledBy?: "attendee" | "host";
|
|
52
|
+
zoomMeetingResolved?: boolean;
|
|
44
53
|
}): Promise<Booking>;
|
|
45
54
|
export declare function markNoShow(uid: string, attendeeEmail: string): Promise<void>;
|
|
46
55
|
//# sourceMappingURL=booking-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"booking-service.d.ts","sourceRoot":"","sources":["../../src/server/booking-service.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,SAAS,EACV,MAAM,oBAAoB,CAAC;AAkB5B,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"booking-service.d.ts","sourceRoot":"","sources":["../../src/server/booking-service.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EACV,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,SAAS,EACV,MAAM,oBAAoB,CAAC;AAkB5B,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAG5C,QAAQ,CAAC,UAAU,EAAE,GAAG,GAAG,GAAG;IAC9B,QAAQ,CAAC,SAAS,EACd,8BAA8B,GAC9B,8BAA8B,GAC9B,+BAA+B;IANrC,YACE,OAAO,EAAE,MAAM,EACN,UAAU,EAAE,GAAG,GAAG,GAAG,EACrB,SAAS,EACd,8BAA8B,GAC9B,8BAA8B,GAC9B,+BAA+B,EAGpC;CACF;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,OAAO,CAAC,CAwFlB;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACpC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,OAAO,CAAC,OAAO,CAAC,CAwEnB;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAClC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBnB;AAsDD,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -5,6 +5,15 @@ import { getSchedulingContext } from "./context.js";
|
|
|
5
5
|
import { getEventTypeById } from "./event-types-repo.js";
|
|
6
6
|
import { onBookingCreated, onBookingCancelled, onBookingRescheduled, } from "./hooks.js";
|
|
7
7
|
import { getCalendarProvider, getVideoProvider } from "./providers/registry.js";
|
|
8
|
+
export class BookingLifecycleError extends Error {
|
|
9
|
+
statusCode;
|
|
10
|
+
errorCode;
|
|
11
|
+
constructor(message, statusCode, errorCode) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.statusCode = statusCode;
|
|
14
|
+
this.errorCode = errorCode;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
8
17
|
export async function createBooking(input) {
|
|
9
18
|
const eventType = input.eventType;
|
|
10
19
|
const title = input.title ??
|
|
@@ -37,6 +46,7 @@ export async function createBooking(input) {
|
|
|
37
46
|
ownerEmail: input.hostEmail,
|
|
38
47
|
orgId: input.orgId,
|
|
39
48
|
});
|
|
49
|
+
let usableZoomMeeting = false;
|
|
40
50
|
// Create video meeting if location is a video kind
|
|
41
51
|
if (booking.location && isVideoKind(booking.location.kind)) {
|
|
42
52
|
const provider = getVideoProvider(videoProviderKindFor(booking.location.kind));
|
|
@@ -46,7 +56,7 @@ export async function createBooking(input) {
|
|
|
46
56
|
credentialId: booking.location.credentialId,
|
|
47
57
|
booking,
|
|
48
58
|
});
|
|
49
|
-
if (meeting.
|
|
59
|
+
if (meeting.meetingId && booking.location.credentialId) {
|
|
50
60
|
await addBookingReference(booking.id, {
|
|
51
61
|
type: provider.kind,
|
|
52
62
|
externalId: meeting.meetingId,
|
|
@@ -54,13 +64,21 @@ export async function createBooking(input) {
|
|
|
54
64
|
meetingPassword: meeting.meetingPassword,
|
|
55
65
|
credentialId: booking.location.credentialId,
|
|
56
66
|
});
|
|
67
|
+
usableZoomMeeting =
|
|
68
|
+
provider.kind === "zoom_video" && Boolean(meeting.meetingUrl);
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
71
|
catch {
|
|
72
|
+
if (input.requireZoomMeeting) {
|
|
73
|
+
throw new BookingLifecycleError("Replacement Zoom meeting could not be confirmed. The original booking remains active, and the replacement reservation needs host review.", 502, "video_meeting_creation_failed");
|
|
74
|
+
}
|
|
60
75
|
// Continue without the video link; the host can fix on the booking detail page
|
|
61
76
|
}
|
|
62
77
|
}
|
|
63
78
|
}
|
|
79
|
+
if (input.requireZoomMeeting && !usableZoomMeeting) {
|
|
80
|
+
throw new BookingLifecycleError("Replacement Zoom meeting could not be confirmed. The original booking remains active, and the replacement reservation needs host review.", 502, "video_meeting_creation_failed");
|
|
81
|
+
}
|
|
64
82
|
// Write to destination calendar
|
|
65
83
|
await writeToDestinationCalendars(booking);
|
|
66
84
|
// Fire workflow hooks
|
|
@@ -77,6 +95,7 @@ export async function rescheduleBooking(input) {
|
|
|
77
95
|
const eventType = await getEventTypeById(original.eventTypeId);
|
|
78
96
|
if (!eventType)
|
|
79
97
|
throw new Error("Event type missing");
|
|
98
|
+
requireZoomMeetingResolution(original, input.zoomMeetingResolved);
|
|
80
99
|
// Create the new booking (validated against availability, ignoring the
|
|
81
100
|
// original's own slot) before touching the original, so a slot conflict
|
|
82
101
|
// never leaves the original marked "rescheduled" with no successor.
|
|
@@ -97,7 +116,23 @@ export async function rescheduleBooking(input) {
|
|
|
97
116
|
iCalUid: original.iCalUid,
|
|
98
117
|
iCalSequence: original.iCalSequence + 1,
|
|
99
118
|
fromReschedule: input.uid,
|
|
119
|
+
requireZoomMeeting: original.location?.kind === "zoom",
|
|
100
120
|
});
|
|
121
|
+
try {
|
|
122
|
+
await deleteVideoMeetings(original);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
try {
|
|
126
|
+
await cancelBooking({ uid: newBooking.uid });
|
|
127
|
+
}
|
|
128
|
+
catch (rollbackError) {
|
|
129
|
+
const cleanupMessage = rollbackError instanceof Error
|
|
130
|
+
? rollbackError.message
|
|
131
|
+
: "unknown error";
|
|
132
|
+
throw new BookingLifecycleError(`The existing meeting could not be canceled, and the replacement booking could not be fully rolled back: ${cleanupMessage}`, 502, "video_meeting_cleanup_failed");
|
|
133
|
+
}
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
101
136
|
// Mark old as rescheduled now that the replacement exists.
|
|
102
137
|
await updateBookingStatus(input.uid, "rescheduled", {
|
|
103
138
|
reschedulingReason: input.reason,
|
|
@@ -125,6 +160,8 @@ export async function cancelBooking(input) {
|
|
|
125
160
|
const booking = await getBookingByUid(input.uid);
|
|
126
161
|
if (!booking)
|
|
127
162
|
throw new Error(`Booking ${input.uid} not found`);
|
|
163
|
+
requireZoomMeetingResolution(booking, input.zoomMeetingResolved);
|
|
164
|
+
await deleteVideoMeetings(booking);
|
|
128
165
|
await updateBookingStatus(input.uid, "cancelled", {
|
|
129
166
|
cancellationReason: input.reason,
|
|
130
167
|
});
|
|
@@ -139,20 +176,40 @@ export async function cancelBooking(input) {
|
|
|
139
176
|
}
|
|
140
177
|
catch { }
|
|
141
178
|
}
|
|
142
|
-
const videoProvider = getVideoProvider(ref.type);
|
|
143
|
-
if (videoProvider?.deleteMeeting) {
|
|
144
|
-
try {
|
|
145
|
-
await videoProvider.deleteMeeting({
|
|
146
|
-
credentialId: ref.credentialId,
|
|
147
|
-
meetingId: ref.externalId,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
catch { }
|
|
151
|
-
}
|
|
152
179
|
}
|
|
153
180
|
await onBookingCancelled(booking);
|
|
154
181
|
return (await getBookingByUid(input.uid));
|
|
155
182
|
}
|
|
183
|
+
function requireZoomMeetingResolution(booking, zoomMeetingResolved) {
|
|
184
|
+
if (booking.location?.kind === "zoom" &&
|
|
185
|
+
!booking.references.some((ref) => ref.type === "zoom_video") &&
|
|
186
|
+
zoomMeetingResolved !== true) {
|
|
187
|
+
throw new BookingLifecycleError("Zoom meeting needs host review; cancel it in Zoom, then confirm it was resolved", 409, "zoom_meeting_review_required");
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async function deleteVideoMeetings(booking) {
|
|
191
|
+
for (const ref of booking.references) {
|
|
192
|
+
const provider = getVideoProvider(ref.type);
|
|
193
|
+
if (!provider) {
|
|
194
|
+
if (ref.type.endsWith("_video") || ref.type === "google_meet") {
|
|
195
|
+
throw new BookingLifecycleError(`${ref.type} provider is unavailable to cancel this meeting`, 502, "video_meeting_cleanup_failed");
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (!provider.deleteMeeting) {
|
|
200
|
+
throw new BookingLifecycleError(`${provider.label} does not support meeting cancellation`, 502, "video_meeting_cleanup_failed");
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
await provider.deleteMeeting({
|
|
204
|
+
credentialId: ref.credentialId,
|
|
205
|
+
meetingId: ref.externalId,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
throw new BookingLifecycleError(`${provider.label} meeting could not be canceled${error instanceof Error ? `: ${error.message}` : ""}`, 502, "video_meeting_cleanup_failed");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
156
213
|
export async function markNoShow(uid, attendeeEmail) {
|
|
157
214
|
const booking = await getBookingByUid(uid);
|
|
158
215
|
if (!booking)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"booking-service.js","sourceRoot":"","sources":["../../src/server/booking-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAoBjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EACL,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"booking-service.js","sourceRoot":"","sources":["../../src/server/booking-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../../src/server/providers/teams.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,YAAY,CAAC,EAAE,CACb,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,IAAI,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,KACE,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAKD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,mBAAmB,GAC1B,aAAa,
|
|
1
|
+
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../../src/server/providers/teams.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D,YAAY,CAAC,EAAE,CACb,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,IAAI,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,KACE,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAKD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,mBAAmB,GAC1B,aAAa,CAyJf"}
|
|
@@ -97,8 +97,9 @@ export function createTeamsProvider(config) {
|
|
|
97
97
|
return { meetingId: meeting.id, meetingUrl: meeting.joinWebUrl };
|
|
98
98
|
},
|
|
99
99
|
async deleteMeeting({ credentialId, meetingId }) {
|
|
100
|
-
if (!credentialId)
|
|
101
|
-
|
|
100
|
+
if (!credentialId) {
|
|
101
|
+
throw new Error("Microsoft Teams requires credentialId");
|
|
102
|
+
}
|
|
102
103
|
const response = await graphRequest(credentialId, `/me/onlineMeetings/${encodeURIComponent(meetingId)}`, { method: "DELETE" });
|
|
103
104
|
if (response.status === 204 || response.status === 404)
|
|
104
105
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../../src/server/providers/teams.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../../src/server/providers/teams.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../../src/server/providers/zoom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,UAAU,EAAE,MAAM;IAD7B,YACW,UAAU,EAAE,MAAM,EAC3B,YAAY,EAAE,MAAM,EAIrB;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D;;;;OAIG;IACH,YAAY,CAAC,EAAE,CACb,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,IAAI,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,KACE,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../../src/server/providers/zoom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,UAAU,EAAE,MAAM;IAD7B,YACW,UAAU,EAAE,MAAM,EAC3B,YAAY,EAAE,MAAM,EAIrB;CACF;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1D;;;;OAIG;IACH,YAAY,CAAC,EAAE,CACb,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE;QACN,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,IAAI,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,KACE,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,aAAa,CAyI5E"}
|
|
@@ -108,12 +108,15 @@ export function createZoomProvider(config) {
|
|
|
108
108
|
},
|
|
109
109
|
async deleteMeeting({ credentialId, meetingId }) {
|
|
110
110
|
if (!credentialId)
|
|
111
|
-
|
|
111
|
+
throw new Error("Zoom requires credentialId");
|
|
112
112
|
const token = await config.getAccessToken(credentialId);
|
|
113
|
-
await fetch(`https://api.zoom.us/v2/meetings/${encodeURIComponent(meetingId)}`, {
|
|
113
|
+
const res = await fetch(`https://api.zoom.us/v2/meetings/${encodeURIComponent(meetingId)}`, {
|
|
114
114
|
method: "DELETE",
|
|
115
115
|
headers: { authorization: `Bearer ${token}` },
|
|
116
116
|
});
|
|
117
|
+
if (!res.ok && res.status !== 404) {
|
|
118
|
+
throw new Error(`Zoom meeting deletion failed: ${res.status}`);
|
|
119
|
+
}
|
|
117
120
|
},
|
|
118
121
|
};
|
|
119
122
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zoom.js","sourceRoot":"","sources":["../../../src/server/providers/zoom.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"zoom.js","sourceRoot":"","sources":["../../../src/server/providers/zoom.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/scheduling",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1-nightly-20260926144920",
|
|
4
4
|
"description": "Scheduling primitives for agent-native apps — event types, availability, bookings, team scheduling, workflows, and routing forms.",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -66,10 +66,10 @@
|
|
|
66
66
|
"react": "^19.2.5",
|
|
67
67
|
"typescript-7": "npm:typescript@^7.0.2",
|
|
68
68
|
"vitest": "^4.1.5",
|
|
69
|
-
"@agent-native/core": "0.192.
|
|
69
|
+
"@agent-native/core": "0.192.1-nightly-20260926144920"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@agent-native/core": "
|
|
72
|
+
"@agent-native/core": "0.192.1-nightly-20260926144920",
|
|
73
73
|
"@tabler/icons-react": "^3.41.1",
|
|
74
74
|
"drizzle-orm": ">=0.45",
|
|
75
75
|
"react": ">=18",
|
|
@@ -15,6 +15,7 @@ import * as schema from "../schema/index.js";
|
|
|
15
15
|
import { setSchedulingContext } from "../server/context.js";
|
|
16
16
|
import addBookingAttendee from "./add-booking-attendee.js";
|
|
17
17
|
import addBookingNote from "./add-booking-note.js";
|
|
18
|
+
import cancelBooking from "./cancel-booking.js";
|
|
18
19
|
import confirmBooking from "./confirm-booking.js";
|
|
19
20
|
import markNoShow from "./mark-no-show.js";
|
|
20
21
|
import removeBookingAttendee from "./remove-booking-attendee.js";
|
|
@@ -183,6 +184,33 @@ async function noteCount(): Promise<number> {
|
|
|
183
184
|
return rows.length;
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
async function setZoomLocation(): Promise<void> {
|
|
188
|
+
await execute({
|
|
189
|
+
sql: "UPDATE bookings SET location = ? WHERE uid = ?",
|
|
190
|
+
args: [JSON.stringify({ kind: "zoom" }), BOOKING_UID],
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
describe("cancel-booking Zoom review authorization", () => {
|
|
195
|
+
it("does not let a public cancellation token assert that Zoom was resolved", async () => {
|
|
196
|
+
currentUserEmail = undefined;
|
|
197
|
+
await setZoomLocation();
|
|
198
|
+
|
|
199
|
+
await expect(
|
|
200
|
+
cancelBooking.run({
|
|
201
|
+
uid: BOOKING_UID,
|
|
202
|
+
token: "cancel-token-1",
|
|
203
|
+
zoomMeetingResolved: true,
|
|
204
|
+
}),
|
|
205
|
+
).rejects.toMatchObject({
|
|
206
|
+
message: expect.stringMatching(/Zoom meeting needs host review/i),
|
|
207
|
+
statusCode: 409,
|
|
208
|
+
errorCode: "zoom_meeting_review_required",
|
|
209
|
+
});
|
|
210
|
+
expect(await bookingStatus()).toBe("pending");
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
186
214
|
describe("confirm-booking authorization", () => {
|
|
187
215
|
it("rejects a non-host caller and leaves the booking unchanged", async () => {
|
|
188
216
|
currentUserEmail = OUTSIDER_EMAIL;
|
|
@@ -1,27 +1,49 @@
|
|
|
1
|
-
import { defineAction } from "@agent-native/core/action";
|
|
1
|
+
import { defineAction, fail } from "@agent-native/core/action";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
BookingLifecycleError,
|
|
6
|
+
cancelBooking,
|
|
7
|
+
} from "../server/booking-service.js";
|
|
5
8
|
import { getBookingByUid } from "../server/bookings-repo.js";
|
|
6
9
|
import { currentUserEmailOrNull } from "./_helpers.js";
|
|
7
10
|
|
|
8
11
|
export default defineAction({
|
|
9
|
-
description:
|
|
12
|
+
description:
|
|
13
|
+
"Cancel a booking and its linked video meetings. If a Zoom booking has no recorded meeting, the host must review and resolve it first.",
|
|
10
14
|
schema: z.object({
|
|
11
|
-
uid: z.string(),
|
|
12
|
-
reason: z.string().optional(),
|
|
13
|
-
cancelledBy: z
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
uid: z.string().describe("Booking uid"),
|
|
16
|
+
reason: z.string().optional().describe("Reason for canceling the booking"),
|
|
17
|
+
cancelledBy: z
|
|
18
|
+
.enum(["attendee", "host"])
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('Who canceled the booking: "attendee" or "host"'),
|
|
21
|
+
zoomMeetingResolved: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe("Host confirms the Zoom meeting was checked and resolved"),
|
|
25
|
+
token: z.string().optional().describe("Booking cancellation token"),
|
|
18
26
|
}),
|
|
27
|
+
run: async (args) => {
|
|
28
|
+
try {
|
|
29
|
+
return { booking: await cancelBookingAfterAccessCheck(args) };
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof BookingLifecycleError) {
|
|
32
|
+
fail(error.message, {
|
|
33
|
+
statusCode: error.statusCode,
|
|
34
|
+
errorCode: error.errorCode,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
19
40
|
});
|
|
20
41
|
|
|
21
42
|
async function cancelBookingAfterAccessCheck(args: {
|
|
22
43
|
uid: string;
|
|
23
44
|
reason?: string;
|
|
24
45
|
cancelledBy?: "attendee" | "host";
|
|
46
|
+
zoomMeetingResolved?: boolean;
|
|
25
47
|
token?: string;
|
|
26
48
|
}) {
|
|
27
49
|
const booking = await getBookingByUid(args.uid);
|
|
@@ -36,5 +58,6 @@ async function cancelBookingAfterAccessCheck(args: {
|
|
|
36
58
|
uid: args.uid,
|
|
37
59
|
reason: args.reason,
|
|
38
60
|
cancelledBy: args.cancelledBy,
|
|
61
|
+
zoomMeetingResolved: isHost && args.zoomMeetingResolved,
|
|
39
62
|
});
|
|
40
63
|
}
|
|
@@ -1,23 +1,44 @@
|
|
|
1
|
-
import { defineAction } from "@agent-native/core/action";
|
|
1
|
+
import { defineAction, fail } from "@agent-native/core/action";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
BookingLifecycleError,
|
|
6
|
+
rescheduleBooking,
|
|
7
|
+
} from "../server/booking-service.js";
|
|
5
8
|
import { getBookingByUid } from "../server/bookings-repo.js";
|
|
6
9
|
import { currentUserEmailOrNull } from "./_helpers.js";
|
|
7
10
|
|
|
8
11
|
export default defineAction({
|
|
9
|
-
description:
|
|
12
|
+
description:
|
|
13
|
+
"Reschedule a booking, deleting its existing video meeting after the replacement is ready. If a Zoom booking has no recorded meeting, the host must review and resolve it first.",
|
|
10
14
|
schema: z.object({
|
|
11
|
-
uid: z.string(),
|
|
12
|
-
newStartTime: z.string(),
|
|
13
|
-
newEndTime: z.string(),
|
|
14
|
-
reason: z.string().optional(),
|
|
15
|
-
rescheduledBy: z
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
uid: z.string().describe("Booking uid"),
|
|
16
|
+
newStartTime: z.string().describe("New start time in ISO 8601 format"),
|
|
17
|
+
newEndTime: z.string().describe("New end time in ISO 8601 format"),
|
|
18
|
+
reason: z.string().optional().describe("Reason for rescheduling"),
|
|
19
|
+
rescheduledBy: z
|
|
20
|
+
.enum(["attendee", "host"])
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Who requested the reschedule: "attendee" or "host"'),
|
|
23
|
+
zoomMeetingResolved: z
|
|
24
|
+
.boolean()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Host confirms the Zoom meeting was checked and resolved"),
|
|
27
|
+
token: z.string().optional().describe("Booking reschedule token"),
|
|
20
28
|
}),
|
|
29
|
+
run: async (args) => {
|
|
30
|
+
try {
|
|
31
|
+
return { booking: await rescheduleBookingAfterAccessCheck(args) };
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (error instanceof BookingLifecycleError) {
|
|
34
|
+
fail(error.message, {
|
|
35
|
+
statusCode: error.statusCode,
|
|
36
|
+
errorCode: error.errorCode,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
},
|
|
21
42
|
});
|
|
22
43
|
|
|
23
44
|
async function rescheduleBookingAfterAccessCheck(args: {
|
|
@@ -26,6 +47,7 @@ async function rescheduleBookingAfterAccessCheck(args: {
|
|
|
26
47
|
newEndTime: string;
|
|
27
48
|
reason?: string;
|
|
28
49
|
rescheduledBy?: "attendee" | "host";
|
|
50
|
+
zoomMeetingResolved?: boolean;
|
|
29
51
|
token?: string;
|
|
30
52
|
}) {
|
|
31
53
|
const booking = await getBookingByUid(args.uid);
|
|
@@ -42,5 +64,6 @@ async function rescheduleBookingAfterAccessCheck(args: {
|
|
|
42
64
|
newEndTime: args.newEndTime,
|
|
43
65
|
reason: args.reason,
|
|
44
66
|
rescheduledBy: args.rescheduledBy,
|
|
67
|
+
zoomMeetingResolved: isHost && args.zoomMeetingResolved,
|
|
45
68
|
});
|
|
46
69
|
}
|
|
@@ -3,16 +3,23 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { closeDbExec, createGetDb, getDbExec } from "@agent-native/core/db";
|
|
6
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
7
|
|
|
8
8
|
import * as schema from "../schema/index.js";
|
|
9
9
|
import type { EventType } from "../shared/index.js";
|
|
10
10
|
import { SlotConflictError } from "./availability-engine.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
cancelBooking,
|
|
13
|
+
createBooking,
|
|
14
|
+
rescheduleBooking,
|
|
15
|
+
} from "./booking-service.js";
|
|
12
16
|
import { getBookingByUid, insertBooking } from "./bookings-repo.js";
|
|
13
17
|
import { setSchedulingContext } from "./context.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
18
|
+
import {
|
|
19
|
+
registerCalendarProvider,
|
|
20
|
+
registerVideoProvider,
|
|
21
|
+
} from "./providers/registry.js";
|
|
22
|
+
import type { CalendarProvider, VideoProvider } from "./providers/types.js";
|
|
16
23
|
|
|
17
24
|
const HOST_EMAIL = "host@example.com";
|
|
18
25
|
const ATTENDEE_EMAIL = "attendee@example.com";
|
|
@@ -33,6 +40,9 @@ async function execute(statement: SqlStatement) {
|
|
|
33
40
|
});
|
|
34
41
|
}
|
|
35
42
|
const GUEST_EMAIL = "guest@example.com";
|
|
43
|
+
const createZoomMeetingMock = vi.fn<VideoProvider["createMeeting"]>();
|
|
44
|
+
const deleteZoomMeetingMock =
|
|
45
|
+
vi.fn<NonNullable<VideoProvider["deleteMeeting"]>>();
|
|
36
46
|
|
|
37
47
|
let dbDir: string;
|
|
38
48
|
|
|
@@ -85,6 +95,17 @@ function makeCalendarProvider(
|
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
beforeEach(async () => {
|
|
98
|
+
createZoomMeetingMock.mockReset().mockResolvedValue({
|
|
99
|
+
meetingUrl: "https://zoom.us/j/123456789",
|
|
100
|
+
meetingId: "zoom-meeting-1",
|
|
101
|
+
});
|
|
102
|
+
deleteZoomMeetingMock.mockReset().mockResolvedValue();
|
|
103
|
+
registerVideoProvider({
|
|
104
|
+
kind: "zoom_video",
|
|
105
|
+
label: "Test Zoom",
|
|
106
|
+
createMeeting: createZoomMeetingMock,
|
|
107
|
+
deleteMeeting: deleteZoomMeetingMock,
|
|
108
|
+
});
|
|
88
109
|
dbDir = mkdtempSync(join(tmpdir(), "scheduling-booking-test-"));
|
|
89
110
|
process.env.DATABASE_URL = `pglite:${dbDir}`;
|
|
90
111
|
await execute(`
|
|
@@ -214,6 +235,21 @@ beforeEach(async () => {
|
|
|
214
235
|
visibility TEXT NOT NULL DEFAULT 'private'
|
|
215
236
|
);
|
|
216
237
|
`);
|
|
238
|
+
await execute(`
|
|
239
|
+
CREATE TABLE scheduled_reminders (
|
|
240
|
+
id TEXT PRIMARY KEY,
|
|
241
|
+
booking_id TEXT NOT NULL,
|
|
242
|
+
workflow_step_id TEXT NOT NULL,
|
|
243
|
+
method TEXT NOT NULL,
|
|
244
|
+
scheduled_for TEXT NOT NULL,
|
|
245
|
+
sent BOOLEAN NOT NULL DEFAULT false,
|
|
246
|
+
sent_at TEXT,
|
|
247
|
+
failed BOOLEAN NOT NULL DEFAULT false,
|
|
248
|
+
failure_reason TEXT,
|
|
249
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
250
|
+
created_at TEXT NOT NULL
|
|
251
|
+
);
|
|
252
|
+
`);
|
|
217
253
|
await execute(`
|
|
218
254
|
CREATE TABLE event_types (
|
|
219
255
|
id TEXT PRIMARY KEY,
|
|
@@ -567,4 +603,177 @@ describe("rescheduleBooking", () => {
|
|
|
567
603
|
const stillOriginal = await getBookingByUid(original.uid);
|
|
568
604
|
expect(stillOriginal?.status).toBe("confirmed");
|
|
569
605
|
});
|
|
606
|
+
|
|
607
|
+
it("deletes the old Zoom meeting before releasing the original slot", async () => {
|
|
608
|
+
const eventType = makeEventType();
|
|
609
|
+
await seedEventType(eventType);
|
|
610
|
+
const original = await createBooking({
|
|
611
|
+
eventType,
|
|
612
|
+
hostEmail: HOST_EMAIL,
|
|
613
|
+
startTime: "2026-08-10T10:00:00.000Z",
|
|
614
|
+
endTime: "2026-08-10T10:30:00.000Z",
|
|
615
|
+
timezone: "UTC",
|
|
616
|
+
location: { kind: "zoom", credentialId: "zoom-account" },
|
|
617
|
+
attendee: { email: ATTENDEE_EMAIL, name: "Attendee One" },
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
await rescheduleBooking({
|
|
621
|
+
uid: original.uid,
|
|
622
|
+
newStartTime: "2026-08-10T11:00:00.000Z",
|
|
623
|
+
newEndTime: "2026-08-10T11:30:00.000Z",
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
expect(deleteZoomMeetingMock).toHaveBeenCalledWith({
|
|
627
|
+
credentialId: "zoom-account",
|
|
628
|
+
meetingId: "zoom-meeting-1",
|
|
629
|
+
});
|
|
630
|
+
expect((await getBookingByUid(original.uid))?.status).toBe("rescheduled");
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it("keeps the original active when the replacement Zoom meeting is missing", async () => {
|
|
634
|
+
const eventType = makeEventType();
|
|
635
|
+
await seedEventType(eventType);
|
|
636
|
+
const original = await createBooking({
|
|
637
|
+
eventType,
|
|
638
|
+
hostEmail: HOST_EMAIL,
|
|
639
|
+
startTime: "2026-08-10T10:00:00.000Z",
|
|
640
|
+
endTime: "2026-08-10T10:30:00.000Z",
|
|
641
|
+
timezone: "UTC",
|
|
642
|
+
location: { kind: "zoom", credentialId: "zoom-account" },
|
|
643
|
+
attendee: { email: ATTENDEE_EMAIL, name: "Attendee One" },
|
|
644
|
+
});
|
|
645
|
+
createZoomMeetingMock.mockRejectedValueOnce(
|
|
646
|
+
new Error("Zoom creation failed"),
|
|
647
|
+
);
|
|
648
|
+
deleteZoomMeetingMock.mockRejectedValueOnce(
|
|
649
|
+
new Error("old meeting must not be touched"),
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
await expect(
|
|
653
|
+
rescheduleBooking({
|
|
654
|
+
uid: original.uid,
|
|
655
|
+
newStartTime: "2026-08-10T11:00:00.000Z",
|
|
656
|
+
newEndTime: "2026-08-10T11:30:00.000Z",
|
|
657
|
+
}),
|
|
658
|
+
).rejects.toMatchObject({
|
|
659
|
+
statusCode: 502,
|
|
660
|
+
errorCode: "video_meeting_creation_failed",
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
expect(deleteZoomMeetingMock).not.toHaveBeenCalled();
|
|
664
|
+
expect((await getBookingByUid(original.uid))?.status).toBe("confirmed");
|
|
665
|
+
const { rows } = await execute({
|
|
666
|
+
sql: "SELECT uid, status FROM bookings WHERE from_reschedule = ?",
|
|
667
|
+
args: [original.uid],
|
|
668
|
+
});
|
|
669
|
+
expect(rows).toHaveLength(1);
|
|
670
|
+
const replacement = await getBookingByUid(String(rows[0]!.uid));
|
|
671
|
+
expect(replacement).toMatchObject({ status: "confirmed", references: [] });
|
|
672
|
+
await expect(cancelBooking({ uid: replacement!.uid })).rejects.toThrow(
|
|
673
|
+
/Zoom meeting needs host review/i,
|
|
674
|
+
);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it("keeps the original slot confirmed when old Zoom cleanup fails", async () => {
|
|
678
|
+
const eventType = makeEventType();
|
|
679
|
+
await seedEventType(eventType);
|
|
680
|
+
const original = await createBooking({
|
|
681
|
+
eventType,
|
|
682
|
+
hostEmail: HOST_EMAIL,
|
|
683
|
+
startTime: "2026-08-11T10:00:00.000Z",
|
|
684
|
+
endTime: "2026-08-11T10:30:00.000Z",
|
|
685
|
+
timezone: "UTC",
|
|
686
|
+
location: { kind: "zoom", credentialId: "zoom-account" },
|
|
687
|
+
attendee: { email: ATTENDEE_EMAIL, name: "Attendee One" },
|
|
688
|
+
});
|
|
689
|
+
deleteZoomMeetingMock.mockRejectedValueOnce(
|
|
690
|
+
new Error("Zoom is unavailable"),
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
await expect(
|
|
694
|
+
rescheduleBooking({
|
|
695
|
+
uid: original.uid,
|
|
696
|
+
newStartTime: "2026-08-11T11:00:00.000Z",
|
|
697
|
+
newEndTime: "2026-08-11T11:30:00.000Z",
|
|
698
|
+
}),
|
|
699
|
+
).rejects.toThrow("Zoom is unavailable");
|
|
700
|
+
expect((await getBookingByUid(original.uid))?.status).toBe("confirmed");
|
|
701
|
+
const { rows } = await execute("SELECT status FROM bookings");
|
|
702
|
+
expect(rows.map((row: any) => row.status).sort()).toEqual([
|
|
703
|
+
"cancelled",
|
|
704
|
+
"confirmed",
|
|
705
|
+
]);
|
|
706
|
+
expect(deleteZoomMeetingMock).toHaveBeenCalledTimes(2);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
it("requires host resolution before rescheduling an unrecorded Zoom meeting", async () => {
|
|
710
|
+
const eventType = makeEventType();
|
|
711
|
+
await seedEventType(eventType);
|
|
712
|
+
createZoomMeetingMock.mockRejectedValueOnce(
|
|
713
|
+
new Error("ambiguous Zoom failure"),
|
|
714
|
+
);
|
|
715
|
+
const original = await createBooking({
|
|
716
|
+
eventType,
|
|
717
|
+
hostEmail: HOST_EMAIL,
|
|
718
|
+
startTime: "2026-08-12T10:00:00.000Z",
|
|
719
|
+
endTime: "2026-08-12T10:30:00.000Z",
|
|
720
|
+
timezone: "UTC",
|
|
721
|
+
location: { kind: "zoom", credentialId: "zoom-account" },
|
|
722
|
+
attendee: { email: ATTENDEE_EMAIL, name: "Attendee One" },
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
await expect(
|
|
726
|
+
rescheduleBooking({
|
|
727
|
+
uid: original.uid,
|
|
728
|
+
newStartTime: "2026-08-12T11:00:00.000Z",
|
|
729
|
+
newEndTime: "2026-08-12T11:30:00.000Z",
|
|
730
|
+
}),
|
|
731
|
+
).rejects.toThrow(/Zoom meeting needs host review/i);
|
|
732
|
+
expect((await getBookingByUid(original.uid))?.status).toBe("confirmed");
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
describe("cancelBooking", () => {
|
|
737
|
+
it("keeps the slot confirmed when Zoom deletion fails", async () => {
|
|
738
|
+
const booking = await createBooking({
|
|
739
|
+
eventType: makeEventType(),
|
|
740
|
+
hostEmail: HOST_EMAIL,
|
|
741
|
+
startTime: "2026-08-13T10:00:00.000Z",
|
|
742
|
+
endTime: "2026-08-13T10:30:00.000Z",
|
|
743
|
+
timezone: "UTC",
|
|
744
|
+
location: { kind: "zoom", credentialId: "zoom-account" },
|
|
745
|
+
attendee: { email: ATTENDEE_EMAIL, name: "Attendee One" },
|
|
746
|
+
});
|
|
747
|
+
deleteZoomMeetingMock.mockRejectedValueOnce(
|
|
748
|
+
new Error("Zoom is unavailable"),
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
await expect(cancelBooking({ uid: booking.uid })).rejects.toThrow(
|
|
752
|
+
"Zoom is unavailable",
|
|
753
|
+
);
|
|
754
|
+
expect((await getBookingByUid(booking.uid))?.status).toBe("confirmed");
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
it("requires host resolution before canceling an unrecorded Zoom meeting", async () => {
|
|
758
|
+
createZoomMeetingMock.mockRejectedValueOnce(
|
|
759
|
+
new Error("ambiguous Zoom failure"),
|
|
760
|
+
);
|
|
761
|
+
const booking = await createBooking({
|
|
762
|
+
eventType: makeEventType(),
|
|
763
|
+
hostEmail: HOST_EMAIL,
|
|
764
|
+
startTime: "2026-08-14T10:00:00.000Z",
|
|
765
|
+
endTime: "2026-08-14T10:30:00.000Z",
|
|
766
|
+
timezone: "UTC",
|
|
767
|
+
location: { kind: "zoom", credentialId: "zoom-account" },
|
|
768
|
+
attendee: { email: ATTENDEE_EMAIL, name: "Attendee One" },
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
await expect(cancelBooking({ uid: booking.uid })).rejects.toThrow(
|
|
772
|
+
/Zoom meeting needs host review/i,
|
|
773
|
+
);
|
|
774
|
+
expect((await getBookingByUid(booking.uid))?.status).toBe("confirmed");
|
|
775
|
+
|
|
776
|
+
await cancelBooking({ uid: booking.uid, zoomMeetingResolved: true });
|
|
777
|
+
expect((await getBookingByUid(booking.uid))?.status).toBe("cancelled");
|
|
778
|
+
});
|
|
570
779
|
});
|
|
@@ -52,6 +52,21 @@ export interface CreateBookingInput {
|
|
|
52
52
|
orgId?: string;
|
|
53
53
|
/** If set, we're rescheduling from this booking uid */
|
|
54
54
|
fromReschedule?: string;
|
|
55
|
+
/** Keep Zoom reschedules uncommitted until the replacement meeting is usable. */
|
|
56
|
+
requireZoomMeeting?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class BookingLifecycleError extends Error {
|
|
60
|
+
constructor(
|
|
61
|
+
message: string,
|
|
62
|
+
readonly statusCode: 409 | 502,
|
|
63
|
+
readonly errorCode:
|
|
64
|
+
| "zoom_meeting_review_required"
|
|
65
|
+
| "video_meeting_cleanup_failed"
|
|
66
|
+
| "video_meeting_creation_failed",
|
|
67
|
+
) {
|
|
68
|
+
super(message);
|
|
69
|
+
}
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
export async function createBooking(
|
|
@@ -90,6 +105,8 @@ export async function createBooking(
|
|
|
90
105
|
orgId: input.orgId,
|
|
91
106
|
});
|
|
92
107
|
|
|
108
|
+
let usableZoomMeeting = false;
|
|
109
|
+
|
|
93
110
|
// Create video meeting if location is a video kind
|
|
94
111
|
if (booking.location && isVideoKind(booking.location.kind)) {
|
|
95
112
|
const provider = getVideoProvider(
|
|
@@ -101,7 +118,7 @@ export async function createBooking(
|
|
|
101
118
|
credentialId: booking.location.credentialId,
|
|
102
119
|
booking,
|
|
103
120
|
});
|
|
104
|
-
if (meeting.
|
|
121
|
+
if (meeting.meetingId && booking.location.credentialId) {
|
|
105
122
|
await addBookingReference(booking.id, {
|
|
106
123
|
type: provider.kind,
|
|
107
124
|
externalId: meeting.meetingId,
|
|
@@ -109,13 +126,30 @@ export async function createBooking(
|
|
|
109
126
|
meetingPassword: meeting.meetingPassword,
|
|
110
127
|
credentialId: booking.location.credentialId,
|
|
111
128
|
});
|
|
129
|
+
usableZoomMeeting =
|
|
130
|
+
provider.kind === "zoom_video" && Boolean(meeting.meetingUrl);
|
|
112
131
|
}
|
|
113
132
|
} catch {
|
|
133
|
+
if (input.requireZoomMeeting) {
|
|
134
|
+
throw new BookingLifecycleError(
|
|
135
|
+
"Replacement Zoom meeting could not be confirmed. The original booking remains active, and the replacement reservation needs host review.",
|
|
136
|
+
502,
|
|
137
|
+
"video_meeting_creation_failed",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
114
140
|
// Continue without the video link; the host can fix on the booking detail page
|
|
115
141
|
}
|
|
116
142
|
}
|
|
117
143
|
}
|
|
118
144
|
|
|
145
|
+
if (input.requireZoomMeeting && !usableZoomMeeting) {
|
|
146
|
+
throw new BookingLifecycleError(
|
|
147
|
+
"Replacement Zoom meeting could not be confirmed. The original booking remains active, and the replacement reservation needs host review.",
|
|
148
|
+
502,
|
|
149
|
+
"video_meeting_creation_failed",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
119
153
|
// Write to destination calendar
|
|
120
154
|
await writeToDestinationCalendars(booking);
|
|
121
155
|
|
|
@@ -133,11 +167,13 @@ export async function rescheduleBooking(input: {
|
|
|
133
167
|
newEndTime: string;
|
|
134
168
|
reason?: string;
|
|
135
169
|
rescheduledBy?: "attendee" | "host";
|
|
170
|
+
zoomMeetingResolved?: boolean;
|
|
136
171
|
}): Promise<Booking> {
|
|
137
172
|
const original = await getBookingByUid(input.uid);
|
|
138
173
|
if (!original) throw new Error(`Booking ${input.uid} not found`);
|
|
139
174
|
const eventType = await getEventTypeById(original.eventTypeId);
|
|
140
175
|
if (!eventType) throw new Error("Event type missing");
|
|
176
|
+
requireZoomMeetingResolution(original, input.zoomMeetingResolved);
|
|
141
177
|
|
|
142
178
|
// Create the new booking (validated against availability, ignoring the
|
|
143
179
|
// original's own slot) before touching the original, so a slot conflict
|
|
@@ -159,8 +195,28 @@ export async function rescheduleBooking(input: {
|
|
|
159
195
|
iCalUid: original.iCalUid,
|
|
160
196
|
iCalSequence: original.iCalSequence + 1,
|
|
161
197
|
fromReschedule: input.uid,
|
|
198
|
+
requireZoomMeeting: original.location?.kind === "zoom",
|
|
162
199
|
});
|
|
163
200
|
|
|
201
|
+
try {
|
|
202
|
+
await deleteVideoMeetings(original);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
try {
|
|
205
|
+
await cancelBooking({ uid: newBooking.uid });
|
|
206
|
+
} catch (rollbackError) {
|
|
207
|
+
const cleanupMessage =
|
|
208
|
+
rollbackError instanceof Error
|
|
209
|
+
? rollbackError.message
|
|
210
|
+
: "unknown error";
|
|
211
|
+
throw new BookingLifecycleError(
|
|
212
|
+
`The existing meeting could not be canceled, and the replacement booking could not be fully rolled back: ${cleanupMessage}`,
|
|
213
|
+
502,
|
|
214
|
+
"video_meeting_cleanup_failed",
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
|
|
164
220
|
// Mark old as rescheduled now that the replacement exists.
|
|
165
221
|
await updateBookingStatus(input.uid, "rescheduled", {
|
|
166
222
|
reschedulingReason: input.reason,
|
|
@@ -190,9 +246,13 @@ export async function cancelBooking(input: {
|
|
|
190
246
|
uid: string;
|
|
191
247
|
reason?: string;
|
|
192
248
|
cancelledBy?: "attendee" | "host";
|
|
249
|
+
zoomMeetingResolved?: boolean;
|
|
193
250
|
}): Promise<Booking> {
|
|
194
251
|
const booking = await getBookingByUid(input.uid);
|
|
195
252
|
if (!booking) throw new Error(`Booking ${input.uid} not found`);
|
|
253
|
+
requireZoomMeetingResolution(booking, input.zoomMeetingResolved);
|
|
254
|
+
await deleteVideoMeetings(booking);
|
|
255
|
+
|
|
196
256
|
await updateBookingStatus(input.uid, "cancelled", {
|
|
197
257
|
cancellationReason: input.reason,
|
|
198
258
|
});
|
|
@@ -207,21 +267,64 @@ export async function cancelBooking(input: {
|
|
|
207
267
|
});
|
|
208
268
|
} catch {}
|
|
209
269
|
}
|
|
210
|
-
const videoProvider = getVideoProvider(ref.type);
|
|
211
|
-
if (videoProvider?.deleteMeeting) {
|
|
212
|
-
try {
|
|
213
|
-
await videoProvider.deleteMeeting({
|
|
214
|
-
credentialId: ref.credentialId,
|
|
215
|
-
meetingId: ref.externalId,
|
|
216
|
-
});
|
|
217
|
-
} catch {}
|
|
218
|
-
}
|
|
219
270
|
}
|
|
220
271
|
|
|
221
272
|
await onBookingCancelled(booking);
|
|
222
273
|
return (await getBookingByUid(input.uid))!;
|
|
223
274
|
}
|
|
224
275
|
|
|
276
|
+
function requireZoomMeetingResolution(
|
|
277
|
+
booking: Booking,
|
|
278
|
+
zoomMeetingResolved?: boolean,
|
|
279
|
+
): void {
|
|
280
|
+
if (
|
|
281
|
+
booking.location?.kind === "zoom" &&
|
|
282
|
+
!booking.references.some((ref) => ref.type === "zoom_video") &&
|
|
283
|
+
zoomMeetingResolved !== true
|
|
284
|
+
) {
|
|
285
|
+
throw new BookingLifecycleError(
|
|
286
|
+
"Zoom meeting needs host review; cancel it in Zoom, then confirm it was resolved",
|
|
287
|
+
409,
|
|
288
|
+
"zoom_meeting_review_required",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function deleteVideoMeetings(booking: Booking): Promise<void> {
|
|
294
|
+
for (const ref of booking.references) {
|
|
295
|
+
const provider = getVideoProvider(ref.type);
|
|
296
|
+
if (!provider) {
|
|
297
|
+
if (ref.type.endsWith("_video") || ref.type === "google_meet") {
|
|
298
|
+
throw new BookingLifecycleError(
|
|
299
|
+
`${ref.type} provider is unavailable to cancel this meeting`,
|
|
300
|
+
502,
|
|
301
|
+
"video_meeting_cleanup_failed",
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (!provider.deleteMeeting) {
|
|
307
|
+
throw new BookingLifecycleError(
|
|
308
|
+
`${provider.label} does not support meeting cancellation`,
|
|
309
|
+
502,
|
|
310
|
+
"video_meeting_cleanup_failed",
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
await provider.deleteMeeting({
|
|
315
|
+
credentialId: ref.credentialId,
|
|
316
|
+
meetingId: ref.externalId,
|
|
317
|
+
});
|
|
318
|
+
} catch (error) {
|
|
319
|
+
throw new BookingLifecycleError(
|
|
320
|
+
`${provider.label} meeting could not be canceled${error instanceof Error ? `: ${error.message}` : ""}`,
|
|
321
|
+
502,
|
|
322
|
+
"video_meeting_cleanup_failed",
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
225
328
|
export async function markNoShow(
|
|
226
329
|
uid: string,
|
|
227
330
|
attendeeEmail: string,
|
|
@@ -232,6 +232,13 @@ describe("createTeamsProvider", () => {
|
|
|
232
232
|
).resolves.toBeUndefined();
|
|
233
233
|
});
|
|
234
234
|
|
|
235
|
+
it("requires a credential before deleting a meeting", async () => {
|
|
236
|
+
await expect(
|
|
237
|
+
provider().deleteMeeting!({ meetingId: "meeting-example" }),
|
|
238
|
+
).rejects.toThrow("Microsoft Teams requires credentialId");
|
|
239
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
240
|
+
});
|
|
241
|
+
|
|
235
242
|
it.each([401, 403])(
|
|
236
243
|
"marks a credential invalid on delete HTTP %s",
|
|
237
244
|
async (status) => {
|
|
@@ -165,7 +165,9 @@ export function createTeamsProvider(
|
|
|
165
165
|
},
|
|
166
166
|
|
|
167
167
|
async deleteMeeting({ credentialId, meetingId }) {
|
|
168
|
-
if (!credentialId)
|
|
168
|
+
if (!credentialId) {
|
|
169
|
+
throw new Error("Microsoft Teams requires credentialId");
|
|
170
|
+
}
|
|
169
171
|
const response = await graphRequest(
|
|
170
172
|
credentialId,
|
|
171
173
|
`/me/onlineMeetings/${encodeURIComponent(meetingId)}`,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { createZoomProvider } from "./zoom.js";
|
|
4
|
+
|
|
5
|
+
describe("createZoomProvider.deleteMeeting", () => {
|
|
6
|
+
const fetchMock = vi.fn<typeof fetch>();
|
|
7
|
+
const getAccessToken = vi.fn(async () => "access-token-example");
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
vi.unstubAllGlobals();
|
|
15
|
+
vi.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
function provider() {
|
|
19
|
+
return createZoomProvider({
|
|
20
|
+
clientId: "client-id-example",
|
|
21
|
+
clientSecret: "client-secret-example",
|
|
22
|
+
getAccessToken,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
it("deletes a known meeting", async () => {
|
|
27
|
+
fetchMock.mockResolvedValueOnce(new Response(null, { status: 204 }));
|
|
28
|
+
|
|
29
|
+
await provider().deleteMeeting!({
|
|
30
|
+
credentialId: "zoom-account-example",
|
|
31
|
+
meetingId: "meeting-id-example",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(fetchMock).toHaveBeenCalledWith(
|
|
35
|
+
"https://api.zoom.us/v2/meetings/meeting-id-example",
|
|
36
|
+
{
|
|
37
|
+
method: "DELETE",
|
|
38
|
+
headers: { authorization: "Bearer access-token-example" },
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("treats an already-missing meeting as deleted", async () => {
|
|
44
|
+
fetchMock.mockResolvedValueOnce(new Response(null, { status: 404 }));
|
|
45
|
+
|
|
46
|
+
await expect(
|
|
47
|
+
provider().deleteMeeting!({
|
|
48
|
+
credentialId: "zoom-account-example",
|
|
49
|
+
meetingId: "missing-meeting-example",
|
|
50
|
+
}),
|
|
51
|
+
).resolves.toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("preserves a booking when Zoom rejects meeting deletion", async () => {
|
|
55
|
+
fetchMock.mockResolvedValueOnce(new Response(null, { status: 503 }));
|
|
56
|
+
|
|
57
|
+
await expect(
|
|
58
|
+
provider().deleteMeeting!({
|
|
59
|
+
credentialId: "zoom-account-example",
|
|
60
|
+
meetingId: "meeting-id-example",
|
|
61
|
+
}),
|
|
62
|
+
).rejects.toThrow("Zoom meeting deletion failed: 503");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("requires a credential before deleting a meeting", async () => {
|
|
66
|
+
await expect(
|
|
67
|
+
provider().deleteMeeting!({ meetingId: "meeting-id-example" }),
|
|
68
|
+
).rejects.toThrow("Zoom requires credentialId");
|
|
69
|
+
expect(getAccessToken).not.toHaveBeenCalled();
|
|
70
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -171,15 +171,18 @@ export function createZoomProvider(config: ZoomProviderConfig): VideoProvider {
|
|
|
171
171
|
},
|
|
172
172
|
|
|
173
173
|
async deleteMeeting({ credentialId, meetingId }) {
|
|
174
|
-
if (!credentialId)
|
|
174
|
+
if (!credentialId) throw new Error("Zoom requires credentialId");
|
|
175
175
|
const token = await config.getAccessToken(credentialId);
|
|
176
|
-
await fetch(
|
|
176
|
+
const res = await fetch(
|
|
177
177
|
`https://api.zoom.us/v2/meetings/${encodeURIComponent(meetingId)}`,
|
|
178
178
|
{
|
|
179
179
|
method: "DELETE",
|
|
180
180
|
headers: { authorization: `Bearer ${token}` },
|
|
181
181
|
},
|
|
182
182
|
);
|
|
183
|
+
if (!res.ok && res.status !== 404) {
|
|
184
|
+
throw new Error(`Zoom meeting deletion failed: ${res.status}`);
|
|
185
|
+
}
|
|
183
186
|
},
|
|
184
187
|
};
|
|
185
188
|
}
|