@tellescope/sdk 1.2.0 → 1.2.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.
@@ -10,6 +10,7 @@ import {
10
10
  } from "@tellescope/types-client"
11
11
  import {
12
12
  AutomationAction,
13
+ FormResponseValue,
13
14
  ModelName,
14
15
  } from "@tellescope/types-models"
15
16
 
@@ -1008,13 +1009,29 @@ const chat_room_tests = async () => {
1008
1009
  }
1009
1010
 
1010
1011
  const chat_tests = async() => {
1012
+ log_header("Chat")
1013
+
1011
1014
  const sdk2 = new Session({ host })
1012
1015
  await sdk2.authenticate(nonAdminEmail, nonAdminPassword) // non-admin has access restrictions we want to test
1013
1016
 
1017
+ const enduser = await sdk.api.endusers.createOne({ email })
1018
+ await sdk.api.endusers.set_password({ id: enduser.id, password }).catch(console.error)
1019
+ await enduserSDK.authenticate(email, password).catch(console.error)
1020
+
1014
1021
  const room = await sdk.api.chat_rooms.createOne({ type: 'internal', userIds: [userId] })
1015
1022
  const chat = await sdk.api.chats.createOne({ roomId: room.id, message: "Hello!" })
1016
1023
  const chat2 = await sdk.api.chats.createOne({ roomId: room.id, message: "Hello..." })
1017
1024
 
1025
+ const enduserRoom = await sdk.api.chat_rooms.createOne({ type: 'internal', userIds: [userId], enduserIds: [enduser.id] })
1026
+ await sdk.api.chats.createOne({ roomId: enduserRoom.id, message: 'enduser'})
1027
+ await enduserSDK.api.chats.createOne({ roomId: enduserRoom.id, message: 'enduser'})
1028
+
1029
+ await async_test(
1030
+ `get-chats for enduser`,
1031
+ () => enduserSDK.api.chats.getSome({ filter: { roomId: enduserRoom.id } }),
1032
+ { onResult: c => c?.length === 2 }
1033
+ )
1034
+
1018
1035
  // await async_test(
1019
1036
  // `get-chat (without filter)`,
1020
1037
  // () => sdk.api.chats.getOne(chat.id),
@@ -1110,6 +1127,11 @@ const chat_tests = async() => {
1110
1127
  () => sdk.api.chats.getOne(chat2Null.id),
1111
1128
  { onResult: c => c.replyId === null }
1112
1129
  )
1130
+
1131
+ await Promise.all([
1132
+ sdk.api.endusers.deleteOne(enduser.id),
1133
+ sdk.api.chat_rooms.deleteOne(enduserRoom.id),
1134
+ ])
1113
1135
  }
1114
1136
 
