@agent-native/scheduling 0.1.60 → 0.1.61-nightly-20260910160638
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/get-event-type.js +4 -1
- package/dist/actions/get-event-type.js.map +1 -1
- package/dist/actions/list-routing-form-responses.js +2 -0
- package/dist/actions/list-routing-form-responses.js.map +1 -1
- package/package.json +4 -4
- package/src/actions/event-type-authz.spec.ts +106 -0
- package/src/actions/get-event-type.ts +5 -1
- package/src/actions/list-routing-form-responses.ts +2 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import { getEventTypeById, getEventTypeBySlug, } from "../server/event-types-repo.js";
|
|
4
5
|
export default defineAction({
|
|
@@ -10,8 +11,10 @@ export default defineAction({
|
|
|
10
11
|
teamId: z.string().optional(),
|
|
11
12
|
}),
|
|
12
13
|
run: async (args) => {
|
|
13
|
-
if (args.id)
|
|
14
|
+
if (args.id) {
|
|
15
|
+
await assertAccess("event-type", args.id, "viewer");
|
|
14
16
|
return { eventType: await getEventTypeById(args.id) };
|
|
17
|
+
}
|
|
15
18
|
if (args.slug) {
|
|
16
19
|
return {
|
|
17
20
|
eventType: await getEventTypeBySlug({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-event-type.js","sourceRoot":"","sources":["../../src/actions/get-event-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC,eAAe,YAAY,CAAC;IAC1B,WAAW,EAAE,+CAA+C;IAC5D,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACzB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACjC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,IAAI,IAAI,CAAC,EAAE;
|
|
1
|
+
{"version":3,"file":"get-event-type.js","sourceRoot":"","sources":["../../src/actions/get-event-type.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AAEvC,eAAe,YAAY,CAAC;IAC1B,WAAW,EAAE,+CAA+C;IAC5D,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACzB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QACjC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC9B,CAAC;IACF,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;YACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO;gBACL,SAAS,EAAE,MAAM,kBAAkB,CAAC;oBAClC,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC;aACH,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACxC,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
2
3
|
import { eq, desc } from "drizzle-orm";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { getSchedulingContext } from "../server/context.js";
|
|
@@ -6,6 +7,7 @@ export default defineAction({
|
|
|
6
7
|
description: "List responses to a routing form",
|
|
7
8
|
schema: z.object({ formId: z.string(), limit: z.number().optional() }),
|
|
8
9
|
run: async (args) => {
|
|
10
|
+
await assertAccess("routing-form", args.formId, "viewer");
|
|
9
11
|
const { getDb, schema } = getSchedulingContext();
|
|
10
12
|
const rows = await getDb()
|
|
11
13
|
.select()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-routing-form-responses.js","sourceRoot":"","sources":["../../src/actions/list-routing-form-responses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,eAAe,YAAY,CAAC;IAC1B,WAAW,EAAE,kCAAkC;IAC/C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;IACtE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,oBAAoB,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,MAAM,KAAK,EAAE;aACvB,MAAM,EAAE;aACR,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;aACjC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;aAC1D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;aACpD,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;QAC5B,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBAC/B,GAAG,CAAC;gBACJ,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;aACjC,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"list-routing-form-responses.js","sourceRoot":"","sources":["../../src/actions/list-routing-form-responses.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,eAAe,YAAY,CAAC;IAC1B,WAAW,EAAE,kCAAkC;IAC/C,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;IACtE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClB,MAAM,YAAY,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC1D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,oBAAoB,EAAE,CAAC;QACjD,MAAM,IAAI,GAAG,MAAM,KAAK,EAAE;aACvB,MAAM,EAAE;aACR,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC;aACjC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;aAC1D,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;aACpD,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;QAC5B,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBAC/B,GAAG,CAAC;gBACJ,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;aACjC,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/scheduling",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.61-nightly-20260910160638",
|
|
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": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"nanoid": "^5.1.9",
|
|
57
57
|
"rrule": "^2.8.1",
|
|
58
58
|
"zod": "^4.3.6",
|
|
59
|
-
"@agent-native/toolkit": "^0.19.
|
|
59
|
+
"@agent-native/toolkit": "^0.19.7-nightly-20260910160638"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@tabler/icons-react": "^3.41.1",
|
|
@@ -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.178.
|
|
69
|
+
"@agent-native/core": "0.178.1-nightly-20260910160638"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|
|
72
|
-
"@agent-native/core": "
|
|
72
|
+
"@agent-native/core": "0.178.1-nightly-20260910160638",
|
|
73
73
|
"@tabler/icons-react": "^3.41.1",
|
|
74
74
|
"drizzle-orm": ">=0.45",
|
|
75
75
|
"react": ">=18",
|
|
@@ -23,6 +23,8 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
|
23
23
|
import * as schema from "../schema/index.js";
|
|
24
24
|
import { setSchedulingContext } from "../server/context.js";
|
|
25
25
|
import duplicateEventType from "./duplicate-event-type.js";
|
|
26
|
+
import getEventType from "./get-event-type.js";
|
|
27
|
+
import listRoutingFormResponses from "./list-routing-form-responses.js";
|
|
26
28
|
import revokePrivateLink from "./revoke-private-link.js";
|
|
27
29
|
|
|
28
30
|
const OWNER_EMAIL = "owner@example.com";
|
|
@@ -115,6 +117,48 @@ beforeEach(async () => {
|
|
|
115
117
|
notified_at TEXT
|
|
116
118
|
);
|
|
117
119
|
`);
|
|
120
|
+
await execute(`
|
|
121
|
+
CREATE TABLE routing_forms (
|
|
122
|
+
id TEXT PRIMARY KEY,
|
|
123
|
+
name TEXT NOT NULL,
|
|
124
|
+
description TEXT,
|
|
125
|
+
team_id TEXT,
|
|
126
|
+
disabled BOOLEAN NOT NULL DEFAULT false,
|
|
127
|
+
fields TEXT NOT NULL DEFAULT '[]',
|
|
128
|
+
rules TEXT NOT NULL DEFAULT '[]',
|
|
129
|
+
fallback TEXT,
|
|
130
|
+
created_at TEXT NOT NULL,
|
|
131
|
+
updated_at TEXT NOT NULL,
|
|
132
|
+
owner_email TEXT NOT NULL DEFAULT 'local@localhost',
|
|
133
|
+
org_id TEXT,
|
|
134
|
+
visibility TEXT NOT NULL DEFAULT 'private'
|
|
135
|
+
);
|
|
136
|
+
`);
|
|
137
|
+
await execute(`
|
|
138
|
+
CREATE TABLE routing_form_shares (
|
|
139
|
+
id TEXT PRIMARY KEY,
|
|
140
|
+
resource_id TEXT NOT NULL,
|
|
141
|
+
principal_type TEXT NOT NULL,
|
|
142
|
+
principal_id TEXT NOT NULL,
|
|
143
|
+
role TEXT NOT NULL DEFAULT 'viewer',
|
|
144
|
+
created_by TEXT NOT NULL,
|
|
145
|
+
created_at TEXT NOT NULL,
|
|
146
|
+
notified_at TEXT
|
|
147
|
+
);
|
|
148
|
+
`);
|
|
149
|
+
await execute(`
|
|
150
|
+
CREATE TABLE routing_form_responses (
|
|
151
|
+
id TEXT PRIMARY KEY,
|
|
152
|
+
form_id TEXT NOT NULL,
|
|
153
|
+
response TEXT NOT NULL,
|
|
154
|
+
booking_id TEXT,
|
|
155
|
+
matched_rule_id TEXT,
|
|
156
|
+
routed_to TEXT,
|
|
157
|
+
submitter_email TEXT,
|
|
158
|
+
submitter_ip TEXT,
|
|
159
|
+
created_at TEXT NOT NULL
|
|
160
|
+
);
|
|
161
|
+
`);
|
|
118
162
|
|
|
119
163
|
const db = createGetDb(schema)();
|
|
120
164
|
setSchedulingContext({
|
|
@@ -133,6 +177,13 @@ beforeEach(async () => {
|
|
|
133
177
|
displayName: "Event Type",
|
|
134
178
|
getDb: () => db,
|
|
135
179
|
});
|
|
180
|
+
registerShareableResource({
|
|
181
|
+
type: "routing-form",
|
|
182
|
+
resourceTable: schema.routingForms,
|
|
183
|
+
sharesTable: schema.routingFormShares,
|
|
184
|
+
displayName: "Routing Form",
|
|
185
|
+
getDb: () => db,
|
|
186
|
+
});
|
|
136
187
|
|
|
137
188
|
const now = new Date().toISOString();
|
|
138
189
|
await execute({
|
|
@@ -150,6 +201,23 @@ beforeEach(async () => {
|
|
|
150
201
|
"private",
|
|
151
202
|
],
|
|
152
203
|
});
|
|
204
|
+
await execute({
|
|
205
|
+
sql: `INSERT INTO routing_forms (
|
|
206
|
+
id, name, created_at, updated_at, owner_email, visibility
|
|
207
|
+
) VALUES (?, ?, ?, ?, ?, ?)`,
|
|
208
|
+
args: ["form-1", "Test Form", now, now, OWNER_EMAIL, "private"],
|
|
209
|
+
});
|
|
210
|
+
await execute({
|
|
211
|
+
sql: `INSERT INTO routing_form_responses (
|
|
212
|
+
id, form_id, response, created_at
|
|
213
|
+
) VALUES (?, ?, ?, ?)`,
|
|
214
|
+
args: [
|
|
215
|
+
"resp-1",
|
|
216
|
+
"form-1",
|
|
217
|
+
JSON.stringify({ email: "lead@example.com" }),
|
|
218
|
+
now,
|
|
219
|
+
],
|
|
220
|
+
});
|
|
153
221
|
await execute({
|
|
154
222
|
sql: `INSERT INTO hashed_links (id, hash, event_type_id, created_at) VALUES (?, ?, ?, ?)`,
|
|
155
223
|
args: ["link-1", HASH, EVENT_TYPE_ID, now],
|
|
@@ -221,3 +289,41 @@ describe("duplicate-event-type authorization", () => {
|
|
|
221
289
|
expect(await eventTypeCount()).toBe(2);
|
|
222
290
|
});
|
|
223
291
|
});
|
|
292
|
+
|
|
293
|
+
describe("get-event-type authorization", () => {
|
|
294
|
+
it("rejects an outsider querying by id without access", async () => {
|
|
295
|
+
await expect(
|
|
296
|
+
runWithRequestContext({ userEmail: OUTSIDER_EMAIL }, () =>
|
|
297
|
+
getEventType.run({ id: EVENT_TYPE_ID }),
|
|
298
|
+
),
|
|
299
|
+
).rejects.toBeInstanceOf(ForbiddenError);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("allows the owner to get an event type by id", async () => {
|
|
303
|
+
const result: any = await runWithRequestContext(
|
|
304
|
+
{ userEmail: OWNER_EMAIL },
|
|
305
|
+
() => getEventType.run({ id: EVENT_TYPE_ID }),
|
|
306
|
+
);
|
|
307
|
+
expect(result.eventType?.id).toBe(EVENT_TYPE_ID);
|
|
308
|
+
expect(result.eventType?.title).toBe("30 Min Meeting");
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe("list-routing-form-responses authorization", () => {
|
|
313
|
+
it("rejects an outsider with no access to the routing form", async () => {
|
|
314
|
+
await expect(
|
|
315
|
+
runWithRequestContext({ userEmail: OUTSIDER_EMAIL }, () =>
|
|
316
|
+
listRoutingFormResponses.run({ formId: "form-1" }),
|
|
317
|
+
),
|
|
318
|
+
).rejects.toBeInstanceOf(ForbiddenError);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it("allows the owner to list form responses", async () => {
|
|
322
|
+
const result: any = await runWithRequestContext(
|
|
323
|
+
{ userEmail: OWNER_EMAIL },
|
|
324
|
+
() => listRoutingFormResponses.run({ formId: "form-1" }),
|
|
325
|
+
);
|
|
326
|
+
expect(result.responses).toHaveLength(1);
|
|
327
|
+
expect(result.responses[0].id).toBe("resp-1");
|
|
328
|
+
});
|
|
329
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
|
|
4
5
|
import {
|
|
@@ -15,7 +16,10 @@ export default defineAction({
|
|
|
15
16
|
teamId: z.string().optional(),
|
|
16
17
|
}),
|
|
17
18
|
run: async (args) => {
|
|
18
|
-
if (args.id)
|
|
19
|
+
if (args.id) {
|
|
20
|
+
await assertAccess("event-type", args.id, "viewer");
|
|
21
|
+
return { eventType: await getEventTypeById(args.id) };
|
|
22
|
+
}
|
|
19
23
|
if (args.slug) {
|
|
20
24
|
return {
|
|
21
25
|
eventType: await getEventTypeBySlug({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineAction } from "@agent-native/core/action";
|
|
2
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
2
3
|
import { eq, desc } from "drizzle-orm";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
|
|
@@ -8,6 +9,7 @@ export default defineAction({
|
|
|
8
9
|
description: "List responses to a routing form",
|
|
9
10
|
schema: z.object({ formId: z.string(), limit: z.number().optional() }),
|
|
10
11
|
run: async (args) => {
|
|
12
|
+
await assertAccess("routing-form", args.formId, "viewer");
|
|
11
13
|
const { getDb, schema } = getSchedulingContext();
|
|
12
14
|
const rows = await getDb()
|
|
13
15
|
.select()
|