@webitel/api-services 0.1.70 → 0.1.72

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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/src/api/clients/emails/emails.ts +50 -49
  3. package/src/api/clients/imClients/imClients.ts +102 -0
  4. package/src/api/clients/index.ts +2 -0
  5. package/src/api/clients/phones/phones.ts +148 -104
  6. package/src/api/clients/queues/defaults/__tests__/queueTypeDefaults.spec.ts +46 -0
  7. package/src/api/clients/queues/defaults/amd.ts +1 -1
  8. package/src/api/clients/queues/defaults/defaultQueue.ts +2 -2
  9. package/src/api/clients/queues/defaults/processing.ts +1 -1
  10. package/src/api/clients/queues/defaults/queueTypeDefaults.ts +33 -33
  11. package/src/api/clients/queues/queueLogs.ts +2 -1
  12. package/src/api/clients/queues/queueMembers.ts +12 -3
  13. package/src/api/clients/variables/variables.ts +293 -0
  14. package/src/api/clients//321/201ontacts/index.ts +1 -0
  15. package/src/api/clients//321/201ontacts/types/ContactEntity.types.ts +25 -0
  16. package/src/validations/contact/contact.validations.ts +13 -0
  17. package/src/validations/contactEmail/contactEmail.validations.ts +9 -0
  18. package/src/validations/contactPhone/contactPhone.validations.ts +9 -0
  19. package/src/validations/index.ts +4 -0
  20. package/src/validations/variable/variable.validations.ts +8 -0
  21. package/types/.tsbuildinfo +1 -1
  22. package/types/api/clients/emails/emails.d.ts +10 -9
  23. package/types/api/clients/imClients/imClients.d.ts +19 -0
  24. package/types/api/clients/index.d.ts +2 -0
  25. package/types/api/clients/phones/phones.d.ts +29 -39
  26. package/types/api/clients/queues/defaults/amd.d.ts +1 -1
  27. package/types/api/clients/queues/defaults/defaultQueue.d.ts +2 -2
  28. package/types/api/clients/queues/defaults/processing.d.ts +1 -1
  29. package/types/api/clients/variables/variables.d.ts +51 -0
  30. package/types/api/clients//321/201ontacts/index.d.ts +1 -0
  31. package/types/api/clients//321/201ontacts/types/ContactEntity.types.d.ts +14 -0
  32. package/types/validations/contact/contact.validations.d.ts +25 -0
  33. package/types/validations/contactEmail/contactEmail.validations.d.ts +14 -0
  34. package/types/validations/contactPhone/contactPhone.validations.d.ts +14 -0
  35. package/types/validations/index.d.ts +4 -0
  36. package/types/validations/variable/variable.validations.d.ts +12 -0
@@ -141,3 +141,49 @@ describe('getQueueDefaults', () => {
141
141
  expect(hasQueueTypeDefaults(undefined)).toBe(false);
142
142
  });
143
143
  });