1115
1137
  const enduserAccessTests = async () => {
@@ -1432,7 +1454,12 @@ const formEventTests = async () => {
1432
1454
  const journey = await sdk.api.journeys.createOne({ title: 'test journey '})
1433
1455
  const form = await sdk.api.forms.createOne({
1434
1456
  title: 'test form',
1435
- fields: [{ title: 'question', type: 'string' }],
1457
+ })
1458
+ const field = await sdk.api.form_fields.createOne({
1459
+ formId: form.id,
1460
+ title: 'question',
1461
+ type: 'string',
1462
+ previousFields: [{ type: 'root', info: {} }]
1436
1463
  })
1437
1464
 
1438
1465
  const triggerStep = await sdk.api.automation_steps.createOne({
@@ -1460,8 +1487,19 @@ const formEventTests = async () => {
1460
1487
  const { accessCode: acNoStep } = await sdk.api.form_responses.prepare_form_response({ formId: form.id, enduserId: enduser.id })
1461
1488
  const { accessCode: acStep } = await sdk.api.form_responses.prepare_form_response({ formId: form.id, enduserId: enduser.id })
1462
1489
 
1463
- await sdk.api.form_responses.submit_form_response({ accessCode: acNoStep, responses: ['answer'] })
1464
- await sdk.api.form_responses.submit_form_response({ accessCode: acStep, automationStepId: triggerStep.id, responses: ['answer'] })
1490
+ const testResponse: FormResponseValue = {
1491
+ answer: {
1492
+ type: 'string',
1493
+ value: 'answer'
1494
+ },
1495
+ fieldId: field.id,
1496
+ fieldTitle: field.title,
1497
+ }
1498
+
1499
+ const { formResponse } = await sdk.api.form_responses.submit_form_response({ accessCode: acNoStep, responses: [testResponse] })
1500
+ assert(objects_equivalent(formResponse.responses, [testResponse]), 'bad form resonse returned', 'form response returned correctly')
1501
+
1502
+ await sdk.api.form_responses.submit_form_response({ accessCode: acStep, automationStepId: triggerStep.id, responses: [testResponse] })
1465
1503
  await wait(undefined, 250) // allow background creation with generous pause
1466
1504
 
1467
1505
  await async_test(
@@ -1606,10 +1644,81 @@ const ticketEventTests = async () => {
1606
1644
  ])
1607
1645
  }
1608
1646
 
1647
+ const removeFromJourneyTests = async () => {
1648
+ log_header("Remove from Journey")
1649
+
1650
+ const journey = await sdk.api.journeys.createOne({ title: 'test journey'})
1651
+ const journey2 = await sdk.api.journeys.createOne({ title: 'other journey'})
1652
+
1653
+ const enduser = await sdk.api.endusers.createOne({ email: 'test@tellescope.com' })
1654
+ const enduser2 = await sdk.api.endusers.createOne({ email: 'test2@tellescope.com' })
1655
+ await sdk.api.endusers.updateOne(enduser.id, { journeys: { [journey.id]: 'Added', [journey2.id]: 'Added2' }})
1656
+
1657
+ const step = await (
1658
+ sdk.api.automation_steps.createOne({
1659
+ journeyId: journey.id,
1660
+ event: { type: 'onJourneyStart', info: { } },
1661
+ action: { type: 'setEnduserStatus', info: { status: 'Root' }, },
1662
+ })
1663
+ )
1664
+ const step2 = await (
1665
+ sdk.api.automation_steps.createOne({
1666
+ journeyId: journey2.id,
1667
+ event: { type: 'onJourneyStart', info: { } },
1668
+ action: { type: 'setEnduserStatus', info: { status: 'Root' }, },
1669
+ })
1670
+ )
1671
+
1672
+ const createAction = (journeyId: string, step: { id: string }, enduserId?: string) => (
1673
+ sdk.api.automated_actions.createOne({
1674
+ journeyId,
1675
+ automationStepId: step.id,
1676
+ cancelConditions: [],
1677
+ enduserId: enduserId ?? enduser.id,
1678
+ processAfter: Date.now() + 1000000, // add delay to make sure it doesn't happen
1679
+ status: 'active',
1680
+ event: { type: 'onJourneyStart', info: { } },
1681
+ action: { type: 'setEnduserStatus', info: { status: 'Test Status' }, },
1682
+ })
1683
+ )
1684
+
1685
+ const numberOfActions = 4
1686
+ for (let i = 0; i < numberOfActions; i++) {
1687
+ await createAction(journey.id, step);
1688
+ await createAction(journey2.id, step2);
1689
+ await createAction(journey.id, step, enduser2.id);
1690
+ }
1691
+
1692
+ // remove from journey, should set all statuses to cancelled
1693
+ await sdk.api.endusers.updateOne(enduser.id, { journeys: { } }, { replaceObjectFields: true })
1694
+ await wait(undefined, 250)
1695
+
1696
+ await async_test(
1697
+ `Automated actions for handle ticket created`,
1698
+ () => sdk.api.automated_actions.getSome(),
1699
+ { onResult: (actions => (
1700
+ // enduser removed from multiple journeys
1701
+ actions.filter(a => a.status === 'cancelled').length === numberOfActions * 2
1702
+
1703
+ // other enduser is unaffected
1704
+ && actions.filter(a => a.status === 'active').length === numberOfActions
1705
+ ))}
1706
+ )
1707
+
1708
+
1709
+ await Promise.all([
1710
+ sdk.api.journeys.deleteOne(journey.id),
1711
+ sdk.api.journeys.deleteOne(journey2.id),
1712
+ sdk.api.endusers.deleteOne(enduser.id),
1713
+ sdk.api.endusers.deleteOne(enduser2.id),
1714
+ ])
1715
+ }
1716
+
1609
1717
  const automation_events_tests = async () => {
1610
1718
  log_header("Automation Events")
1611
1719
  await formEventTests()
1612
1720
  await ticketEventTests()
1721
+ await removeFromJourneyTests()
1613
1722
 
1614
1723
  }
1615
1724
 
@@ -1622,21 +1731,60 @@ const form_response_tests = async () => {
1622
1731
  const enduser = await sdk.api.endusers.createOne({ email: "formresponse@tellescope.com" })
1623
1732
  const form = await sdk.api.forms.createOne({
1624
1733
  title: 'test form',
1625
- fields: [{
1626
- title: stringTitle,
1627
- description: 'Enter a string',
1628
- type: 'string',
1629
- isOptional: false,
1630
- intakeField: stringIntakeField
1631
- }]
1632
1734
  })
1735
+ assert(form.numFields === 0, 'numFields bad init', 'num fields on init')
1736
+
1737
+ const field = await sdk.api.form_fields.createOne({
1738
+ formId: form.id,
1739
+ title: stringTitle,
1740
+ description: 'Enter a string',
1741
+ type: 'string',
1742
+ isOptional: false,
1743
+ intakeField: stringIntakeField,
1744
+ previousFields: [{ type: 'root', info: {} }]
1745
+ })
1746
+ const field2 = await sdk.api.form_fields.createOne({
1747
+ formId: form.id,
1748
+ title: stringTitle,
1749
+ description: 'Enter a string',
1750
+ type: 'string',
1751
+ isOptional: false,
1752
+ intakeField: stringIntakeField,
1753
+ previousFields: [{ type: 'root', info: {} }]
1754
+ })
1755
+ await wait(undefined, 250)
1756
+
1757
+ await async_test(
1758
+ `numFields incremented on new field`,
1759
+ () => sdk.api.forms.getOne(form.id),
1760
+ { onResult: f => f.numFields === 2},
1761
+ )
1762
+
1763
+ await sdk.api.form_fields.deleteOne(field2.id)
1764
+ await wait(undefined, 250)
1765
+ await async_test(
1766
+ `numFields decremented after delete`,
1767
+ () => sdk.api.forms.getOne(form.id),
1768
+ { onResult: f => f.numFields === 1},
1769
+ )
1770
+
1771
+
1633
1772
  // await sdk.api.automation_steps.createOne({
1634
1773
  // event: { type: "formResponse", info: { formId: form.id } },
1635
1774
  // action: { type: 'sendWebhook', info: { message: 'test' } },
1636
1775
  // })
1637
1776
 
1638
1777
  const { accessCode } = await sdk.api.form_responses.prepare_form_response({ formId: form.id, enduserId: enduser.id })
1639
- await sdk.api.form_responses.submit_form_response({ accessCode, responses: [stringResponse] })
1778
+ await sdk.api.form_responses.submit_form_response({ accessCode, responses: [
1779
+ {
1780
+ fieldTitle: 'doesnot matter',
1781
+ fieldId: field.id,
1782
+ answer: {
1783
+ type: 'string',
1784
+ value: stringResponse,
1785
+ },
1786
+ }
1787
+ ]})
1640
1788
 
1641
1789
  // const [triggeredAutomation] = await sdk.api.automated_actions.getSome()
1642
1790
  const enduserWithUpdate = await sdk.api.endusers.getOne(enduser.id)
@@ -1645,7 +1793,7 @@ const form_response_tests = async () => {
1645
1793
  // assert(triggeredAutomation?.event?.type === 'formResponse', 'no form response event', 'form response event triggered')
1646
1794
  assert(enduserWithUpdate?.fields?.[stringIntakeField] === stringResponse, 'no enduser update', 'enduser updated')
1647
1795
  assert(
1648
- recordedResponse?.responses?.length === 1 && recordedResponse.responses[0]?.[stringTitle] === stringResponse,
1796
+ recordedResponse?.responses?.length === 1 && recordedResponse.responses[0]?.answer.value === stringResponse,
1649
1797
  'response not recorded',
1650
1798
  'response recorded'
1651
1799
  )
@@ -2100,6 +2248,87 @@ const enduser_redaction_tests = async () => {
2100
2248
  ])
2101
2249
  }
2102
2250
 
2251
+ const public_form_tests = async () => {
2252
+ log_header("Public Form")
2253
+
2254
+ const journey = await sdk.api.journeys.createOne({ title: 'test journey '})
2255
+ const nonPublicForm = await sdk.api.forms.createOne({
2256
+ title: 'test form',
2257
+ intakePhone: 'optional',
2258
+ })
2259
+ const form = await sdk.api.forms.createOne({
2260
+ title: 'test form',
2261
+ allowPublicURL: true,
2262
+ intakePhone: 'optional',
2263
+ })
2264
+ const submitInfo = {
2265
+ businessId: form.businessId,
2266
+ email: 'publicformtest@tellescope.com',
2267
+ formId: form.id,
2268
+ fname: 'sebastian',
2269
+ lname: 'coates',
2270
+ }
2271
+ const submitInfoNonPublic = { ...submitInfo, formId: nonPublicForm.id }
2272
+
2273
+ await async_test(
2274
+ 'non-public form blocked',
2275
+ () => enduserSDK.api.form_responses. session_for_public_form(submitInfoNonPublic),
2276
+ handleAnyError,
2277
+ )
2278
+ await async_test(
2279
+ 'no questions form blocked',
2280
+ () => enduserSDK.api.form_responses. session_for_public_form(submitInfo),
2281
+ handleAnyError,
2282
+ )
2283
+
2284
+ const field = await sdk.api.form_fields.createOne({
2285
+ formId: form.id,
2286
+ title: 'question',
2287
+ type: 'string',
2288
+ previousFields: [{ type: 'root', info: {} }]
2289
+ })
2290
+ const testResponse: FormResponseValue = {
2291
+ answer: {
2292
+ type: 'string',
2293
+ value: 'answer'
2294
+ },
2295
+ fieldId: field.id,
2296
+ fieldTitle: field.title,
2297
+ }
2298
+
2299
+ // upserts enduser
2300
+ const responseInfo = await enduserSDK.api.form_responses. session_for_public_form(submitInfo)
2301
+
2302
+ // verify enduser is actually upserted
2303
+ const enduser = await sdk.api.endusers.getOne({ email: 'publicformtest@tellescope.com'})
2304
+
2305
+ // test case for existing enduser
2306
+ await enduserSDK.api.form_responses. session_for_public_form(submitInfo)
2307
+
2308
+ enduserSDK.setAuthToken(responseInfo.authToken)
2309
+ await enduserSDK.refresh_session() // should be allowed
2310
+
2311
+ // assert((enduserSDK.userInfo as any).allowedPaths.length === 3, 'allowed paths not preserved', 'allowed paths preserved after refresh')
2312
+
2313
+ await async_test(
2314
+ 'enduser cannot use non-allowed path',
2315
+ () => enduserSDK.api.endusers.updateOne(enduserSDK.userInfo.id, { fields: { testFiedl: 'testValue' } }),
2316
+ handleAnyError,
2317
+ )
2318
+
2319
+ await async_test(
2320
+ 'enduser can submit public form',
2321
+ () => enduserSDK.api.form_responses.submit_form_response({ accessCode: responseInfo.accessCode, responses: [testResponse] }),
2322
+ passOnAnyResult,
2323
+ )
2324
+
2325
+ await Promise.all([
2326
+ sdk.api.forms.deleteOne(form.id),
2327
+ sdk.api.journeys.deleteOne(journey.id),
2328
+ sdk.api.endusers.deleteOne(enduser.id),
2329
+ ])
2330
+ }
2331
+
2103
2332
  const NO_TEST = () => {}
2104
2333
  const tests: { [K in keyof ClientModelForName]: () => void } = {
2105
2334
  chats: chat_tests,
@@ -2118,6 +2347,7 @@ const tests: { [K in keyof ClientModelForName]: () => void } = {
2118
2347
  meetings: meetings_tests,
2119
2348
  notes: NO_TEST,
2120
2349
  forms: NO_TEST,
2350
+ form_fields: NO_TEST,
2121
2351
  form_responses: form_response_tests,
2122
2352
  calendar_events: calendar_events_tests,
2123
2353
  webhooks: NO_TEST, // tested separately,
@@ -2133,6 +2363,7 @@ const tests: { [K in keyof ClientModelForName]: () => void } = {
2133
2363
  managed_content_records: NO_TEST,
2134
2364
  post_comments: NO_TEST,
2135
2365
  post_likes: NO_TEST,
2366
+ organizations: NO_TEST,
2136
2367
  };
2137
2368
 
2138
2369
  (async () => {
@@ -2145,6 +2376,7 @@ const tests: { [K in keyof ClientModelForName]: () => void } = {
2145
2376
  ])
2146
2377
  await setup_tests()
2147
2378
  await multi_tenant_tests() // should come right after setup tests
2379
+ await public_form_tests()
2148
2380
  await search_tests()
2149
2381
  await badInputTests()
2150
2382
  await filterTests()
@@ -2155,9 +2387,13 @@ const tests: { [K in keyof ClientModelForName]: () => void } = {
2155
2387
  await enduser_session_tests()
2156
2388
  await role_based_access_tests()
2157
2389
  await enduser_redaction_tests()
2158
- } catch(err) {
2390
+ } catch(err: any) {
2159
2391
  console.error("Failed during custom test")
2160
- console.error(err)
2392
+ if (err.message && err.info) {
2393
+ console.error(err.message, JSON.stringify(err.info, null, 2))
2394
+ } else {
2395
+ console.error(err)
2396
+ }
2161
2397
  process.exit(1)
2162
2398
  }
2163
2399