@qikdev/sdk 0.1.16 → 0.1.20

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.
@@ -23,6 +23,484 @@ export default function(qik) {
23
23
  debug: false,
24
24
  }
25
25
 
26
+ ///////////////////////////////////////////////////
27
+
28
+ /**
29
+ *
30
+ * Retrieves the glossary of content types available to the user
31
+ * @alias content.list
32
+ * @param {String} type The type or definition of records we want to retrieve
33
+ * @example
34
+ *
35
+ * QikContent.glossary()
36
+ */
37
+
38
+ const glossary = {}
39
+
40
+ service.glossary = async function(options) {
41
+ options = options || {};
42
+
43
+ var reload = options.refresh || options.reload || !glossary.data;
44
+
45
+ if (reload) {
46
+ console.log('reloading the glossary')
47
+ const { data } = await qik.api.get(`/glossary`);
48
+ glossary.data = data;
49
+ }
50
+
51
+ if (options.hash) {
52
+ var result = qik.utils.hash(glossary.data, 'key');
53
+ return result;
54
+ }
55
+
56
+ return glossary.data;
57
+ }
58
+
59
+ ///////////////////////////////////////////////////
60
+
61
+ /**
62
+ *
63
+ * Retrieves all available filter comparators for the various field types
64
+ * @alias content.comparators
65
+ * @example
66
+ *
67
+ * QikContent.comparators()
68
+ */
69
+
70
+ const comparators = {}
71
+
72
+ service.comparators = async function(options) {
73
+ options = options || {};
74
+
75
+ var reload = options.refresh || !comparators.data;
76
+
77
+ if (reload) {
78
+ const { data } = await qik.api.get(`/system/comparators`);
79
+
80
+
81
+ /////////////////////////////
82
+
83
+ data.available = {};
84
+
85
+ Object.entries(data.types).forEach(([key, value]) => {
86
+ data.available[key] = value.map(function(comparatorKey) {
87
+ return data.hash[comparatorKey];
88
+ })
89
+ });
90
+
91
+
92
+ /////////////////////////////
93
+
94
+ comparators.data = data;
95
+ }
96
+
97
+
98
+
99
+ // if (options.hash) {
100
+ // var result = qik.utils.hash(comparators.data, 'key');
101
+ // return result;
102
+ // }
103
+
104
+ return comparators.data;
105
+ }
106
+
107
+ ///////////////////////////////////////////////////
108
+
109
+
110
+ function getLimits(fieldDefinition) {
111
+
112
+ var { minimum, maximum } = fieldDefinition;
113
+ minimum = qik.utils.parseInt(minimum);
114
+ minimum = Math.max(minimum, 0);
115
+
116
+ maximum = qik.utils.parseInt(maximum);
117
+ maximum = Math.max(maximum, 0); // cant be less than 0
118
+ if (maximum == 0) {
119
+ //Unlimited maximum
120
+ } else {
121
+ maximum = Math.max(maximum, minimum); // cant be less than the minimum
122
+ }
123
+ return { minimum, maximum };
124
+ }
125
+
126
+
127
+ service.meetsValidationRequirements = function(input, fieldType, validationCriteria) {
128
+
129
+ if (validationCriteria.minLength) {
130
+ var stringLength = String(input).length;
131
+ if (stringLength < validationCriteria.minLength) {
132
+ return `must be at least ${validationCriteria.minLength} characters`;
133
+
134
+ }
135
+ }
136
+
137
+ if (validationCriteria.maxLength) {
138
+ var stringLength = String(input).length;
139
+ if (stringLength > validationCriteria.maxLength) {
140
+ return `must be less than ${validationCriteria.maxLength} characters`;
141
+
142
+ }
143
+ }
144
+
145
+ if (validationCriteria.minValue) {
146
+ var number = Number(input);
147
+ if (number < validationCriteria.minValue) {
148
+ return `must be greater than ${validationCriteria.minValue}`;
149
+
150
+ }
151
+ }
152
+
153
+ if (validationCriteria.maxValue) {
154
+ var number = Number(input);
155
+ if (number > validationCriteria.maxValue) {
156
+ return `must be less than ${validationCriteria.maxValue}`;
157
+
158
+ }
159
+ }
160
+ }
161
+
162
+ service.validateField = function(input, fieldDefinition, options) {
163
+
164
+ if (!options) {
165
+ options = {}
166
+ }
167
+
168
+ var fieldType = fieldDefinition.type || 'string';
169
+ var { minimum, maximum } = getLimits(fieldDefinition);
170
+
171
+ var isObject = (fieldDefinition.type == 'group' && fieldDefinition.asObject);
172
+ var singleValue = isObject ? minimum === maximum && maximum === 1 : maximum === 1;
173
+ var multiValue = !singleValue;
174
+
175
+ //////////////////
176
+
177
+ var isNumeric;
178
+ switch (fieldType) {
179
+ case 'integer':
180
+ case 'decimal':
181
+ case 'number':
182
+ case 'float':
183
+ isNumeric = true;
184
+ break;
185
+ }
186
+
187
+ //////////////////
188
+
189
+ var inputWasProvided = qik.utils.exists(input);
190
+
191
+ //If an answer is required
192
+ if (minimum) {
193
+ //but none was provided
194
+ if (!inputWasProvided) {
195
+ //Throw an error
196
+ return {
197
+ valid: false,
198
+ message: `${fieldDefinition.title} is a required field`,
199
+ status: 400,
200
+ }
201
+ }
202
+ } else {
203
+ //No answer is needed and none was provided
204
+ if (!inputWasProvided) {
205
+ return {
206
+ valid: true,
207
+ }
208
+ }
209
+ }
210
+
211
+ //////////////////
212
+
213
+
214
+ //If we are requiring multiple values
215
+ if (multiValue) {
216
+
217
+ //But the input is not an array
218
+ if (!Array.isArray(input)) {
219
+
220
+ if (minimum) {
221
+ return {
222
+ valid: false,
223
+ message: `${fieldDefinition.title} requires at least ${minimum} values`,
224
+ status: 400,
225
+ }
226
+ } else {
227
+ return {
228
+ valid: false,
229
+ message: `${fieldDefinition.title} must be provided as an array`,
230
+ status: 400,
231
+ }
232
+ }
233
+ }
234
+
235
+
236
+
237
+ ////////////////////////////
238
+
239
+ var compacted = input;
240
+
241
+ compacted = compacted.filter(function(v) {
242
+ //Check if we care about this value
243
+ var empty = v === undefined || v === null || v === '';
244
+ return !empty;
245
+ })
246
+
247
+
248
+ // console.log('COMPACTED', input.length, compacted.length);
249
+
250
+ ////////////////////////////
251
+
252
+ //We need an exact number of answers
253
+ if (minimum == maximum) {
254
+
255
+ //But we don't have the number of answers needed
256
+ if (minimum && (compacted.length != minimum)) {
257
+ return {
258
+ valid: false,
259
+ message: `${fieldDefinition.title} requires exactly ${maximum} values`,
260
+ status: 400,
261
+ }
262
+ }
263
+ }
264
+
265
+ //We have too many answers
266
+ if (maximum && (compacted.length > maximum)) {
267
+
268
+ return {
269
+ valid: false,
270
+ message: `${fieldDefinition.title} requires less than ${maximum+1} values`,
271
+ status: 400,
272
+ }
273
+ }
274
+
275
+ //We don't have enough answers
276
+ if (compacted.length < minimum) {
277
+
278
+ // console.log(fieldDefinition.title, 'INPUT', input.length, minimum, input, compacted)
279
+
280
+ return {
281
+ valid: false,
282
+ message: `${fieldDefinition.title} requires at least ${minimum} values`,
283
+ status: 400,
284
+ }
285
+ }
286
+
287
+ ////////////////////////////
288
+
289
+ var foundBadEntry;
290
+
291
+ //Find any bad values
292
+ var badEntry = compacted.find(function(val) {
293
+
294
+ var strict = options.strict;
295
+
296
+ var valueFieldType = fieldType;
297
+
298
+ if (fieldDefinition.type == 'group') {
299
+ valueFieldType = 'object';
300
+ }
301
+
302
+ var isValid = qik.utils.isValidValue(val, valueFieldType, strict);
303
+
304
+ ////////////////////////////
305
+
306
+ if (!isValid) {
307
+ foundBadEntry = true;
308
+ return true;
309
+ }
310
+
311
+ ////////////////////////////
312
+
313
+ //Is there additional validation requirements
314
+ if (fieldDefinition.validation) {
315
+ var additionalValidationErrors = service.meetsValidationRequirements(val, fieldType, fieldDefinition.validation)
316
+ if (additionalValidationErrors) {
317
+ foundBadEntry = true;
318
+ return true;
319
+ }
320
+ }
321
+ })
322
+
323
+ if (foundBadEntry) {
324
+
325
+ // var badValueMessage = `${badEntry || "''"} is not a valid ${fieldType} value for ${fieldDefinition.title}`;
326
+ // switch(fieldType) {
327
+ // case 'string':
328
+ // badValueMessage = `Empty or invalid value for ${fieldDefinition.title}`;
329
+ // break;
330
+ // }
331
+
332
+ console.log('BAD ENTRY', badEntry, fieldDefinition)
333
+ let badValueMessage = `Invalid input for ${fieldDefinition.title}`;
334
+ return {
335
+ valid: false,
336
+ message: badValueMessage,
337
+ status: 400,
338
+ }
339
+ }
340
+
341
+ } else {
342
+
343
+ //////////////////
344
+
345
+ var cleanedValue = getCleanedValue();
346
+
347
+
348
+ //////////////////
349
+
350
+ function getCleanedValue() {
351
+ switch (fieldType) {
352
+ case 'number':
353
+ case 'float':
354
+ case 'decimal':
355
+ if (!qik.utils.exists(input)) {
356
+ return undefined;
357
+ } else {
358
+ return Number(input);
359
+ }
360
+ break;
361
+ case 'integer':
362
+ if (!qik.utils.exists(input)) {
363
+ return undefined;
364
+ } else {
365
+ return parseInt(input);
366
+ }
367
+ break;
368
+ case 'boolean':
369
+ if (!qik.utils.exists(input)) {
370
+ return undefined;
371
+ }
372
+ return qik.utils.parseBoolean(input);
373
+ break;
374
+ case 'email':
375
+ return options.strict ? input : String(input).toLowerCase();
376
+ break;
377
+ case 'reference':
378
+ return options.strict ? input : qik.utils.id(input);
379
+ break;
380
+ default:
381
+ return options.strict ? input : qik.utils.cleanValue(input, fieldType, options);
382
+ break
383
+ }
384
+
385
+ return input;
386
+ }
387
+
388
+ //////////////////
389
+
390
+ var dataType = fieldType;
391
+ if (dataType == 'group') {
392
+ dataType = 'object';
393
+ }
394
+
395
+ //////////////////
396
+
397
+ var isValidValue = qik.utils.isValidValue(cleanedValue, dataType, options.strict);
398
+
399
+ //////////////////
400
+
401
+ //Invalid input
402
+ if (!isValidValue) {
403
+
404
+ console.log('FAILED', input, fieldType, cleanedValue, dataType, isValidValue)
405
+ return {
406
+ valid: false,
407
+ message: `Single value '${input}' is not a valid ${fieldType} for ${fieldDefinition.title}`,
408
+ criteria: {
409
+ isValidValue,
410
+ cleanedValue,
411
+ fieldType,
412
+ options,
413
+ },
414
+ status: 400,
415
+ }
416
+ }
417
+
418
+ //Is there additional validation requirements
419
+ if (fieldDefinition.validation) {
420
+ var additionalValidationError = service.meetsValidationRequirements(input, fieldType, fieldDefinition.validation)
421
+ if (additionalValidationError) {
422
+ return {
423
+ valid: false,
424
+ message: additionalValidationError,
425
+ status: 400,
426
+ };
427
+ }
428
+ }
429
+ }
430
+
431
+ //////////////////
432
+
433
+ return {
434
+ valid: true,
435
+ }
436
+
437
+ }
438
+
439
+
440
+
441
+
442
+ // service.validateFieldValue = function(value, field, errorLog, options) {
443
+ // options = options || {};
444
+ // errorLog = errorLog || [];
445
+
446
+
447
+ // ///////////////////////////////////////////
448
+
449
+ // let { key, type } = field;
450
+ // let { minimum, maximum } = = getLimits(field);
451
+ // let existingValue = value;
452
+
453
+ // let isLayout = field.type == 'group' && !field.asObject;
454
+
455
+ // if (isLayout) {
456
+ // var subFields = service.mapFields(field.fields, { depth: 1 });
457
+ // subFields.forEach(function(subField) {
458
+ // service.validateFieldValue(input, subField, errorLog, options);
459
+ // })
460
+ // } else {
461
+ // //If it's a group of fields
462
+ // if (isGroup && asObject) {
463
+ // //Get all the fields
464
+ // var subFields = service.mapFields(field.fields, { depth: 1 });
465
+
466
+ // //If the group is a single object
467
+ // if (maximum === 1) {
468
+ // var groupObjectInput = existingValue;
469
+
470
+ // //Loop through each field
471
+ // subFields.forEach(function(subField) {
472
+ // //Update the entry data
473
+ // service.validateFieldValue(groupObjectInput, subField, errorLog, options);
474
+ // })
475
+ // } else {
476
+
477
+ // //Generate a new array
478
+ // (existingValue || []).forEach(function(entry) {
479
+ // //Loop through each field
480
+ // subFields.forEach(function(subField) {
481
+ // //Update the entry data
482
+ // service.validateFieldValue(entry, subField, errorLog, options);
483
+ // })
484
+ // });
485
+ // }
486
+ // } else {
487
+ // //We're a standard field so just wash and set and move on
488
+ // var { valid, message } = service.validate(existingValue, field, options)
489
+
490
+ // if (!valid) {
491
+ // errorLog.push({
492
+ // field,
493
+ // message,
494
+ // })
495
+ // }
496
+ // }
497
+ // }
498
+ // }
499
+
500
+
501
+
502
+
503
+
26
504
  ///////////////////////////////////////////////////
