@tellescope/sdk 1.242.9 → 1.243.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.
- package/lib/cjs/enduser.d.ts +20 -0
- package/lib/cjs/enduser.d.ts.map +1 -1
- package/lib/cjs/sdk.d.ts +45 -0
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +2 -0
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.js +169 -0
- package/lib/cjs/tests/api_tests/concurrent_build_threads.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/custom_aggregation.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/custom_aggregation.test.js +109 -7
- package/lib/cjs/tests/api_tests/custom_aggregation.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/custom_dashboards.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/custom_dashboards.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/custom_dashboards.test.js +304 -0
- package/lib/cjs/tests/api_tests/custom_dashboards.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js +655 -139
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.d.ts +20 -0
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.js +481 -0
- package/lib/cjs/tests/api_tests/no_access_permission_checks.test.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +125 -112
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/enduser.d.ts +20 -0
- package/lib/esm/enduser.d.ts.map +1 -1
- package/lib/esm/sdk.d.ts +45 -0
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +2 -0
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.js +165 -0
- package/lib/esm/tests/api_tests/concurrent_build_threads.test.js.map +1 -0
- package/lib/esm/tests/api_tests/custom_aggregation.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/custom_aggregation.test.js +110 -8
- package/lib/esm/tests/api_tests/custom_aggregation.test.js.map +1 -1
- package/lib/esm/tests/api_tests/custom_dashboards.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/custom_dashboards.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/custom_dashboards.test.js +300 -0
- package/lib/esm/tests/api_tests/custom_dashboards.test.js.map +1 -0
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js +655 -139
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.d.ts +20 -0
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.js +477 -0
- package/lib/esm/tests/api_tests/no_access_permission_checks.test.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +125 -112
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/sdk.ts +9 -1
- package/src/tests/api_tests/concurrent_build_threads.test.ts +103 -0
- package/src/tests/api_tests/custom_aggregation.test.ts +74 -0
- package/src/tests/api_tests/custom_dashboards.test.ts +258 -0
- package/src/tests/api_tests/inbox_thread_assignment_updates.test.ts +431 -1
- package/src/tests/api_tests/no_access_permission_checks.test.ts +365 -0
- package/src/tests/tests.ts +8 -1
- package/test_generated.pdf +0 -0
|
@@ -825,7 +825,437 @@ export const inbox_thread_assignment_updates_tests = async ({ sdk, sdkNonAdmin }
|
|
|
825
825
|
sdk.api.inbox_threads.deleteOne(readByEmailTestThread!.id),
|
|
826
826
|
])
|
|
827
827
|
|
|
828
|
-
|
|
828
|
+
// Test 33: Outbound SMS with NARROW time range should NOT reset readBy
|
|
829
|
+
// This tests the production scenario where incremental builds only include recent messages
|
|
830
|
+
console.log("Testing outbound SMS with NARROW time range should NOT reset readBy...")
|
|
831
|
+
|
|
832
|
+
const narrowRangeTestSMS1 = await sdk.api.sms_messages.createOne({
|
|
833
|
+
message: "Inbound for narrow range test",
|
|
834
|
+
enduserId: testEnduser.id,
|
|
835
|
+
inbound: true,
|
|
836
|
+
phoneNumber: "+15555559999",
|
|
837
|
+
enduserPhoneNumber: "+15555559998",
|
|
838
|
+
logOnly: true,
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
// Build threads with wide range (includes inbound)
|
|
842
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
843
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
844
|
+
|
|
845
|
+
const narrowRangeThreads = await sdk.api.inbox_threads.load_threads({})
|
|
846
|
+
const narrowRangeThread = narrowRangeThreads.threads.find(t =>
|
|
847
|
+
t.type === 'SMS' && t.phoneNumber === "+15555559999"
|
|
848
|
+
)
|
|
849
|
+
assert(!!narrowRangeThread, "Narrow range test thread should exist")
|
|
850
|
+
|
|
851
|
+
// Mark thread as read
|
|
852
|
+
await sdk.api.inbox_threads.updateOne(narrowRangeThread!.id, {
|
|
853
|
+
readBy: { [sdk.userInfo.id]: new Date() }
|
|
854
|
+
})
|
|
855
|
+
|
|
856
|
+
// Wait and capture a timestamp AFTER the inbound was created
|
|
857
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
858
|
+
const narrowRangeFrom = new Date()
|
|
859
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
860
|
+
|
|
861
|
+
// Create outbound message (after narrowRangeFrom)
|
|
862
|
+
const narrowRangeTestSMS2 = await sdk.api.sms_messages.createOne({
|
|
863
|
+
message: "Outbound for narrow range test",
|
|
864
|
+
enduserId: testEnduser.id,
|
|
865
|
+
inbound: false,
|
|
866
|
+
phoneNumber: "+15555559999",
|
|
867
|
+
enduserPhoneNumber: "+15555559998",
|
|
868
|
+
logOnly: true,
|
|
869
|
+
})
|
|
870
|
+
|
|
871
|
+
// Rebuild with NARROW range that excludes the original inbound
|
|
872
|
+
await sdk.api.inbox_threads.build_threads({ from: narrowRangeFrom, to: new Date() })
|
|
873
|
+
|
|
874
|
+
// readBy should still be preserved (not reset)
|
|
875
|
+
const threadAfterNarrowBuild = (await sdk.api.inbox_threads.load_threads({ ids: [narrowRangeThread!.id] })).threads[0]
|
|
876
|
+
assert(
|
|
877
|
+
!!threadAfterNarrowBuild.readBy?.[sdk.userInfo.id],
|
|
878
|
+
`readBy should remain set after outbound-only incremental build, got ${JSON.stringify(threadAfterNarrowBuild.readBy)}`
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
console.log("Outbound SMS with narrow time range does NOT reset readBy test passed")
|
|
882
|
+
|
|
883
|
+
// Cleanup narrow range test
|
|
884
|
+
await Promise.all([
|
|
885
|
+
sdk.api.sms_messages.deleteOne(narrowRangeTestSMS1.id),
|
|
886
|
+
sdk.api.sms_messages.deleteOne(narrowRangeTestSMS2.id),
|
|
887
|
+
sdk.api.inbox_threads.deleteOne(narrowRangeThread!.id),
|
|
888
|
+
])
|
|
889
|
+
|
|
890
|
+
// Test 34: Outbound Email with NARROW time range should NOT reset readBy
|
|
891
|
+
// This tests the production scenario where incremental builds only include recent messages
|
|
892
|
+
console.log("Testing outbound Email with NARROW time range should NOT reset readBy...")
|
|
893
|
+
|
|
894
|
+
const narrowRangeTestEmail1 = await sdk.api.emails.createOne({
|
|
895
|
+
subject: "Inbound email for narrow range test",
|
|
896
|
+
textContent: "Test inbound email content",
|
|
897
|
+
enduserId: testEnduser.id,
|
|
898
|
+
inbound: true,
|
|
899
|
+
logOnly: true,
|
|
900
|
+
})
|
|
901
|
+
|
|
902
|
+
// Build threads with wide range (includes inbound)
|
|
903
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
904
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
905
|
+
|
|
906
|
+
const narrowRangeEmailThreads = await sdk.api.inbox_threads.load_threads({})
|
|
907
|
+
const narrowRangeEmailThread = narrowRangeEmailThreads.threads.find(t =>
|
|
908
|
+
t.type === 'Email' && t.enduserIds.includes(testEnduser.id) && t.title === "Inbound email for narrow range test"
|
|
909
|
+
)
|
|
910
|
+
assert(!!narrowRangeEmailThread, "Narrow range test Email thread should exist")
|
|
911
|
+
|
|
912
|
+
// Mark thread as read
|
|
913
|
+
await sdk.api.inbox_threads.updateOne(narrowRangeEmailThread!.id, {
|
|
914
|
+
readBy: { [sdk.userInfo.id]: new Date() }
|
|
915
|
+
})
|
|
916
|
+
|
|
917
|
+
// Wait and capture a timestamp AFTER the inbound was created
|
|
918
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
919
|
+
const narrowRangeEmailFrom = new Date()
|
|
920
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
921
|
+
|
|
922
|
+
// Create outbound email (after narrowRangeEmailFrom)
|
|
923
|
+
const narrowRangeTestEmail2 = await sdk.api.emails.createOne({
|
|
924
|
+
subject: "Re: Inbound email for narrow range test",
|
|
925
|
+
textContent: "Outbound reply for narrow range test",
|
|
926
|
+
enduserId: testEnduser.id,
|
|
927
|
+
inbound: false,
|
|
928
|
+
logOnly: true,
|
|
929
|
+
})
|
|
930
|
+
|
|
931
|
+
// Rebuild with NARROW range that excludes the original inbound
|
|
932
|
+
await sdk.api.inbox_threads.build_threads({ from: narrowRangeEmailFrom, to: new Date() })
|
|
933
|
+
|
|
934
|
+
// readBy should still be preserved (not reset)
|
|
935
|
+
const emailThreadAfterNarrowBuild = (await sdk.api.inbox_threads.load_threads({ ids: [narrowRangeEmailThread!.id] })).threads[0]
|
|
936
|
+
assert(
|
|
937
|
+
!!emailThreadAfterNarrowBuild.readBy?.[sdk.userInfo.id],
|
|
938
|
+
`Email readBy should remain set after outbound-only incremental build, got ${JSON.stringify(emailThreadAfterNarrowBuild.readBy)}`
|
|
939
|
+
)
|
|
940
|
+
|
|
941
|
+
console.log("Outbound Email with narrow time range does NOT reset readBy test passed")
|
|
942
|
+
|
|
943
|
+
// Cleanup narrow range email test
|
|
944
|
+
await Promise.all([
|
|
945
|
+
sdk.api.emails.deleteOne(narrowRangeTestEmail1.id),
|
|
946
|
+
sdk.api.emails.deleteOne(narrowRangeTestEmail2.id),
|
|
947
|
+
sdk.api.inbox_threads.deleteOne(narrowRangeEmailThread!.id),
|
|
948
|
+
])
|
|
949
|
+
|
|
950
|
+
// ========== Zendesk Thread Tests ==========
|
|
951
|
+
// Test 35: Basic Zendesk thread building
|
|
952
|
+
console.log("Testing Zendesk thread building...")
|
|
953
|
+
|
|
954
|
+
const zendeskTicketThread = await sdk.api.ticket_threads.createOne({
|
|
955
|
+
enduserId: testEnduser.id,
|
|
956
|
+
subject: "Test Zendesk Thread",
|
|
957
|
+
})
|
|
958
|
+
|
|
959
|
+
const zendeskTicketComment = await sdk.api.ticket_thread_comments.createOne({
|
|
960
|
+
ticketThreadId: zendeskTicketThread.id,
|
|
961
|
+
enduserId: testEnduser.id,
|
|
962
|
+
plaintext: "Test ticket comment",
|
|
963
|
+
html: "<p>Test ticket comment</p>",
|
|
964
|
+
public: true,
|
|
965
|
+
inbound: true,
|
|
966
|
+
})
|
|
967
|
+
|
|
968
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
969
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
970
|
+
|
|
971
|
+
const zendeskThreads = await sdk.api.inbox_threads.load_threads({ mdbFilter: { type: 'Zendesk' } })
|
|
972
|
+
const zendeskThread = zendeskThreads.threads.find(t => t.threadId === zendeskTicketThread.id)
|
|
973
|
+
|
|
974
|
+
assert(!!zendeskThread, "Zendesk thread should exist")
|
|
975
|
+
assert(zendeskThread!.type === 'Zendesk', "Type should be Zendesk")
|
|
976
|
+
assert(zendeskThread!.title === "Test Zendesk Thread", "Title should match subject")
|
|
977
|
+
assert(zendeskThread!.preview.includes("Test ticket comment"), "Preview should match comment")
|
|
978
|
+
assert(zendeskThread!.enduserIds.includes(testEnduser.id), "Should have enduser")
|
|
979
|
+
|
|
980
|
+
console.log("Basic Zendesk thread building test passed")
|
|
981
|
+
|
|
982
|
+
// Test 36: Zendesk assignment updates
|
|
983
|
+
console.log("Testing Zendesk assignment updates...")
|
|
984
|
+
|
|
985
|
+
// Update comment assignment
|
|
986
|
+
await sdk.api.ticket_thread_comments.updateOne(zendeskTicketComment.id, {
|
|
987
|
+
assignedTo: [testUser.id]
|
|
988
|
+
}, { replaceObjectFields: true })
|
|
989
|
+
|
|
990
|
+
// Wait for side effects (the side effect should update the inbox thread directly)
|
|
991
|
+
await new Promise(resolve => setTimeout(resolve, 1500))
|
|
992
|
+
|
|
993
|
+
// Verify - NO rebuild needed, side effect should have handled it
|
|
994
|
+
const updatedZendeskThreads = await sdk.api.inbox_threads.load_threads({ ids: [zendeskThread!.id] })
|
|
995
|
+
const updatedZendeskThread = updatedZendeskThreads.threads[0]
|
|
996
|
+
|
|
997
|
+
assert(
|
|
998
|
+
JSON.stringify(updatedZendeskThread.assignedTo) === JSON.stringify([testUser.id]),
|
|
999
|
+
`Zendesk thread assignment should be updated from comment, got ${JSON.stringify(updatedZendeskThread.assignedTo)}`
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
console.log("Zendesk assignment update test passed")
|
|
1003
|
+
|
|
1004
|
+
// Test 37: Zendesk outbound comment should NOT reset readBy
|
|
1005
|
+
console.log("Testing Zendesk outbound should NOT reset readBy...")
|
|
1006
|
+
|
|
1007
|
+
// Mark thread as read
|
|
1008
|
+
await sdk.api.inbox_threads.updateOne(zendeskThread!.id, {
|
|
1009
|
+
readBy: { [sdk.userInfo.id]: new Date() }
|
|
1010
|
+
})
|
|
1011
|
+
|
|
1012
|
+
// Verify marked as read
|
|
1013
|
+
const zendeskThreadAfterRead = (await sdk.api.inbox_threads.load_threads({ ids: [zendeskThread!.id] })).threads[0]
|
|
1014
|
+
assert(!!zendeskThreadAfterRead.readBy?.[sdk.userInfo.id], "Zendesk thread should be marked as read")
|
|
1015
|
+
|
|
1016
|
+
// Create outbound comment
|
|
1017
|
+
const zendeskOutboundComment = await sdk.api.ticket_thread_comments.createOne({
|
|
1018
|
+
ticketThreadId: zendeskTicketThread.id,
|
|
1019
|
+
enduserId: testEnduser.id,
|
|
1020
|
+
plaintext: "Outbound staff reply",
|
|
1021
|
+
html: "<p>Outbound staff reply</p>",
|
|
1022
|
+
public: true,
|
|
1023
|
+
inbound: false,
|
|
1024
|
+
userId: sdk.userInfo.id,
|
|
1025
|
+
})
|
|
1026
|
+
|
|
1027
|
+
// Rebuild threads
|
|
1028
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
1029
|
+
|
|
1030
|
+
// readBy should remain set
|
|
1031
|
+
const zendeskThreadAfterOutbound = (await sdk.api.inbox_threads.load_threads({ ids: [zendeskThread!.id] })).threads[0]
|
|
1032
|
+
assert(
|
|
1033
|
+
!!zendeskThreadAfterOutbound.readBy?.[sdk.userInfo.id],
|
|
1034
|
+
`readBy should remain set after outbound Zendesk comment, got ${JSON.stringify(zendeskThreadAfterOutbound.readBy)}`
|
|
1035
|
+
)
|
|
1036
|
+
assert(!!zendeskThreadAfterOutbound.outboundTimestamp, "outboundTimestamp should be set")
|
|
1037
|
+
assert(!!zendeskThreadAfterOutbound.outboundPreview?.includes("Outbound staff reply"), "outboundPreview should match")
|
|
1038
|
+
|
|
1039
|
+
console.log("Zendesk outbound does NOT reset readBy test passed")
|
|
1040
|
+
|
|
1041
|
+
// Test 38: New Zendesk inbound SHOULD clear readBy
|
|
1042
|
+
console.log("Testing new Zendesk inbound SHOULD clear readBy...")
|
|
1043
|
+
|
|
1044
|
+
await new Promise(resolve => setTimeout(resolve, 1100))
|
|
1045
|
+
|
|
1046
|
+
const zendeskNewInboundComment = await sdk.api.ticket_thread_comments.createOne({
|
|
1047
|
+
ticketThreadId: zendeskTicketThread.id,
|
|
1048
|
+
enduserId: testEnduser.id,
|
|
1049
|
+
plaintext: "New inbound from customer",
|
|
1050
|
+
html: "<p>New inbound from customer</p>",
|
|
1051
|
+
public: true,
|
|
1052
|
+
inbound: true,
|
|
1053
|
+
})
|
|
1054
|
+
|
|
1055
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
1056
|
+
|
|
1057
|
+
const zendeskThreadAfterNewInbound = (await sdk.api.inbox_threads.load_threads({ ids: [zendeskThread!.id] })).threads[0]
|
|
1058
|
+
assert(
|
|
1059
|
+
!zendeskThreadAfterNewInbound.readBy?.[sdk.userInfo.id],
|
|
1060
|
+
`readBy SHOULD be cleared after new inbound Zendesk comment, got ${JSON.stringify(zendeskThreadAfterNewInbound.readBy)}`
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
console.log("New Zendesk inbound DOES clear readBy test passed")
|
|
1064
|
+
|
|
1065
|
+
// Test 39: Zendesk narrow range build preserves readBy
|
|
1066
|
+
console.log("Testing Zendesk narrow range build preserves readBy...")
|
|
1067
|
+
|
|
1068
|
+
// Reset and build fresh
|
|
1069
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
1070
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
1071
|
+
|
|
1072
|
+
// Get fresh thread
|
|
1073
|
+
const freshZendeskThreads = await sdk.api.inbox_threads.load_threads({ mdbFilter: { type: 'Zendesk', threadId: zendeskTicketThread.id } })
|
|
1074
|
+
const freshZendeskThread = freshZendeskThreads.threads.find(t => t.threadId === zendeskTicketThread.id)
|
|
1075
|
+
assert(!!freshZendeskThread, "Fresh Zendesk thread should exist")
|
|
1076
|
+
|
|
1077
|
+
// Mark as read
|
|
1078
|
+
await sdk.api.inbox_threads.updateOne(freshZendeskThread!.id, {
|
|
1079
|
+
readBy: { [sdk.userInfo.id]: new Date() }
|
|
1080
|
+
})
|
|
1081
|
+
|
|
1082
|
+
// Wait and capture narrow range start
|
|
1083
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
1084
|
+
const zendeskNarrowRangeFrom = new Date()
|
|
1085
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
1086
|
+
|
|
1087
|
+
// Create outbound comment after narrowRangeFrom
|
|
1088
|
+
const zendeskNarrowRangeOutbound = await sdk.api.ticket_thread_comments.createOne({
|
|
1089
|
+
ticketThreadId: zendeskTicketThread.id,
|
|
1090
|
+
enduserId: testEnduser.id,
|
|
1091
|
+
plaintext: "Outbound in narrow range",
|
|
1092
|
+
html: "<p>Outbound in narrow range</p>",
|
|
1093
|
+
public: true,
|
|
1094
|
+
inbound: false,
|
|
1095
|
+
userId: sdk.userInfo.id,
|
|
1096
|
+
})
|
|
1097
|
+
|
|
1098
|
+
// Rebuild with narrow range (excludes original inbound)
|
|
1099
|
+
await sdk.api.inbox_threads.build_threads({ from: zendeskNarrowRangeFrom, to: new Date() })
|
|
1100
|
+
|
|
1101
|
+
// readBy should still be preserved
|
|
1102
|
+
const zendeskThreadAfterNarrowBuild = (await sdk.api.inbox_threads.load_threads({ ids: [freshZendeskThread!.id] })).threads[0]
|
|
1103
|
+
assert(
|
|
1104
|
+
!!zendeskThreadAfterNarrowBuild.readBy?.[sdk.userInfo.id],
|
|
1105
|
+
`readBy should remain set after outbound-only Zendesk incremental build, got ${JSON.stringify(zendeskThreadAfterNarrowBuild.readBy)}`
|
|
1106
|
+
)
|
|
1107
|
+
|
|
1108
|
+
console.log("Zendesk narrow range build preserves readBy test passed")
|
|
1109
|
+
|
|
1110
|
+
// Test 40: Zendesk channel filtering
|
|
1111
|
+
console.log("Testing Zendesk channel filtering...")
|
|
1112
|
+
|
|
1113
|
+
// Filter by Zendesk type
|
|
1114
|
+
const filteredZendeskThreads = await sdk.api.inbox_threads.load_threads({
|
|
1115
|
+
mdbFilter: { type: 'Zendesk' }
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
assert(filteredZendeskThreads.threads.length >= 1, "Should find Zendesk threads")
|
|
1119
|
+
assert(filteredZendeskThreads.threads.every(t => t.type === 'Zendesk'), "All filtered threads should be Zendesk type")
|
|
1120
|
+
|
|
1121
|
+
// Filter by multiple types including Zendesk
|
|
1122
|
+
const multiTypeZendeskThreads = await sdk.api.inbox_threads.load_threads({
|
|
1123
|
+
mdbFilter: { type: { $in: ['Zendesk', 'SMS'] } }
|
|
1124
|
+
})
|
|
1125
|
+
|
|
1126
|
+
assert(
|
|
1127
|
+
multiTypeZendeskThreads.threads.every(t => t.type === 'Zendesk' || t.type === 'SMS'),
|
|
1128
|
+
"Multi-type filter should work with Zendesk"
|
|
1129
|
+
)
|
|
1130
|
+
|
|
1131
|
+
console.log("Zendesk channel filtering test passed")
|
|
1132
|
+
|
|
1133
|
+
// Test 41: Zendesk thread without comments is skipped
|
|
1134
|
+
console.log("Testing Zendesk thread without comments is skipped...")
|
|
1135
|
+
|
|
1136
|
+
const emptyZendeskTicketThread = await sdk.api.ticket_threads.createOne({
|
|
1137
|
+
enduserId: testEnduser.id,
|
|
1138
|
+
subject: "Empty Zendesk Thread",
|
|
1139
|
+
})
|
|
1140
|
+
|
|
1141
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
1142
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
1143
|
+
|
|
1144
|
+
const allZendeskThreadsAfterEmpty = await sdk.api.inbox_threads.load_threads({ mdbFilter: { type: 'Zendesk' } })
|
|
1145
|
+
const emptyZendeskThread = allZendeskThreadsAfterEmpty.threads.find(t => t.threadId === emptyZendeskTicketThread.id)
|
|
1146
|
+
|
|
1147
|
+
assert(!emptyZendeskThread, "Zendesk thread without comments should NOT be built")
|
|
1148
|
+
|
|
1149
|
+
console.log("Zendesk thread without comments is skipped test passed")
|
|
1150
|
+
|
|
1151
|
+
// Test 42: Zendesk access control - verify access is blocked when user lacks ticket_threads permission
|
|
1152
|
+
console.log("Testing Zendesk access control (blocking)...")
|
|
1153
|
+
|
|
1154
|
+
// Create a new Zendesk thread for testing
|
|
1155
|
+
const accessControlTicketThread = await sdk.api.ticket_threads.createOne({
|
|
1156
|
+
enduserId: testEnduser.id,
|
|
1157
|
+
subject: "Access Control Test Thread",
|
|
1158
|
+
})
|
|
1159
|
+
|
|
1160
|
+
const accessControlComment = await sdk.api.ticket_thread_comments.createOne({
|
|
1161
|
+
ticketThreadId: accessControlTicketThread.id,
|
|
1162
|
+
enduserId: testEnduser.id,
|
|
1163
|
+
plaintext: "Access control test comment",
|
|
1164
|
+
html: "<p>Access control test comment</p>",
|
|
1165
|
+
public: true,
|
|
1166
|
+
inbound: true,
|
|
1167
|
+
})
|
|
1168
|
+
|
|
1169
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
1170
|
+
await sdk.api.inbox_threads.build_threads({ from: new Date(Date.now() - 60000), to: new Date() })
|
|
1171
|
+
|
|
1172
|
+
// Verify admin can see the Zendesk thread
|
|
1173
|
+
const adminZendeskThreads = await sdk.api.inbox_threads.load_threads({
|
|
1174
|
+
mdbFilter: { type: 'Zendesk' }
|
|
1175
|
+
})
|
|
1176
|
+
assert(adminZendeskThreads.threads.length >= 1, "Admin should see Zendesk threads")
|
|
1177
|
+
const accessControlThread = adminZendeskThreads.threads.find(t => t.threadId === accessControlTicketThread.id)
|
|
1178
|
+
assert(!!accessControlThread, "Admin should see the access control test thread")
|
|
1179
|
+
|
|
1180
|
+
// Create a role with NO ticket_threads access
|
|
1181
|
+
const noTicketThreadsRole = await sdk.api.role_based_access_permissions.createOne({
|
|
1182
|
+
role: 'No Ticket Threads Access',
|
|
1183
|
+
permissions: {
|
|
1184
|
+
ticket_threads: { read: null, create: null, update: null, delete: null },
|
|
1185
|
+
// Give access to other inbox types so we can verify selective blocking
|
|
1186
|
+
emails: { read: 'Default', create: 'Default', update: 'Default', delete: 'Default' },
|
|
1187
|
+
sms_messages: { read: 'Default', create: 'Default', update: 'Default', delete: 'Default' },
|
|
1188
|
+
},
|
|
1189
|
+
})
|
|
1190
|
+
|
|
1191
|
+
// Create a test user for access control testing
|
|
1192
|
+
const accessControlTestEmail = 'zendesk.access.control.test@tellescope.com'
|
|
1193
|
+
const accessControlTestUser = (
|
|
1194
|
+
await sdk.api.users.getOne({ email: accessControlTestEmail }).catch(() => null)
|
|
1195
|
+
) || (
|
|
1196
|
+
await sdk.api.users.createOne({ email: accessControlTestEmail, notificationEmailsDisabled: true, verifiedEmail: true })
|
|
1197
|
+
)
|
|
1198
|
+
|
|
1199
|
+
// Assign the restricted role to the test user
|
|
1200
|
+
await sdk.api.users.updateOne(accessControlTestUser.id, { roles: [noTicketThreadsRole.role] }, { replaceObjectFields: true })
|
|
1201
|
+
await new Promise(resolve => setTimeout(resolve, 2000)) // Wait for role change
|
|
1202
|
+
|
|
1203
|
+
// Create SDK session for the restricted user
|
|
1204
|
+
const sdkNoTicketAccess = new Session({
|
|
1205
|
+
host,
|
|
1206
|
+
authToken: (await sdk.api.users.generate_auth_token({ id: accessControlTestUser.id })).authToken,
|
|
1207
|
+
})
|
|
1208
|
+
|
|
1209
|
+
// User WITHOUT ticket_threads access should NOT see Zendesk threads
|
|
1210
|
+
const restrictedUserZendeskThreads = await sdkNoTicketAccess.api.inbox_threads.load_threads({
|
|
1211
|
+
mdbFilter: { type: 'Zendesk' }
|
|
1212
|
+
})
|
|
1213
|
+
|
|
1214
|
+
assert(
|
|
1215
|
+
restrictedUserZendeskThreads.threads.length === 0,
|
|
1216
|
+
`User without ticket_threads access should NOT see Zendesk threads, but found ${restrictedUserZendeskThreads.threads.length}`
|
|
1217
|
+
)
|
|
1218
|
+
console.log("Verified: User without ticket_threads access cannot see Zendesk threads")
|
|
1219
|
+
|
|
1220
|
+
// Verify the same user CAN still see other thread types they have access to (if any exist)
|
|
1221
|
+
// This confirms the filter is selective, not a blanket block
|
|
1222
|
+
const restrictedUserAllThreads = await sdkNoTicketAccess.api.inbox_threads.load_threads({})
|
|
1223
|
+
const restrictedUserHasZendesk = restrictedUserAllThreads.threads.some(t => t.type === 'Zendesk')
|
|
1224
|
+
assert(
|
|
1225
|
+
!restrictedUserHasZendesk,
|
|
1226
|
+
"User without ticket_threads access should not see ANY Zendesk threads even in unfiltered query"
|
|
1227
|
+
)
|
|
1228
|
+
console.log("Verified: Zendesk threads are filtered from all queries for restricted user")
|
|
1229
|
+
|
|
1230
|
+
// Cleanup access control test resources
|
|
1231
|
+
await Promise.all([
|
|
1232
|
+
sdk.api.ticket_thread_comments.deleteOne(accessControlComment.id).catch(() => {}),
|
|
1233
|
+
sdk.api.ticket_threads.deleteOne(accessControlTicketThread.id).catch(() => {}),
|
|
1234
|
+
sdk.api.users.deleteOne(accessControlTestUser.id).catch(() => {}),
|
|
1235
|
+
sdk.api.role_based_access_permissions.deleteOne(noTicketThreadsRole.id).catch(() => {}),
|
|
1236
|
+
])
|
|
1237
|
+
|
|
1238
|
+
console.log("Zendesk access control (blocking) test passed")
|
|
1239
|
+
|
|
1240
|
+
// Cleanup Zendesk test resources
|
|
1241
|
+
await Promise.all([
|
|
1242
|
+
sdk.api.ticket_thread_comments.deleteOne(zendeskTicketComment.id).catch(() => {}),
|
|
1243
|
+
sdk.api.ticket_thread_comments.deleteOne(zendeskOutboundComment.id).catch(() => {}),
|
|
1244
|
+
sdk.api.ticket_thread_comments.deleteOne(zendeskNewInboundComment.id).catch(() => {}),
|
|
1245
|
+
sdk.api.ticket_thread_comments.deleteOne(zendeskNarrowRangeOutbound.id).catch(() => {}),
|
|
1246
|
+
sdk.api.ticket_threads.deleteOne(zendeskTicketThread.id).catch(() => {}),
|
|
1247
|
+
sdk.api.ticket_threads.deleteOne(emptyZendeskTicketThread.id).catch(() => {}),
|
|
1248
|
+
])
|
|
1249
|
+
|
|
1250
|
+
// Delete any Zendesk inbox threads
|
|
1251
|
+
const remainingZendeskThreads = await sdk.api.inbox_threads.load_threads({ mdbFilter: { type: 'Zendesk' } })
|
|
1252
|
+
await Promise.all(
|
|
1253
|
+
remainingZendeskThreads.threads.map(t => sdk.api.inbox_threads.deleteOne(t.id).catch(() => {}))
|
|
1254
|
+
)
|
|
1255
|
+
|
|
1256
|
+
console.log("All Zendesk thread tests passed!")
|
|
1257
|
+
|
|
1258
|
+
console.log("All InboxThread assignment update tests passed!")
|
|
829
1259
|
|
|
830
1260
|
} finally {
|
|
831
1261
|
// Cleanup
|