@becollective/utils 2.0.13 → 2.0.14
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/.semaphore/utils.yml +13 -22
- package/jestconfig.json +12 -1
- package/lib/date-time.js +1 -1
- package/lib/forms.d.ts +3 -0
- package/lib/forms.js +3 -0
- package/package.json +10 -1
- package/src/date-time.ts +1 -1
- package/src/forms.ts +3 -0
- package/tests/forms.test.ts +121 -1
- package/lib/rrule/parsing.test.d.ts +0 -1
- package/lib/rrule/parsing.test.js +0 -11
- package/lib/src/FeatureFlag.d.ts +0 -12
- package/lib/src/FeatureFlag.js +0 -51
- package/lib/src/date-time.d.ts +0 -28
- package/lib/src/date-time.js +0 -79
- package/lib/src/forms.d.ts +0 -1
- package/lib/src/forms.js +0 -728
- package/lib/src/locality.d.ts +0 -5
- package/lib/src/locality.js +0 -15
- package/lib/src/money.d.ts +0 -7
- package/lib/src/money.js +0 -32
- package/lib/src/opportunity.d.ts +0 -1
- package/lib/src/opportunity.js +0 -11
- package/lib/src/opportunityUser.d.ts +0 -8
- package/lib/src/opportunityUser.js +0 -29
- package/lib/src/password.d.ts +0 -8
- package/lib/src/password.js +0 -43
package/lib/src/forms.js
DELETED
|
@@ -1,728 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
/* eslint-disable no-prototype-builtins */
|
|
7
|
-
const ajv_1 = __importDefault(require("ajv"));
|
|
8
|
-
const ajv = new ajv_1.default({
|
|
9
|
-
allErrors: true,
|
|
10
|
-
verbose: true,
|
|
11
|
-
});
|
|
12
|
-
const phoneNumber = require('libphonenumber-js');
|
|
13
|
-
const BC_REPORTABLE_FIELDS = {
|
|
14
|
-
BC_ADDRESS: 'BC_ADDRESS',
|
|
15
|
-
BC_PHONE: 'BC_PHONE',
|
|
16
|
-
BC_OWN_VEHICLE: 'BC_OWN_VEHICLE',
|
|
17
|
-
BC_DRIVERS_LICENCE: 'BC_DRIVERS_LICENCE',
|
|
18
|
-
BC_WORKING_WITH_CHILDREN: 'BC_WORKING_WITH_CHILDREN',
|
|
19
|
-
BC_POLICE_CHECK: 'BC_POLICE_CHECK',
|
|
20
|
-
BC_MEDICAL_CONDITIONS: 'BC_MEDICAL_CONDITIONS',
|
|
21
|
-
BC_COUNTRY_OF_BIRTH: 'BC_COUNTRY_OF_BIRTH',
|
|
22
|
-
};
|
|
23
|
-
const widgetTypes = [
|
|
24
|
-
'WidgetDate',
|
|
25
|
-
'WidgetDateRange',
|
|
26
|
-
'WidgetDropDown',
|
|
27
|
-
'WidgetHeading',
|
|
28
|
-
'WidgetSpacer',
|
|
29
|
-
'WidgetLocation',
|
|
30
|
-
'WidgetNumber',
|
|
31
|
-
'WidgetPhone',
|
|
32
|
-
'WidgetRadio',
|
|
33
|
-
'WidgetTextArea',
|
|
34
|
-
'WidgetTextBox',
|
|
35
|
-
'WidgetTextParagraph',
|
|
36
|
-
'WidgetTickbox',
|
|
37
|
-
'WidgetToggle',
|
|
38
|
-
'WidgetSkillsRequired',
|
|
39
|
-
'WidgetFile',
|
|
40
|
-
'WidgetQualifications',
|
|
41
|
-
'WidgetCountry',
|
|
42
|
-
'WidgetSocialPrescribing',
|
|
43
|
-
];
|
|
44
|
-
const countDecimals = (value) => {
|
|
45
|
-
if (Math.floor(value) === value)
|
|
46
|
-
return 0;
|
|
47
|
-
const str = value.toString();
|
|
48
|
-
// This handles scientific notation on long decimals from toString ie "1e-9"
|
|
49
|
-
if (str.indexOf('.') !== -1 && str.indexOf('-') !== -1) {
|
|
50
|
-
return parseInt(str.split('-')[1] || 0);
|
|
51
|
-
}
|
|
52
|
-
else if (str.indexOf('.') !== -1) {
|
|
53
|
-
return parseInt(str.split('.')[1].length || 0);
|
|
54
|
-
}
|
|
55
|
-
return parseInt(str.split('-')[1] || 0);
|
|
56
|
-
};
|
|
57
|
-
const isValidYear = (value) => {
|
|
58
|
-
const year = new Date(value).getFullYear();
|
|
59
|
-
return year < 9999 && year > 999;
|
|
60
|
-
};
|
|
61
|
-
const validateType = async (params) => {
|
|
62
|
-
const { type, data, schema, error } = params;
|
|
63
|
-
if (validator.dataTypeIsValid[type](data, schema)) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
throw {
|
|
67
|
-
name: error || `Property is not a ${type}`
|
|
68
|
-
};
|
|
69
|
-
};
|
|
70
|
-
const validator = {
|
|
71
|
-
validateSchema: async (schema, uischema) => {
|
|
72
|
-
if (!schema.hasOwnProperty('properties')) {
|
|
73
|
-
throw {
|
|
74
|
-
name: 'missing-properties'
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
else if (!schema.hasOwnProperty('required')) {
|
|
78
|
-
throw {
|
|
79
|
-
name: 'missing-required'
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
let err;
|
|
83
|
-
let result;
|
|
84
|
-
const errors = [];
|
|
85
|
-
const keys = Object.keys(schema.properties);
|
|
86
|
-
result = validator.validateSchemaPropertiesExist(schema.required, keys, 'required-item-not-found');
|
|
87
|
-
if (result) {
|
|
88
|
-
throw result;
|
|
89
|
-
}
|
|
90
|
-
result = validator.validateSchemaPropertiesExist(uischema['ui:order'], keys, 'order-item-not-found');
|
|
91
|
-
if (result) {
|
|
92
|
-
throw result;
|
|
93
|
-
}
|
|
94
|
-
result = validator.validateSchemaPropertiesExist(keys, Object.keys(uischema), 'ui-data-schema-mismatch');
|
|
95
|
-
if (result) {
|
|
96
|
-
throw result;
|
|
97
|
-
}
|
|
98
|
-
result = validator.validatePropertiesTitleUniqueness(schema.properties, 'Titles must be unique.');
|
|
99
|
-
if (result) {
|
|
100
|
-
throw result;
|
|
101
|
-
}
|
|
102
|
-
for await (const item of keys) {
|
|
103
|
-
const o = schema.properties[item];
|
|
104
|
-
/*
|
|
105
|
-
o is each property. eg.
|
|
106
|
-
{
|
|
107
|
-
type: 'array',
|
|
108
|
-
title: 'Where did you hear about us?',
|
|
109
|
-
items: {
|
|
110
|
-
type: 'string',
|
|
111
|
-
enum: [ 'Newspaper', 'Google', 'Word of mouth' ]
|
|
112
|
-
},
|
|
113
|
-
uniqueItems: true,
|
|
114
|
-
bcReportable: 'BC_HOW_DID_YOU_HEAR',
|
|
115
|
-
bcRequired: false
|
|
116
|
-
}
|
|
117
|
-
*/
|
|
118
|
-
const f = validator.getValidatorForType(o.bcType || o.type);
|
|
119
|
-
if (o.hasOwnProperty('bcReportable')) {
|
|
120
|
-
if (!BC_REPORTABLE_FIELDS[o.bcReportable] && !o.bcReportable.startsWith('USER_')) {
|
|
121
|
-
errors.push({
|
|
122
|
-
name: 'invalid-reportable-field',
|
|
123
|
-
object: o,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (f) {
|
|
128
|
-
const valid = ajv.validate(f, o);
|
|
129
|
-
if (valid !== true) {
|
|
130
|
-
const errorMsgs = ajv.errors.map(({ instancePath, message }) => {
|
|
131
|
-
let property = instancePath.replace(/\//g, '.');
|
|
132
|
-
if (property.charAt(0) === '.') {
|
|
133
|
-
property = property.slice(1, property.length);
|
|
134
|
-
}
|
|
135
|
-
return {
|
|
136
|
-
key: item,
|
|
137
|
-
property,
|
|
138
|
-
message,
|
|
139
|
-
};
|
|
140
|
-
});
|
|
141
|
-
errors.push(...errorMsgs);
|
|
142
|
-
}
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
errors.push({ name: 'invalid-schema-type', object: o });
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (errors.length) {
|
|
151
|
-
throw errors;
|
|
152
|
-
}
|
|
153
|
-
Object.keys(uischema).map((o) => {
|
|
154
|
-
if (o === 'ui:order') {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const data = uischema[o];
|
|
158
|
-
const valid = ajv.validate(validator.uiSchema, data);
|
|
159
|
-
if (valid !== true) {
|
|
160
|
-
errors.push({ name: 'invalid-uischema-property', object: o });
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
if (errors.length) {
|
|
164
|
-
throw errors;
|
|
165
|
-
}
|
|
166
|
-
return true;
|
|
167
|
-
},
|
|
168
|
-
validateSchemaPropertiesExist: (properties, keys, err) => {
|
|
169
|
-
const errors = [];
|
|
170
|
-
for (const i in properties) {
|
|
171
|
-
if (keys.indexOf(properties[i]) === -1) {
|
|
172
|
-
errors.push({
|
|
173
|
-
name: err,
|
|
174
|
-
key: i,
|
|
175
|
-
value: properties[i],
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
if (errors.length) {
|
|
180
|
-
return errors;
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
},
|
|
184
|
-
/**
|
|
185
|
-
* Check if every field in the form has a unique title
|
|
186
|
-
* @param {object} properties fields properties
|
|
187
|
-
* @param {string} err error name
|
|
188
|
-
*/
|
|
189
|
-
validatePropertiesTitleUniqueness: (properties, err) => {
|
|
190
|
-
const errors = [];
|
|
191
|
-
const titleCounts = {};
|
|
192
|
-
for (const key in properties) {
|
|
193
|
-
// "spacer" and "textParagraph" are special fields whose title is fixed and
|
|
194
|
-
// doesn't show in UI, hence no unique check required for them
|
|
195
|
-
if (properties[key].bcType &&
|
|
196
|
-
(properties[key].bcType === 'spacer' || properties[key].bcType === 'textParagraph')) {
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
const title = properties[key].title.trim().toLowerCase();
|
|
200
|
-
titleCounts[title] = titleCounts[title] ? [...titleCounts[title], key] : [key];
|
|
201
|
-
}
|
|
202
|
-
for (const title in titleCounts) {
|
|
203
|
-
if (titleCounts[title].length > 1) {
|
|
204
|
-
titleCounts[title].forEach((key) => {
|
|
205
|
-
errors.push({
|
|
206
|
-
key,
|
|
207
|
-
property: key,
|
|
208
|
-
message: err,
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
if (errors.length) {
|
|
214
|
-
return errors;
|
|
215
|
-
}
|
|
216
|
-
return null;
|
|
217
|
-
},
|
|
218
|
-
/**
|
|
219
|
-
* Creates a new object from the input obj.
|
|
220
|
-
* All entries from the input whose values are empty arrays are missing in the new object
|
|
221
|
-
* @param {Object} data 1-depth obj
|
|
222
|
-
* @returns A new object without empty arrays
|
|
223
|
-
*/
|
|
224
|
-
stripEmptyArraysFromData: (data) => {
|
|
225
|
-
const strippedObject = {};
|
|
226
|
-
Object.keys(data).forEach((k) => {
|
|
227
|
-
if (!(Array.isArray(data[k]) && data[k].length === 0))
|
|
228
|
-
strippedObject[k] = data[k];
|
|
229
|
-
});
|
|
230
|
-
return strippedObject;
|
|
231
|
-
},
|
|
232
|
-
validateData: async (schema, data) => {
|
|
233
|
-
// data is the user submitted form data.
|
|
234
|
-
const keys = Object.keys(data);
|
|
235
|
-
const properties = Object.keys(schema.properties);
|
|
236
|
-
const errors = [];
|
|
237
|
-
keys.map((o) => {
|
|
238
|
-
const found = false;
|
|
239
|
-
if (properties.indexOf(o) === -1) {
|
|
240
|
-
errors.push({
|
|
241
|
-
name: 'properties-not-found',
|
|
242
|
-
key: o,
|
|
243
|
-
value: data[o],
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
schema.required.map((o) => {
|
|
248
|
-
const isFileType = schema.properties[o].bcType === 'file';
|
|
249
|
-
if (keys.indexOf(o) === -1 && !isFileType) {
|
|
250
|
-
errors.push({
|
|
251
|
-
name: 'required-data-not-found',
|
|
252
|
-
key: o,
|
|
253
|
-
value: data[o],
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
if (errors.length) {
|
|
258
|
-
throw errors;
|
|
259
|
-
}
|
|
260
|
-
for await (const item of properties) {
|
|
261
|
-
if (!data.hasOwnProperty(item)) {
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
// property is the schema property.
|
|
265
|
-
// eg. { title: 'Phone Number', type: 'string', bcType: 'phone' };
|
|
266
|
-
const property = schema.properties[item];
|
|
267
|
-
const value = data[item];
|
|
268
|
-
// value is the data the user submits for the above property
|
|
269
|
-
// eg. +61434111222
|
|
270
|
-
try {
|
|
271
|
-
const v = validator.getValidatorForTypeData(property.bcType || property.type);
|
|
272
|
-
await v(property, value);
|
|
273
|
-
}
|
|
274
|
-
catch (e) {
|
|
275
|
-
errors.push(e);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (errors.length) {
|
|
279
|
-
throw (errors);
|
|
280
|
-
}
|
|
281
|
-
return;
|
|
282
|
-
},
|
|
283
|
-
getValidatorForType: (type) => {
|
|
284
|
-
return validator.schemaTypes[type] || null;
|
|
285
|
-
},
|
|
286
|
-
getValidatorForTypeData: (type) => {
|
|
287
|
-
const v = validator.dataTypes[type];
|
|
288
|
-
if (v) {
|
|
289
|
-
return v;
|
|
290
|
-
}
|
|
291
|
-
throw {
|
|
292
|
-
name: 'Datatype not found',
|
|
293
|
-
};
|
|
294
|
-
},
|
|
295
|
-
uiSchema: {
|
|
296
|
-
type: 'object',
|
|
297
|
-
required: ['ui:title', 'ui:widget'],
|
|
298
|
-
properties: {
|
|
299
|
-
'ui:title': {
|
|
300
|
-
type: 'string',
|
|
301
|
-
},
|
|
302
|
-
'ui:helpertext': {
|
|
303
|
-
type: 'string',
|
|
304
|
-
},
|
|
305
|
-
'ui:bc-helpertext': {
|
|
306
|
-
type: 'string',
|
|
307
|
-
},
|
|
308
|
-
'ui:bc-required': {
|
|
309
|
-
type: 'boolean',
|
|
310
|
-
},
|
|
311
|
-
'ui:bc-multiSelect': {
|
|
312
|
-
type: 'boolean',
|
|
313
|
-
},
|
|
314
|
-
'ui:widget': {
|
|
315
|
-
type: 'string',
|
|
316
|
-
enum: widgetTypes,
|
|
317
|
-
},
|
|
318
|
-
},
|
|
319
|
-
},
|
|
320
|
-
schemaTypes: {
|
|
321
|
-
string: {
|
|
322
|
-
type: 'object',
|
|
323
|
-
required: ['title'],
|
|
324
|
-
properties: {
|
|
325
|
-
title: {
|
|
326
|
-
type: 'string',
|
|
327
|
-
minLength: 1,
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
},
|
|
331
|
-
number: {
|
|
332
|
-
type: 'object',
|
|
333
|
-
required: ['title'],
|
|
334
|
-
properties: {
|
|
335
|
-
title: {
|
|
336
|
-
type: 'string',
|
|
337
|
-
minLength: 1,
|
|
338
|
-
},
|
|
339
|
-
},
|
|
340
|
-
},
|
|
341
|
-
boolean: {
|
|
342
|
-
type: 'object',
|
|
343
|
-
required: ['title'],
|
|
344
|
-
properties: {
|
|
345
|
-
title: {
|
|
346
|
-
type: 'string',
|
|
347
|
-
minLength: 1,
|
|
348
|
-
},
|
|
349
|
-
},
|
|
350
|
-
},
|
|
351
|
-
date: {
|
|
352
|
-
type: 'object',
|
|
353
|
-
required: ['title'],
|
|
354
|
-
properties: {
|
|
355
|
-
title: {
|
|
356
|
-
type: 'string',
|
|
357
|
-
minLength: 1,
|
|
358
|
-
},
|
|
359
|
-
type: {
|
|
360
|
-
const: 'string',
|
|
361
|
-
},
|
|
362
|
-
},
|
|
363
|
-
},
|
|
364
|
-
dateRange: {
|
|
365
|
-
type: 'object',
|
|
366
|
-
required: ['title'],
|
|
367
|
-
properties: {
|
|
368
|
-
title: {
|
|
369
|
-
type: 'string',
|
|
370
|
-
minLength: 1,
|
|
371
|
-
},
|
|
372
|
-
type: {
|
|
373
|
-
const: 'object',
|
|
374
|
-
},
|
|
375
|
-
},
|
|
376
|
-
},
|
|
377
|
-
phone: {
|
|
378
|
-
type: 'object',
|
|
379
|
-
required: ['title'],
|
|
380
|
-
properties: {
|
|
381
|
-
title: {
|
|
382
|
-
type: 'string',
|
|
383
|
-
minLength: 1,
|
|
384
|
-
},
|
|
385
|
-
type: {
|
|
386
|
-
const: 'string',
|
|
387
|
-
},
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
skills: {
|
|
391
|
-
type: 'object',
|
|
392
|
-
required: ['title'],
|
|
393
|
-
properties: {
|
|
394
|
-
title: {
|
|
395
|
-
type: 'string',
|
|
396
|
-
minLength: 1,
|
|
397
|
-
},
|
|
398
|
-
uniqueItems: {
|
|
399
|
-
type: 'boolean',
|
|
400
|
-
},
|
|
401
|
-
items: {
|
|
402
|
-
type: 'object',
|
|
403
|
-
properties: {
|
|
404
|
-
anyOf: {
|
|
405
|
-
type: 'array',
|
|
406
|
-
minItems: 1,
|
|
407
|
-
items: {
|
|
408
|
-
type: 'object',
|
|
409
|
-
properties: {
|
|
410
|
-
type: { type: 'string' },
|
|
411
|
-
title: { type: 'string' },
|
|
412
|
-
enum: {
|
|
413
|
-
type: 'array',
|
|
414
|
-
minItems: 1,
|
|
415
|
-
items: {
|
|
416
|
-
type: 'string',
|
|
417
|
-
minLength: 1,
|
|
418
|
-
},
|
|
419
|
-
},
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
},
|
|
425
|
-
},
|
|
426
|
-
},
|
|
427
|
-
qualifications: {
|
|
428
|
-
type: 'object',
|
|
429
|
-
required: ['title'],
|
|
430
|
-
properties: {
|
|
431
|
-
title: {
|
|
432
|
-
type: 'string',
|
|
433
|
-
minLength: 1,
|
|
434
|
-
},
|
|
435
|
-
uniqueItems: {
|
|
436
|
-
type: 'boolean',
|
|
437
|
-
},
|
|
438
|
-
items: {
|
|
439
|
-
type: 'object',
|
|
440
|
-
properties: {
|
|
441
|
-
type: {
|
|
442
|
-
const: 'string',
|
|
443
|
-
},
|
|
444
|
-
enum: {
|
|
445
|
-
type: 'array',
|
|
446
|
-
minItems: 1,
|
|
447
|
-
items: [{ type: 'string' }],
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
|
-
array: {
|
|
454
|
-
type: 'object',
|
|
455
|
-
required: ['title'],
|
|
456
|
-
properties: {
|
|
457
|
-
title: {
|
|
458
|
-
type: 'string',
|
|
459
|
-
minLength: 1,
|
|
460
|
-
},
|
|
461
|
-
uniqueItems: {
|
|
462
|
-
type: 'boolean',
|
|
463
|
-
},
|
|
464
|
-
items: {
|
|
465
|
-
type: 'object',
|
|
466
|
-
properties: {
|
|
467
|
-
anyOf: {
|
|
468
|
-
type: 'array',
|
|
469
|
-
minItems: 1,
|
|
470
|
-
uniqueItems: true,
|
|
471
|
-
items: {
|
|
472
|
-
type: 'object',
|
|
473
|
-
properties: {
|
|
474
|
-
type: { type: 'string' },
|
|
475
|
-
title: { type: 'string' },
|
|
476
|
-
enum: {
|
|
477
|
-
type: 'array',
|
|
478
|
-
minItems: 1,
|
|
479
|
-
items: {
|
|
480
|
-
type: 'string',
|
|
481
|
-
minLength: 1,
|
|
482
|
-
},
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
},
|
|
486
|
-
},
|
|
487
|
-
},
|
|
488
|
-
},
|
|
489
|
-
},
|
|
490
|
-
},
|
|
491
|
-
heading: {
|
|
492
|
-
type: 'object',
|
|
493
|
-
required: ['title'],
|
|
494
|
-
properties: {
|
|
495
|
-
title: {
|
|
496
|
-
type: 'string',
|
|
497
|
-
minLength: 1,
|
|
498
|
-
},
|
|
499
|
-
type: {
|
|
500
|
-
const: 'string',
|
|
501
|
-
},
|
|
502
|
-
},
|
|
503
|
-
},
|
|
504
|
-
spacer: {
|
|
505
|
-
type: 'object',
|
|
506
|
-
properties: {
|
|
507
|
-
type: {
|
|
508
|
-
const: 'string',
|
|
509
|
-
},
|
|
510
|
-
},
|
|
511
|
-
},
|
|
512
|
-
location: {
|
|
513
|
-
type: 'object',
|
|
514
|
-
required: ['title'],
|
|
515
|
-
properties: {
|
|
516
|
-
title: {
|
|
517
|
-
type: 'string',
|
|
518
|
-
minLength: 1,
|
|
519
|
-
},
|
|
520
|
-
type: {
|
|
521
|
-
const: 'object',
|
|
522
|
-
},
|
|
523
|
-
},
|
|
524
|
-
},
|
|
525
|
-
file: {
|
|
526
|
-
type: 'object',
|
|
527
|
-
required: ['title'],
|
|
528
|
-
properties: {
|
|
529
|
-
title: {
|
|
530
|
-
type: 'string',
|
|
531
|
-
minLength: 1,
|
|
532
|
-
},
|
|
533
|
-
},
|
|
534
|
-
},
|
|
535
|
-
textParagraph: {
|
|
536
|
-
type: 'object',
|
|
537
|
-
required: ['text'],
|
|
538
|
-
properties: {
|
|
539
|
-
text: {
|
|
540
|
-
type: 'string',
|
|
541
|
-
minLength: 1,
|
|
542
|
-
},
|
|
543
|
-
},
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
// NOTE: schema below is not the whole schema but the individual property
|
|
547
|
-
// eg. { title: 'Phone Number', type: 'string', bcType: 'phone' };
|
|
548
|
-
dataTypeIsValid: {
|
|
549
|
-
string: (data, schema) => {
|
|
550
|
-
const isString = schema && schema.required ? !!(typeof data === 'string' && data.length) : !!(typeof data === 'string');
|
|
551
|
-
if (schema && schema.items && schema.items.enum) {
|
|
552
|
-
return isString && schema.items.enum.includes(data);
|
|
553
|
-
}
|
|
554
|
-
return isString;
|
|
555
|
-
},
|
|
556
|
-
boolean: (data) => {
|
|
557
|
-
return data === true || data === false;
|
|
558
|
-
},
|
|
559
|
-
number: (data) => {
|
|
560
|
-
return typeof data === 'number' && countDecimals(data) <= 10;
|
|
561
|
-
},
|
|
562
|
-
// date and dateRange must be ISOString format
|
|
563
|
-
date: (data) => {
|
|
564
|
-
return typeof data === 'string' && new Date(data).toString() !== 'Invalid Date' && isValidYear(data);
|
|
565
|
-
},
|
|
566
|
-
dateRange: (data) => {
|
|
567
|
-
const fromIsValid = validator.dataTypeIsValid.date(data.from);
|
|
568
|
-
const toIsValid = validator.dataTypeIsValid.date(data.to);
|
|
569
|
-
const toDateIsAFterFromDate = new Date(data.from) <= new Date(data.to);
|
|
570
|
-
return fromIsValid && toIsValid && toDateIsAFterFromDate;
|
|
571
|
-
},
|
|
572
|
-
phone: (data) => {
|
|
573
|
-
return phoneNumber.isValidNumber(data || '');
|
|
574
|
-
},
|
|
575
|
-
skills: (data, schema) => {
|
|
576
|
-
if (!schema || !schema.items)
|
|
577
|
-
return false;
|
|
578
|
-
let hasAnyOf = false;
|
|
579
|
-
try {
|
|
580
|
-
hasAnyOf =
|
|
581
|
-
ajv.validate({
|
|
582
|
-
type: 'array',
|
|
583
|
-
items: schema.items.anyOf,
|
|
584
|
-
minItems: 1,
|
|
585
|
-
}, data) === true;
|
|
586
|
-
}
|
|
587
|
-
catch (e) { }
|
|
588
|
-
return hasAnyOf;
|
|
589
|
-
},
|
|
590
|
-
qualifications: (data, schema) => {
|
|
591
|
-
if (!schema || !schema.items)
|
|
592
|
-
return false;
|
|
593
|
-
return (ajv.validate({
|
|
594
|
-
type: 'array',
|
|
595
|
-
items: schema.items,
|
|
596
|
-
minItems: 1,
|
|
597
|
-
}, data) === true);
|
|
598
|
-
},
|
|
599
|
-
array: (data, schema) => {
|
|
600
|
-
if (!schema || !schema.items)
|
|
601
|
-
return false;
|
|
602
|
-
let hasAnyOf = false;
|
|
603
|
-
let hasItems = false;
|
|
604
|
-
try {
|
|
605
|
-
hasAnyOf =
|
|
606
|
-
ajv.validate({
|
|
607
|
-
type: 'array',
|
|
608
|
-
items: schema.items.anyOf,
|
|
609
|
-
minItems: 1,
|
|
610
|
-
}, data) === true;
|
|
611
|
-
hasItems =
|
|
612
|
-
ajv.validate({
|
|
613
|
-
type: 'array',
|
|
614
|
-
items: schema.items,
|
|
615
|
-
minItems: 1,
|
|
616
|
-
}, data) === true;
|
|
617
|
-
}
|
|
618
|
-
catch (e) { }
|
|
619
|
-
return hasItems || hasAnyOf;
|
|
620
|
-
},
|
|
621
|
-
heading: () => true,
|
|
622
|
-
spacer: () => true,
|
|
623
|
-
location: (data) => {
|
|
624
|
-
const s = {
|
|
625
|
-
type: 'object',
|
|
626
|
-
required: ['geo'],
|
|
627
|
-
properties: {
|
|
628
|
-
geo: {
|
|
629
|
-
type: 'object',
|
|
630
|
-
required: ['lat', 'lng'],
|
|
631
|
-
properties: {
|
|
632
|
-
lat: {
|
|
633
|
-
type: 'number',
|
|
634
|
-
minimum: -90,
|
|
635
|
-
maximum: 90,
|
|
636
|
-
},
|
|
637
|
-
lng: {
|
|
638
|
-
type: 'number',
|
|
639
|
-
minimum: -180,
|
|
640
|
-
maximum: 180,
|
|
641
|
-
},
|
|
642
|
-
},
|
|
643
|
-
},
|
|
644
|
-
locality: {
|
|
645
|
-
type: 'object',
|
|
646
|
-
properties: {
|
|
647
|
-
long: {
|
|
648
|
-
type: 'string',
|
|
649
|
-
},
|
|
650
|
-
short: {
|
|
651
|
-
type: 'string',
|
|
652
|
-
},
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
region: {
|
|
656
|
-
type: 'object',
|
|
657
|
-
properties: {
|
|
658
|
-
long: {
|
|
659
|
-
type: 'string',
|
|
660
|
-
},
|
|
661
|
-
short: {
|
|
662
|
-
type: 'string',
|
|
663
|
-
},
|
|
664
|
-
},
|
|
665
|
-
},
|
|
666
|
-
country: {
|
|
667
|
-
type: 'object',
|
|
668
|
-
properties: {
|
|
669
|
-
long: {
|
|
670
|
-
type: 'string',
|
|
671
|
-
},
|
|
672
|
-
short: {
|
|
673
|
-
type: 'string',
|
|
674
|
-
},
|
|
675
|
-
},
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
};
|
|
679
|
-
if (ajv.validate(s, data) === true) {
|
|
680
|
-
return true;
|
|
681
|
-
}
|
|
682
|
-
return false;
|
|
683
|
-
},
|
|
684
|
-
file: (data) => {
|
|
685
|
-
if (data === null || typeof data.name === 'string') {
|
|
686
|
-
return true;
|
|
687
|
-
}
|
|
688
|
-
const s = {
|
|
689
|
-
type: 'object',
|
|
690
|
-
required: ['opportunityId', 'volunteerId', 'filename'],
|
|
691
|
-
properties: {
|
|
692
|
-
opportunityId: {
|
|
693
|
-
type: 'string',
|
|
694
|
-
pattern: '[a-z0-9]{24}',
|
|
695
|
-
},
|
|
696
|
-
volunteerId: {
|
|
697
|
-
type: 'string',
|
|
698
|
-
pattern: '[a-z0-9]{24}',
|
|
699
|
-
},
|
|
700
|
-
filename: {
|
|
701
|
-
type: 'string',
|
|
702
|
-
},
|
|
703
|
-
},
|
|
704
|
-
};
|
|
705
|
-
if (ajv.validate(s, data) === true) {
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
return false;
|
|
709
|
-
},
|
|
710
|
-
},
|
|
711
|
-
dataTypes: {
|
|
712
|
-
string: (schema, data) => validateType({ type: 'string', data, schema }),
|
|
713
|
-
boolean: (schema, data) => validateType({ type: 'boolean', data }),
|
|
714
|
-
date: (schema, data) => validateType({ type: 'date', data }),
|
|
715
|
-
dateRange: (schema, data) => validateType({ type: 'dateRange', error: 'Property is not a date range', data }),
|
|
716
|
-
phone: (schema, data) => validateType({ type: 'phone', error: 'Property is not a phone number', data }),
|
|
717
|
-
skills: (schema, data) => validateType({ type: 'skills', error: 'Property is not valid list of skills', data, schema }),
|
|
718
|
-
qualifications: (schema, data) => validateType({ type: 'qualifications', error: 'Property is not valid list of qualifications', data, schema }),
|
|
719
|
-
array: (schema, data) => validateType({ type: 'array', error: 'Datatype is not an array of strings', data, schema }),
|
|
720
|
-
heading: (schema, data) => validateType({ type: 'heading', data }),
|
|
721
|
-
spacer: (schema, data) => validateType({ type: 'spacer', data: null }),
|
|
722
|
-
location: (schema, data) => validateType({ type: 'location', error: 'Datatype is not valid location object', data }),
|
|
723
|
-
file: (schema, data) => validateType({ type: 'file', error: 'File must not be set', data }),
|
|
724
|
-
number: (schema, data) => validateType({ type: 'number', error: 'Property is not a number', data }),
|
|
725
|
-
textParagraph: (schema, data) => validateType({ type: 'string', data, schema }),
|
|
726
|
-
},
|
|
727
|
-
};
|
|
728
|
-
module.exports = validator;
|