@unboundcx/sdk 4.13.76 → 4.13.80
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/index.js +6 -0
- package/package.json +1 -1
- package/services/permissions.js +13 -4
- package/services/reporting.js +329 -0
- package/services/reportingAgents.js +184 -0
- package/services/users.js +134 -0
package/index.js
CHANGED
|
@@ -38,6 +38,7 @@ import { FaxService } from './services/fax.js';
|
|
|
38
38
|
import { DocumentsService } from './services/documents.js';
|
|
39
39
|
import { EsignService } from './services/esign.js';
|
|
40
40
|
import { PermissionsService } from './services/permissions.js';
|
|
41
|
+
import { UsersService } from './services/users.js';
|
|
41
42
|
import { TriggersService } from './services/triggers.js';
|
|
42
43
|
import { RecentsService } from './services/recents.js';
|
|
43
44
|
import { SearchService } from './services/search.js';
|
|
@@ -45,6 +46,7 @@ import { DirectoryService } from './services/directory.js';
|
|
|
45
46
|
import { ChatService } from './services/chat.js';
|
|
46
47
|
import { DeveloperApisService } from './services/developerApis.js';
|
|
47
48
|
import { TextService } from './services/text.js';
|
|
49
|
+
import { ReportingService } from './services/reporting.js';
|
|
48
50
|
import {
|
|
49
51
|
DataImportService,
|
|
50
52
|
DataExportService,
|
|
@@ -136,6 +138,8 @@ class UnboundSDK extends BaseSDK {
|
|
|
136
138
|
this.documents = new DocumentsService(this);
|
|
137
139
|
this.esign = new EsignService(this);
|
|
138
140
|
this.permissions = new PermissionsService(this);
|
|
141
|
+
this.users = new UsersService(this);
|
|
142
|
+
this.reporting = new ReportingService(this);
|
|
139
143
|
this.triggers = new TriggersService(this);
|
|
140
144
|
this.recents = new RecentsService(this);
|
|
141
145
|
this.search = new SearchService(this);
|
|
@@ -340,6 +344,8 @@ export { KnowledgeBaseService } from './services/knowledgeBase.js';
|
|
|
340
344
|
export { FaxService } from './services/fax.js';
|
|
341
345
|
export { EsignService, EsignPublicService } from './services/esign.js';
|
|
342
346
|
export { PermissionsService } from './services/permissions.js';
|
|
347
|
+
export { UsersService } from './services/users.js';
|
|
348
|
+
export { ReportingService } from './services/reporting.js';
|
|
343
349
|
export { RecentsService } from './services/recents.js';
|
|
344
350
|
export { SearchService } from './services/search.js';
|
|
345
351
|
export { DirectoryService } from './services/directory.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundcx/sdk",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.80",
|
|
4
4
|
"description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
package/services/permissions.js
CHANGED
|
@@ -20,24 +20,32 @@ export class PermissionsService {
|
|
|
20
20
|
* @param {Object} group - Group configuration
|
|
21
21
|
* @param {string} group.name - Group name (required)
|
|
22
22
|
* @param {string} group.description - Group description
|
|
23
|
+
* @param {string} [group.type] - 'access' (default) or 'team'
|
|
24
|
+
* @param {string} [group.managerUserId] - Manager user id, for `team` groups
|
|
23
25
|
* @returns {Promise<Object>} Created group
|
|
24
26
|
* @example
|
|
25
27
|
* await sdk.permissions.createGroup({
|
|
26
28
|
* name: 'Support Leads',
|
|
27
29
|
* description: 'Escalation-tier support agents',
|
|
30
|
+
* type: 'team',
|
|
31
|
+
* managerUserId: 'user-456',
|
|
28
32
|
* });
|
|
29
33
|
*/
|
|
30
|
-
async createGroup({ name, description }) {
|
|
34
|
+
async createGroup({ name, description, type, managerUserId }) {
|
|
31
35
|
this.sdk.validateParams(
|
|
32
|
-
{ name, description },
|
|
36
|
+
{ name, description, type, managerUserId },
|
|
33
37
|
{
|
|
34
38
|
name: { type: 'string', required: true },
|
|
35
39
|
description: { type: 'string', required: false },
|
|
40
|
+
type: { type: 'string', required: false },
|
|
41
|
+
managerUserId: { type: 'string', required: false },
|
|
36
42
|
},
|
|
37
43
|
);
|
|
38
44
|
|
|
39
45
|
const groupData = { name };
|
|
40
46
|
if (description !== undefined) groupData.description = description;
|
|
47
|
+
if (type !== undefined) groupData.type = type;
|
|
48
|
+
if (managerUserId !== undefined) groupData.managerUserId = managerUserId;
|
|
41
49
|
|
|
42
50
|
const params = {
|
|
43
51
|
body: groupData,
|
|
@@ -50,10 +58,11 @@ export class PermissionsService {
|
|
|
50
58
|
/**
|
|
51
59
|
* Update an existing permission group
|
|
52
60
|
* @param {string} groupId - Group ID to update
|
|
53
|
-
* @param {Object} data - Fields to update (
|
|
61
|
+
* @param {Object} data - Fields to update (name, description, type,
|
|
62
|
+
* managerUserId)
|
|
54
63
|
* @returns {Promise<Object>} Updated group
|
|
55
64
|
* @example
|
|
56
|
-
* await sdk.permissions.updateGroup('group-123', {
|
|
65
|
+
* await sdk.permissions.updateGroup('group-123', { type: 'team', managerUserId: 'user-456' });
|
|
57
66
|
*/
|
|
58
67
|
async updateGroup(groupId, data) {
|
|
59
68
|
groupId = String(groupId);
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { internalRequest } from '../base.js';
|
|
2
|
+
import { ReportingAgentsService } from './reportingAgents.js';
|
|
3
|
+
|
|
4
|
+
// Q2: same queryString helper as reportingAgents.js (kept local -- no
|
|
5
|
+
// shared util module between the two files today).
|
|
6
|
+
function queryString(params = {}) {
|
|
7
|
+
const parts = [];
|
|
8
|
+
for (const [key, value] of Object.entries(params)) {
|
|
9
|
+
if (value === undefined || value === null || value === '') continue;
|
|
10
|
+
const v = Array.isArray(value) ? value.join(',') : value;
|
|
11
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
|
|
12
|
+
}
|
|
13
|
+
return parts.length ? `?${parts.join('&')}` : '';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// F7 (cc-reporting-foundation-plan.md §8) -- reporting API surface.
|
|
17
|
+
export class ReportingService {
|
|
18
|
+
constructor(sdk) {
|
|
19
|
+
this.sdk = sdk;
|
|
20
|
+
this.views = {
|
|
21
|
+
list: (...args) => this.listViews(...args),
|
|
22
|
+
create: (...args) => this.createView(...args),
|
|
23
|
+
update: (...args) => this.updateView(...args),
|
|
24
|
+
remove: (...args) => this.removeView(...args),
|
|
25
|
+
};
|
|
26
|
+
this.schedules = {
|
|
27
|
+
create: (...args) => this.createSchedule(...args),
|
|
28
|
+
list: (...args) => this.listSchedules(...args),
|
|
29
|
+
update: (...args) => this.updateSchedule(...args),
|
|
30
|
+
remove: (...args) => this.removeSchedule(...args),
|
|
31
|
+
runs: (...args) => this.listScheduleRuns(...args),
|
|
32
|
+
runNow: (...args) => this.runScheduleNow(...args),
|
|
33
|
+
};
|
|
34
|
+
// P3 (agent-reporting-plan.md §7 + §8.1): thin presets over the same
|
|
35
|
+
// registry above -- /reporting/agents/*. Split into its own file/class
|
|
36
|
+
// (house 400-line limit) since this file was already at 215 lines.
|
|
37
|
+
this.agents = new ReportingAgentsService(sdk);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Metrics + dimensions catalogue.
|
|
42
|
+
* @returns {Promise<Object>} { metrics: [...], dimensions: [...] }
|
|
43
|
+
* @example
|
|
44
|
+
* const { metrics } = await sdk.reporting.definitions();
|
|
45
|
+
*/
|
|
46
|
+
async definitions() {
|
|
47
|
+
return internalRequest(this.sdk, '/reporting/definitions', 'GET');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Run a report over the reporting rollups (+ today stitch).
|
|
52
|
+
* @param {Object} body - { metrics, dimensions, filters, from, to, grain, tz, continuous }
|
|
53
|
+
* @returns {Promise<Object>} { columns, rows, definitions, meta }
|
|
54
|
+
* @example
|
|
55
|
+
* await sdk.reporting.query({ metrics: ['handled','slPct'], dimensions: ['queue','time'], from, to, grain: 'day' });
|
|
56
|
+
*/
|
|
57
|
+
async query(body) {
|
|
58
|
+
this.sdk.validateParams(
|
|
59
|
+
{ metrics: body?.metrics, from: body?.from, to: body?.to },
|
|
60
|
+
{
|
|
61
|
+
metrics: { type: 'array', required: true },
|
|
62
|
+
from: { type: 'string', required: true },
|
|
63
|
+
to: { type: 'string', required: true },
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
return internalRequest(this.sdk, '/reporting/query', 'POST', { body });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Same as query(), but streams a CSV (forces HTTP transport).
|
|
71
|
+
* @param {Object} body
|
|
72
|
+
* @returns {Promise<Object>} raw CSV response (transport-dependent)
|
|
73
|
+
* @example
|
|
74
|
+
* await sdk.reporting.csv({ metrics: ['handled'], dimensions: ['queue'], from, to });
|
|
75
|
+
*/
|
|
76
|
+
async csv(body) {
|
|
77
|
+
return internalRequest(this.sdk, '/reporting/query?format=csv', 'POST', { body, httpOnly: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Drilldown: the raw records underlying one metric cell.
|
|
82
|
+
* @param {Object} body - { metric, cell: { dimensions, bucketFrom, bucketTo }, cursor, limit }
|
|
83
|
+
* @returns {Promise<Object>} { rows, cursor, hasMore }
|
|
84
|
+
* @example
|
|
85
|
+
* await sdk.reporting.detail({ metric: 'handled', cell: { dimensions: { queueId }, bucketFrom, bucketTo } });
|
|
86
|
+
*/
|
|
87
|
+
async detail(body) {
|
|
88
|
+
this.sdk.validateParams(
|
|
89
|
+
{ metric: body?.metric },
|
|
90
|
+
{ metric: { type: 'string', required: true } },
|
|
91
|
+
);
|
|
92
|
+
return internalRequest(this.sdk, '/reporting/detail', 'POST', { body });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Q2 preset: disposition matrix (code x queue|agent|code), incl. a
|
|
97
|
+
* noDisposition count for groupBy=queue|code.
|
|
98
|
+
* @param {Object} params - { from, to, queueIds, userIds, groupBy: 'queue'|'agent'|'code' }
|
|
99
|
+
* @returns {Promise<Object>} { groupBy, columns, rows, noDisposition, meta }
|
|
100
|
+
* @example
|
|
101
|
+
* await sdk.reporting.dispositions({ from, to, groupBy: 'queue' });
|
|
102
|
+
*/
|
|
103
|
+
async dispositions({ from, to, queueIds, userIds, groupBy } = {}) {
|
|
104
|
+
this.sdk.validateParams(
|
|
105
|
+
{ from, to },
|
|
106
|
+
{ from: { type: 'string', required: true }, to: { type: 'string', required: true } },
|
|
107
|
+
);
|
|
108
|
+
return internalRequest(
|
|
109
|
+
this.sdk,
|
|
110
|
+
`/reporting/dispositions${queryString({ from, to, queueIds, userIds, groupBy })}`,
|
|
111
|
+
'GET',
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Q2 preset: exec summary (originating-tasks-only KPI tiles + channel mix
|
|
117
|
+
* + trend), with a server-computed delta vs the previous period.
|
|
118
|
+
* @param {Object} params - { from, to, compareTo: 'previous' }
|
|
119
|
+
* @returns {Promise<Object>} { kpis, channelMix, trend, fcr, previousPeriod }
|
|
120
|
+
* @example
|
|
121
|
+
* await sdk.reporting.execSummary({ from, to, compareTo: 'previous' });
|
|
122
|
+
*/
|
|
123
|
+
async execSummary({ from, to, compareTo } = {}) {
|
|
124
|
+
this.sdk.validateParams(
|
|
125
|
+
{ from, to },
|
|
126
|
+
{ from: { type: 'string', required: true }, to: { type: 'string', required: true } },
|
|
127
|
+
);
|
|
128
|
+
return internalRequest(
|
|
129
|
+
this.sdk,
|
|
130
|
+
`/reporting/execSummary${queryString({ from, to, compareTo })}`,
|
|
131
|
+
'GET',
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Q2 preset: first-contact-resolution / repeat-contact count over
|
|
137
|
+
* accounts.fcrWindowHours.
|
|
138
|
+
* @param {Object} params - { from, to, queueIds }
|
|
139
|
+
* @returns {Promise<Object>} { windowHours, repeatContacts, originatingHandled, fcrPct }
|
|
140
|
+
* @example
|
|
141
|
+
* await sdk.reporting.fcr({ from, to });
|
|
142
|
+
*/
|
|
143
|
+
async fcr({ from, to, queueIds } = {}) {
|
|
144
|
+
this.sdk.validateParams(
|
|
145
|
+
{ from, to },
|
|
146
|
+
{ from: { type: 'string', required: true }, to: { type: 'string', required: true } },
|
|
147
|
+
);
|
|
148
|
+
return internalRequest(
|
|
149
|
+
this.sdk,
|
|
150
|
+
`/reporting/fcr${queryString({ from, to, queueIds })}`,
|
|
151
|
+
'GET',
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Rollup health: last computed bucket, lag, missing buckets.
|
|
157
|
+
* @returns {Promise<Object>}
|
|
158
|
+
* @example
|
|
159
|
+
* const { lagSeconds } = await sdk.reporting.health();
|
|
160
|
+
*/
|
|
161
|
+
async health() {
|
|
162
|
+
return internalRequest(this.sdk, '/reporting/health', 'GET');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @returns {Promise<Object>} { results: [...] }
|
|
167
|
+
* @example
|
|
168
|
+
* const { results } = await sdk.reporting.views.list();
|
|
169
|
+
*/
|
|
170
|
+
async listViews() {
|
|
171
|
+
return internalRequest(this.sdk, '/reporting/views', 'GET');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* @param {Object} view - { name, query, isShared }
|
|
176
|
+
* @returns {Promise<Object>} created view
|
|
177
|
+
* @example
|
|
178
|
+
* await sdk.reporting.views.create({ name: 'Daily SL', query: {...} });
|
|
179
|
+
*/
|
|
180
|
+
async createView({ name, query, isShared } = {}) {
|
|
181
|
+
this.sdk.validateParams(
|
|
182
|
+
{ name, query },
|
|
183
|
+
{ name: { type: 'string', required: true }, query: { type: 'object', required: true } },
|
|
184
|
+
);
|
|
185
|
+
const body = { name, query };
|
|
186
|
+
if (isShared !== undefined) body.isShared = isShared;
|
|
187
|
+
return internalRequest(this.sdk, '/reporting/views', 'POST', { body });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param {string} id
|
|
192
|
+
* @param {Object} data - { name, query, isShared }
|
|
193
|
+
* @returns {Promise<Object>} updated view
|
|
194
|
+
* @example
|
|
195
|
+
* await sdk.reporting.views.update('263...', { isShared: true });
|
|
196
|
+
*/
|
|
197
|
+
async updateView(id, data) {
|
|
198
|
+
id = String(id);
|
|
199
|
+
this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
|
|
200
|
+
return internalRequest(this.sdk, `/reporting/views/${id}`, 'PUT', { body: data });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* @param {string} id
|
|
205
|
+
* @returns {Promise<Object>} { id, deleted: true }
|
|
206
|
+
* @example
|
|
207
|
+
* await sdk.reporting.views.remove('263...');
|
|
208
|
+
*/
|
|
209
|
+
async removeView(id) {
|
|
210
|
+
id = String(id);
|
|
211
|
+
this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
|
|
212
|
+
return internalRequest(this.sdk, `/reporting/views/${id}`, 'DELETE');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @param {string} viewId
|
|
217
|
+
* @param {Object} schedule - { cron, timezone, recipients, format, runAs, isEnabled }
|
|
218
|
+
* @returns {Promise<Object>} created schedule
|
|
219
|
+
* @example
|
|
220
|
+
* await sdk.reporting.schedules.create('263...', { cron: '0 8 * * MON', timezone: 'America/Denver', recipients: ['a@b.com'] });
|
|
221
|
+
*/
|
|
222
|
+
async createSchedule(viewId, { cron, timezone, recipients, format, runAs, isEnabled } = {}) {
|
|
223
|
+
viewId = String(viewId);
|
|
224
|
+
this.sdk.validateParams(
|
|
225
|
+
{ viewId, cron, timezone, recipients },
|
|
226
|
+
{
|
|
227
|
+
viewId: { type: 'string', required: true },
|
|
228
|
+
cron: { type: 'string', required: true },
|
|
229
|
+
timezone: { type: 'string', required: true },
|
|
230
|
+
recipients: { type: 'array', required: true },
|
|
231
|
+
},
|
|
232
|
+
);
|
|
233
|
+
const body = { cron, timezone, recipients };
|
|
234
|
+
if (format !== undefined) body.format = format;
|
|
235
|
+
if (runAs !== undefined) body.runAs = runAs;
|
|
236
|
+
if (isEnabled !== undefined) body.isEnabled = isEnabled;
|
|
237
|
+
return internalRequest(this.sdk, `/reporting/views/${viewId}/schedule`, 'POST', { body });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* @param {string} viewId
|
|
242
|
+
* @returns {Promise<Object>} { results: [...] }
|
|
243
|
+
* @example
|
|
244
|
+
* await sdk.reporting.schedules.list('263...');
|
|
245
|
+
*/
|
|
246
|
+
async listSchedules(viewId) {
|
|
247
|
+
viewId = String(viewId);
|
|
248
|
+
this.sdk.validateParams({ viewId }, { viewId: { type: 'string', required: true } });
|
|
249
|
+
return internalRequest(this.sdk, `/reporting/views/${viewId}/schedule`, 'GET');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @param {string} viewId
|
|
254
|
+
* @param {string} scheduleId
|
|
255
|
+
* @param {Object} data - partial { cron, timezone, recipients, format, isEnabled }
|
|
256
|
+
* @returns {Promise<Object>} updated schedule
|
|
257
|
+
* @example
|
|
258
|
+
* await sdk.reporting.schedules.update('263...', '264...', { isEnabled: false });
|
|
259
|
+
*/
|
|
260
|
+
async updateSchedule(viewId, scheduleId, data = {}) {
|
|
261
|
+
viewId = String(viewId);
|
|
262
|
+
scheduleId = String(scheduleId);
|
|
263
|
+
this.sdk.validateParams(
|
|
264
|
+
{ viewId, scheduleId },
|
|
265
|
+
{ viewId: { type: 'string', required: true }, scheduleId: { type: 'string', required: true } },
|
|
266
|
+
);
|
|
267
|
+
return internalRequest(
|
|
268
|
+
this.sdk,
|
|
269
|
+
`/reporting/views/${viewId}/schedule/${scheduleId}`,
|
|
270
|
+
'PUT',
|
|
271
|
+
{ body: data },
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @param {string} viewId
|
|
277
|
+
* @param {string} scheduleId
|
|
278
|
+
* @returns {Promise<Object>} { id, deleted: true }
|
|
279
|
+
* @example
|
|
280
|
+
* await sdk.reporting.schedules.remove('263...', '264...');
|
|
281
|
+
*/
|
|
282
|
+
async removeSchedule(viewId, scheduleId) {
|
|
283
|
+
viewId = String(viewId);
|
|
284
|
+
scheduleId = String(scheduleId);
|
|
285
|
+
this.sdk.validateParams(
|
|
286
|
+
{ viewId, scheduleId },
|
|
287
|
+
{ viewId: { type: 'string', required: true }, scheduleId: { type: 'string', required: true } },
|
|
288
|
+
);
|
|
289
|
+
return internalRequest(this.sdk, `/reporting/views/${viewId}/schedule/${scheduleId}`, 'DELETE');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @returns {Promise<Array>} schedule runs (status, startedAt, finishedAt, error, attempt)
|
|
294
|
+
* @example
|
|
295
|
+
* await sdk.reporting.schedules.runs('263...', '264...');
|
|
296
|
+
*/
|
|
297
|
+
async listScheduleRuns(viewId, scheduleId) {
|
|
298
|
+
viewId = String(viewId);
|
|
299
|
+
scheduleId = String(scheduleId);
|
|
300
|
+
this.sdk.validateParams(
|
|
301
|
+
{ viewId, scheduleId },
|
|
302
|
+
{ viewId: { type: 'string', required: true }, scheduleId: { type: 'string', required: true } },
|
|
303
|
+
);
|
|
304
|
+
return internalRequest(this.sdk, `/reporting/views/${viewId}/schedule/${scheduleId}/runs`, 'GET');
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Manual trigger -- runs the same delivery path as the cron tick, awaits
|
|
309
|
+
* sent/failed instead of just queuing.
|
|
310
|
+
* @param {string} viewId
|
|
311
|
+
* @param {string} scheduleId
|
|
312
|
+
* @returns {Promise<Object>} { id, status, attempt, error? }
|
|
313
|
+
* @example
|
|
314
|
+
* await sdk.reporting.schedules.runNow('263...', '264...');
|
|
315
|
+
*/
|
|
316
|
+
async runScheduleNow(viewId, scheduleId) {
|
|
317
|
+
viewId = String(viewId);
|
|
318
|
+
scheduleId = String(scheduleId);
|
|
319
|
+
this.sdk.validateParams(
|
|
320
|
+
{ viewId, scheduleId },
|
|
321
|
+
{ viewId: { type: 'string', required: true }, scheduleId: { type: 'string', required: true } },
|
|
322
|
+
);
|
|
323
|
+
return internalRequest(
|
|
324
|
+
this.sdk,
|
|
325
|
+
`/reporting/views/${viewId}/schedule/${scheduleId}/run-now`,
|
|
326
|
+
'POST',
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { internalRequest } from '../base.js';
|
|
2
|
+
|
|
3
|
+
function queryString(params = {}) {
|
|
4
|
+
const parts = [];
|
|
5
|
+
for (const [key, value] of Object.entries(params)) {
|
|
6
|
+
if (value === undefined || value === null || value === '') continue;
|
|
7
|
+
const v = Array.isArray(value) ? value.join(',') : value;
|
|
8
|
+
parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
|
|
9
|
+
}
|
|
10
|
+
return parts.length ? `?${parts.join('&')}` : '';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// P3 (agent-reporting-plan.md §7 + §8.1) -- /reporting/agents/* thin
|
|
14
|
+
// presets over the Foundation registry. sdk.reporting.agents.*
|
|
15
|
+
export class ReportingAgentsService {
|
|
16
|
+
constructor(sdk) {
|
|
17
|
+
this.sdk = sdk;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Agent summary rows (§6 metrics), grouped by agent, queue, or team.
|
|
22
|
+
* @param {Object} params - { from, to, userIds, queueIds, teamIds, groupBy }
|
|
23
|
+
* @returns {Promise<Object>} { columns, rows, definitions, meta }
|
|
24
|
+
* @example
|
|
25
|
+
* await sdk.reporting.agents.summary({ from, to, groupBy: 'team' });
|
|
26
|
+
*/
|
|
27
|
+
async summary({ from, to, userIds, queueIds, teamIds, groupBy } = {}) {
|
|
28
|
+
this.sdk.validateParams(
|
|
29
|
+
{ from, to },
|
|
30
|
+
{ from: { type: 'string', required: true }, to: { type: 'string', required: true } },
|
|
31
|
+
);
|
|
32
|
+
return internalRequest(
|
|
33
|
+
this.sdk,
|
|
34
|
+
`/reporting/agents/summary${queryString({ from, to, userIds, queueIds, teamIds, groupBy })}`,
|
|
35
|
+
'GET',
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Collapsed presence timeline for one agent. userId 'me' resolves to the
|
|
41
|
+
* caller.
|
|
42
|
+
* @param {string} userId
|
|
43
|
+
* @param {Object} params - { from, to }
|
|
44
|
+
* @returns {Promise<Object>} { rows }
|
|
45
|
+
* @example
|
|
46
|
+
* await sdk.reporting.agents.states('me', { from, to });
|
|
47
|
+
*/
|
|
48
|
+
async states(userId, { from, to } = {}) {
|
|
49
|
+
userId = String(userId);
|
|
50
|
+
this.sdk.validateParams(
|
|
51
|
+
{ userId, from, to },
|
|
52
|
+
{
|
|
53
|
+
userId: { type: 'string', required: true },
|
|
54
|
+
from: { type: 'string', required: true },
|
|
55
|
+
to: { type: 'string', required: true },
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
return internalRequest(
|
|
59
|
+
this.sdk,
|
|
60
|
+
`/reporting/agents/${userId}/states${queryString({ from, to })}`,
|
|
61
|
+
'GET',
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Per-task interaction rows for one agent, paginated.
|
|
67
|
+
* @param {string} userId
|
|
68
|
+
* @param {Object} params - { from, to, cursor, limit }
|
|
69
|
+
* @returns {Promise<Object>} { rows, cursor, hasMore }
|
|
70
|
+
* @example
|
|
71
|
+
* await sdk.reporting.agents.interactions('me', { from, to });
|
|
72
|
+
*/
|
|
73
|
+
async interactions(userId, { from, to, cursor, limit } = {}) {
|
|
74
|
+
userId = String(userId);
|
|
75
|
+
this.sdk.validateParams(
|
|
76
|
+
{ userId, from, to },
|
|
77
|
+
{
|
|
78
|
+
userId: { type: 'string', required: true },
|
|
79
|
+
from: { type: 'string', required: true },
|
|
80
|
+
to: { type: 'string', required: true },
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
return internalRequest(
|
|
84
|
+
this.sdk,
|
|
85
|
+
`/reporting/agents/${userId}/interactions${queryString({ from, to, cursor, limit })}`,
|
|
86
|
+
'GET',
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Per-agent per-local-day timesheet rows (net paid hours).
|
|
92
|
+
* @param {Object} params - { from, to, userIds, teamIds }
|
|
93
|
+
* @returns {Promise<Object>} { columns, rows }
|
|
94
|
+
* @example
|
|
95
|
+
* await sdk.reporting.agents.timesheet({ from, to, userIds: ['u1'] });
|
|
96
|
+
*/
|
|
97
|
+
async timesheet({ from, to, userIds, teamIds } = {}) {
|
|
98
|
+
this.sdk.validateParams(
|
|
99
|
+
{ from, to },
|
|
100
|
+
{ from: { type: 'string', required: true }, to: { type: 'string', required: true } },
|
|
101
|
+
);
|
|
102
|
+
return internalRequest(
|
|
103
|
+
this.sdk,
|
|
104
|
+
`/reporting/agents/timesheet${queryString({ from, to, userIds, teamIds })}`,
|
|
105
|
+
'GET',
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* CSV export of any of the four views above.
|
|
111
|
+
* @param {Object} params - { view: 'summary'|'states'|'interactions'|'timesheet', from, to, userId, userIds, queueIds, teamIds, groupBy }
|
|
112
|
+
* @returns {Promise<Object>} raw CSV response (transport-dependent)
|
|
113
|
+
* @example
|
|
114
|
+
* await sdk.reporting.agents.export({ view: 'timesheet', from, to });
|
|
115
|
+
*/
|
|
116
|
+
async export({ view, from, to, userId, userIds, queueIds, teamIds, groupBy } = {}) {
|
|
117
|
+
this.sdk.validateParams(
|
|
118
|
+
{ from, to },
|
|
119
|
+
{ from: { type: 'string', required: true }, to: { type: 'string', required: true } },
|
|
120
|
+
);
|
|
121
|
+
const qs = queryString({
|
|
122
|
+
view,
|
|
123
|
+
from,
|
|
124
|
+
to,
|
|
125
|
+
userId,
|
|
126
|
+
userIds,
|
|
127
|
+
queueIds,
|
|
128
|
+
teamIds,
|
|
129
|
+
groupBy,
|
|
130
|
+
format: 'csv',
|
|
131
|
+
});
|
|
132
|
+
return internalRequest(this.sdk, `/reporting/agents/export${qs}`, 'GET', { httpOnly: true });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* §8.1 quality trend: aiScoreAvg/humanScoreAvg + n/N counts, per day.
|
|
137
|
+
* @param {string} userId
|
|
138
|
+
* @param {Object} params - { from, to, grain }
|
|
139
|
+
* @returns {Promise<Object>} { columns, rows, definitions, meta }
|
|
140
|
+
* @example
|
|
141
|
+
* await sdk.reporting.agents.qualityTrend('me', { from, to, grain: 'day' });
|
|
142
|
+
*/
|
|
143
|
+
async qualityTrend(userId, { from, to, grain } = {}) {
|
|
144
|
+
userId = String(userId);
|
|
145
|
+
this.sdk.validateParams(
|
|
146
|
+
{ userId, from, to },
|
|
147
|
+
{
|
|
148
|
+
userId: { type: 'string', required: true },
|
|
149
|
+
from: { type: 'string', required: true },
|
|
150
|
+
to: { type: 'string', required: true },
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
return internalRequest(
|
|
154
|
+
this.sdk,
|
|
155
|
+
`/reporting/agents/${userId}/qualityTrend${queryString({ from, to, grain })}`,
|
|
156
|
+
'GET',
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* §8.1 best/lowest playbook-scored tasks for one agent.
|
|
162
|
+
* @param {string} userId
|
|
163
|
+
* @param {Object} params - { from, to, order: 'best'|'lowest', limit }
|
|
164
|
+
* @returns {Promise<Object>} { rows }
|
|
165
|
+
* @example
|
|
166
|
+
* await sdk.reporting.agents.topTasks('me', { from, to, order: 'lowest', limit: 10 });
|
|
167
|
+
*/
|
|
168
|
+
async topTasks(userId, { from, to, order, limit } = {}) {
|
|
169
|
+
userId = String(userId);
|
|
170
|
+
this.sdk.validateParams(
|
|
171
|
+
{ userId, from, to },
|
|
172
|
+
{
|
|
173
|
+
userId: { type: 'string', required: true },
|
|
174
|
+
from: { type: 'string', required: true },
|
|
175
|
+
to: { type: 'string', required: true },
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
return internalRequest(
|
|
179
|
+
this.sdk,
|
|
180
|
+
`/reporting/agents/${userId}/topTasks${queryString({ from, to, order, limit })}`,
|
|
181
|
+
'GET',
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { internalRequest } from '../base.js';
|
|
2
|
+
|
|
3
|
+
// agent-reporting-plan.md P0 — status reasons (§5.1) + a typed wrapper over
|
|
4
|
+
// the existing status-set path (objects.updateById on the `users` object;
|
|
5
|
+
// see app1-api src/services/objects/functions/customHandlers/update/users.js).
|
|
6
|
+
// No existing "users" service in the SDK to extend — this is a new one.
|
|
7
|
+
export class UsersService {
|
|
8
|
+
constructor(sdk) {
|
|
9
|
+
this.sdk = sdk;
|
|
10
|
+
this.statusReasons = {
|
|
11
|
+
list: (...args) => this.listStatusReasons(...args),
|
|
12
|
+
create: (...args) => this.createStatusReason(...args),
|
|
13
|
+
update: (...args) => this.updateStatusReason(...args),
|
|
14
|
+
remove: (...args) => this.removeStatusReason(...args),
|
|
15
|
+
};
|
|
16
|
+
this.status = {
|
|
17
|
+
set: (...args) => this.setStatus(...args),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* List agent status reasons (includes system rows).
|
|
23
|
+
* @returns {Promise<Object>} Object with results: Array of status reasons
|
|
24
|
+
* @example
|
|
25
|
+
* const { results } = await sdk.users.statusReasons.list();
|
|
26
|
+
*/
|
|
27
|
+
async listStatusReasons() {
|
|
28
|
+
return internalRequest(this.sdk, '/userStatusReasons', 'GET');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create an admin-defined status reason.
|
|
33
|
+
* @param {Object} reason
|
|
34
|
+
* @param {string} reason.code - Unique code (required)
|
|
35
|
+
* @param {string} reason.label - Display label (required)
|
|
36
|
+
* @param {string} reason.state - 'Away' | 'Busy' | 'Offline' (required)
|
|
37
|
+
* @param {string} [reason.emoji]
|
|
38
|
+
* @param {number} [reason.maxSeconds]
|
|
39
|
+
* @param {boolean} [reason.isPaid]
|
|
40
|
+
* @param {number} [reason.order]
|
|
41
|
+
* @returns {Promise<Object>} Created status reason
|
|
42
|
+
* @example
|
|
43
|
+
* await sdk.users.statusReasons.create({ code: 'standup', label: 'Standup', state: 'Away' });
|
|
44
|
+
*/
|
|
45
|
+
async createStatusReason({ code, label, state, emoji, maxSeconds, isPaid, order }) {
|
|
46
|
+
this.sdk.validateParams(
|
|
47
|
+
{ code, label, state },
|
|
48
|
+
{
|
|
49
|
+
code: { type: 'string', required: true },
|
|
50
|
+
label: { type: 'string', required: true },
|
|
51
|
+
state: { type: 'string', required: true },
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const body = { code, label, state };
|
|
56
|
+
if (emoji !== undefined) body.emoji = emoji;
|
|
57
|
+
if (maxSeconds !== undefined) body.maxSeconds = maxSeconds;
|
|
58
|
+
if (isPaid !== undefined) body.isPaid = isPaid;
|
|
59
|
+
if (order !== undefined) body.order = order;
|
|
60
|
+
|
|
61
|
+
return internalRequest(this.sdk, '/userStatusReasons', 'POST', { body });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Update a status reason (label/emoji/maxSeconds/isPaid/order only —
|
|
66
|
+
* code/state are immutable for every row).
|
|
67
|
+
* @param {string} id
|
|
68
|
+
* @param {Object} data
|
|
69
|
+
* @returns {Promise<Object>} Updated status reason
|
|
70
|
+
* @example
|
|
71
|
+
* await sdk.users.statusReasons.update('usr_break', { maxSeconds: 600 });
|
|
72
|
+
*/
|
|
73
|
+
async updateStatusReason(id, data) {
|
|
74
|
+
id = String(id);
|
|
75
|
+
this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
|
|
76
|
+
|
|
77
|
+
return internalRequest(this.sdk, `/userStatusReasons/${id}`, 'PUT', {
|
|
78
|
+
body: data,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Delete (soft) an admin-defined status reason. System reasons cannot be
|
|
84
|
+
* deleted.
|
|
85
|
+
* @param {string} id
|
|
86
|
+
* @returns {Promise<Object>} Deletion confirmation
|
|
87
|
+
* @example
|
|
88
|
+
* await sdk.users.statusReasons.remove('usr_standup');
|
|
89
|
+
*/
|
|
90
|
+
async removeStatusReason(id) {
|
|
91
|
+
id = String(id);
|
|
92
|
+
this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
|
|
93
|
+
|
|
94
|
+
return internalRequest(this.sdk, `/userStatusReasons/${id}`, 'DELETE');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set the current user's (or another user's, with permission) presence
|
|
99
|
+
* status. Thin typed wrapper over
|
|
100
|
+
* `sdk.objects.updateById({ object: 'users', ... })` — the underlying
|
|
101
|
+
* write path is unchanged, this just gives status-set a discoverable,
|
|
102
|
+
* typed entry point that also carries `reasonId`.
|
|
103
|
+
* @param {string} userId
|
|
104
|
+
* @param {Object} status
|
|
105
|
+
* @param {string} status.state - e.g. 'Available' | 'Away' | 'Busy' | 'Offline'
|
|
106
|
+
* @param {string} [status.reasonId] - id from statusReasons.list()
|
|
107
|
+
* @param {string} [status.label] - custom label, when no reasonId applies
|
|
108
|
+
* @param {string} [status.emoji] - custom emoji, when no reasonId applies
|
|
109
|
+
* @returns {Promise<Object>} Updated user
|
|
110
|
+
* @example
|
|
111
|
+
* await sdk.users.status.set('user-123', { state: 'Away', reasonId: 'usr_break' });
|
|
112
|
+
*/
|
|
113
|
+
async setStatus(userId, { state, reasonId, label, emoji } = {}) {
|
|
114
|
+
userId = String(userId);
|
|
115
|
+
this.sdk.validateParams(
|
|
116
|
+
{ userId, state },
|
|
117
|
+
{
|
|
118
|
+
userId: { type: 'string', required: true },
|
|
119
|
+
state: { type: 'string', required: true },
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const status = { state };
|
|
124
|
+
if (reasonId !== undefined) status.reasonId = reasonId;
|
|
125
|
+
if (label !== undefined) status.label = label;
|
|
126
|
+
if (emoji !== undefined) status.emoji = emoji;
|
|
127
|
+
|
|
128
|
+
return this.sdk.objects.updateById({
|
|
129
|
+
object: 'users',
|
|
130
|
+
id: userId,
|
|
131
|
+
update: { status },
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|