@tellescope/sdk 1.232.0 → 1.233.0

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.
@@ -0,0 +1,140 @@
1
+ require('source-map-support').install();
2
+
3
+ import { Session } from "../../sdk"
4
+ import {
5
+ async_test,
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
+ // Main test function that can be called independently
13
+ export const custom_aggregation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
14
+ log_header("Custom Aggregation Tests")
15
+
16
+ // Create test data: an enduser with a password to ensure hashedPassword gets set
17
+ const testEnduser = await sdk.api.endusers.createOne({
18
+ fname: 'CustomAgg',
19
+ lname: 'TestUser',
20
+ email: 'custom-agg-test@example.com',
21
+ })
22
+
23
+ // Set a password on the enduser (this creates a hashedPassword field)
24
+ await sdk.api.endusers.set_password({
25
+ id: testEnduser.id,
26
+ password: 'TestPassword123!',
27
+ })
28
+
29
+ try {
30
+ // Test 1: Basic aggregation works
31
+ await async_test(
32
+ "Custom aggregation - basic query works",
33
+ () => sdk.api.analytics_frames.custom_aggregation({
34
+ modelName: 'endusers',
35
+ aggregation: [
36
+ { $match: { fname: 'CustomAgg' } },
37
+ { $count: 'total' }
38
+ ]
39
+ }),
40
+ { onResult: r => r.result[0]?.total === 1 }
41
+ )
42
+
43
+ // Test 2: Aggregation returns enduser data
44
+ await async_test(
45
+ "Custom aggregation - returns enduser fields",
46
+ () => sdk.api.analytics_frames.custom_aggregation({
47
+ modelName: 'endusers',
48
+ aggregation: [
49
+ { $match: { fname: 'CustomAgg' } },
50
+ { $project: { fname: 1, lname: 1, email: 1 } }
51
+ ]
52
+ }),
53
+ { onResult: r => {
54
+ const user = r.result[0]
55
+ return user.fname === 'CustomAgg'
56
+ && user.lname === 'TestUser'
57
+ && user.email === 'custom-agg-test@example.com'
58
+ }}
59
+ )
60
+
61
+ // Test 3: CRITICAL - hashedPassword is redacted even if requested
62
+ await async_test(
63
+ "Custom aggregation - hashedPassword is redacted (explicit project)",
64
+ () => sdk.api.analytics_frames.custom_aggregation({
65
+ modelName: 'endusers',
66
+ aggregation: [
67
+ { $match: { fname: 'CustomAgg' } },
68
+ { $project: { fname: 1, hashedPassword: 1 } } // Explicitly request password
69
+ ]
70
+ }),
71
+ { onResult: r => {
72
+ const user = r.result[0]
73
+ // Should have fname but NOT hashedPassword
74
+ return user.fname === 'CustomAgg' && user.hashedPassword === undefined
75
+ }}
76
+ )
77
+
78
+ // Test 4: hashedPassword is redacted even without explicit project
79
+ await async_test(
80
+ "Custom aggregation - hashedPassword is redacted (no project)",
81
+ () => sdk.api.analytics_frames.custom_aggregation({
82
+ modelName: 'endusers',
83
+ aggregation: [
84
+ { $match: { fname: 'CustomAgg' } },
85
+ { $limit: 1 }
86
+ ]
87
+ }),
88
+ { onResult: r => {
89
+ const user = r.result[0]
90
+ // Should return user data but NOT hashedPassword
91
+ return user.fname === 'CustomAgg' && user.hashedPassword === undefined
92
+ }}
93
+ )
94
+
95
+ // Test 5: Aggregation with grouping doesn't leak hashedPassword
96
+ await async_test(
97
+ "Custom aggregation - hashedPassword redacted in grouped results",
98
+ () => sdk.api.analytics_frames.custom_aggregation({
99
+ modelName: 'endusers',
100
+ aggregation: [
101
+ { $match: { fname: 'CustomAgg' } },
102
+ { $group: { _id: '$fname', count: { $sum: 1 }, data: { $push: '$$ROOT' } } }
103
+ ]
104
+ }),
105
+ { onResult: r => {
106
+ const group = r.result[0]
107
+ const user = group.data[0]
108
+ // Even in grouped results, hashedPassword should not be present
109
+ return group.count === 1 && user.hashedPassword === undefined
110
+ }}
111
+ )
112
+
113
+ console.log("✅ All custom aggregation tests passed")
114
+ } finally {
115
+ // Cleanup
116
+ await sdk.api.endusers.deleteOne(testEnduser.id)
117
+ }
118
+ }
119
+
120
+ // Allow running this test file independently
121
+ if (require.main === module) {
122
+ console.log(`🌐 Using API URL: ${host}`)
123
+ const sdk = new Session({ host })
124
+ const sdkNonAdmin = new Session({ host })
125
+
126
+ const runTests = async () => {
127
+ await setup_tests(sdk, sdkNonAdmin)
128
+ await custom_aggregation_tests({ sdk, sdkNonAdmin })
129
+ }
130
+
131
+ runTests()
132
+ .then(() => {
133
+ console.log("✅ Custom aggregation test suite completed successfully")
134
+ process.exit(0)
135
+ })
136
+ .catch((error) => {
137
+ console.error("❌ Custom aggregation test suite failed:", error)
138
+ process.exit(1)
139
+ })
140
+ }
@@ -72,6 +72,7 @@ import { message_assignment_trigger_tests } from "./api_tests/message_assignment
72
72
  import { time_tracks_tests } from "./api_tests/time_tracks.test";
