@tellescope/sdk 1.256.12 → 1.256.13
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/lib/cjs/sdk.d.ts +1 -0
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +1 -0
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +128 -124
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/sdk.d.ts +1 -0
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +1 -0
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +128 -124
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/sdk.ts +4 -0
- package/src/tests/api_tests/out_of_office_endpoint.test.ts +162 -0
- package/src/tests/tests.ts +2 -0
- package/test_generated.pdf +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tellescope/sdk",
|
|
3
|
-
"version": "1.256.
|
|
3
|
+
"version": "1.256.13",
|
|
4
4
|
"description": "Code for interacting with the Tellescope API",
|
|
5
5
|
"main": "./lib/cjs/sdk.js",
|
|
6
6
|
"module": "./lib/esm/sdk.js",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://github.com/tellescope-os/tellescope#readme",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@tellescope/constants": "1.256.
|
|
36
|
-
"@tellescope/schema": "1.256.
|
|
37
|
-
"@tellescope/testing": "1.256.
|
|
38
|
-
"@tellescope/types-client": "1.256.
|
|
39
|
-
"@tellescope/types-models": "1.256.
|
|
40
|
-
"@tellescope/types-utilities": "1.256.
|
|
41
|
-
"@tellescope/utilities": "1.256.
|
|
42
|
-
"@tellescope/validation": "1.256.
|
|
35
|
+
"@tellescope/constants": "1.256.13",
|
|
36
|
+
"@tellescope/schema": "1.256.13",
|
|
37
|
+
"@tellescope/testing": "1.256.13",
|
|
38
|
+
"@tellescope/types-client": "1.256.13",
|
|
39
|
+
"@tellescope/types-models": "1.256.13",
|
|
40
|
+
"@tellescope/types-utilities": "1.256.13",
|
|
41
|
+
"@tellescope/utilities": "1.256.13",
|
|
42
|
+
"@tellescope/validation": "1.256.13",
|
|
43
43
|
"axios": "0.30.3",
|
|
44
44
|
"dotenv": "14.3.2",
|
|
45
45
|
"form-data": "4.0.4",
|
|
@@ -55,5 +55,5 @@
|
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
57
57
|
},
|
|
58
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "457e037174ead2b05f585d810de05f724659430a"
|
|
59
59
|
}
|
package/src/sdk.ts
CHANGED
|
@@ -810,6 +810,9 @@ type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
|
|
|
810
810
|
load_events: (args: extractFields<CustomActions['calendar_events']['load_events']['parameters']>) => (
|
|
811
811
|
Promise<extractFields<CustomActions['calendar_events']['load_events']['returns']>>
|
|
812
812
|
),
|
|
813
|
+
out_of_office: (args?: extractFields<CustomActions['calendar_events']['out_of_office']['parameters']>) => (
|
|
814
|
+
Promise<extractFields<CustomActions['calendar_events']['out_of_office']['returns']>>
|
|
815
|
+
),
|
|
813
816
|
generate_zoom_meeting: (args: extractFields<CustomActions['calendar_events']['generate_zoom_meeting']['parameters']>) => (
|
|
814
817
|
Promise<extractFields<CustomActions['calendar_events']['generate_zoom_meeting']['returns']>>
|
|
815
818
|
),
|
|
@@ -1146,6 +1149,7 @@ export class Session extends SessionManager {
|
|
|
1146
1149
|
queries.calendar_events.session_for_start_link = a => this._GET(`/v1${schema.calendar_events.publicActions.session_for_start_link.path}`, a)
|
|
1147
1150
|
queries.calendar_events.get_events_for_user = a => this._GET(`/v1/${schema.calendar_events.customActions.get_events_for_user.path}`, a)
|
|
1148
1151
|
queries.calendar_events.load_events = a => this._GET(`/v1/${schema.calendar_events.customActions.load_events.path}`, a)
|
|
1152
|
+
queries.calendar_events.out_of_office = a => this._GET(`/v1${schema.calendar_events.customActions.out_of_office.path}`, a)
|
|
1149
1153
|
queries.calendar_events.generate_meeting_link = a => this._POST(`/v1/${schema.calendar_events.customActions.generate_meeting_link.path}`, a)
|
|
1150
1154
|
queries.calendar_events.generate_zoom_meeting = a => this._POST(`/v1/${schema.calendar_events.customActions.generate_zoom_meeting.path}`, a)
|
|
1151
1155
|
queries.calendar_events.change_zoom_host = a => this._POST(`/v1/${schema.calendar_events.customActions.change_zoom_host.path}`, a)
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
assert,
|
|
6
|
+
log_header,
|
|
7
|
+
} from "@tellescope/testing"
|
|
8
|
+
import { setup_tests } from "../setup"
|
|
9
|
+
|
|
10
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
11
|
+
|
|
12
|
+
const MS_PER_MINUTE = 60 * 1000
|
|
13
|
+
const MS_PER_HOUR = 60 * MS_PER_MINUTE
|
|
14
|
+
|
|
15
|
+
// Covers the calendar_events.out_of_office endpoint, whose whole reason to exist is that it reads
|
|
16
|
+
// ACROSS users: calendar_events is row-filtered on attendees.id and a non-admin defaults to
|
|
17
|
+
// read: 'Assigned', so the ordinary read path returns only the caller's own blocks. The endpoint
|
|
18
|
+
// therefore queries organization-wide on purpose, and returns no event content to make that safe.
|
|
19
|
+
//
|
|
20
|
+
// The cross-user assertion below is the one that matters: swapping the handler's
|
|
21
|
+
// buildOrganizationWideQueries back to the request-scoped DB would silently break peer visibility in
|
|
22
|
+
// Team Chat without breaking a build or any other test.
|
|
23
|
+
export const out_of_office_endpoint_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
24
|
+
log_header("Out of Office Endpoint")
|
|
25
|
+
|
|
26
|
+
const adminId = sdk.userInfo.id
|
|
27
|
+
const nonAdminId = sdkNonAdmin.userInfo.id
|
|
28
|
+
const now = Date.now()
|
|
29
|
+
|
|
30
|
+
// active right now, on the ADMIN, so the non-admin is neither an attendee nor the creator
|
|
31
|
+
const active = await sdk.api.calendar_events.createOne({
|
|
32
|
+
title: 'Out of Office',
|
|
33
|
+
outOfOffice: true,
|
|
34
|
+
startTimeInMS: now - 2 * MS_PER_HOUR,
|
|
35
|
+
durationInMinutes: 8 * 60,
|
|
36
|
+
attendees: [{ id: adminId, type: 'user' }],
|
|
37
|
+
})
|
|
38
|
+
// ended three hours ago — inside the query's start-time range, so only the handler's computed
|
|
39
|
+
// end-time check excludes it
|
|
40
|
+
const ended = await sdk.api.calendar_events.createOne({
|
|
41
|
+
title: 'Out of Office',
|
|
42
|
+
outOfOffice: true,
|
|
43
|
+
startTimeInMS: now - 5 * MS_PER_HOUR,
|
|
44
|
+
durationInMinutes: 2 * 60,
|
|
45
|
+
attendees: [{ id: adminId, type: 'user' }],
|
|
46
|
+
})
|
|
47
|
+
const cancelled = await sdk.api.calendar_events.createOne({
|
|
48
|
+
title: 'Out of Office',
|
|
49
|
+
outOfOffice: true,
|
|
50
|
+
startTimeInMS: now - MS_PER_HOUR,
|
|
51
|
+
durationInMinutes: 4 * 60,
|
|
52
|
+
attendees: [{ id: adminId, type: 'user' }],
|
|
53
|
+
cancelledAt: new Date(),
|
|
54
|
+
})
|
|
55
|
+
// not flagged: a normal event overlapping now must never appear
|
|
56
|
+
const normal = await sdk.api.calendar_events.createOne({
|
|
57
|
+
title: 'Regular Appointment',
|
|
58
|
+
startTimeInMS: now - MS_PER_HOUR,
|
|
59
|
+
durationInMinutes: 4 * 60,
|
|
60
|
+
attendees: [{ id: adminId, type: 'user' }],
|
|
61
|
+
})
|
|
62
|
+
// on the non-admin, used for the userIds scoping check
|
|
63
|
+
const nonAdminOwn = await sdk.api.calendar_events.createOne({
|
|
64
|
+
title: 'Out of Office',
|
|
65
|
+
outOfOffice: true,
|
|
66
|
+
startTimeInMS: now - MS_PER_HOUR,
|
|
67
|
+
durationInMinutes: 4 * 60,
|
|
68
|
+
attendees: [{ id: nonAdminId, type: 'user' }],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const { outOfOffice } = await sdkNonAdmin.api.calendar_events.out_of_office()
|
|
73
|
+
|
|
74
|
+
// assert(assertion, failureMessage, successTitle)
|
|
75
|
+
assert(
|
|
76
|
+
!!outOfOffice.find(o => o.userId === adminId && o.startTimeInMS === active.startTimeInMS),
|
|
77
|
+
'non-admin does NOT see a peer out of office window — is the handler using the request-scoped DB instead of buildOrganizationWideQueries?',
|
|
78
|
+
'non-admin sees a peer out of office window',
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
assert(
|
|
82
|
+
outOfOffice.every(o => (
|
|
83
|
+
Object.keys(o).sort().join(',') === 'endTimeInMS,startTimeInMS,userId'
|
|
84
|
+
)),
|
|
85
|
+
`response leaked event fields: ${JSON.stringify(outOfOffice[0] ?? {})}`,
|
|
86
|
+
'response carries no event content',
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const forAdmin = outOfOffice.filter(o => o.userId === adminId)
|
|
90
|
+
assert(
|
|
91
|
+
forAdmin.length === 1,
|
|
92
|
+
`expected exactly the one active window for the admin, got ${forAdmin.length}: ${JSON.stringify(forAdmin)}`,
|
|
93
|
+
'ended, cancelled and unflagged events are excluded',
|
|
94
|
+
)
|
|
95
|
+
assert(
|
|
96
|
+
forAdmin[0]?.endTimeInMS === active.startTimeInMS + active.durationInMinutes * MS_PER_MINUTE,
|
|
97
|
+
`endTimeInMS was ${forAdmin[0]?.endTimeInMS}, expected ${active.startTimeInMS + active.durationInMinutes * MS_PER_MINUTE}`,
|
|
98
|
+
'endTimeInMS is resolved from startTimeInMS + durationInMinutes',
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
// An admin's reads are unrestricted anyway, so this passes even if the handler were reverted to
|
|
102
|
+
// the request-scoped DB — it's the non-admin assertion above that pins the design. Kept as the
|
|
103
|
+
// pair, so the two together show the result is role-independent rather than role-dependent.
|
|
104
|
+
const asAdmin = await sdk.api.calendar_events.out_of_office()
|
|
105
|
+
assert(
|
|
106
|
+
!!asAdmin.outOfOffice.find(o => o.userId === adminId && o.startTimeInMS === active.startTimeInMS)
|
|
107
|
+
&& !!asAdmin.outOfOffice.find(o => o.userId === nonAdminId),
|
|
108
|
+
`admin load missing windows: ${JSON.stringify(asAdmin.outOfOffice)}`,
|
|
109
|
+
'admin sees both its own and a peer out of office window',
|
|
110
|
+
)
|
|
111
|
+
assert(
|
|
112
|
+
asAdmin.outOfOffice.filter(o => o.userId === adminId).length === forAdmin.length,
|
|
113
|
+
`admin and non-admin disagree on the admin's windows: ${JSON.stringify(asAdmin.outOfOffice.filter(o => o.userId === adminId))} vs ${JSON.stringify(forAdmin)}`,
|
|
114
|
+
'admin and non-admin loads return the same windows',
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
const scoped = await sdkNonAdmin.api.calendar_events.out_of_office({ userIds: [nonAdminId] })
|
|
118
|
+
assert(
|
|
119
|
+
scoped.outOfOffice.length === 1 && scoped.outOfOffice[0].userId === nonAdminId,
|
|
120
|
+
`expected only the requested user, got ${JSON.stringify(scoped.outOfOffice)}`,
|
|
121
|
+
'userIds scopes the result',
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
// a real ObjectId that belongs to no user in this business
|
|
125
|
+
const foreign = await sdkNonAdmin.api.calendar_events.out_of_office({ userIds: ['5fdb3d4a4e6e2a0016a1a1a1'] })
|
|
126
|
+
assert(
|
|
127
|
+
foreign.outOfOffice.length === 0,
|
|
128
|
+
`expected empty for unknown userIds, got ${JSON.stringify(foreign.outOfOffice)}`,
|
|
129
|
+
'unknown userIds return nothing',
|
|
130
|
+
)
|
|
131
|
+
} finally {
|
|
132
|
+
await Promise.all([
|
|
133
|
+
sdk.api.calendar_events.deleteOne(active.id),
|
|
134
|
+
sdk.api.calendar_events.deleteOne(ended.id),
|
|
135
|
+
sdk.api.calendar_events.deleteOne(cancelled.id),
|
|
136
|
+
sdk.api.calendar_events.deleteOne(normal.id),
|
|
137
|
+
sdk.api.calendar_events.deleteOne(nonAdminOwn.id),
|
|
138
|
+
])
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Allow running this test file independently
|
|
143
|
+
if (require.main === module) {
|
|
144
|
+
console.log(`🌐 Using API URL: ${host}`)
|
|
145
|
+
const sdk = new Session({ host })
|
|
146
|
+
const sdkNonAdmin = new Session({ host })
|
|
147
|
+
|
|
148
|
+
const runTests = async () => {
|
|
149
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
150
|
+
await out_of_office_endpoint_tests({ sdk, sdkNonAdmin })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
runTests()
|
|
154
|
+
.then(() => {
|
|
155
|
+
console.log("✅ Out of office endpoint test suite completed successfully")
|
|
156
|
+
process.exit(0)
|
|
157
|
+
})
|
|
158
|
+
.catch((error) => {
|
|
159
|
+
console.error("❌ Out of office endpoint test suite failed:", error)
|
|
160
|
+
process.exit(1)
|
|
161
|
+
})
|
|
162
|
+
}
|
package/src/tests/tests.ts
CHANGED
|
@@ -103,6 +103,7 @@ import { outbound_chat_sent_trigger_tests } from "./api_tests/outbound_chat_sent
|
|
|
103
103
|
import { time_tracks_tests, time_tracks_historical_tests, time_tracks_correction_tests, time_tracks_review_tests, time_tracks_lock_tests, time_tracks_edge_case_tests, time_tracks_resubmit_tests, time_tracks_appointment_duration_tests } from "./api_tests/time_tracks.test";
|
|
104
104
|
import { monthly_availability_restrictions_tests } from "./api_tests/monthly_availability_restrictions.test";
|
|
105
105
|
import { calendar_event_limits_tests } from "./api_tests/calendar_event_limits.test";
|
|
106
|
+
import { out_of_office_endpoint_tests } from "./api_tests/out_of_office_endpoint.test";
|
|
106
107
|
import { appointment_link_expiration_tests } from "./api_tests/appointment_link_expiration.test";
|
|
107
108
|
import { custom_aggregation_tests } from "./api_tests/custom_aggregation.test";
|
|
108
109
|
import { chats_analytics_tests } from "./api_tests/chats_analytics.test";
|
|
@@ -15446,6 +15447,7 @@ const ip_address_form_tests = async () => {
|
|
|
15446
15447
|
await time_tracks_resubmit_tests({ sdk, sdkNonAdmin })
|
|
15447
15448
|
await time_tracks_appointment_duration_tests({ sdk, sdkNonAdmin })
|
|
15448
15449
|
await calendar_event_limits_tests({ sdk, sdkNonAdmin })
|
|
15450
|
+
await out_of_office_endpoint_tests({ sdk, sdkNonAdmin })
|
|
15449
15451
|
await appointment_link_expiration_tests({ sdk, sdkNonAdmin })
|
|
15450
15452
|
await get_some_projection_tests({ sdk, sdkNonAdmin })
|
|
15451
15453
|
await file_download_unicode_names_tests({ sdk, sdkNonAdmin })
|
package/test_generated.pdf
CHANGED
|
Binary file
|