@tellescope/sdk 1.247.0 → 1.249.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.
Files changed (126) hide show
  1. package/.env +3 -0
  2. package/lib/cjs/sdk.d.ts +7 -1
  3. package/lib/cjs/sdk.d.ts.map +1 -1
  4. package/lib/cjs/sdk.js +3 -0
  5. package/lib/cjs/sdk.js.map +1 -1
  6. package/lib/cjs/tests/api_tests/chats_analytics.test.d.ts +6 -0
  7. package/lib/cjs/tests/api_tests/chats_analytics.test.d.ts.map +1 -0
  8. package/lib/cjs/tests/api_tests/chats_analytics.test.js +256 -0
  9. package/lib/cjs/tests/api_tests/chats_analytics.test.js.map +1 -0
  10. package/lib/cjs/tests/api_tests/cross_org_api_key.test.d.ts +6 -0
  11. package/lib/cjs/tests/api_tests/cross_org_api_key.test.d.ts.map +1 -0
  12. package/lib/cjs/tests/api_tests/cross_org_api_key.test.js +748 -0
  13. package/lib/cjs/tests/api_tests/cross_org_api_key.test.js.map +1 -0
  14. package/lib/cjs/tests/api_tests/date_string_validation.test.d.ts +6 -0
  15. package/lib/cjs/tests/api_tests/date_string_validation.test.d.ts.map +1 -0
  16. package/lib/cjs/tests/api_tests/date_string_validation.test.js +142 -0
  17. package/lib/cjs/tests/api_tests/date_string_validation.test.js.map +1 -0
  18. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.d.ts +6 -0
  19. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.d.ts.map +1 -0
  20. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js +667 -0
  21. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js.map +1 -0
  22. package/lib/cjs/tests/api_tests/eom_billing_codes.test.d.ts +6 -0
  23. package/lib/cjs/tests/api_tests/eom_billing_codes.test.d.ts.map +1 -0
  24. package/lib/cjs/tests/api_tests/eom_billing_codes.test.js +162 -0
  25. package/lib/cjs/tests/api_tests/eom_billing_codes.test.js.map +1 -0
  26. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.d.ts +6 -0
  27. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.d.ts.map +1 -0
  28. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.js +339 -0
  29. package/lib/cjs/tests/api_tests/eom_procedure_codes.test.js.map +1 -0
  30. package/lib/cjs/tests/api_tests/field_redaction.test.d.ts +13 -0
  31. package/lib/cjs/tests/api_tests/field_redaction.test.d.ts.map +1 -0
  32. package/lib/cjs/tests/api_tests/field_redaction.test.js +818 -0
  33. package/lib/cjs/tests/api_tests/field_redaction.test.js.map +1 -0
  34. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.d.ts +6 -0
  35. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.d.ts.map +1 -0
  36. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.js +429 -0
  37. package/lib/cjs/tests/api_tests/form_submitted_trigger.test.js.map +1 -0
  38. package/lib/cjs/tests/api_tests/integrations_redacted.test.d.ts +6 -0
  39. package/lib/cjs/tests/api_tests/integrations_redacted.test.d.ts.map +1 -0
  40. package/lib/cjs/tests/api_tests/integrations_redacted.test.js +273 -0
  41. package/lib/cjs/tests/api_tests/integrations_redacted.test.js.map +1 -0
  42. package/lib/cjs/tests/api_tests/managed_content_file_access.test.d.ts +13 -0
  43. package/lib/cjs/tests/api_tests/managed_content_file_access.test.d.ts.map +1 -0
  44. package/lib/cjs/tests/api_tests/managed_content_file_access.test.js +385 -0
  45. package/lib/cjs/tests/api_tests/managed_content_file_access.test.js.map +1 -0
  46. package/lib/cjs/tests/api_tests/openloop_webhooks.test.d.ts.map +1 -1
  47. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js +108 -24
  48. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  49. package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.d.ts.map +1 -1
  50. package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.js +25 -2
  51. package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.js.map +1 -1
  52. package/lib/cjs/tests/tests.d.ts.map +1 -1
  53. package/lib/cjs/tests/tests.js +310 -183
  54. package/lib/cjs/tests/tests.js.map +1 -1
  55. package/lib/esm/sdk.d.ts +9 -3
  56. package/lib/esm/sdk.d.ts.map +1 -1
  57. package/lib/esm/sdk.js +3 -0
  58. package/lib/esm/sdk.js.map +1 -1
  59. package/lib/esm/session.d.ts +1 -0
  60. package/lib/esm/session.d.ts.map +1 -1
  61. package/lib/esm/tests/api_tests/chats_analytics.test.d.ts +6 -0
  62. package/lib/esm/tests/api_tests/chats_analytics.test.d.ts.map +1 -0
  63. package/lib/esm/tests/api_tests/chats_analytics.test.js +252 -0
  64. package/lib/esm/tests/api_tests/chats_analytics.test.js.map +1 -0
  65. package/lib/esm/tests/api_tests/cross_org_api_key.test.d.ts +6 -0
  66. package/lib/esm/tests/api_tests/cross_org_api_key.test.d.ts.map +1 -0
  67. package/lib/esm/tests/api_tests/cross_org_api_key.test.js +744 -0
  68. package/lib/esm/tests/api_tests/cross_org_api_key.test.js.map +1 -0
  69. package/lib/esm/tests/api_tests/date_string_validation.test.d.ts +6 -0
  70. package/lib/esm/tests/api_tests/date_string_validation.test.d.ts.map +1 -0
  71. package/lib/esm/tests/api_tests/date_string_validation.test.js +138 -0
  72. package/lib/esm/tests/api_tests/date_string_validation.test.js.map +1 -0
  73. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.d.ts +6 -0
  74. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.d.ts.map +1 -0
  75. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js +663 -0
  76. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js.map +1 -0
  77. package/lib/esm/tests/api_tests/eom_billing_codes.test.d.ts +6 -0
  78. package/lib/esm/tests/api_tests/eom_billing_codes.test.d.ts.map +1 -0
  79. package/lib/esm/tests/api_tests/eom_billing_codes.test.js +158 -0
  80. package/lib/esm/tests/api_tests/eom_billing_codes.test.js.map +1 -0
  81. package/lib/esm/tests/api_tests/eom_procedure_codes.test.d.ts +6 -0
  82. package/lib/esm/tests/api_tests/eom_procedure_codes.test.d.ts.map +1 -0
  83. package/lib/esm/tests/api_tests/eom_procedure_codes.test.js +335 -0
  84. package/lib/esm/tests/api_tests/eom_procedure_codes.test.js.map +1 -0
  85. package/lib/esm/tests/api_tests/field_redaction.test.d.ts +13 -0
  86. package/lib/esm/tests/api_tests/field_redaction.test.d.ts.map +1 -0
  87. package/lib/esm/tests/api_tests/field_redaction.test.js +814 -0
  88. package/lib/esm/tests/api_tests/field_redaction.test.js.map +1 -0
  89. package/lib/esm/tests/api_tests/form_submitted_trigger.test.d.ts +6 -0
  90. package/lib/esm/tests/api_tests/form_submitted_trigger.test.d.ts.map +1 -0
  91. package/lib/esm/tests/api_tests/form_submitted_trigger.test.js +425 -0
  92. package/lib/esm/tests/api_tests/form_submitted_trigger.test.js.map +1 -0
  93. package/lib/esm/tests/api_tests/integrations_redacted.test.d.ts +6 -0
  94. package/lib/esm/tests/api_tests/integrations_redacted.test.d.ts.map +1 -0
  95. package/lib/esm/tests/api_tests/integrations_redacted.test.js +269 -0
  96. package/lib/esm/tests/api_tests/integrations_redacted.test.js.map +1 -0
  97. package/lib/esm/tests/api_tests/managed_content_file_access.test.d.ts +13 -0
  98. package/lib/esm/tests/api_tests/managed_content_file_access.test.d.ts.map +1 -0
  99. package/lib/esm/tests/api_tests/managed_content_file_access.test.js +358 -0
  100. package/lib/esm/tests/api_tests/managed_content_file_access.test.js.map +1 -0
  101. package/lib/esm/tests/api_tests/openloop_webhooks.test.d.ts.map +1 -1
  102. package/lib/esm/tests/api_tests/openloop_webhooks.test.js +108 -24
  103. package/lib/esm/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  104. package/lib/esm/tests/api_tests/organization_settings_duplicates.test.d.ts.map +1 -1
  105. package/lib/esm/tests/api_tests/organization_settings_duplicates.test.js +25 -2
  106. package/lib/esm/tests/api_tests/organization_settings_duplicates.test.js.map +1 -1
  107. package/lib/esm/tests/tests.d.ts.map +1 -1
  108. package/lib/esm/tests/tests.js +310 -183
  109. package/lib/esm/tests/tests.js.map +1 -1
  110. package/lib/tsconfig.tsbuildinfo +1 -1
  111. package/package.json +10 -10
  112. package/src/sdk.ts +14 -0
  113. package/src/tests/api_tests/chats_analytics.test.ts +182 -0
  114. package/src/tests/api_tests/cross_org_api_key.test.ts +665 -0
  115. package/src/tests/api_tests/date_string_validation.test.ts +107 -0
  116. package/src/tests/api_tests/enduser_session_invalidation.test.ts +361 -0
  117. package/src/tests/api_tests/eom_procedure_codes.test.ts +296 -0
  118. package/src/tests/api_tests/field_redaction.test.ts +669 -0
  119. package/src/tests/api_tests/form_started_trigger.test.ts +1 -1
  120. package/src/tests/api_tests/form_submitted_trigger.test.ts +281 -0
  121. package/src/tests/api_tests/integrations_redacted.test.ts +245 -0
  122. package/src/tests/api_tests/managed_content_file_access.test.ts +214 -0
  123. package/src/tests/api_tests/openloop_webhooks.test.ts +64 -0
  124. package/src/tests/api_tests/organization_settings_duplicates.test.ts +14 -0
  125. package/src/tests/tests.ts +95 -7
  126. package/test_generated.pdf +0 -0
