@tellescope/sdk 1.244.1 → 1.244.3
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 +3 -0
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/session.d.ts +8 -1
- package/lib/cjs/session.d.ts.map +1 -1
- package/lib/cjs/session.js +2 -4
- package/lib/cjs/session.js.map +1 -1
- package/lib/cjs/tests/api_tests/elation_user_id.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/elation_user_id.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/elation_user_id.test.js +106 -0
- package/lib/cjs/tests/api_tests/elation_user_id.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/get_some_projection.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/get_some_projection.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/get_some_projection.test.js +373 -0
- package/lib/cjs/tests/api_tests/get_some_projection.test.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +268 -184
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/sdk.d.ts +5 -2
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/session.d.ts +8 -1
- package/lib/esm/session.d.ts.map +1 -1
- package/lib/esm/session.js +2 -4
- package/lib/esm/session.js.map +1 -1
- package/lib/esm/tests/api_tests/elation_user_id.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/elation_user_id.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/elation_user_id.test.js +102 -0
- package/lib/esm/tests/api_tests/elation_user_id.test.js.map +1 -0
- package/lib/esm/tests/api_tests/get_some_projection.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/get_some_projection.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/get_some_projection.test.js +369 -0
- package/lib/esm/tests/api_tests/get_some_projection.test.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +268 -184
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/sdk.ts +5 -4
- package/src/session.ts +13 -3
- package/src/tests/api_tests/elation_user_id.test.ts +59 -0
- package/src/tests/api_tests/get_some_projection.test.ts +245 -0
- package/src/tests/tests.ts +113 -12
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
async_test,
|
|
6
|
+
handleAnyError,
|
|
7
|
+
log_header,
|
|
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
|
+
export const elation_user_id_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
14
|
+
log_header("Elation User ID Tests")
|
|
15
|
+
|
|
16
|
+
const randomElationUserId = Math.floor(Math.random() * 1000000)
|
|
17
|
+
|
|
18
|
+
// Admin can set elationUserId
|
|
19
|
+
await async_test(
|
|
20
|
+
`admin can update elationUserId`,
|
|
21
|
+
() => sdk.api.users.updateOne(sdk.userInfo.id, { elationUserId: randomElationUserId }),
|
|
22
|
+
{ shouldError: false, onResult: (u) => u.elationUserId === randomElationUserId }
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// Verify the value persists on read
|
|
26
|
+
await async_test(
|
|
27
|
+
`elationUserId persists after update`,
|
|
28
|
+
() => sdk.api.users.getOne(sdk.userInfo.id),
|
|
29
|
+
{ shouldError: false, onResult: (u) => u.elationUserId === randomElationUserId }
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// Non-admin cannot update elationUserId
|
|
33
|
+
await async_test(
|
|
34
|
+
`non-admin cannot update elationUserId`,
|
|
35
|
+
() => sdkNonAdmin.api.users.updateOne(sdkNonAdmin.userInfo.id, { elationUserId: 999999 }),
|
|
36
|
+
handleAnyError
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Allow running this test file independently
|
|
41
|
+
if (require.main === module) {
|
|
42
|
+
const sdk = new Session({ host })
|
|
43
|
+
const sdkNonAdmin = new Session({ host })
|
|
44
|
+
|
|
45
|
+
const runTests = async () => {
|
|
46
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
47
|
+
await elation_user_id_tests({ sdk, sdkNonAdmin })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
runTests()
|
|
51
|
+
.then(() => {
|
|
52
|
+
console.log("✅ Elation User ID test suite completed successfully")
|
|
53
|
+
process.exit(0)
|
|
54
|
+
})
|
|
55
|
+
.catch((error) => {
|
|
56
|
+
console.error("❌ Elation User ID test suite failed:", error)
|
|
57
|
+
process.exit(1)
|
|
58
|
+
})
|
|
59
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
assert,
|
|
6
|
+
async_test,
|
|
7
|
+
log_header,
|
|
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
|
+
// Main test function that can be called independently
|
|
14
|
+
export const get_some_projection_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
15
|
+
log_header("getSome Projection Support")
|
|
16
|
+
|
|
17
|
+
// Create test enduser
|
|
18
|
+
const testEnduser = await sdk.api.endusers.createOne({ fname: 'ProjectionTest', lname: 'User', email: 'projection-test@tellescope.com' })
|
|
19
|
+
|
|
20
|
+
let observationIds: string[] = []
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Create test observations
|
|
24
|
+
const obs1 = await sdk.api.enduser_observations.createOne({
|
|
25
|
+
enduserId: testEnduser.id,
|
|
26
|
+
status: 'final',
|
|
27
|
+
category: 'vital-signs',
|
|
28
|
+
measurement: { value: 120, unit: 'mmHg' },
|
|
29
|
+
})
|
|
30
|
+
const obs2 = await sdk.api.enduser_observations.createOne({
|
|
31
|
+
enduserId: testEnduser.id,
|
|
32
|
+
status: 'final',
|
|
33
|
+
category: 'vital-signs',
|
|
34
|
+
measurement: { value: 80, unit: 'mmHg' },
|
|
35
|
+
})
|
|
36
|
+
const obs3 = await sdk.api.enduser_observations.createOne({
|
|
37
|
+
enduserId: testEnduser.id,
|
|
38
|
+
status: 'final',
|
|
39
|
+
category: 'vital-signs',
|
|
40
|
+
measurement: { value: 98, unit: 'bpm' },
|
|
41
|
+
})
|
|
42
|
+
observationIds = [obs1.id, obs2.id, obs3.id]
|
|
43
|
+
|
|
44
|
+
// Test 1: Inclusion projection returns only specified fields
|
|
45
|
+
await async_test(
|
|
46
|
+
'projection-inclusion',
|
|
47
|
+
async () => {
|
|
48
|
+
const results = await sdk.api.enduser_observations.getSome({
|
|
49
|
+
filter: { enduserId: testEnduser.id },
|
|
50
|
+
projection: { timestamp: 1, measurement: 1, enduserId: 1 },
|
|
51
|
+
})
|
|
52
|
+
assert(results.length === 3, 'Expected 3 observations with projection', 'Got correct count with projection')
|
|
53
|
+
|
|
54
|
+
for (const r of results) {
|
|
55
|
+
assert(r.id !== undefined, 'id should always be present')
|
|
56
|
+
assert(r.createdAt !== undefined, 'createdAt should always be present')
|
|
57
|
+
assert(r.measurement !== undefined, 'measurement should be present in projection')
|
|
58
|
+
assert(r.enduserId !== undefined, 'enduserId should be present in projection')
|
|
59
|
+
assert(r.timestamp !== undefined, 'timestamp should be present in projection')
|
|
60
|
+
// Fields NOT in projection should be excluded
|
|
61
|
+
assert((r as any).status === undefined, 'status should NOT be present when not in projection')
|
|
62
|
+
assert((r as any).category === undefined, 'category should NOT be present when not in projection')
|
|
63
|
+
}
|
|
64
|
+
return results
|
|
65
|
+
},
|
|
66
|
+
{ onResult: () => true },
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
// Test 2: No projection returns all fields (backward compatibility)
|
|
70
|
+
await async_test(
|
|
71
|
+
'no-projection-returns-all-fields',
|
|
72
|
+
async () => {
|
|
73
|
+
const results = await sdk.api.enduser_observations.getSome({
|
|
74
|
+
filter: { enduserId: testEnduser.id },
|
|
75
|
+
})
|
|
76
|
+
assert(results.length === 3, 'Expected 3 observations without projection')
|
|
77
|
+
|
|
78
|
+
for (const r of results) {
|
|
79
|
+
assert(r.id !== undefined, 'id should be present')
|
|
80
|
+
assert(r.status !== undefined, 'status should be present without projection')
|
|
81
|
+
assert(r.category !== undefined, 'category should be present without projection')
|
|
82
|
+
assert(r.measurement !== undefined, 'measurement should be present without projection')
|
|
83
|
+
assert(r.enduserId !== undefined, 'enduserId should be present without projection')
|
|
84
|
+
}
|
|
85
|
+
return results
|
|
86
|
+
},
|
|
87
|
+
{ onResult: () => true },
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
// Test 3: Projection with other filters (filter, limit, sortBy)
|
|
91
|
+
await async_test(
|
|
92
|
+
'projection-with-filters',
|
|
93
|
+
async () => {
|
|
94
|
+
const results = await sdk.api.enduser_observations.getSome({
|
|
95
|
+
filter: { enduserId: testEnduser.id },
|
|
96
|
+
projection: { measurement: 1 },
|
|
97
|
+
limit: 2,
|
|
98
|
+
sortBy: 'timestamp',
|
|
99
|
+
})
|
|
100
|
+
assert(results.length === 2, 'Expected 2 observations with limit=2', `Got ${results.length}`)
|
|
101
|
+
|
|
102
|
+
for (const r of results) {
|
|
103
|
+
assert(r.measurement !== undefined, 'measurement should be present in projection')
|
|
104
|
+
assert((r as any).status === undefined, 'status should NOT be present when not in projection')
|
|
105
|
+
}
|
|
106
|
+
return results
|
|
107
|
+
},
|
|
108
|
+
{ onResult: () => true },
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
// Test 4: Projection works across different models (endusers)
|
|
112
|
+
await async_test(
|
|
113
|
+
'projection-on-endusers',
|
|
114
|
+
async () => {
|
|
115
|
+
const results = await sdk.api.endusers.getSome({
|
|
116
|
+
filter: { email: 'projection-test@tellescope.com' },
|
|
117
|
+
projection: { fname: 1, email: 1 },
|
|
118
|
+
})
|
|
119
|
+
assert(results.length >= 1, 'Expected at least 1 enduser')
|
|
120
|
+
|
|
121
|
+
const enduser = results[0]
|
|
122
|
+
assert(enduser.id !== undefined, 'id should always be present')
|
|
123
|
+
assert(enduser.fname !== undefined, 'fname should be present in projection')
|
|
124
|
+
assert(enduser.email !== undefined, 'email should be present in projection')
|
|
125
|
+
assert((enduser as any).lname === undefined, 'lname should NOT be present when not in projection')
|
|
126
|
+
return results
|
|
127
|
+
},
|
|
128
|
+
{ onResult: () => true },
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
// Test 5: Projection with pagination (lastId)
|
|
132
|
+
await async_test(
|
|
133
|
+
'projection-with-pagination',
|
|
134
|
+
async () => {
|
|
135
|
+
// First page
|
|
136
|
+
const page1 = await sdk.api.enduser_observations.getSome({
|
|
137
|
+
filter: { enduserId: testEnduser.id },
|
|
138
|
+
projection: { measurement: 1, enduserId: 1 },
|
|
139
|
+
limit: 2,
|
|
140
|
+
sort: 'oldFirst',
|
|
141
|
+
})
|
|
142
|
+
assert(page1.length === 2, 'Expected 2 observations on first page')
|
|
143
|
+
|
|
144
|
+
// Second page using lastId from first page
|
|
145
|
+
const lastId = page1[page1.length - 1].id
|
|
146
|
+
const page2 = await sdk.api.enduser_observations.getSome({
|
|
147
|
+
filter: { enduserId: testEnduser.id },
|
|
148
|
+
projection: { measurement: 1, enduserId: 1 },
|
|
149
|
+
limit: 2,
|
|
150
|
+
sort: 'oldFirst',
|
|
151
|
+
lastId,
|
|
152
|
+
})
|
|
153
|
+
assert(page2.length === 1, 'Expected 1 observation on second page')
|
|
154
|
+
|
|
155
|
+
// Verify consistent fields across pages
|
|
156
|
+
for (const r of [...page1, ...page2]) {
|
|
157
|
+
assert(r.measurement !== undefined, 'measurement should be present across pages')
|
|
158
|
+
assert(r.enduserId !== undefined, 'enduserId should be present across pages')
|
|
159
|
+
assert((r as any).status === undefined, 'status should NOT be present across pages')
|
|
160
|
+
}
|
|
161
|
+
return [...page1, ...page2]
|
|
162
|
+
},
|
|
163
|
+
{ onResult: () => true },
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
// Test 6: Empty projection returns all fields (no crash)
|
|
167
|
+
await async_test(
|
|
168
|
+
'empty-projection',
|
|
169
|
+
async () => {
|
|
170
|
+
const results = await sdk.api.enduser_observations.getSome({
|
|
171
|
+
filter: { enduserId: testEnduser.id },
|
|
172
|
+
projection: {},
|
|
173
|
+
})
|
|
174
|
+
assert(results.length === 3, 'Expected 3 observations with empty projection')
|
|
175
|
+
|
|
176
|
+
for (const r of results) {
|
|
177
|
+
assert(r.status !== undefined, 'status should be present with empty projection')
|
|
178
|
+
assert(r.category !== undefined, 'category should be present with empty projection')
|
|
179
|
+
assert(r.measurement !== undefined, 'measurement should be present with empty projection')
|
|
180
|
+
}
|
|
181
|
+
return results
|
|
182
|
+
},
|
|
183
|
+
{ onResult: () => true },
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
// Test 7: Non-admin access with projection (RBA still applies)
|
|
187
|
+
await async_test(
|
|
188
|
+
'non-admin-projection',
|
|
189
|
+
async () => {
|
|
190
|
+
const results = await sdkNonAdmin.api.enduser_observations.getSome({
|
|
191
|
+
filter: { enduserId: testEnduser.id },
|
|
192
|
+
projection: { measurement: 1, enduserId: 1 },
|
|
193
|
+
})
|
|
194
|
+
// Non-admin should still get results (RBA should apply)
|
|
195
|
+
assert(Array.isArray(results), 'Non-admin should receive an array response')
|
|
196
|
+
|
|
197
|
+
for (const r of results) {
|
|
198
|
+
assert(r.measurement !== undefined, 'measurement should be present for non-admin projection')
|
|
199
|
+
assert(r.enduserId !== undefined, 'enduserId should be present for non-admin projection')
|
|
200
|
+
}
|
|
201
|
+
return results
|
|
202
|
+
},
|
|
203
|
+
{ onResult: () => true },
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
} finally {
|
|
207
|
+
// Cleanup: Delete test resources
|
|
208
|
+
try {
|
|
209
|
+
for (const obsId of observationIds) {
|
|
210
|
+
await sdk.api.enduser_observations.deleteOne(obsId)
|
|
211
|
+
}
|
|
212
|
+
await sdk.api.endusers.deleteOne(testEnduser.id)
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error('Cleanup error:', error)
|
|
215
|
+
// Try to at least delete the enduser (cascade deletes observations)
|
|
216
|
+
try {
|
|
217
|
+
await sdk.api.endusers.deleteOne(testEnduser.id)
|
|
218
|
+
} catch (deleteError) {
|
|
219
|
+
console.error('Failed to delete test enduser:', deleteError)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Allow running this test file independently
|
|
226
|
+
if (require.main === module) {
|
|
227
|
+
console.log(`🌐 Using API URL: ${host}`)
|
|
228
|
+
const sdk = new Session({ host })
|
|
229
|
+
const sdkNonAdmin = new Session({ host })
|
|
230
|
+
|
|
231
|
+
const runTests = async () => {
|
|
232
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
233
|
+
await get_some_projection_tests({ sdk, sdkNonAdmin })
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
runTests()
|
|
237
|
+
.then(() => {
|
|
238
|
+
console.log("✅ getSome projection test suite completed successfully")
|
|
239
|
+
process.exit(0)
|
|
240
|
+
})
|
|
241
|
+
.catch((error) => {
|
|
242
|
+
console.error("❌ getSome projection test suite failed:", error)
|
|
243
|
+
process.exit(1)
|
|
244
|
+
})
|
|
245
|
+
}
|
package/src/tests/tests.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
|
|
37
37
|
import { Session, APIQuery, EnduserSession } from "../sdk"
|
|
38
38
|
import { enduser_observations_acknowledge_tests } from "./api_tests/enduser_observations_acknowledge.test"
|
|
39
|
+
import { get_some_projection_tests } from "./api_tests/get_some_projection.test"
|
|
39
40
|
import { create_user_notifications_trigger_tests } from "./api_tests/create_user_notifications_trigger.test"
|
|
40
41
|
import { inbox_thread_assignment_updates_tests } from "./api_tests/inbox_thread_assignment_updates.test"
|
|
41
42
|
import { inbox_thread_draft_scheduled_tests } from "./api_tests/inbox_thread_draft_scheduled.test"
|
|
@@ -85,6 +86,7 @@ import { database_cascade_delete_tests } from "./api_tests/database_cascade_dele
|
|
|
85
86
|
import { ai_conversations_tests } from "./api_tests/ai_conversations.test";
|
|
86
87
|
import { load_team_chat_tests } from "./api_tests/load_team_chat.test";
|
|
87
88
|
import { form_started_trigger_tests } from "./api_tests/form_started_trigger.test";
|
|
89
|
+
import { elation_user_id_tests } from "./api_tests/elation_user_id.test";
|
|
88
90
|
|
|
89
91
|
const UniquenessViolationMessage = 'Uniqueness Violation'
|
|
90
92
|
|
|
@@ -3415,6 +3417,12 @@ const order_status_equals_tests = async () => {
|
|
|
3415
3417
|
status: 'Active',
|
|
3416
3418
|
title: "Title Partial Condition"
|
|
3417
3419
|
})
|
|
3420
|
+
const t8 = await sdk.api.automation_triggers.createOne({
|
|
3421
|
+
event: { type: 'Order Status Equals', info: { source: 'Source', status: "Update", titlePartialsAnd: ['PARTIAL-A', 'PARTIAL-B'] } },
|
|
3422
|
+
action: { type: 'Add Tags', info: { tags: ['Title Partial And Update'] }},
|
|
3423
|
+
status: 'Active',
|
|
3424
|
+
title: "Title Partial And Condition"
|
|
3425
|
+
})
|
|
3418
3426
|
|
|
3419
3427
|
const e = await sdk.api.endusers.createOne({})
|
|
3420
3428
|
|
|
@@ -3565,6 +3573,37 @@ const order_status_equals_tests = async () => {
|
|
|
3565
3573
|
) }
|
|
3566
3574
|
)
|
|
3567
3575
|
|
|
3576
|
+
await sdk.api.enduser_orders.updateOne(u.id, { status: 'Toggle', externalId: "also avoid rate limit 5" })
|
|
3577
|
+
await sdk.api.enduser_orders.updateOne(u.id, { status: "Update", title: "PARTIAL-A only" })
|
|
3578
|
+
await wait(undefined, 500) // allow triggers to happen
|
|
3579
|
+
await async_test(
|
|
3580
|
+
"Title partial AND no match (only one partial present)",
|
|
3581
|
+
() => sdk.api.endusers.getOne(e.id),
|
|
3582
|
+
{ onResult: e => !!(
|
|
3583
|
+
e.tags?.length === 7
|
|
3584
|
+
&& !e.tags?.includes('Title Partial And Update')
|
|
3585
|
+
) }
|
|
3586
|
+
)
|
|
3587
|
+
|
|
3588
|
+
await sdk.api.enduser_orders.updateOne(u.id, { status: 'Toggle', externalId: "also avoid rate limit 6" })
|
|
3589
|
+
await sdk.api.enduser_orders.updateOne(u.id, { status: "Update", title: "PARTIAL-A and PARTIAL-B together" })
|
|
3590
|
+
await wait(undefined, 500) // allow triggers to happen
|
|
3591
|
+
await async_test(
|
|
3592
|
+
"Title partial AND update tag added (both partials present)",
|
|
3593
|
+
() => sdk.api.endusers.getOne(e.id),
|
|
3594
|
+
{ onResult: e => !!(
|
|
3595
|
+
e.tags?.length === 8
|
|
3596
|
+
&& e.tags?.includes('Source')
|
|
3597
|
+
&& e.tags?.includes('Fill')
|
|
3598
|
+
&& e.tags?.includes('Status Update')
|
|
3599
|
+
&& e.tags?.includes('Fill Update')
|
|
3600
|
+
&& e.tags?.includes('SKU Update')
|
|
3601
|
+
&& e.tags?.includes('SKU Partial Update')
|
|
3602
|
+
&& e.tags?.includes('Title Partial Update')
|
|
3603
|
+
&& e.tags?.includes('Title Partial And Update')
|
|
3604
|
+
) }
|
|
3605
|
+
)
|
|
3606
|
+
|
|
3568
3607
|
await Promise.all([
|
|
3569
3608
|
sdk.api.automation_triggers.deleteOne(t1.id),
|
|
3570
3609
|
sdk.api.automation_triggers.deleteOne(t2.id),
|
|
@@ -3573,6 +3612,7 @@ const order_status_equals_tests = async () => {
|
|
|
3573
3612
|
sdk.api.automation_triggers.deleteOne(t5.id),
|
|
3574
3613
|
sdk.api.automation_triggers.deleteOne(t6.id),
|
|
3575
3614
|
sdk.api.automation_triggers.deleteOne(t7.id),
|
|
3615
|
+
sdk.api.automation_triggers.deleteOne(t8.id),
|
|
3576
3616
|
sdk.api.endusers.deleteOne(e.id),
|
|
3577
3617
|
])
|
|
3578
3618
|
}
|
|
@@ -4970,6 +5010,7 @@ const trigger_events_api_tests = async () => {
|
|
|
4970
5010
|
const automation_trigger_tests = async () => {
|
|
4971
5011
|
log_header("Automation Trigger Tests")
|
|
4972
5012
|
|
|
5013
|
+
await order_status_equals_tests()
|
|
4973
5014
|
await appointment_cancelled_tests()
|
|
4974
5015
|
await set_fields_tests()
|
|
4975
5016
|
await purchase_made_trigger_tests({ sdk, sdkNonAdmin })
|
|
@@ -4977,7 +5018,6 @@ const automation_trigger_tests = async () => {
|
|
|
4977
5018
|
await form_response_set_fields_trigger_tests()
|
|
4978
5019
|
await form_response_set_fields_journey_tests()
|
|
4979
5020
|
await appointment_completed_trigger_tests({ sdk, sdkNonAdmin })
|
|
4980
|
-
await order_status_equals_tests()
|
|
4981
5021
|
await trigger_events_api_tests()
|
|
4982
5022
|
await fields_changed_tests()
|
|
4983
5023
|
await field_equals_trigger_tests()
|
|
@@ -5572,28 +5612,87 @@ const redaction_tests = async () => {
|
|
|
5572
5612
|
log_header("Redaction")
|
|
5573
5613
|
|
|
5574
5614
|
const enduser = await sdk.api.endusers.createOne({ email })
|
|
5575
|
-
const enduserOther = await sdk.api.endusers.createOne({
|
|
5615
|
+
const enduserOther = await sdk.api.endusers.createOne({
|
|
5616
|
+
email: 'otherenduser@tellescope.com',
|
|
5617
|
+
unredactedFields: [{ field: 'testField', value: 'testValue' }] as any,
|
|
5618
|
+
})
|
|
5576
5619
|
await sdk.api.endusers.set_password({ id: enduser.id, password }).catch(console.error)
|
|
5577
|
-
await enduserSDK.authenticate(email, password).catch(console.error)
|
|
5620
|
+
await enduserSDK.authenticate(email, password).catch(console.error)
|
|
5578
5621
|
|
|
5579
5622
|
const endusers = await enduserSDK.api.endusers.getSome()
|
|
5580
5623
|
const forUser = await sdk.api.endusers.getSome()
|
|
5581
5624
|
assert(endusers.length > 0, "enduser can't fetch others", "enduser get others successful")
|
|
5582
5625
|
|
|
5626
|
+
// endusers reading OTHER endusers should only see id, displayName, unredactedFields, and unredactedTags
|
|
5627
|
+
const ALLOWED_OTHER_ENDUSER_FIELDS = ['id', 'displayName', 'unredactedFields', 'unredactedTags']
|
|
5628
|
+
const otherEndusers = endusers.filter(e => e.id !== enduser.id)
|
|
5629
|
+
assert(otherEndusers.length > 0, 'no other endusers found', 'found other endusers to check redaction')
|
|
5630
|
+
for (const other of otherEndusers) {
|
|
5631
|
+
const keys = Object.keys(other)
|
|
5632
|
+
assert(
|
|
5633
|
+
keys.every(k => ALLOWED_OTHER_ENDUSER_FIELDS.includes(k)),
|
|
5634
|
+
`other enduser has extra fields: ${keys.filter(k => !ALLOWED_OTHER_ENDUSER_FIELDS.includes(k)).join(', ')}`,
|
|
5635
|
+
'other enduser correctly redacted to only id, displayName, unredactedFields, unredactedTags',
|
|
5636
|
+
)
|
|
5637
|
+
assert(
|
|
5638
|
+
keys.includes('id'),
|
|
5639
|
+
`other enduser missing id`,
|
|
5640
|
+
'other enduser has id',
|
|
5641
|
+
)
|
|
5642
|
+
}
|
|
5643
|
+
|
|
5644
|
+
// verify unredactedFields are preserved for other endusers when defined
|
|
5645
|
+
const otherWithUnredacted = otherEndusers.find(e => e.id === enduserOther.id)
|
|
5646
|
+
if (otherWithUnredacted) {
|
|
5647
|
+
assert(
|
|
5648
|
+
!!(otherWithUnredacted as any).unredactedFields?.length,
|
|
5649
|
+
'unredactedFields missing on other enduser',
|
|
5650
|
+
'unredactedFields preserved on other enduser when defined',
|
|
5651
|
+
)
|
|
5652
|
+
}
|
|
5653
|
+
|
|
5654
|
+
// verify unredactedFields/unredactedTags are omitted when not set on the enduser
|
|
5655
|
+
const otherWithoutUnredacted = otherEndusers.find(e => e.id !== enduserOther.id)
|
|
5656
|
+
if (otherWithoutUnredacted) {
|
|
5657
|
+
assert(
|
|
5658
|
+
!('unredactedFields' in otherWithoutUnredacted),
|
|
5659
|
+
'unredactedFields present when not set',
|
|
5660
|
+
'unredactedFields omitted when not defined on enduser',
|
|
5661
|
+
)
|
|
5662
|
+
assert(
|
|
5663
|
+
!('unredactedTags' in otherWithoutUnredacted),
|
|
5664
|
+
'unredactedTags present when not set',
|
|
5665
|
+
'unredactedTags omitted when not defined on enduser',
|
|
5666
|
+
)
|
|
5667
|
+
}
|
|
5668
|
+
|
|
5669
|
+
// enduser reading their own profile should still have non-redacted fields (not just id + displayName)
|
|
5670
|
+
const selfEnduser = endusers.find(e => e.id === enduser.id)
|
|
5671
|
+
if (selfEnduser) {
|
|
5672
|
+
assert(
|
|
5673
|
+
Object.keys(selfEnduser).length > ALLOWED_OTHER_ENDUSER_FIELDS.length,
|
|
5674
|
+
'self enduser overly redacted',
|
|
5675
|
+
'self enduser retains non-redacted fields',
|
|
5676
|
+
)
|
|
5677
|
+
}
|
|
5678
|
+
|
|
5679
|
+
// verify schema-level redactions still apply to self
|
|
5583
5680
|
const redactedFields = (
|
|
5584
5681
|
Object.keys(schema.endusers.fields)
|
|
5585
|
-
.filter(f =>
|
|
5682
|
+
.filter(f =>
|
|
5586
5683
|
schema.endusers.fields[f as keyof typeof schema.endusers.fields]?.redactions?.includes('enduser')
|
|
5587
5684
|
|| schema.endusers.fields[f as keyof typeof schema.endusers.fields]?.redactions?.includes('all')
|
|
5588
5685
|
)
|
|
5589
5686
|
)
|
|
5590
5687
|
assert(redactedFields.length > 0, 'no redacted fields', 'redacted fields exists')
|
|
5591
5688
|
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5689
|
+
if (selfEnduser) {
|
|
5690
|
+
assert(
|
|
5691
|
+
redactedFields.filter(f => !!(selfEnduser as any)[f]).length === 0,
|
|
5692
|
+
'self enduser got redacted data',
|
|
5693
|
+
'self enduser data correctly redacted per schema',
|
|
5694
|
+
)
|
|
5695
|
+
}
|
|
5597
5696
|
|
|
5598
5697
|
assert(
|
|
5599
5698
|
!forUser.find(u => u.hashedPassword),
|
|
@@ -10124,8 +10223,8 @@ export const ticket_reminder_tests = async () => {
|
|
|
10124
10223
|
const title = 't'
|
|
10125
10224
|
|
|
10126
10225
|
const LEEWAY = 200
|
|
10127
|
-
const withLeeway = (source: number | undefined, target: number) => (
|
|
10128
|
-
source
|
|
10226
|
+
const withLeeway = (source: number | null | undefined, target: number) => (
|
|
10227
|
+
source != null && ((source - LEEWAY) < target || (source + LEEWAY) > target)
|
|
10129
10228
|
)
|
|
10130
10229
|
|
|
10131
10230
|
await async_test(
|
|
@@ -13947,6 +14046,9 @@ const ip_address_form_tests = async () => {
|
|
|
13947
14046
|
await replace_enduser_template_values_tests()
|
|
13948
14047
|
await mfa_tests()
|
|
13949
14048
|
await setup_tests(sdk, sdkNonAdmin)
|
|
14049
|
+
await automation_trigger_tests()
|
|
14050
|
+
await get_some_projection_tests({ sdk, sdkNonAdmin })
|
|
14051
|
+
await elation_user_id_tests({ sdk, sdkNonAdmin })
|
|
13950
14052
|
await custom_dashboards_tests({ sdk, sdkNonAdmin })
|
|
13951
14053
|
await concurrent_build_threads_tests({ sdk, sdkNonAdmin })
|
|
13952
14054
|
await custom_aggregation_tests({ sdk, sdkNonAdmin })
|
|
@@ -13961,7 +14063,6 @@ const ip_address_form_tests = async () => {
|
|
|
13961
14063
|
await inbox_threads_new_fields_tests()
|
|
13962
14064
|
await auto_merge_form_submission_tests({ sdk, sdkNonAdmin })
|
|
13963
14065
|
await threadKeyTests()
|
|
13964
|
-
await automation_trigger_tests()
|
|
13965
14066
|
await managed_content_enduser_access_tests({ sdk, sdkNonAdmin })
|
|
13966
14067
|
await afteraction_day_of_month_delay_tests({ sdk, sdkNonAdmin })
|
|
13967
14068
|
await bulk_assignment_tests({ sdk, sdkNonAdmin })
|
package/test_generated.pdf
CHANGED
|
Binary file
|