@tellescope/sdk 1.66.8 → 1.67.1

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,9 @@ import {
10
10
  UserDisplayInfo,
11
11
  } from "@tellescope/types-client"
12
12
  import {
13
+ CreateTicketActionInfo,
14
+ CreateTicketAssignmentStrategies,
15
+ CreateTicketAssignmentStrategy,
13
16
  FormResponseValue,
14
17
  ModelName,
15
18
  } from "@tellescope/types-models"
@@ -5026,6 +5029,305 @@ const nextReminderInMS_tests = async () => {
5026
5029
  ])
5027
5030
  }
5028
5031
 
5032
+ const pollForResults = async <T>(f: () => Promise<T[]>, intervalInMS=500, iterations=20) => {
5033
+ for (let i = 0; i < iterations; i++) {
5034
+ await wait(undefined, intervalInMS)
5035
+ const result = await f()
5036
+ if (result.length) return result
5037
+ }
5038
+
5039
+ throw new Error("failed pollForResults")
5040
+ }
5041
+
5042
+ const test_ticket_automation_assignment_and_optimization = async () => {
5043
+ log_header("Ticket Automation / Assignment Tests")
5044
+
5045
+ const users = await sdk.api.users.getSome()
5046
+ if (users.length < 3) throw new Error("Must have at least 3 users to detect invalid assignment")
5047
+
5048
+ await sdk.api.users.updateOne(sdk.userInfo.id, { tags: ['tag1', 'tag2'] })
5049
+ await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { tags: ['tag1', 'tag3'] })
5050
+
5051
+ const journey = await sdk.api.journeys.createOne({ title: "Testing" })
5052
+
5053
+ let foregroundTestCounter = 0
5054
+ const testForegroundTicket = async ({
5055
+ assignedTo,
5056
+ info,
5057
+ validOwners,
5058
+ enduser,
5059
+ closedForReason,
5060
+ testDelayedChild,
5061
+ } : {
5062
+ assignedTo: string[]
5063
+ info: Pick<CreateTicketActionInfo, 'assignmentStrategy' | 'defaultAssignee'>,
5064
+ validOwners: string[],
5065
+ closedForReason?: string,
5066
+ enduser?: Enduser,
5067
+ testDelayedChild?: boolean,
5068
+ }) => {
5069
+ const e = enduser || await sdk.api.endusers.createOne({ assignedTo, journeys: { [journey.id]: '' } })
5070
+
5071
+ const step = await sdk.api.automation_steps.createOne({
5072
+ action: { type: 'createTicket', info: { ...info, title: 'background ticket' } },
5073
+ events: [{
5074
+ type: 'ticketCompleted',
5075
+ info: closedForReason ? { automationStepId: PLACEHOLDER_ID, closedForReason } : { automationStepId: PLACEHOLDER_ID }
5076
+ }],
5077
+ journeyId: journey.id,
5078
+ })
5079
+ const statusStep = await sdk.api.automation_steps.createOne({
5080
+ action: { type: 'setEnduserStatus', info: { status: 'Test Status' } },
5081
+ events: [{
5082
+ type: 'ticketCompleted',
5083
+ info: closedForReason ? { automationStepId: PLACEHOLDER_ID, closedForReason } : { automationStepId: PLACEHOLDER_ID }
5084
+ }],
5085
+ journeyId: journey.id,
5086
+ })
5087
+ const child = await sdk.api.automation_steps.createOne({
5088
+ action: { type: 'setEnduserStatus', info: { status: 'Test Status' } },
5089
+ events: [{
5090
+ type: 'afterAction',
5091
+ info: {
5092
+ automationStepId: step.id,
5093
+ delay: 0, delayInMS: 0, unit: 'Days',
5094
+ }
5095
+ }],
5096
+ journeyId: journey.id,
5097
+ })
5098
+
5099
+ const ticket = await sdk.api.tickets.createOne({
5100
+ title: 'foreground ticket',
5101
+ enduserId: e.id,
5102
+ automationStepId: PLACEHOLDER_ID,
5103
+ journeyId: journey.id,
5104
+ owner: validOwners[0],
5105
+ closedForReason,
5106
+ })
5107
+
5108
+ await async_test(
5109
+ `Foreground ticket assignment ${++foregroundTestCounter}`,
5110
+ () => sdk.api.tickets.close_ticket({ ticketId: ticket.id, closedForReason }),
5111
+ { onResult: ({ generated }) => !!generated?.owner && validOwners.includes(generated.owner) }
5112
+ )
5113
+ await async_test(
5114
+ `Foreground ticket nop, no duplicates`,
5115
+ () => sdk.api.automated_actions.getSome({ filter: { automationStepId: step.id } }),
5116
+ { onResult: steps => steps.length === 1 && !!steps[0].isNOP }
5117
+ )
5118
+ await async_test(
5119
+ `Background action queued, no duplicates`,
5120
+ () => sdk.api.automated_actions.getSome({ filter: { automationStepId: statusStep.id } }),
5121
+ {
5122
+ onResult: steps => steps.length === 1 && !steps[0].isNOP
5123
+ }
5124
+ )
5125
+
5126
+ // verify that ticket generated by close_ticket goes on to generate its own delayed actions
5127
+ if (testDelayedChild) {
5128
+ await async_test(
5129
+ `Delayed child ticket`,
5130
+ () => pollForResults(() => sdk.api.automated_actions.getSome({ filter: { automationStepId: child.id } })),
5131
+ { onResult: steps => steps.length === 1 && !steps[0].isNOP }
5132
+ )
5133
+ }
5134
+
5135
+ await Promise.all([
5136
+ sdk.api.endusers.deleteOne(e.id),
5137
+ sdk.api.automation_steps.deleteOne(step.id),
5138
+ sdk.api.automation_steps.deleteOne(statusStep.id),
5139
+ sdk.api.automation_steps.deleteOne(child.id),
5140
+ ])
5141
+ }
5142
+
5143
+ await testForegroundTicket({
5144
+ assignedTo: [],
5145
+ info: {
5146
+ assignmentStrategy: { type: 'default', info: {} } ,
5147
+ defaultAssignee: sdk.userInfo.id
5148
+ },
5149
+ validOwners: [sdk.userInfo.id],
5150
+ testDelayedChild: true,
5151
+ })
5152
+ await testForegroundTicket({
5153
+ assignedTo: [],
5154
+ info: {
5155
+ assignmentStrategy: { type: 'default', info: {} } ,
5156
+ defaultAssignee: sdk.userInfo.id
5157
+ },
5158
+ validOwners: [sdk.userInfo.id],
5159
+ closedForReason: "closedForReason test",
5160
+ testDelayedChild: true,
5161
+ })
5162
+ await testForegroundTicket({
5163
+ assignedTo: [],
5164
+ info: {
5165
+ assignmentStrategy: { type: 'default', info: {} } ,
5166
+ defaultAssignee: sdkNonAdmin.userInfo.id
5167
+ },
5168
+ validOwners: [sdkNonAdmin.userInfo.id],
5169
+ })
5170
+ await testForegroundTicket({
5171
+ assignedTo: [],
5172
+ info: {
5173
+ assignmentStrategy: { type: 'previous-owner', info: {} } ,
5174
+ defaultAssignee: sdk.userInfo.id
5175
+ },
5176
+ validOwners: [sdkNonAdmin.userInfo.id],
5177
+ })
5178
+ await testForegroundTicket({
5179
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5180
+ info: {
5181
+ assignmentStrategy: { type: 'care-team-primary', info: {} } ,
5182
+ defaultAssignee: sdk.userInfo.id
5183
+ },
5184
+ validOwners: [sdkNonAdmin.userInfo.id],
5185
+ })
5186
+ await testForegroundTicket({
5187
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5188
+ info: {
5189
+ assignmentStrategy: { type: 'care-team-random', info: {} } ,
5190
+ defaultAssignee: sdk.userInfo.id
5191
+ },
5192
+ validOwners: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5193
+ })
5194
+ await testForegroundTicket({
5195
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5196
+ info: {
5197
+ assignmentStrategy: { type: 'by-tags', info: { qualifier: 'One Of', values: ['tag1']} } ,
5198
+ defaultAssignee: sdkNonAdmin.userInfo.id,
5199
+ },
5200
+ validOwners: [sdk.userInfo.id, sdkNonAdmin.userInfo.id, ],
5201
+ })
5202
+ await testForegroundTicket({
5203
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5204
+ info: {
5205
+ assignmentStrategy: { type: 'by-tags', info: { qualifier: 'One Of', values: ['tag2']} } ,
5206
+ defaultAssignee: sdkNonAdmin.userInfo.id
5207
+ },
5208
+ validOwners: [sdk.userInfo.id],
5209
+ })
5210
+ await testForegroundTicket({
5211
+ assignedTo: [],
5212
+ info: {
5213
+ assignmentStrategy: { type: 'by-tags', info: { qualifier: 'One Of', values: ['tag3']} } ,
5214
+ defaultAssignee: sdk.userInfo.id
5215
+ },
5216
+ validOwners: [sdkNonAdmin.userInfo.id],
5217
+ })
5218
+
5219
+
5220
+ let backgroundTestCounter = 0
5221
+ const testBackgroundTicket = async ({
5222
+ assignedTo,
5223
+ info,
5224
+ validOwners,
5225
+ enduser,
5226
+ } : {
5227
+ assignedTo: string[]
5228
+ info: Pick<CreateTicketActionInfo, 'assignmentStrategy' | 'defaultAssignee'>,
5229
+ validOwners: string[],
5230
+ enduser?: Enduser,
5231
+ }) => {
5232
+ const e = enduser || await sdk.api.endusers.createOne({ assignedTo })
5233
+ await sdk.api.automated_actions.createOne({
5234
+ action: { type: 'createTicket', info: { ...info, title: 'background ticket' } },
5235
+ automationStepId: PLACEHOLDER_ID,
5236
+ enduserId: e.id,
5237
+ event: { type: 'afterAction', info: { automationStepId: PLACEHOLDER_ID, delay: 0, delayInMS: 0, unit: 'Days' } },
5238
+ journeyId: journey.id,
5239
+ status: 'active',
5240
+ processAfter: Date.now(),
5241
+ })
5242
+
5243
+ await async_test(
5244
+ `Background ticket assignment ${++backgroundTestCounter}`,
5245
+ () => pollForResults(() => sdk.api.tickets.getSome({ filter: { enduserId: e.id, title: 'background ticket' } })),
5246
+ { onResult: ts => ts.length === 1 && !!ts[0].owner && validOwners.includes(ts[0].owner) }
5247
+ )
5248
+
5249
+ await sdk.api.endusers.deleteOne(e.id)
5250
+ }
5251
+
5252
+ await testBackgroundTicket({
5253
+ assignedTo: [],
5254
+ info: {
5255
+ assignmentStrategy: { type: 'default', info: {} } ,
5256
+ defaultAssignee: sdk.userInfo.id
5257
+ },
5258
+ validOwners: [sdk.userInfo.id],
5259
+ })
5260
+ await testBackgroundTicket({
5261
+ assignedTo: [],
5262
+ info: {
5263
+ assignmentStrategy: { type: 'default', info: {} } ,
5264
+ defaultAssignee: sdkNonAdmin.userInfo.id
5265
+ },
5266
+ validOwners: [sdkNonAdmin.userInfo.id],
5267
+ })
5268
+
5269
+ // ticket needs existing enduser, previous owner for test to work
5270
+ const enduser = await sdk.api.endusers.createOne({ fname: 'previous-owner-test'})
5271
+ await sdk.api.tickets.createOne({
5272
+ // title should be different than 'background test' so it doesn't create false positive test
5273
+ title: 'previous-owner-test', enduserId: enduser.id, journeyId: journey.id, owner: sdkNonAdmin.userInfo.id
5274
+ })
5275
+ await testBackgroundTicket({
5276
+ assignedTo: [],
5277
+ enduser,
5278
+ info: {
5279
+ assignmentStrategy: { type: 'previous-owner', info: {} } ,
5280
+ defaultAssignee: sdk.userInfo.id
5281
+ },
5282
+ validOwners: [sdkNonAdmin.userInfo.id],
5283
+ })
5284
+
5285
+ await testBackgroundTicket({
5286
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5287
+ info: {
5288
+ assignmentStrategy: { type: 'care-team-primary', info: {} } ,
5289
+ defaultAssignee: sdk.userInfo.id
5290
+ },
5291
+ validOwners: [sdkNonAdmin.userInfo.id],
5292
+ })
5293
+ await testBackgroundTicket({
5294
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5295
+ info: {
5296
+ assignmentStrategy: { type: 'care-team-random', info: {} } ,
5297
+ defaultAssignee: sdk.userInfo.id
5298
+ },
5299
+ validOwners: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5300
+ })
5301
+ await testBackgroundTicket({
5302
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5303
+ info: {
5304
+ assignmentStrategy: { type: 'by-tags', info: { qualifier: 'One Of', values: ['tag1']} } ,
5305
+ defaultAssignee: sdkNonAdmin.userInfo.id,
5306
+ },
5307
+ validOwners: [sdk.userInfo.id, sdkNonAdmin.userInfo.id, ],
5308
+ })
5309
+ await testBackgroundTicket({
5310
+ assignedTo: [sdkNonAdmin.userInfo.id, sdk.userInfo.id],
5311
+ info: {
5312
+ assignmentStrategy: { type: 'by-tags', info: { qualifier: 'One Of', values: ['tag2']} } ,
5313
+ defaultAssignee: sdkNonAdmin.userInfo.id
5314
+ },
5315
+ validOwners: [sdk.userInfo.id],
5316
+ })
5317
+ await testBackgroundTicket({
5318
+ assignedTo: [],
5319
+ info: {
5320
+ assignmentStrategy: { type: 'by-tags', info: { qualifier: 'One Of', values: ['tag3']} } ,
5321
+ defaultAssignee: sdk.userInfo.id
5322
+ },
5323
+ validOwners: [sdkNonAdmin.userInfo.id],
5324
+ })
5325
+
5326
+ return Promise.all([
5327
+ await sdk.api.journeys.deleteOne(journey.id)
5328
+ ])
5329
+ }
5330
+
5029
5331
  const NO_TEST = () => {}
5030
5332
  const tests: { [K in keyof ClientModelForName]: () => void } = {
5031
5333
  phone_trees: NO_TEST,
@@ -5165,6 +5467,7 @@ const validate_schema = () => {
5165
5467
  await mfa_tests()
5166
5468
  await setup_tests()
5167
5469
  await multi_tenant_tests() // should come right after setup tests
5470
+ await test_ticket_automation_assignment_and_optimization()
5168
5471
  await role_based_access_tests()
5169
5472
  await automation_trigger_tests()
5170
5473
  await enduser_session_tests()
@@ -223,7 +223,7 @@ const endusers_tests = async (isSubscribed: boolean) => {
223
223
 
224
224
  const update = { assignedTo: [sdk.userInfo.id] }
225
225
  await sdk.api.endusers.updateOne(enduser.id, update)
226
-
226
+ await sdk.api.endusers.updateOne(enduser.id, { fields: { 'dontIncludeInWebhook': true } }, { dontSendWebhook: true })
227
227
  await check_next_webhook(
228
228
  a => {
229
229
  delete a.updates?.[0]?.recordBeforeUpdate.humanReadableId
Binary file