144
+
145
+ /**
146
+ * A lookup the user has not filled must be seeded as `undefined`, not `{}`.
147
+ *
148
+ * The key still has to be there — regle builds its `$fields` from the state
149
+ * keys — but an empty *object* makes regle treat the field as a collection and
150
+ * file a root-level `superRefine` issue for it under an index, where nothing
151
+ * reads it: the field shows no error, `r$.$error` stays false so the save
152
+ * button stays enabled, and the save then aborts silently on `$validate()`.
153
+ *
154
+ * [WTEL-10140](https://webitel.atlassian.net/browse/WTEL-10140)
155
+ */
156
+ describe('lookup defaults', () => {
157
+ const lookupKeys = [
158
+ 'calendar',
159
+ 'dncList',
160
+ 'team',
161
+ 'ringtone',
162
+ 'grantee',
163
+ 'schema',
164
+ 'doSchema',
165
+ 'afterSchema',
166
+ 'formSchema',
167
+ ];
168
+
169
+ it('seeds unset lookups as undefined, keeping the key', () => {
170
+ for (const type of allQueueTypes) {
171
+ const defaults = getQueueDefaults(type) as Record<string, unknown>;
172
+
173
+ for (const key of lookupKeys) {
174
+ if (!(key in defaults)) continue;
175
+
176
+ expect(
177
+ defaults[key],
178
+ `${key} on queue type ${type} must be undefined, not an empty object`,
179
+ ).toBeUndefined();
180
+ }
181
+ }
182
+ });
183
+
184
+ it('keeps taskProcessing.formSchema out of the empty-object shape too', () => {
185
+ const taskProcessing = processing() as Record<string, unknown>;
186
+
187
+ expect(taskProcessing.formSchema).toBeUndefined();
188
+ });
189
+ });
@@ -2,7 +2,7 @@ export const amd = () => ({
2
2
  enabled: false,
3
3
  ai: false,
4
4
  positive: [],
5
- playback: {},
5
+ playback: undefined,
6
6
  allowNotSure: false,
7
7
  silenceNotSure: false,
8
8
  maxWordLength: 5000,
@@ -3,7 +3,7 @@ export const defaultQueue = () => ({
3
3
  description: '',
4
4
  priority: 0,
5
5
  tags: [],
6
- dncList: {},
7
- calendar: {},
6
+ dncList: undefined,
7
+ calendar: undefined,
8
8
  variables: [],
9
9
  });
@@ -4,7 +4,7 @@ export const processing = (overrides = {}) =>
4
4
  deepmerge(
5
5
  {
6
6
  enabled: false,
7
- formSchema: {},
7
+ formSchema: undefined,
8
8
  sec: 30,
9
9
  renewalSec: 15,
10
10
  prolongationOptions: {
@@ -33,11 +33,11 @@ export interface QueueDefaults {
33
33
  const offlineQueue = (): QueueDefaults => ({
34
34
  ...defaultQueue(),
35
35
  type: QueueType.OFFLINE_QUEUE,
36
- team: {},
36
+ team: undefined,
37
37
  strategy: QueueStrategy.FIFO,
38
- doSchema: {},
39
- afterSchema: {},
40
- grantee: {},
38
+ doSchema: undefined,
39
+ afterSchema: undefined,
40
+ grantee: undefined,
41
41
  taskProcessing: processing(),
42
42
  payload: {
43
43
  maxAttempts: 3,
@@ -54,9 +54,9 @@ const offlineQueue = (): QueueDefaults => ({
54
54
  const inboundQueue = (): QueueDefaults => ({
55
55
  ...defaultQueue(),
56
56
  type: QueueType.INBOUND_QUEUE,
57
- team: {},
58
- ringtone: {},
59
- grantee: {},
57
+ team: undefined,
58
+ ringtone: undefined,
59
+ grantee: undefined,
60
60
  stickyAgent: false,
61
61
  taskProcessing: processing(),
62
62
  payload: {
@@ -80,10 +80,10 @@ const outboundIVRQueue = (): QueueDefaults => ({
80
80
  ...defaultQueue(),
81
81
  type: QueueType.OUTBOUND_IVR_QUEUE,
82
82
  strategy: QueueStrategy.FIFO,
83
- schema: {},
84
- doSchema: {},
85
- afterSchema: {},
86
- grantee: {},
83
+ schema: undefined,
84
+ doSchema: undefined,
85
+ afterSchema: undefined,
86
+ grantee: undefined,
87
87
  payload: {
88
88
  minDuration: 3,
89
89
  maxAttempts: 3,
@@ -104,11 +104,11 @@ const outboundIVRQueue = (): QueueDefaults => ({
104
104
  const previewDialer = (): QueueDefaults => ({
105
105
  ...defaultQueue(),
106
106
  type: QueueType.PREVIEW_DIALER,
107
- team: {},
107
+ team: undefined,
108
108
  strategy: QueueStrategy.FIFO,
109
- doSchema: {},
110
- afterSchema: {},
111
- grantee: {},
109
+ doSchema: undefined,
110
+ afterSchema: undefined,
111
+ grantee: undefined,
112
112
  stickyAgent: false,
113
113
  taskProcessing: processing(),
114
114
  payload: {
@@ -132,12 +132,12 @@ const previewDialer = (): QueueDefaults => ({
132
132
  const progressiveDialer = (): QueueDefaults => ({
133
133
  ...defaultQueue(),
134
134
  type: QueueType.PROGRESSIVE_DIALER,
135
- team: {},
135
+ team: undefined,
136
136
  strategy: QueueStrategy.FIFO,
137
- doSchema: {},
138
- afterSchema: {},
139
- ringtone: {},
140
- grantee: {},
137
+ doSchema: undefined,
138
+ afterSchema: undefined,
139
+ ringtone: undefined,
140
+ grantee: undefined,
141
141
  stickyAgent: false,
142
142
  taskProcessing: processing(),
143
143
  payload: {
@@ -165,11 +165,11 @@ const predictiveDialer = (): QueueDefaults => ({
165
165
  ...defaultQueue(),
166
166
  type: QueueType.PREDICTIVE_DIALER,
167
167
  strategy: QueueStrategy.FIFO,
168
- team: {},
169
- doSchema: {},
170
- afterSchema: {},
171
- ringtone: {},
172
- grantee: {},
168
+ team: undefined,
169
+ doSchema: undefined,
170
+ afterSchema: undefined,
171
+ ringtone: undefined,
172
+ grantee: undefined,
173
173
  stickyAgent: false,
174
174
  taskProcessing: processing(),
175
175
  payload: {
@@ -209,9 +209,9 @@ const predictiveDialer = (): QueueDefaults => ({
209
209
  const chatInboundQueue = (): QueueDefaults => ({
210
210
  ...defaultQueue(),
211
211
  type: QueueType.CHAT_INBOUND_QUEUE,
212
- team: {},
212
+ team: undefined,
213
213
  strategy: QueueStrategy.FIFO,
214
- formSchema: {},
214
+ formSchema: undefined,
215
215
  stickyAgent: false,
216
216
  taskProcessing: processing(),
217
217
  payload: {
@@ -241,10 +241,10 @@ const imChatQueue = (): QueueDefaults => ({
241
241
  const inboundJobQueue = (): QueueDefaults => ({
242
242
  ...defaultQueue(),
243
243
  type: QueueType.INBOUND_JOB_QUEUE,
244
- team: {},
244
+ team: undefined,
245
245
  strategy: QueueStrategy.FIFO,
246
- doSchema: {},
247
- afterSchema: {},
246
+ doSchema: undefined,
247
+ afterSchema: undefined,
248
248
  stickyAgent: false,
249
249
  taskProcessing: processing(),
250
250
  payload: {
@@ -262,9 +262,9 @@ const outboundJobQueue = (): QueueDefaults => ({
262
262
  ...defaultQueue(),
263
263
  type: QueueType.OUTBOUND_JOB_QUEUE,
264
264
  strategy: QueueStrategy.FIFO,
265
- schema: {},
266
- doSchema: {},
267
- afterSchema: {},
265
+ schema: undefined,
266
+ doSchema: undefined,
267
+ afterSchema: undefined,
268
268
  payload: {
269
269
  maxAttempts: 3,
270
270
  originateTimeout: 60,
@@ -1,4 +1,5 @@
1
1
  import { getMemberService } from '@webitel/api-services/gen';
2
+ import type { SearchAttemptsHistoryParams } from '@webitel/api-services/gen/models';
2
3
  import { normalizeDatetimeRange } from '../../../scripts';
3
4
  import { getDefaultGetListResponse, getDefaultGetParams } from '../../defaults';
4
5
  import {
@@ -59,7 +60,7 @@ const getQueueLogs = async (params: ApiParams) => {
59
60
  'offering_at.to': offeringAt?.to,
60
61
  'duration.from': duration?.from,
61
62
  'duration.to': duration?.to,
62
- });
63
+ } as SearchAttemptsHistoryParams);
63
64
  const { items, next } = applyTransform(response.data, [
64
65
  snakeToCamel(),
65
66
  merge(getDefaultGetListResponse()),
@@ -1,4 +1,5 @@
1
1
  import { getMemberService } from '@webitel/api-services/gen';
2
+ import type { SearchMemberInQueueParams } from '@webitel/api-services/gen/models';
2
3
  import { getShallowFieldsToSendFromZodSchema } from '@webitel/api-services/gen/utils';
3
4
  import { queueMemberSchema } from '@webitel/api-services/validations';
4
5
  import deepCopy from 'deep-copy';
@@ -152,7 +153,7 @@ const getMembersList = async (params: ApiParams) => {
152
153
  'attempts.to': attempts?.to,
153
154
  name,
154
155
  destination,
155
- },
156
+ } as SearchMemberInQueueParams,
156
157
  );
157
158
  const { items, next } = applyTransform(response.data, [
158
159
  snakeToCamel(doNotConvertKeys),
@@ -383,12 +384,20 @@ const deleteMembersBulk = async ({
383
384
  id?: ApiId[];
384
385
  filters?: ApiParams;
385
386
  }) => {
386
- const created = range(filters.createdAt, filters.from, filters.to);
387
+ const created = normalizeDatetimeRange(
388
+ filters.createdAt ??
389
+ (filters.from != null || filters.to != null
390
+ ? {
391
+ from: filters.from,
392
+ to: filters.to,
393
+ }
394
+ : undefined),
395
+ );
387
396
 
388
397
  let body: ApiParams = {
389
398
  id,
390
399
  q: filters.search,
391
- createdAt: created.from || created.to ? created : undefined,
400
+ createdAt: created?.from || created?.to ? created : undefined,
392
401
  priority: filters.memberPriority ?? filters.priority,
393
402
  stopCause: filters.stopCause ?? filters.cause,
394
403
  bucketId: filters.bucket,
@@ -0,0 +1,293 @@
1
+ import {
2
+ getVariables,
3
+ ListVariablesQueryParams,
4
+ MergeVariablesBodyItem,
5
+ UpdateVariableBody,
6
+ } from '@webitel/api-services/gen';
7
+ import type {
8
+ ContactsInputVariable,
9
+ DeleteVariablesParams,
10
+ MergeVariablesParams,
11
+ ResetVariablesParams,
12
+ } from '@webitel/api-services/gen/models';
13
+ import { getShallowFieldsToSendFromZodSchema } from '@webitel/api-services/gen/utils';
14
+ import type { AxiosRequestConfig } from 'axios';
15
+ import { getDefaultGetListResponse, getDefaultGetParams } from '../../defaults';
16
+ import {
17
+ applyTransform,
18
+ camelToSnake,
19
+ merge,
20
+ notify,
21
+ sanitize,
22
+ snakeToCamel,
23
+ starToSearch,
24
+ } from '../../transformers';
25
+
26
+ const getVariablesList = async ({
27
+ parentId,
28
+ ...rest
29
+ }: Record<string, unknown> & {
30
+ parentId: string;
31
+ }) => {
32
+ const listFieldsToSend = getShallowFieldsToSendFromZodSchema(
33
+ ListVariablesQueryParams,
34
+ );
35
+
36
+ const { fields = [], ...queryParams } = applyTransform(rest, [
37
+ sanitize(listFieldsToSend),
38
+ merge(getDefaultGetParams()),
39
+ starToSearch('q'),
40
+ ]);
41
+ try {
42
+ const response = await getVariables().listVariables(parentId, {
43
+ ...queryParams,
44
+ fields: [
45
+ 'etag',
46
+ ...fields,
47
+ ],
48
+ });
49
+ const { data, next } = applyTransform(response.data, [
50
+ snakeToCamel(),
51
+ merge(getDefaultGetListResponse()),
52
+ ]);
53
+ return {
54
+ items: data,
55
+ next,
56
+ };
57
+ } catch (err) {
58
+ throw applyTransform(err, [
59
+ notify,
60
+ ]);
61
+ }
62
+ };
63
+
64
+ // no dedicated "locate one" endpoint exists for variables — reuse the list
65
+ // endpoint filtered to a single id, matching what the crm-local adapter did
66
+ const getVariable = async ({
67
+ itemId,
68
+ parentId,
69
+ }: {
70
+ itemId: string;
71
+ parentId: string;
72
+ }) => {
73
+ try {
74
+ const response = await getVariables().listVariables(parentId, {
75
+ page: 1,
76
+ size: 1,
77
+ fields: [
78
+ 'key',
79
+ 'value',
80
+ 'etag',
81
+ ],
82
+ id: [
83
+ itemId,
84
+ ],
85
+ });
86
+ const { data } = applyTransform(response.data, [
87
+ snakeToCamel(),
88
+ ]);
89
+ return data[0];
90
+ } catch (err) {
91
+ throw applyTransform(err, [
92
+ notify,
93
+ ]);
94
+ }
95
+ };
96
+
97
+ const addFieldsToSend = getShallowFieldsToSendFromZodSchema(
98
+ MergeVariablesBodyItem,
99
+ );
100
+
101
+ const addVariable = async ({
102
+ parentId,
103
+ itemInstance,
104
+ }: {
105
+ parentId: string;
106
+ itemInstance: Record<string, unknown>;
107
+ }) => {
108
+ const item = applyTransform(itemInstance, [
109
+ sanitize(addFieldsToSend),
110
+ camelToSnake(),
111
+ ]);
112
+ try {
113
+ const response = await getVariables().mergeVariables(parentId, [
114
+ item,
115
+ ]);
116
+ const { data } = applyTransform(response.data, [
117
+ snakeToCamel(),
118
+ ]);
119
+ return data[0];
120
+ } catch (err) {
121
+ throw applyTransform(err, [
122
+ notify,
123
+ ]);
124
+ }
125
+ };
126
+
127
+ const updateFieldsToSend =
128
+ getShallowFieldsToSendFromZodSchema(UpdateVariableBody);
129
+
130
+ const updateVariable = async ({
131
+ itemInstance,
132
+ parentId,
133
+ }: {
134
+ itemInstance: Record<string, unknown> & {
135
+ etag: string;
136
+ };
137
+ parentId: string;
138
+ }) => {
139
+ const item = applyTransform(itemInstance, [
140
+ sanitize(updateFieldsToSend),
141
+ camelToSnake(),
142
+ ]);
143
+ try {
144
+ const response = await getVariables().updateVariable(
145
+ parentId,
146
+ itemInstance.etag,
147
+ item,
148
+ );
149
+ const { data } = applyTransform(response.data, [
150
+ snakeToCamel(),
151
+ ]);
152
+ return data[0];
153
+ } catch (err) {
154
+ throw applyTransform(err, [
155
+ notify,
156
+ ]);
157
+ }
158
+ };
159
+
160
+ const deleteVariable = async ({
161
+ etag,
162
+ parentId,
163
+ }: {
164
+ etag: string;
165
+ parentId: string;
166
+ }) => {
167
+ try {
168
+ const response = await getVariables().deleteVariable(parentId, etag);
169
+ return applyTransform(response.data, []);
170
+ } catch (err) {
171
+ throw applyTransform(err, [
172
+ notify,
173
+ ]);
174
+ }
175
+ };
176
+
177
+ const getVariablesLookup = (
178
+ params: Record<string, unknown> & {
179
+ parentId: string;
180
+ },
181
+ ) =>
182
+ getVariablesList({
183
+ ...params,
184
+ fields: (params.fields as string[]) || [
185
+ 'etag',
186
+ 'key',
187
+ 'value',
188
+ ],
189
+ });
190
+
191
+ /**
192
+ * raw bulk endpoints — take/return the whole variables array in one call,
193
+ * unlike add/update above which normalize a single item for createCardStore
194
+ */
195
+ const mergeVariables = async ({
196
+ contactId,
197
+ variables,
198
+ params,
199
+ options,
200
+ }: {
201
+ contactId: string;
202
+ variables: ContactsInputVariable[];
203
+ params?: MergeVariablesParams;
204
+ options?: AxiosRequestConfig;
205
+ }) => {
206
+ const body = applyTransform(variables, [
207
+ camelToSnake(),
208
+ ]);
209
+ try {
210
+ const response = await getVariables().mergeVariables(
211
+ contactId,
212
+ body,
213
+ params,
214
+ options,
215
+ );
216
+ return applyTransform(response.data, [
217
+ snakeToCamel(),
218
+ ]);
219
+ } catch (err) {
220
+ throw applyTransform(err, [
221
+ notify,
222
+ ]);
223
+ }
224
+ };
225
+
226
+ const resetVariables = async ({
227
+ contactId,
228
+ variables,
229
+ params,
230
+ options,
231
+ }: {
232
+ contactId: string;
233
+ variables: ContactsInputVariable[];
234
+ params?: ResetVariablesParams;
235
+ options?: AxiosRequestConfig;
236
+ }) => {
237
+ const body = applyTransform(variables, [
238
+ camelToSnake(),
239
+ ]);
240
+ try {
241
+ const response = await getVariables().resetVariables(
242
+ contactId,
243
+ body,
244
+ params,
245
+ options,
246
+ );
247
+ return applyTransform(response.data, [
248
+ snakeToCamel(),
249
+ ]);
250
+ } catch (err) {
251
+ throw applyTransform(err, [
252
+ notify,
253
+ ]);
254
+ }
255
+ };
256
+
257
+ const deleteVariables = async ({
258
+ contactId,
259
+ params,
260
+ options,
261
+ }: {
262
+ contactId: string;
263
+ params: DeleteVariablesParams;
264
+ options?: AxiosRequestConfig;
265
+ }) => {
266
+ try {
267
+ const response = await getVariables().deleteVariables(
268
+ contactId,
269
+ params,
270
+ options,
271
+ );
272
+ return applyTransform(response.data, [
273
+ snakeToCamel(),
274
+ ]);
275
+ } catch (err) {
276
+ throw applyTransform(err, [
277
+ notify,
278
+ ]);
279
+ }
280
+ };
281
+
282
+ export const VariablesAPI = {
283
+ getList: getVariablesList,
284
+ get: getVariable,
285
+ add: addVariable,
286
+ update: updateVariable,
287
+ delete: deleteVariable,
288
+ getLookup: getVariablesLookup,
289
+
290
+ merge: mergeVariables,
291
+ reset: resetVariables,
292
+ deleteMany: deleteVariables,
293
+ };
@@ -1,3 +1,4 @@
1
1
  export * from './contactChatMessagesHistory';
2
2
  export * from './contacts';
3
3
  export * from './enums/ContactsSearchMode';
4
+ export * from './types/ContactEntity.types';
@@ -0,0 +1,25 @@
1
+ import type {
2
+ ContactsManager,
3
+ ContactsTimezone,
4
+ WebitelContactsContact,
5
+ WebitelContactsLabel,
6
+ WebitelContactsLookup,
7
+ } from '@webitel/api-services/gen/models';
8
+
9
+ /**
10
+ * `ContactsAPI` (../contacts.ts) unwraps the generated `WebitelContactsContact`'s
11
+ * paginated `{data:[...]}` envelopes for these fields into plain arrays (and
12
+ * `name` into a plain string) on read, and wraps them back on write. This type
13
+ * reflects that actual runtime shape.
14
+ */
15
+ export interface ContactEntity
16
+ extends Omit<
17
+ WebitelContactsContact,
18
+ 'name' | 'timezones' | 'managers' | 'labels' | 'groups'
19
+ > {
20
+ name?: string;
21
+ timezones?: ContactsTimezone[];
22
+ managers?: ContactsManager[];
23
+ labels?: WebitelContactsLabel[];
24
+ groups?: WebitelContactsLookup[];
25
+ }
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ import type { ContactEntity } from '../../api/clients/сontacts/types/ContactEntity.types';
3
+ import type { ZodShape } from '../types';
4
+
5
+ export const contactSchema = z.object<ZodShape<ContactEntity>>({
6
+ name: z.string().optional(),
7
+ timezones: z.array(z.any()).optional(),
8
+ managers: z.array(z.any()).optional(),
9
+ groups: z.array(z.any()).optional(),
10
+ labels: z.array(z.any()).optional(),
11
+ about: z.string().nullish(),
12
+ user: z.any().nullish(),
13
+ });
@@ -0,0 +1,9 @@
1
+ import type { ContactsEmailAddress } from '@webitel/api-services/gen/models';
2
+ import { z } from 'zod';
3
+ import type { ZodShape } from '../types';
4
+
5
+ export const contactEmailSchema = z.object<ZodShape<ContactsEmailAddress>>({
6
+ email: z.string().min(1).email(),
7
+ type: z.any(),
8
+ primary: z.boolean().optional(),
9
+ });
@@ -0,0 +1,9 @@
1
+ import type { ContactsPhoneNumber } from '@webitel/api-services/gen/models';
2
+ import { z } from 'zod';
3
+ import type { ZodShape } from '../types';
4
+
5
+ export const contactPhoneSchema = z.object<ZodShape<ContactsPhoneNumber>>({
6
+ number: z.string().min(1),
7
+ type: z.any(),
8
+ primary: z.boolean().optional(),
9
+ });
@@ -12,8 +12,11 @@ export * from './caseServiceCatalog/caseServiceCatalog.validations';
12
12
  export * from './caseSource/caseSource.validations';
13
13
  export * from './caseStatus/caseStatus.validations';
14
14
  export * from './caseStatusCondition/caseStatusCondition.validations';
15
+ export * from './contact/contact.validations';
16
+ export * from './contactEmail/contactEmail.validations';
15
17
  export * from './contactGroup/contactGroup.validations';
16
18
  export * from './contactGroupCondition/contactGroupCondition.validations';
19
+ export * from './contactPhone/contactPhone.validations';
17
20
  export * from './OAuth/OAuth.validations';
18
21
  export * from './onlineSkill/onlineSkill.validations';
19
22
  export * from './queue/queue.rules';
@@ -30,3 +33,4 @@ export * from './queueSkill/queueSkill.validations';
30
33
  export * from './sla/sla.validations';
31
34
  export * from './slaCondition/slaCondition.validations';
32
35
  export * from './types';
36
+ export * from './variable/variable.validations';
@@ -0,0 +1,8 @@
1
+ import type { ContactsVariable } from '@webitel/api-services/gen/models';
2
+ import { z } from 'zod';
3
+ import type { ZodShape } from '../types';
4
+
5
+ export const variableSchema = z.object<ZodShape<ContactsVariable>>({
6
+ key: z.string().min(1),
7
+ value: z.string().min(1),
8
+ });