27
505
 
28
506
  /**
@@ -31,14 +509,24 @@ export default function(qik) {
31
509
  * @alias content.list
32
510
  * @param {String} type The type or definition of records we want to retrieve
33
511
  * @param {Object} options The options for our query
34
- * @param {Object} options.search Freeform text keywords
512
+ * @param {String} options.search Freeform text keywords
35
513
  * @param {Object} options.sort How to sort the results
514
+ * @param {String} options.sort.key Which key to sort on
515
+ * @param {String} options.sort.direction Which direction to sort on
516
+ * @param {Object} options.sort.type What type of data is being sorted
517
+ * @param {Object} options.page Page configuration
518
+ * @param {Number} options.page.size Page size
519
+ * @param {Number} options.page.index Page index
36
520
  * @param {Object} options.filter How to filter the results
37
521
  * @example
38
522
  *
39
523
  *
40
524
  * QikContent.list('profile', {
41
525
  * search:'Jim',
526
+ * page:{
527
+ * size:50,
528
+ * index:2,
529
+ * },
42
530
  * sort:{
43
531
  * key:'age',
44
532
  * direction:'asc',
@@ -55,11 +543,41 @@ export default function(qik) {
55
543
  * })
56
544
  */
57
545
 
58
- service.list = async function(type, options) {
546
+ service.list = async function(type, options, advanced) {
547
+
548
+
549
+ if (!advanced) {
550
+ advanced = {};
551
+ }
552
+
553
+ if (advanced.cancellable) {
554
+
555
+ if (!advanced.config) {
556
+ advanced.config = {}
557
+ }
558
+
559
+ //Create a cancel token
560
+ const CancelToken = qik.api.CancelToken;
561
+ const source = CancelToken.source();
562
+ advanced.config.cancelToken = source.token;
563
+
564
+ const promise = qik.api.post(`/content/${type}/list`, options, advanced.config);
565
+ return {
566
+ promise,
567
+ cancel(message) {
568
+ source.cancel(message || 'Operation canceled by the user.');
569
+ },
570
+ }
571
+
572
+ } else {
573
+ const { data } = await qik.api.post(`/content/${type}/list`, options, advanced.config);
574
+ return data;
575
+ }
576
+
577
+
578
+
579
+
59
580
 
60
- console.log('Get the things', type, options);
61
- return qik.api.post(`/content/${type}/list`, options);
62
-
63
581
  }
64
582
 
65
583
  ///////////////////////////////////////////////////
@@ -83,9 +601,21 @@ export default function(qik) {
83
601
  * })