@@ -0,0 +1,214 @@
1
+ require('source-map-support').install();
2
+
3
+ import * as buffer from 'buffer'
4
+ import { Session, EnduserSession } from "../../sdk"
5
+ import {
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
+ const businessId = '60398b1131a295e64f084ff6'
13
+
14
+ /**
15
+ * Tests for file_download_URL enduser access via managed content fallback.
16
+ *
17
+ * Verifies the backend fallback logic that allows endusers to access files
18
+ * attached to managed content records they have access to, even when the
19
+ * file itself does not have enduserId set or publicRead: true.
20
+ */
21
+ export const managed_content_file_access_tests = async ({ sdk, sdkNonAdmin }: { sdk: Session, sdkNonAdmin: Session }) => {
22
+ log_header("Managed Content File Access Tests")
23
+
24
+ const contentRecordIds: string[] = []
25
+ const assignmentIds: string[] = []
26
+ const fileIds: string[] = []
27
+ let testEnduserId: string | undefined
28
+ let otherEnduserId: string | undefined
29
+ let enduserSDK: EnduserSession | undefined
30
+ let otherEnduserSDK: EnduserSession | undefined
31
+
32
+ const uploadFile = async (
33
+ name: string,
34
+ opts: { enduserId?: string, publicRead?: boolean } = {}
35
+ ) => {
36
+ const buff = buffer.Buffer.from(`test file content for ${name}`)
37
+ const { presignedUpload, file } = await sdk.api.files.prepare_file_upload({
38
+ name,
39
+ type: 'text/plain',
40
+ size: buff.byteLength,
41
+ ...opts,
42
+ })
43
+ await sdk.UPLOAD(presignedUpload as any, buff)
44
+ await sdk.api.files.confirm_file_upload({ id: file.id })
45
+ fileIds.push(file.id)
46
+ return file
47
+ }
48
+
49
+ try {
50
+ console.log("Setting up test data...")
51
+
52
+ const testEnduser = await sdk.api.endusers.createOne({
53
+ email: `mcr_file_access_test_${Date.now()}@test.tellescope.com`,
54
+ })
55
+ testEnduserId = testEnduser.id
56
+ await sdk.api.endusers.set_password({ id: testEnduser.id, password: 'TestPassword123!' })
57
+
58
+ const otherEnduser = await sdk.api.endusers.createOne({
59
+ email: `mcr_file_access_other_${Date.now()}@test.tellescope.com`,
60
+ })
61
+ otherEnduserId = otherEnduser.id
62
+ await sdk.api.endusers.set_password({ id: otherEnduser.id, password: 'TestPassword123!' })
63
+
64
+ enduserSDK = new EnduserSession({ host, businessId })
65
+ await enduserSDK.authenticate(testEnduser.email!, 'TestPassword123!')
66
+
67
+ otherEnduserSDK = new EnduserSession({ host, businessId })
68
+ await otherEnduserSDK.authenticate(otherEnduser.email!, 'TestPassword123!')
69
+
70
+ // ===== Test 1: File attached to assignmentType: 'All' content - enduser CAN access =====
71
+ const fileForAll = await uploadFile('mcr-file-all.txt')
72
+ const contentAll = await sdk.api.managed_content_records.createOne({
73
+ title: 'MCR File Access - All',
74
+ htmlContent: '<p>All</p>',
75
+ textContent: 'All',
76
+ assignmentType: 'All',
77
+ attachments: [{ secureName: fileForAll.secureName, type: 'file', name: fileForAll.name }],
78
+ })
79
+ contentRecordIds.push(contentAll.id)
80
+
81
+ await async_test(
82
+ 'staff-uploaded file in assignmentType:All content - enduser CAN access',
83
+ async () => {
84
+ const result = await enduserSDK!.api.files.file_download_URL({ secureName: fileForAll.secureName })
85
+ return !!result?.downloadURL
86
+ },
87
+ { onResult: (r) => r === true }
88
+ )
89
+
90
+ // ===== Test 2: File attached to Individual content for enduser A - enduser B CANNOT access =====
91
+ const fileForIndividual = await uploadFile('mcr-file-individual.txt')
92
+ const contentIndividual = await sdk.api.managed_content_records.createOne({
93
+ title: 'MCR File Access - Individual',
94
+ htmlContent: '<p>Individual</p>',
95
+ textContent: 'Individual',
96
+ enduserId: testEnduser.id,
97
+ attachments: [{ secureName: fileForIndividual.secureName, type: 'file', name: fileForIndividual.name }],
98
+ })
99
+ contentRecordIds.push(contentIndividual.id)
100
+
101
+ await async_test(
102
+ 'file attached to Individual content - assigned enduser CAN access',
103
+ async () => {
104
+ const result = await enduserSDK!.api.files.file_download_URL({ secureName: fileForIndividual.secureName })
105
+ return !!result?.downloadURL
106
+ },
107
+ { onResult: (r) => r === true }
108
+ )
109
+
110
+ await async_test(
111
+ 'file attached to Individual content - unassigned enduser CANNOT access',
112
+ async () => {
113
+ try {
114
+ await otherEnduserSDK!.api.files.file_download_URL({ secureName: fileForIndividual.secureName })
115
+ return false
116
+ } catch (err) {
117
+ return true
118
+ }
119
+ },
120
+ { onResult: (r) => r === true }
121
+ )
122
+
123
+ // ===== Test 3: File not attached to any managed content - enduser CANNOT access =====
124
+ const fileNoContent = await uploadFile('mcr-file-no-content.txt')
125
+
126
+ await async_test(
127
+ 'file not attached to any managed content - enduser CANNOT access',
128
+ async () => {
129
+ try {
130
+ await enduserSDK!.api.files.file_download_URL({ secureName: fileNoContent.secureName })
131
+ return false
132
+ } catch (err) {
133
+ return true
134
+ }
135
+ },
136
+ { onResult: (r) => r === true }
137
+ )
138
+
139
+ // ===== Test 4: File with enduserId set - existing behavior preserved =====
140
+ const fileWithEnduser = await uploadFile('mcr-file-with-enduser.txt', { enduserId: testEnduser.id })
141
+
142
+ await async_test(
143
+ 'file with enduserId set - enduser CAN access (no fallback needed)',
144
+ async () => {
145
+ const result = await enduserSDK!.api.files.file_download_URL({ secureName: fileWithEnduser.secureName })
146
+ return !!result?.downloadURL
147
+ },
148
+ { onResult: (r) => r === true }
149
+ )
150
+
151
+ // ===== Test 5: File with publicRead: true - existing behavior preserved =====
152
+ const filePublic = await uploadFile('mcr-file-public.txt', { publicRead: true })
153
+
154
+ await async_test(
155
+ 'file with publicRead - enduser CAN access',
156
+ async () => {
157
+ const result = await enduserSDK!.api.files.file_download_URL({ secureName: filePublic.secureName })
158
+ return !!result?.downloadURL
159
+ },
160
+ { onResult: (r) => r === true }
161
+ )
162
+
163
+ console.log("All Managed Content File Access tests passed!")
164
+
165
+ } finally {
166
+ console.log("Cleaning up test data...")
167
+
168
+ try {
169
+ for (const assignmentId of assignmentIds) {
170
+ await sdk.api.managed_content_record_assignments.deleteOne(assignmentId).catch(() => {})
171
+ }
172
+
173
+ for (const recordId of contentRecordIds) {
174
+ await sdk.api.managed_content_records.deleteOne(recordId).catch(() => {})
175
+ }
176
+
177
+ for (const fileId of fileIds) {
178
+ await sdk.api.files.deleteOne(fileId).catch(() => {})
179
+ }
180
+
181
+ if (testEnduserId) {
182
+ await sdk.api.endusers.deleteOne(testEnduserId).catch(() => {})
183
+ }
184
+ if (otherEnduserId) {
185
+ await sdk.api.endusers.deleteOne(otherEnduserId).catch(() => {})
186
+ }
187
+
188
+ console.log("Cleanup completed")
189
+ } catch (error) {
190
+ console.error('Cleanup error:', error)
191
+ }
192
+ }
193
+ }
194
+
195
+ if (require.main === module) {
196
+ console.log(`Using API URL: ${host}`)
197
+ const sdk = new Session({ host })
198
+ const sdkNonAdmin = new Session({ host })
199
+
200
+ const runTests = async () => {
201
+ await setup_tests(sdk, sdkNonAdmin)
202
+ await managed_content_file_access_tests({ sdk, sdkNonAdmin })
203
+ }
204
+
205
+ runTests()
206
+ .then(() => {
207
+ console.log("Managed content file access test suite completed successfully")
208
+ process.exit(0)
209
+ })
210
+ .catch((error) => {
211
+ console.error("Managed content file access test suite failed:", error)
212
+ process.exit(1)
213
+ })
214
+ }
@@ -144,6 +144,27 @@ export const openloop_webhooks_tests = async ({ sdk, sdkNonAdmin }: { sdk: Sessi
144
144
  { onResult: (r: boolean) => r === true }
145
145
  )
146
146
 
147
+ await async_test(
148
+ 'V1: order_confirmation maps program_code to protocol field',
149
+ async () => {
150
+ const orderNum = `ol-conf-protocol-${uid()}`
151
+ const res = await postV1(makeV1Confirmation({
152
+ patientID: healthieId1,
153
+ orderNumber: orderNum,
154
+ program_code: 'Weight-Loss',
155
+ }))
156
+ assert(res.status === 200, `Expected 200, got ${res.status}`)
157
+
158
+ const orders = await sdk.api.enduser_orders.getSome({
159
+ filter: { source: 'OpenLoop', externalId: orderNum }
160
+ })
161
+ assert(orders.length === 1, `Expected 1 order, got ${orders.length}`)
162
+ assert(orders[0].protocol === 'Weight-Loss', `protocol mismatch: ${orders[0].protocol}`)
163
+ return true
164
+ },
165
+ { onResult: (r: boolean) => r === true }
166
+ )
167
+
147
168
  await async_test(
148
169
  'V1: order_confirmation idempotency - same order not duplicated',
149
170
  async () => {
@@ -314,6 +335,28 @@ export const openloop_webhooks_tests = async ({ sdk, sdkNonAdmin }: { sdk: Sessi
314
335
  { onResult: (r: boolean) => r === true }
315
336
  )
316
337
 
338
+ await async_test(
339
+ 'V1: order_shipped maps program_code to protocol field',
340
+ async () => {
341
+ const orderNum = `ol-ship-protocol-${uid()}`
342
+ await postV1(makeV1Confirmation({ patientID: healthieId1, orderNumber: orderNum }))
343
+ const res = await postV1(makeV1Shipped({
344
+ patientID: healthieId1,
345
+ orderNumber: orderNum,
346
+ program_code: 'Diabetes',
347
+ }))
348
+ assert(res.status === 200, `Expected 200, got ${res.status}`)
349
+
350
+ const orders = await sdk.api.enduser_orders.getSome({
351
+ filter: { source: 'OpenLoop', externalId: orderNum }
352
+ })
353
+ assert(orders.length === 1, `Expected 1 order, got ${orders.length}`)
354
+ assert(orders[0].protocol === 'Diabetes', `protocol mismatch: ${orders[0].protocol}`)
355
+ return true
356
+ },
357
+ { onResult: (r: boolean) => r === true }
358
+ )
359
+
317
360
  // ===== SECTION D: V1 enduserId Isolation =====
318
361
  log_header("V1 enduserId Isolation")
319
362
 
@@ -414,6 +457,27 @@ export const openloop_webhooks_tests = async ({ sdk, sdkNonAdmin }: { sdk: Sessi
414
457
  { onResult: (r: boolean) => r === true }
415
458
  )
416
459
 
460
+ await async_test(
461
+ 'V2: prescription-created maps program_code to protocol field',
462
+ async () => {
463
+ const orderNum = `v2-protocol-${uid()}`
464
+ const res = await postV2(makeV2Payload('prescription-created', {
465
+ id: orderNum,
466
+ patientId: healthieId1,
467
+ program_code: 'GLP-1',
468
+ }))
469
+ assert(res.status === 200, `Expected 200, got ${res.status}`)
470
+
471
+ const orders = await sdk.api.enduser_orders.getSome({
472
+ filter: { source: 'OpenLoop', externalId: orderNum }
473
+ })
474
+ assert(orders.length === 1, `Expected 1 order, got ${orders.length}`)
475
+ assert(orders[0].protocol === 'GLP-1', `protocol mismatch: ${orders[0].protocol}`)
476
+ return true
477
+ },
478
+ { onResult: (r: boolean) => r === true }
479
+ )
480
+
417
481
  await async_test(
418
482
  'V2: prescription-shipped updates with carrier and tracking',
419
483
  async () => {
@@ -176,6 +176,20 @@ export const organization_settings_duplicates_tests = async ({ sdk, sdkNonAdmin
176
176
  },
177
177
  }
178
178
  }, { replaceObjectFields: true })
179
+
180
+ // === D. subdomain is readonly ===
181
+ const orgBefore = await sdk.api.organizations.getOne(orgId)
182
+ await async_test(
183
+ "subdomain field is readonly — update does not change persisted value",
184
+ async () => {
185
+ await sdk.api.organizations.updateOne(orgId, {
186
+ subdomain: `readonly-attempt-${Date.now()}`,
187
+ } as any).catch(() => undefined) // tolerate either silent-drop or unknown-field rejection
188
+ const orgAfter = await sdk.api.organizations.getOne(orgId)
189
+ return orgAfter.subdomain === orgBefore.subdomain
190
+ },
191
+ { onResult: (unchanged: boolean) => unchanged === true }
192
+ )
179
193
  }
180
194
 
181
195
  // Allow running this test file independently
@@ -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 { integrations_redacted_tests } from "./api_tests/integrations_redacted.test"
39
40
  import { get_some_projection_tests } from "./api_tests/get_some_projection.test"
40
41
  import { mdb_sort_tests } from "./api_tests/mdb_sort.test"
41
42
  import { create_user_notifications_trigger_tests } from "./api_tests/create_user_notifications_trigger.test"
@@ -73,26 +74,34 @@ import {
73
74
 
74
75
  import fs from "fs"
75
76
  import { load_inbox_data_tests } from "./api_tests/load_inbox_data.test";
77
+ import { eom_procedure_codes_tests } from "./api_tests/eom_procedure_codes.test";
78
+ import { cross_org_api_key_tests } from "./api_tests/cross_org_api_key.test";
76
79
  import { custom_dashboards_tests } from "./api_tests/custom_dashboards.test";
77
80
  import { message_assignment_trigger_tests } from "./api_tests/message_assignment_trigger.test";
78
81
  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 } from "./api_tests/time_tracks.test";
79
82
  import { monthly_availability_restrictions_tests } from "./api_tests/monthly_availability_restrictions.test";
80
83
  import { calendar_event_limits_tests } from "./api_tests/calendar_event_limits.test";
81
84
  import { custom_aggregation_tests } from "./api_tests/custom_aggregation.test";
85
+ import { chats_analytics_tests } from "./api_tests/chats_analytics.test";
82
86
  import { no_access_permission_checks_tests } from "./api_tests/no_access_permission_checks.test";
87
+ import { field_redaction_tests } from "./api_tests/field_redaction.test";
83
88
  import { bulk_assignment_tests } from "./api_tests/bulk_assignment.test";
84
89
  import { managed_content_enduser_access_tests } from "./api_tests/managed_content_enduser_access.test";
90
+ import { managed_content_file_access_tests } from "./api_tests/managed_content_file_access.test";
85
91
  import { auto_merge_form_submission_tests } from "./api_tests/auto_merge_form_submission.test";
86
92
  import { database_cascade_delete_tests } from "./api_tests/database_cascade_delete.test";
87
93
  import { ai_conversations_tests } from "./api_tests/ai_conversations.test";
88
94
  import { load_team_chat_tests } from "./api_tests/load_team_chat.test";
89
95
  import { form_started_trigger_tests } from "./api_tests/form_started_trigger.test";
96
+ import { form_submitted_trigger_tests } from "./api_tests/form_submitted_trigger.test";
90
97
  import { medication_added_trigger_tests } from "./api_tests/medication_added_trigger.test";
91
98
  import { elation_user_id_tests } from "./api_tests/elation_user_id.test";
92
99
  import { organization_settings_duplicates_tests } from "./api_tests/organization_settings_duplicates.test";
93
100
  import { calendar_events_bulk_update_tests } from "./api_tests/calendar_events_bulk_update.test";
94
101
  import { openloop_webhooks_tests } from "./api_tests/openloop_webhooks.test";
95
102
  import { beluga_pharmacy_mappings_tests } from "./api_tests/beluga_pharmacy_mappings.test";
103
+ import { date_string_validation_tests } from "./api_tests/date_string_validation.test";
104
+ import { enduser_session_invalidation_tests } from "./api_tests/enduser_session_invalidation.test";
96
105
 
97
106
  const UniquenessViolationMessage = 'Uniqueness Violation'
98
107
 
@@ -3429,6 +3438,12 @@ const order_status_equals_tests = async () => {
3429
3438
  status: 'Active',
3430
3439
  title: "Title Partial And Condition"
3431
3440
  })
3441
+ const t9 = await sdk.api.automation_triggers.createOne({
3442
+ event: { type: 'Order Status Equals', info: { source: 'Source', status: "Update", protocols: ['Protocol-A'] } },
3443
+ action: { type: 'Add Tags', info: { tags: ['Protocol Match'] }},
3444
+ status: 'Active',
3445
+ title: "Protocol Condition"
3446
+ })
3432
3447
 
3433
3448
  const e = await sdk.api.endusers.createOne({})
3434
3449
 
@@ -3610,6 +3625,38 @@ const order_status_equals_tests = async () => {
3610
3625
  ) }
3611
3626
  )
3612
3627
 
3628
+ await sdk.api.enduser_orders.updateOne(u.id, { status: 'Toggle', externalId: "also avoid rate limit 7" })
3629
+ await sdk.api.enduser_orders.updateOne(u.id, { status: "Update", protocol: 'Protocol-Mismatch', externalId: 'avoid rate limiting 7' })
3630
+ await wait(undefined, 500) // allow triggers to happen
3631
+ await async_test(
3632
+ "Protocol no match (wrong protocol)",
3633
+ () => sdk.api.endusers.getOne(e.id),
3634
+ { onResult: e => !!(
3635
+ e.tags?.length === 8
3636
+ && !e.tags?.includes('Protocol Match')
3637
+ ) }
3638
+ )
3639
+
3640
+ await sdk.api.enduser_orders.updateOne(u.id, { status: 'Toggle', externalId: "also avoid rate limit 8" })
3641
+ await sdk.api.enduser_orders.updateOne(u.id, { status: "Update", protocol: 'Protocol-A', externalId: 'avoid rate limiting 8' })
3642
+ await wait(undefined, 500) // allow triggers to happen
3643
+ await async_test(
3644
+ "Protocol match tag added",
3645
+ () => sdk.api.endusers.getOne(e.id),
3646
+ { onResult: e => !!(
3647
+ e.tags?.length === 9
3648
+ && e.tags?.includes('Source')
3649
+ && e.tags?.includes('Fill')
3650
+ && e.tags?.includes('Status Update')
3651
+ && e.tags?.includes('Fill Update')
3652
+ && e.tags?.includes('SKU Update')
3653
+ && e.tags?.includes('SKU Partial Update')
3654
+ && e.tags?.includes('Title Partial Update')
3655
+ && e.tags?.includes('Title Partial And Update')
3656
+ && e.tags?.includes('Protocol Match')
3657
+ ) }
3658
+ )
3659
+
3613
3660
  await Promise.all([
3614
3661
  sdk.api.automation_triggers.deleteOne(t1.id),
3615
3662
  sdk.api.automation_triggers.deleteOne(t2.id),
@@ -3619,6 +3666,7 @@ const order_status_equals_tests = async () => {
3619
3666
  sdk.api.automation_triggers.deleteOne(t6.id),
3620
3667
  sdk.api.automation_triggers.deleteOne(t7.id),
3621
3668
  sdk.api.automation_triggers.deleteOne(t8.id),
3669
+ sdk.api.automation_triggers.deleteOne(t9.id),
3622
3670
  sdk.api.endusers.deleteOne(e.id),
3623
3671
  ])
3624
3672
  }
@@ -5016,8 +5064,8 @@ const trigger_events_api_tests = async () => {
5016
5064
  const automation_trigger_tests = async () => {
5017
5065
  log_header("Automation Trigger Tests")
5018
5066
 
5019
- await medication_added_trigger_tests({ sdk, sdkNonAdmin })
5020
5067
  await order_status_equals_tests()
5068
+ await medication_added_trigger_tests({ sdk, sdkNonAdmin })
5021
5069
  await appointment_cancelled_tests()
5022
5070
  await set_fields_tests()
5023
5071
  await purchase_made_trigger_tests({ sdk, sdkNonAdmin })
@@ -8588,7 +8636,7 @@ export const formsort_tests = async () => {
8588
8636
  const form = await sdk.api.forms.createOne({ title: "FormSort" })
8589
8637
 
8590
8638
  const postToFormsort = async ({ matchByName=false, createNewEnduser=false, enduserId, returnJSON=false, ...o }: {
8591
- answers: { key: string, value: any }[],
8639
+ answers: { key: string, value: any, label?: string }[],
8592
8640
  responder_uuid: string,
8593
8641
  finalized: boolean,
8594
8642
  matchByName?: boolean,
@@ -8605,8 +8653,8 @@ export const formsort_tests = async () => {
8605
8653
  return await axios.post(url.toString(), o)
8606
8654
  }
8607
8655
 
8608
- const postToFormsortGeneric = async ({ matchByName=false, createNewEnduser=false, ...o }: {
8609
- answers: { key: string, value: any }[],
8656
+ const postToFormsortGeneric = async ({ matchByName=false, createNewEnduser=false, ...o }: {
8657
+ answers: { key: string, value: any, label?: string }[],
8610
8658
  responder_uuid: string,
8611
8659
  finalized: boolean,
8612
8660
  matchByName?: boolean,
@@ -8985,6 +9033,37 @@ export const formsort_tests = async () => {
8985
9033
  onResult: r => r?.fname === 'ChangedFirst' && r?.lname === 'ChangedLast'
8986
9034
  })
8987
9035
 
9036
+ // Test label as fieldTitle
9037
+ const validateFieldTitle = (fr: { responses?: { externalId?: string, fieldTitle?: string }[] }, key: string, expectedTitle: string): boolean => {
9038
+ return fr.responses?.find(r => r.externalId === key)?.fieldTitle === expectedTitle
9039
+ }
9040
+
9041
+ // Label provided: fieldTitle should use label, externalId should use key
9042
+ await postToFormsort({
9043
+ answers: [
9044
+ { key: 'email', value: 'label-test@tellescope.com' },
9045
+ { key: 'label_test_key', value: 'test_value', label: 'My Custom Label' },
9046
+ ],
9047
+ responder_uuid: "label-test",
9048
+ finalized: true,
9049
+ })
9050
+ await async_test(`label as fieldTitle`, () => sdk.api.form_responses.getOne({ externalId: 'label-test' }), {
9051
+ onResult: r => validateFieldTitle(r, 'label_test_key', 'My Custom Label')
9052
+ })
9053
+
9054
+ // No label: fieldTitle should fall back to key
9055
+ await postToFormsort({
9056
+ answers: [
9057
+ { key: 'email', value: 'no-label-test@tellescope.com' },
9058
+ { key: 'no_label_key', value: 'test_value' },
9059
+ ],
9060
+ responder_uuid: "no-label-test",
9061
+ finalized: true,
9062
+ })
9063
+ await async_test(`no label falls back to key as fieldTitle`, () => sdk.api.form_responses.getOne({ externalId: 'no-label-test' }), {
9064
+ onResult: r => validateFieldTitle(r, 'no_label_key', 'no_label_key')
9065
+ })
9066
+
8988
9067
  // cleanup
8989
9068
  const endusers = await sdk.api.endusers.getSome()
8990
9069
  await Promise.all([
@@ -14126,8 +14205,18 @@ const ip_address_form_tests = async () => {
14126
14205
  await replace_enduser_template_values_tests()
14127
14206
  await mfa_tests()
14128
14207
  await setup_tests(sdk, sdkNonAdmin)
14129
- await mdb_sort_tests({ sdk, sdkNonAdmin })
14208
+ await eom_procedure_codes_tests({ sdk, sdkNonAdmin })
14209
+ await cross_org_api_key_tests({ sdk, sdkNonAdmin })
14130
14210
  await organization_settings_duplicates_tests({ sdk, sdkNonAdmin })
14211
+ await enduser_session_invalidation_tests({ sdk, sdkNonAdmin })
14212
+ await chats_analytics_tests({ sdk, sdkNonAdmin })
14213
+ await field_redaction_tests({ sdk, sdkNonAdmin })
14214
+ await form_submitted_trigger_tests({ sdk, sdkNonAdmin })
14215
+ await date_string_validation_tests({ sdk, sdkNonAdmin })
14216
+ await openloop_webhooks_tests({ sdk, sdkNonAdmin })
14217
+ await automation_trigger_tests()
14218
+ await integrations_redacted_tests({ sdk, sdkNonAdmin })
14219
+ await mdb_sort_tests({ sdk, sdkNonAdmin })
14131
14220
  await search_tests()
14132
14221
  await time_tracks_tests({ sdk, sdkNonAdmin })
14133
14222
  await time_tracks_historical_tests({ sdk, sdkNonAdmin })
@@ -14136,8 +14225,6 @@ const ip_address_form_tests = async () => {
14136
14225
  await time_tracks_lock_tests({ sdk, sdkNonAdmin })
14137
14226
  await time_tracks_edge_case_tests({ sdk, sdkNonAdmin })
14138
14227
  await calendar_event_limits_tests({ sdk, sdkNonAdmin })
14139
- await openloop_webhooks_tests({ sdk, sdkNonAdmin })
14140
- await automation_trigger_tests()
14141
14228
  await get_some_projection_tests({ sdk, sdkNonAdmin })
14142
14229
  await elation_user_id_tests({ sdk, sdkNonAdmin })
14143
14230
  await custom_dashboards_tests({ sdk, sdkNonAdmin })
@@ -14156,6 +14243,7 @@ const ip_address_form_tests = async () => {
14156
14243
  await beluga_pharmacy_mappings_tests({ sdk, sdkNonAdmin })
14157
14244
  await threadKeyTests()
14158
14245
  await managed_content_enduser_access_tests({ sdk, sdkNonAdmin })
14246
+ await managed_content_file_access_tests({ sdk, sdkNonAdmin })
14159
14247
  await afteraction_day_of_month_delay_tests({ sdk, sdkNonAdmin })
14160
14248
  await bulk_assignment_tests({ sdk, sdkNonAdmin })
14161
14249
  await formsort_tests()
Binary file