@webitel/api-services 26.8.7 → 26.8.9
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/package.json +1 -1
- package/src/api/clients/queues/__tests__/queueHooksSkillsEnabled.spec.ts +84 -0
- package/src/api/clients/queues/__tests__/queueSkills.spec.ts +75 -0
- package/src/api/clients/queues/queueHooks.ts +3 -0
- package/src/api/clients/queues/queueSkills.ts +11 -1
- package/src/validations/queueHook/__tests__/queueHook.validations.spec.ts +31 -0
- package/src/validations/queueHook/queueHook.validations.ts +1 -1
- package/src/validations/queueSkill/__tests__/queueSkill.validations.spec.ts +98 -0
- package/src/validations/queueSkill/queueSkill.validations.ts +5 -5
- package/types/.tsbuildinfo +1 -1
- package/types/validations/queueHook/queueHook.validations.d.ts +1 -1
- package/types/validations/queueSkill/queueSkill.validations.d.ts +12 -12
package/package.json
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const readQueueHook = vi.fn();
|
|
4
|
+
const readQueueSkill = vi.fn();
|
|
5
|
+
|
|
6
|
+
vi.mock('../../../../gen-wire', () => ({
|
|
7
|
+
getQueueHookService: () => ({
|
|
8
|
+
readQueueHook,
|
|
9
|
+
}),
|
|
10
|
+
getQueueSkillService: () => ({
|
|
11
|
+
readQueueSkill,
|
|
12
|
+
}),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
const { QueueHooksAPI } = await import('../queueHooks');
|
|
16
|
+
const { QueueSkillsAPI } = await import('../queueSkills');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Protobuf omits `enabled` when it is false, so a disabled record reads back
|
|
20
|
+
* without the field. The card store seeds anything missing from the schema,
|
|
21
|
+
* where `enabled` defaults to `true` for newly added records — so `get` has to
|
|
22
|
+
* spell the false out, or an untouched save would re-enable the record.
|
|
23
|
+
*/
|
|
24
|
+
describe('reading a disabled queue hook / skill', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
readQueueHook.mockReset();
|
|
27
|
+
readQueueSkill.mockReset();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('spells out enabled: false for a hook that omits it', async () => {
|
|
31
|
+
readQueueHook.mockResolvedValue({
|
|
32
|
+
data: {
|
|
33
|
+
id: 40,
|
|
34
|
+
event: 'bridged',
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await expect(
|
|
39
|
+
QueueHooksAPI.get({
|
|
40
|
+
parentId: '208',
|
|
41
|
+
itemId: '40',
|
|
42
|
+
}),
|
|
43
|
+
).resolves.toMatchObject({
|
|
44
|
+
enabled: false,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('spells out enabled: false for a skill that omits it', async () => {
|
|
49
|
+
readQueueSkill.mockResolvedValue({
|
|
50
|
+
data: {
|
|
51
|
+
id: 7,
|
|
52
|
+
lvl: 10,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await expect(
|
|
57
|
+
QueueSkillsAPI.get({
|
|
58
|
+
parentId: '208',
|
|
59
|
+
itemId: '7',
|
|
60
|
+
}),
|
|
61
|
+
).resolves.toMatchObject({
|
|
62
|
+
enabled: false,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('keeps enabled: true as sent', async () => {
|
|
67
|
+
readQueueHook.mockResolvedValue({
|
|
68
|
+
data: {
|
|
69
|
+
id: 41,
|
|
70
|
+
event: 'missed',
|
|
71
|
+
enabled: true,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await expect(
|
|
76
|
+
QueueHooksAPI.get({
|
|
77
|
+
parentId: '208',
|
|
78
|
+
itemId: '41',
|
|
79
|
+
}),
|
|
80
|
+
).resolves.toMatchObject({
|
|
81
|
+
enabled: true,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
const searchQueueSkill = vi.fn();
|
|
4
|
+
const readQueueSkill = vi.fn();
|
|
5
|
+
|
|
6
|
+
vi.mock('../../../../gen-wire', () => ({
|
|
7
|
+
getQueueSkillService: () => ({
|
|
8
|
+
searchQueueSkill,
|
|
9
|
+
readQueueSkill,
|
|
10
|
+
}),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
const { QueueSkillsAPI } = await import('../queueSkills');
|
|
14
|
+
|
|
15
|
+
/** live payload for a level-0 skill: `{id, skill, min_capacity: 0, max_capacity: 10}` */
|
|
16
|
+
describe('QueueSkillsAPI reads a level-0 skill as level 0', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
searchQueueSkill.mockReset();
|
|
19
|
+
readQueueSkill.mockReset();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('fills lvl in on get', async () => {
|
|
23
|
+
readQueueSkill.mockResolvedValue({
|
|
24
|
+
data: {
|
|
25
|
+
id: 339,
|
|
26
|
+
skill: {
|
|
27
|
+
id: '45',
|
|
28
|
+
name: 'Liza-test',
|
|
29
|
+
},
|
|
30
|
+
min_capacity: 0,
|
|
31
|
+
max_capacity: 10,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(
|
|
36
|
+
await QueueSkillsAPI.get({
|
|
37
|
+
parentId: '208',
|
|
38
|
+
itemId: '339',
|
|
39
|
+
}),
|
|
40
|
+
).toMatchObject({
|
|
41
|
+
id: 339,
|
|
42
|
+
lvl: 0,
|
|
43
|
+
minCapacity: 0,
|
|
44
|
+
maxCapacity: 10,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('fills lvl in on every list row, without touching the rows that have one', async () => {
|
|
49
|
+
searchQueueSkill.mockResolvedValue({
|
|
50
|
+
data: {
|
|
51
|
+
items: [
|
|
52
|
+
{
|
|
53
|
+
id: 339,
|
|
54
|
+
max_capacity: 10,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 412,
|
|
58
|
+
lvl: 10,
|
|
59
|
+
max_capacity: 10,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
next: false,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const { items } = await QueueSkillsAPI.getList({
|
|
67
|
+
parentId: '208',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(items.map((item: { lvl: number }) => item.lvl)).toEqual([
|
|
71
|
+
0,
|
|
72
|
+
10,
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
applyTransform,
|
|
7
7
|
camelToSnake,
|
|
8
8
|
merge,
|
|
9
|
+
mergeEach,
|
|
9
10
|
notify,
|
|
10
11
|
sanitizeToWire,
|
|
11
12
|
snakeToCamel,
|
|
@@ -32,6 +33,12 @@ const preRequestHandler = (parentId: ApiId) => (item: ApiParams) => ({
|
|
|
32
33
|
queueId: parentId,
|
|
33
34
|
});
|
|
34
35
|
|
|
36
|
+
/** the wire drops a field at its proto3 default: `enabled` when false, `lvl` when 0 */
|
|
37
|
+
const defaultObject = {
|
|
38
|
+
enabled: false,
|
|
39
|
+
lvl: 0,
|
|
40
|
+
};
|
|
41
|
+
|
|
35
42
|
const getQueueSkillsList = async (params: ApiParams) => {
|
|
36
43
|
const { page, size, search, sort, fields, id, parentId, skillId } =
|
|
37
44
|
applyTransform(params, [
|
|
@@ -58,7 +65,9 @@ const getQueueSkillsList = async (params: ApiParams) => {
|
|
|
58
65
|
merge(getDefaultGetListResponse()),
|
|
59
66
|
]);
|
|
60
67
|
return {
|
|
61
|
-
items,
|
|
68
|
+
items: applyTransform(items, [
|
|
69
|
+
mergeEach(defaultObject),
|
|
70
|
+
]),
|
|
62
71
|
next,
|
|
63
72
|
};
|
|
64
73
|
} catch (err) {
|
|
@@ -76,6 +85,7 @@ const getQueueSkill = async ({ parentId, itemId: id }: NestedGetItemParams) => {
|
|
|
76
85
|
);
|
|
77
86
|
return applyTransform(response.data, [
|
|
78
87
|
snakeToCamel(),
|
|
88
|
+
merge(defaultObject),
|
|
79
89
|
]);
|
|
80
90
|
} catch (err) {
|
|
81
91
|
throw applyTransform(err, [
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { getDefaultsFromZodSchema } from '@webitel/api-services/gen/utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { queueHookSchema } from '../queueHook.validations';
|
|
5
|
+
|
|
6
|
+
describe('queueHookSchema', () => {
|
|
7
|
+
/**
|
|
8
|
+
* The card store seeds a new item from the schema defaults, so a hook added
|
|
9
|
+
* from the queue card must come out enabled — as it did before the pinia
|
|
10
|
+
* migration replaced the vuex `resettableItemState`.
|
|
11
|
+
*/
|
|
12
|
+
it('defaults a new hook to enabled', () => {
|
|
13
|
+
expect(getDefaultsFromZodSchema(queueHookSchema, {})).toMatchObject({
|
|
14
|
+
enabled: true,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('keeps a disabled hook disabled', () => {
|
|
19
|
+
const result = queueHookSchema.safeParse({
|
|
20
|
+
event: 'joined',
|
|
21
|
+
schema: {
|
|
22
|
+
id: '1',
|
|
23
|
+
name: 'flow',
|
|
24
|
+
},
|
|
25
|
+
enabled: false,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(result.success).toBe(true);
|
|
29
|
+
expect(result.data?.enabled).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { getDefaultsFromZodSchema } from '@webitel/api-services/gen/utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
import { queueSkillSchema } from '../queueSkill.validations';
|
|
5
|
+
|
|
6
|
+
const filledSkill = {
|
|
7
|
+
id: '1',
|
|
8
|
+
name: 'English',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
describe('queueSkillSchema', () => {
|
|
12
|
+
/** see the hook schema spec — same pre-migration default */
|
|
13
|
+
it('defaults a new skill to enabled', () => {
|
|
14
|
+
expect(getDefaultsFromZodSchema(queueSkillSchema, {})).toMatchObject({
|
|
15
|
+
enabled: true,
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('keeps a disabled skill disabled', () => {
|
|
20
|
+
const result = queueSkillSchema.safeParse({
|
|
21
|
+
skill: {
|
|
22
|
+
id: '1',
|
|
23
|
+
name: 'skill',
|
|
24
|
+
},
|
|
25
|
+
lvl: 1,
|
|
26
|
+
enabled: false,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(result.success).toBe(true);
|
|
30
|
+
expect(result.data?.enabled).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/** `skill` stays empty, and its error must not swallow the other defaults */
|
|
34
|
+
it('seeds a new skill with the defaults the form expects', () => {
|
|
35
|
+
expect(getDefaultsFromZodSchema(queueSkillSchema, {})).toMatchObject({
|
|
36
|
+
lvl: 0,
|
|
37
|
+
minCapacity: 0,
|
|
38
|
+
maxCapacity: 10,
|
|
39
|
+
buckets: [],
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('keeps values already on the draft', () => {
|
|
44
|
+
expect(
|
|
45
|
+
getDefaultsFromZodSchema(queueSkillSchema, {
|
|
46
|
+
skill: filledSkill,
|
|
47
|
+
lvl: 5,
|
|
48
|
+
minCapacity: 2,
|
|
49
|
+
maxCapacity: 8,
|
|
50
|
+
}),
|
|
51
|
+
).toMatchObject({
|
|
52
|
+
skill: filledSkill,
|
|
53
|
+
lvl: 5,
|
|
54
|
+
minCapacity: 2,
|
|
55
|
+
maxCapacity: 8,
|
|
56
|
+
buckets: [],
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('requires a skill', () => {
|
|
61
|
+
const result = queueSkillSchema.safeParse({
|
|
62
|
+
lvl: 0,
|
|
63
|
+
minCapacity: 0,
|
|
64
|
+
maxCapacity: 10,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(result.success).toBe(false);
|
|
68
|
+
expect(result.error?.issues.map((issue) => issue.path.join('.'))).toContain(
|
|
69
|
+
'skill',
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('reports both sides when minCapacity exceeds maxCapacity', () => {
|
|
74
|
+
const result = queueSkillSchema.safeParse({
|
|
75
|
+
skill: filledSkill,
|
|
76
|
+
lvl: 0,
|
|
77
|
+
minCapacity: 10,
|
|
78
|
+
maxCapacity: 5,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(result.success).toBe(false);
|
|
82
|
+
expect(result.error?.issues.map((issue) => issue.path.join('.'))).toEqual([
|
|
83
|
+
'minCapacity',
|
|
84
|
+
'maxCapacity',
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('accepts the defaults as a valid pair', () => {
|
|
89
|
+
expect(
|
|
90
|
+
queueSkillSchema.safeParse({
|
|
91
|
+
skill: filledSkill,
|
|
92
|
+
lvl: 0,
|
|
93
|
+
minCapacity: 0,
|
|
94
|
+
maxCapacity: 10,
|
|
95
|
+
}).success,
|
|
96
|
+
).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
|
|
8
8
|
export const queueSkillSchemaBase = z.object({
|
|
9
9
|
skill: filledLookupSchema,
|
|
10
|
-
lvl: z.number().min(0).max(1000),
|
|
11
|
-
minCapacity: z.number().min(0).max(100).optional(),
|
|
12
|
-
maxCapacity: z.number().min(0).max(100).optional(),
|
|
13
|
-
buckets: z.array(flexibleLookupSchema).optional(),
|
|
14
|
-
enabled: z.boolean().
|
|
10
|
+
lvl: z.number().min(0).max(1000).default(0),
|
|
11
|
+
minCapacity: z.number().min(0).max(100).optional().default(0),
|
|
12
|
+
maxCapacity: z.number().min(0).max(100).optional().default(10),
|
|
13
|
+
buckets: z.array(flexibleLookupSchema).optional().default([]),
|
|
14
|
+
enabled: z.boolean().default(true),
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
/**
|