84
602
  */
85
603
 
86
- service.create = async function(type, data) {
87
- return qik.api.post(`/content/${type}/create`, data);
88
-
604
+ service.create = async function(type, input) {
605
+ const { data } = await qik.api.post(`/content/${type}/create`, input);
606
+ return data;
607
+ }
608
+
609
+ service.update = async function(type, id, input) {
610
+ id = qik.utils.id(id);
611
+ const { data } = await qik.api.put(`/content/${type}/${id}`, input);
612
+ return data;
613
+ }
614
+
615
+ service.patch = async function(type, id, input) {
616
+ id = qik.utils.id(id);
617
+ const { data } = await qik.api.patch(`/content/${type}/${id}`, input);
618
+ return data;
89
619
  }
90
620
 
91
621
 
@@ -3,9 +3,12 @@
3
3
  import QikAPI from './qik.api.js';
4
4
  import QikAuth from './qik.auth.js';
5
5
  import QikUtils from './qik.utils.js';
6
+ import QikFilter from './qik.filter.js';
7
+ import QikFiles from './qik.files.js';
6
8
  import QikCache from './qik.cache.js';
7
9
  import QikAccess from './qik.access.js';
8
10
  import QikContent from './qik.content.js';
11
+ import QikSystem from './qik.system.js';
9
12
  import { EventDispatcher } from './qik.utils.js';
10
13
 
11
14
  ///////////////////////////////////////
@@ -39,8 +42,6 @@ import { EventDispatcher } from './qik.utils.js';
39
42
 
40
43
  import { version } from '../version.js';
41
44
 
42
- console.log('BEFORE class')
43
-
44
45
  export default function(options) {
45
46
  if (!options) {
46
47
  options = {
@@ -66,7 +67,7 @@ export default function(options) {
66
67
  options.apiURL = 'https://api.staging.qik.dev';
67
68
  break;
68
69
  case 'local':
69
- options.apiURL = 'http://localhost:4001';
70
+ options.apiURL = 'http://api.qik.localhost:4001';
70
71
  break;
71
72
  }
72
73
 
@@ -76,6 +77,7 @@ export default function(options) {
76
77
  //Other options
77
78
  // domain:'',
78
79
  version,
80
+ global:{},
79
81
  })
80
82
 
81
83
  console.log('Instantiate class', core)
@@ -94,6 +96,7 @@ export default function(options) {
94
96
  value: cache,
95
97
  writable: false,
96
98
  });
99
+
97
100
  ///////////////////////////////////////
98
101
 
99
102
  /**
@@ -109,6 +112,34 @@ export default function(options) {
109
112
 
110
113
  ///////////////////////////////////////
111
114
 
115
+ /**
116
+ * Provides helper functions for working
117
+ * with Qik filters
118
+ * @type {QikFilter}
119
+ */
120
+ const filter = QikFilter;
121
+ Object.defineProperty(core, 'filter', {
122
+ value: filter,
123
+ writable: false,
124
+ });
125
+
126
+
127
+
128
+ ///////////////////////////////////////
129
+
130
+ /**
131
+ * Provides helper functions for working
132
+ * with Qik files
133
+ * @type {QikFiles}
134
+ */
135
+ const files = new QikFiles(core);
136
+ Object.defineProperty(core, 'files', {
137
+ value: files,
138
+ writable: false,
139
+ });
140
+
141
+ ///////////////////////////////////////
142
+
112
143
  //Create a new global dispatcher so we can trigger events
113
144
  const dispatcher = new EventDispatcher();
114
145
  dispatcher.bootstrap(core);
@@ -162,6 +193,18 @@ export default function(options) {
162
193
  writable: false,
163
194
  });
164
195
 
196
+ ///////////////////////////////////////
197
+
198
+ /**
199
+ * A helper service for content create, update, read and delete operations.
200
+ * @type {QikContent}
201
+ */
202
+ const system = new QikSystem(core);
203
+ Object.defineProperty(core, 'system', {
204
+ value: system,
205
+ writable: false,
206
+ });
207
+
165
208
 
166
209
  ///////////////////////////////////////
167
210