@tellescope/sdk 1.256.0 → 1.256.2
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 +2 -2
- package/lib/cjs/tests/api_tests/appointment_link_expiration.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/appointment_link_expiration.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/appointment_link_expiration.test.js +163 -0
- package/lib/cjs/tests/api_tests/appointment_link_expiration.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.js +224 -0
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/embeddables_auth_token.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/embeddables_auth_token.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/embeddables_auth_token.test.js +354 -0
- package/lib/cjs/tests/api_tests/embeddables_auth_token.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.js +351 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +217 -189
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/cjs/tests/unit_tests/phone_validation.test.d.ts +3 -0
- package/lib/cjs/tests/unit_tests/phone_validation.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/phone_validation.test.js +96 -0
- package/lib/cjs/tests/unit_tests/phone_validation.test.js.map +1 -0
- package/lib/esm/sdk.d.ts +2 -2
- package/lib/esm/tests/api_tests/appointment_link_expiration.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/appointment_link_expiration.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/appointment_link_expiration.test.js +156 -0
- package/lib/esm/tests/api_tests/appointment_link_expiration.test.js.map +1 -0
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.js +220 -0
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.js.map +1 -0
- package/lib/esm/tests/api_tests/embeddables_auth_token.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/embeddables_auth_token.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/embeddables_auth_token.test.js +347 -0
- package/lib/esm/tests/api_tests/embeddables_auth_token.test.js.map +1 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.js +347 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +217 -189
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/esm/tests/unit_tests/phone_validation.test.d.ts +3 -0
- package/lib/esm/tests/unit_tests/phone_validation.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/phone_validation.test.js +93 -0
- package/lib/esm/tests/unit_tests/phone_validation.test.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/tests/api_tests/appointment_link_expiration.test.ts +92 -0
- package/src/tests/api_tests/beluga_trigger_refill.test.ts +152 -0
- package/src/tests/api_tests/embeddables_auth_token.test.ts +417 -0
- package/src/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.ts +292 -0
- package/src/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.ts +179 -0
- package/src/tests/api_tests/start_ai_conversation.test.ts +256 -0
- package/src/tests/tests.ts +14 -0
- package/src/tests/unit_tests/phone_validation.test.ts +108 -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.2",
|
|
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.2",
|
|
36
|
+
"@tellescope/schema": "1.256.2",
|
|
37
|
+
"@tellescope/testing": "1.256.2",
|
|
38
|
+
"@tellescope/types-client": "1.256.2",
|
|
39
|
+
"@tellescope/types-models": "1.256.2",
|
|
40
|
+
"@tellescope/types-utilities": "1.256.2",
|
|
41
|
+
"@tellescope/utilities": "1.256.2",
|
|
42
|
+
"@tellescope/validation": "1.256.2",
|
|
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": "a796b01e2a6ccc1f40aeb07084d8b8922e4d078e"
|
|
59
59
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import axios from "axios"
|
|
4
|
+
|
|
5
|
+
import { Session } from "../../sdk"
|
|
6
|
+
import { log_header, async_test } from "@tellescope/testing"
|
|
7
|
+
import { setup_tests } from "../setup"
|
|
8
|
+
|
|
9
|
+
const host = process.env.API_URL || 'http://localhost:8080'
|
|
10
|
+
|
|
11
|
+
const ONE_DAY_IN_SECONDS = 86400
|
|
12
|
+
const EVENT_DAYS_OUT = 120 // under the old expiry (event lead time + 4 weeks) this token lived ~148 days
|
|
13
|
+
|
|
14
|
+
// Decode a JWT payload WITHOUT verifying it — the signing key is server-side only, and the only
|
|
15
|
+
// thing under test is the lifetime the server stamped on the token.
|
|
16
|
+
const decode_jwt_payload = (token: string) => {
|
|
17
|
+
const [, payload] = token.split('.')
|
|
18
|
+
if (!payload) throw new Error("Not a JWT")
|
|
19
|
+
return JSON.parse(
|
|
20
|
+
Buffer.from(payload.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8')
|
|
21
|
+
) as { iat: number, exp: number }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// The API returns a /r/<token> shortlink, not the destination — follow one hop and pull the
|
|
25
|
+
// `token` query param out of the URL the patient actually lands on.
|
|
26
|
+
const resolve_redirect_token = async (link: string) => {
|
|
27
|
+
const { status, headers } = await axios.get(link, {
|
|
28
|
+
maxRedirects: 0,
|
|
29
|
+
validateStatus: () => true,
|
|
30
|
+
})
|
|
31
|
+
const location = headers['location']
|
|
32
|
+
if (!location) throw new Error(`Expected a redirect from ${link}, got status ${status}`)
|
|
33
|
+
|
|
34
|
+
const token = new URL(location).searchParams.get('token')
|
|
35
|
+
if (!token) throw new Error(`No token query param in redirect destination: ${location}`)
|
|
36
|
+
|
|
37
|
+
return token
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const appointment_link_expiration_tests = async ({ sdk } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
41
|
+
log_header("Appointment Link Expiration")
|
|
42
|
+
|
|
43
|
+
const enduser = await sdk.api.endusers.createOne({
|
|
44
|
+
email: `appointment-link-expiration-${Date.now()}@tellescope.com`,
|
|
45
|
+
})
|
|
46
|
+
const event = await sdk.api.calendar_events.createOne({
|
|
47
|
+
title: 'Appointment Link Expiration Test',
|
|
48
|
+
durationInMinutes: 30,
|
|
49
|
+
startTimeInMS: Date.now() + EVENT_DAYS_OUT * ONE_DAY_IN_SECONDS * 1000,
|
|
50
|
+
enableVideoCall: true,
|
|
51
|
+
attendees: [{ type: 'enduser', id: enduser.id }],
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const { link } = await sdk.api.calendar_events.generate_meeting_link({
|
|
56
|
+
eventId: event.id,
|
|
57
|
+
enduserId: enduser.id,
|
|
58
|
+
})
|
|
59
|
+
const { iat, exp } = decode_jwt_payload(await resolve_redirect_token(link))
|
|
60
|
+
const lifetimeInSeconds = exp - iat
|
|
61
|
+
|
|
62
|
+
await async_test(
|
|
63
|
+
`meeting link token for an event ${EVENT_DAYS_OUT} days out expires within 24h`,
|
|
64
|
+
async () => lifetimeInSeconds,
|
|
65
|
+
{ onResult: (lifetime: number) => lifetime > 0 && lifetime <= ONE_DAY_IN_SECONDS },
|
|
66
|
+
)
|
|
67
|
+
} finally {
|
|
68
|
+
await sdk.api.calendar_events.deleteOne(event.id).catch(console.error)
|
|
69
|
+
await sdk.api.endusers.deleteOne(enduser.id).catch(console.error)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Allow running this test file independently
|
|
74
|
+
if (require.main === module) {
|
|
75
|
+
const sdk = new Session({ host })
|
|
76
|
+
const sdkNonAdmin = new Session({ host })
|
|
77
|
+
|
|
78
|
+
const runTests = async () => {
|
|
79
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
80
|
+
await appointment_link_expiration_tests({ sdk, sdkNonAdmin })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
runTests()
|
|
84
|
+
.then(() => {
|
|
85
|
+
console.log("✅ Appointment link expiration test suite completed successfully")
|
|
86
|
+
process.exit(0)
|
|
87
|
+
})
|
|
88
|
+
.catch((error) => {
|
|
89
|
+
console.error("❌ Appointment link expiration test suite failed:", error)
|
|
90
|
+
process.exit(1)
|
|
91
|
+
})
|
|
92
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
async_test,
|
|
6
|
+
log_header,
|
|
7
|
+
passOnAnyResult,
|
|
8
|
+
} from "@tellescope/testing"
|
|
9
|
+
import { setup_tests } from "../setup"
|
|
10
|
+
|
|
11
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
12
|
+
|
|
13
|
+
// Beluga Trigger Refill journey action tests (CU-86e2gbv7y).
|
|
14
|
+
//
|
|
15
|
+
// Validator-acceptance only: verifies that automation steps configured with the new
|
|
16
|
+
// belugaTriggerRefill action (org-mapping and inline-medIds variants) pass validation, and that
|
|
17
|
+
// invalid shapes are rejected. No live Beluga call is made — external/triggerRefill requires
|
|
18
|
+
// sandbox credentials, and the process endpoint's guard branches (missing integration, missing
|
|
19
|
+
// preferences) mirror the already-tested belugaAutoRx/belugaUpdateVisit handlers.
|
|
20
|
+
export const beluga_trigger_refill_tests = async ({ sdk, sdkNonAdmin }: { sdk: Session, sdkNonAdmin: Session }) => {
|
|
21
|
+
log_header("Beluga Trigger Refill Journey Action Tests")
|
|
22
|
+
|
|
23
|
+
let journeyId: string | undefined
|
|
24
|
+
let formId: string | undefined
|
|
25
|
+
const stepIds: string[] = []
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const journey = await sdk.api.journeys.createOne({ title: 'Beluga Trigger Refill Test Journey' })
|
|
29
|
+
journeyId = journey.id
|
|
30
|
+
|
|
31
|
+
const form = await sdk.api.forms.createOne({ title: 'Beluga Trigger Refill Test Form' })
|
|
32
|
+
formId = form.id
|
|
33
|
+
|
|
34
|
+
// Inline med IDs variant
|
|
35
|
+
await async_test(
|
|
36
|
+
"belugaTriggerRefill step accepted (inline medIds)",
|
|
37
|
+
() => sdk.api.automation_steps.createOne({
|
|
38
|
+
journeyId: journey.id,
|
|
39
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
40
|
+
action: {
|
|
41
|
+
type: 'belugaTriggerRefill',
|
|
42
|
+
info: {
|
|
43
|
+
formId: form.id,
|
|
44
|
+
patientPreferences: [{ medId: 'med-123' }, { medId: 'med-456' }],
|
|
45
|
+
customFieldName: 'Refill Medication',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
}).then(s => { stepIds.push(s.id); return s }),
|
|
49
|
+
passOnAnyResult,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
// Organization mapping variant
|
|
53
|
+
await async_test(
|
|
54
|
+
"belugaTriggerRefill step accepted (organization mapping)",
|
|
55
|
+
() => sdk.api.automation_steps.createOne({
|
|
56
|
+
journeyId: journey.id,
|
|
57
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
58
|
+
action: {
|
|
59
|
+
type: 'belugaTriggerRefill',
|
|
60
|
+
info: {
|
|
61
|
+
formId: form.id,
|
|
62
|
+
useOrganizationMapping: true,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
}).then(s => { stepIds.push(s.id); return s }),
|
|
66
|
+
passOnAnyResult,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
// Invalid formId (not a mongo id) rejected
|
|
70
|
+
await async_test(
|
|
71
|
+
"belugaTriggerRefill step rejected (invalid formId)",
|
|
72
|
+
() => sdk.api.automation_steps.createOne({
|
|
73
|
+
journeyId: journey.id,
|
|
74
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
75
|
+
action: {
|
|
76
|
+
type: 'belugaTriggerRefill',
|
|
77
|
+
info: {
|
|
78
|
+
formId: 'not-a-mongo-id',
|
|
79
|
+
patientPreferences: [{ medId: 'med-123' }],
|
|
80
|
+
},
|
|
81
|
+
} as any,
|
|
82
|
+
}),
|
|
83
|
+
{ shouldError: true, onError: () => true },
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
// Missing formId rejected
|
|
87
|
+
await async_test(
|
|
88
|
+
"belugaTriggerRefill step rejected (missing formId)",
|
|
89
|
+
() => sdk.api.automation_steps.createOne({
|
|
90
|
+
journeyId: journey.id,
|
|
91
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
92
|
+
action: {
|
|
93
|
+
type: 'belugaTriggerRefill',
|
|
94
|
+
info: {
|
|
95
|
+
patientPreferences: [{ medId: 'med-123' }],
|
|
96
|
+
},
|
|
97
|
+
} as any,
|
|
98
|
+
}),
|
|
99
|
+
{ shouldError: true, onError: () => true },
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// Unexpected fields in a patient preference are stripped by objectValidator, not rejected —
|
|
103
|
+
// assert they never reach the stored config (the step itself is accepted)
|
|
104
|
+
await async_test(
|
|
105
|
+
"belugaTriggerRefill step strips unexpected patientPreferences field",
|
|
106
|
+
() => sdk.api.automation_steps.createOne({
|
|
107
|
+
journeyId: journey.id,
|
|
108
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
109
|
+
action: {
|
|
110
|
+
type: 'belugaTriggerRefill',
|
|
111
|
+
info: {
|
|
112
|
+
formId: form.id,
|
|
113
|
+
patientPreferences: [{ medId: 'med-123', pharmacyId: 'not-allowed' }],
|
|
114
|
+
},
|
|
115
|
+
} as any,
|
|
116
|
+
}).then(s => { stepIds.push(s.id); return s }),
|
|
117
|
+
{
|
|
118
|
+
onResult: s => {
|
|
119
|
+
const preference = (s.action.info as any).patientPreferences?.[0]
|
|
120
|
+
return preference?.medId === 'med-123' && preference?.pharmacyId === undefined
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
} finally {
|
|
125
|
+
for (const id of stepIds) {
|
|
126
|
+
await sdk.api.automation_steps.deleteOne(id).catch(console.error)
|
|
127
|
+
}
|
|
128
|
+
if (formId) await sdk.api.forms.deleteOne(formId).catch(console.error)
|
|
129
|
+
if (journeyId) await sdk.api.journeys.deleteOne(journeyId).catch(console.error)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (require.main === module) {
|
|
134
|
+
console.log(`Using API URL: ${host}`)
|
|
135
|
+
const sdk = new Session({ host })
|
|
136
|
+
const sdkNonAdmin = new Session({ host })
|
|
137
|
+
|
|
138
|
+
const runTests = async () => {
|
|
139
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
140
|
+
await beluga_trigger_refill_tests({ sdk, sdkNonAdmin })
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
runTests()
|
|
144
|
+
.then(() => {
|
|
145
|
+
console.log("✅ Beluga trigger refill test suite completed successfully")
|
|
146
|
+
process.exit(0)
|
|
147
|
+
})
|
|
148
|
+
.catch((error) => {
|
|
149
|
+
console.error("❌ Beluga trigger refill test suite failed:", error)
|
|
150
|
+
process.exit(1)
|
|
151
|
+
})
|
|
152
|
+
}
|