73
73
  import { monthly_availability_restrictions_tests } from "./api_tests/monthly_availability_restrictions.test";
74
74
  import { calendar_event_limits_tests } from "./api_tests/calendar_event_limits.test";
75
+ import { custom_aggregation_tests } from "./api_tests/custom_aggregation.test";
75
76
 
76
77
  const UniquenessViolationMessage = 'Uniqueness Violation'
77
78
 
@@ -4403,6 +4404,77 @@ const set_fields_tests = async () => {
4403
4404
  { onResult: e => Number(e.fields?.yyyyDaysBetween) === 10 }
4404
4405
  )
4405
4406
 
4407
+ // Test setting timezone built-in field
4408
+ log_header("Set Fields Built-in Field Tests (Timezone)")
4409
+
4410
+ const e4 = await sdk.api.endusers.createOne({})
4411
+
4412
+ const t7 = await sdk.api.automation_triggers.createOne({
4413
+ title: "Set Timezone Test", status: 'Active',
4414
+ event: { type: 'Field Equals', info: { field: 'triggerTimezone', value: 'set-eastern' } },
4415
+ action: {
4416
+ type: 'Set Fields',
4417
+ info: {
4418
+ fields: [
4419
+ { name: 'timezone', type: 'Custom Value', value: 'US/Eastern' }
4420
+ ]
4421
+ },
4422
+ }
4423
+ })
4424
+
4425
+ await sdk.api.endusers.updateOne(e4.id, { fields: { triggerTimezone: 'set-eastern' } })
4426
+ await wait(undefined, 1000)
4427
+ await async_test(
4428
+ "Set Fields: set timezone built-in field to US/Eastern",
4429
+ () => sdk.api.endusers.getOne(e4.id),
4430
+ { onResult: e => e.timezone === 'US/Eastern' }
4431
+ )
4432
+
4433
+ // Test with different timezone format
4434
+ const t8 = await sdk.api.automation_triggers.createOne({
4435
+ title: "Set Timezone America/Los_Angeles Test", status: 'Active',
4436
+ event: { type: 'Field Equals', info: { field: 'triggerTimezone', value: 'set-pacific' } },
4437
+ action: {
4438
+ type: 'Set Fields',
4439
+ info: {
4440
+ fields: [
4441
+ { name: 'timezone', type: 'Custom Value', value: 'America/Los_Angeles' }
4442
+ ]
4443
+ },
4444
+ }
4445
+ })
4446
+
4447
+ await sdk.api.endusers.updateOne(e4.id, { fields: { triggerTimezone: 'set-pacific' } })
4448
+ await wait(undefined, 1000)
4449
+ await async_test(
4450
+ "Set Fields: update timezone to America/Los_Angeles",
4451
+ () => sdk.api.endusers.getOne(e4.id),
4452
+ { onResult: e => e.timezone === 'America/Los_Angeles' }
4453
+ )
4454
+
4455
+ // Test setting multiple built-in fields including timezone
4456
+ const t9 = await sdk.api.automation_triggers.createOne({
4457
+ title: "Set Multiple Built-ins Test", status: 'Active',
4458
+ event: { type: 'Field Equals', info: { field: 'triggerTimezone', value: 'set-multiple' } },
4459
+ action: {
4460
+ type: 'Set Fields',
4461
+ info: {
4462
+ fields: [
4463
+ { name: 'timezone', type: 'Custom Value', value: 'America/Chicago' },
4464
+ { name: 'defaultFromPhone', type: 'Custom Value', value: '+13125551234' }
4465
+ ]
4466
+ },
4467
+ }
4468
+ })
4469
+
4470
+ await sdk.api.endusers.updateOne(e4.id, { fields: { triggerTimezone: 'set-multiple' } })
4471
+ await wait(undefined, 1000)
4472
+ await async_test(
4473
+ "Set Fields: set multiple built-in fields including timezone",
4474
+ () => sdk.api.endusers.getOne(e4.id),
4475
+ { onResult: e => e.timezone === 'America/Chicago' && e.defaultFromPhone === '+13125551234' }
4476
+ )
4477
+
4406
4478
  return Promise.all([
4407
4479
  sdk.api.automation_triggers.deleteOne(t1.id),
4408
4480
  sdk.api.automation_triggers.deleteOne(t2.id),
@@ -4410,9 +4482,13 @@ const set_fields_tests = async () => {
4410
4482
  sdk.api.automation_triggers.deleteOne(t4.id),
4411
4483
  sdk.api.automation_triggers.deleteOne(t5.id),
4412
4484
  sdk.api.automation_triggers.deleteOne(t6.id),
4485
+ sdk.api.automation_triggers.deleteOne(t7.id),
4486
+ sdk.api.automation_triggers.deleteOne(t8.id),
4487
+ sdk.api.automation_triggers.deleteOne(t9.id),
4413
4488
  sdk.api.endusers.deleteOne(e1.id),
4414
4489
  sdk.api.endusers.deleteOne(e2.id),
4415
4490
  sdk.api.endusers.deleteOne(e3.id),
4491
+ sdk.api.endusers.deleteOne(e4.id),
4416
4492
  sdk.api.calendar_events.deleteOne(a1.id),
4417
4493
  ])
4418
4494
  }
@@ -13046,12 +13122,13 @@ const ip_address_form_tests = async () => {
13046
13122
  await replace_enduser_template_values_tests()
13047
13123
  await mfa_tests()
13048
13124
  await setup_tests(sdk, sdkNonAdmin)
13125
+ await custom_aggregation_tests({ sdk, sdkNonAdmin })
13126
+ await automation_trigger_tests()
13049
13127
  await formsort_tests()
13050
13128
  await self_serve_appointment_booking_tests()
13051
13129
  await time_tracks_tests({ sdk, sdkNonAdmin })
13052
13130
  await calendar_event_limits_tests({ sdk, sdkNonAdmin })
13053
13131
  await test_ticket_automation_assignment_and_optimization()
13054
- await automation_trigger_tests()
13055
13132
  await test_ticket_automation_assignment_and_optimization()
13056
13133
  await afteraction_day_of_month_delay_tests({ sdk, sdkNonAdmin })
13057
13134
  await monthly_availability_restrictions_tests({ sdk, sdkNonAdmin })
Binary file