@bedrockio/model 0.1.0 → 0.1.2

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 (73) hide show
  1. package/README.md +24 -0
  2. package/dist/cjs/access.js +4 -0
  3. package/dist/cjs/assign.js +2 -3
  4. package/dist/cjs/disallowed.js +40 -0
  5. package/dist/cjs/include.js +3 -2
  6. package/dist/cjs/load.js +15 -3
  7. package/dist/cjs/schema.js +29 -12
  8. package/dist/cjs/search.js +16 -16
  9. package/dist/cjs/serialization.js +2 -3
  10. package/dist/cjs/soft-delete.js +234 -55
  11. package/dist/cjs/testing.js +8 -0
  12. package/dist/cjs/validation.js +110 -44
  13. package/package.json +9 -7
  14. package/src/access.js +3 -0
  15. package/src/disallowed.js +63 -0
  16. package/src/include.js +1 -0
  17. package/src/load.js +12 -1
  18. package/src/schema.js +25 -5
  19. package/src/search.js +21 -10
  20. package/src/soft-delete.js +238 -85
  21. package/src/testing.js +7 -0
  22. package/src/validation.js +135 -44
  23. package/types/access.d.ts +7 -0
  24. package/types/access.d.ts.map +1 -0
  25. package/types/assign.d.ts +2 -0
  26. package/types/assign.d.ts.map +1 -0
  27. package/types/const.d.ts +9 -0
  28. package/types/const.d.ts.map +1 -0
  29. package/types/disallowed.d.ts +2 -0
  30. package/types/disallowed.d.ts.map +1 -0
  31. package/types/errors.d.ts +9 -0
  32. package/types/errors.d.ts.map +1 -0
  33. package/types/include.d.ts +4 -0
  34. package/types/include.d.ts.map +1 -0
  35. package/types/index.d.ts +6 -0
  36. package/types/index.d.ts.map +1 -0
  37. package/types/load.d.ts +15 -0
  38. package/types/load.d.ts.map +1 -0
  39. package/types/references.d.ts +2 -0
  40. package/types/references.d.ts.map +1 -0
  41. package/types/schema.d.ts +71 -0
  42. package/types/schema.d.ts.map +1 -0
  43. package/types/search.d.ts +303 -0
  44. package/types/search.d.ts.map +1 -0
  45. package/types/serialization.d.ts +6 -0
  46. package/types/serialization.d.ts.map +1 -0
  47. package/types/slug.d.ts +2 -0
  48. package/types/slug.d.ts.map +1 -0
  49. package/types/soft-delete.d.ts +4 -0
  50. package/types/soft-delete.d.ts.map +1 -0
  51. package/types/testing.d.ts +11 -0
  52. package/types/testing.d.ts.map +1 -0
  53. package/types/utils.d.ts +8 -0
  54. package/types/utils.d.ts.map +1 -0
  55. package/types/validation.d.ts +13 -0
  56. package/types/validation.d.ts.map +1 -0
  57. package/types/warn.d.ts +2 -0
  58. package/types/warn.d.ts.map +1 -0
  59. package/babel.config.cjs +0 -41
  60. package/jest.config.js +0 -8
  61. package/test/assign.test.js +0 -225
  62. package/test/definitions/custom-model.json +0 -9
  63. package/test/definitions/special-category.json +0 -18
  64. package/test/include.test.js +0 -896
  65. package/test/load.test.js +0 -47
  66. package/test/references.test.js +0 -71
  67. package/test/schema.test.js +0 -919
  68. package/test/search.test.js +0 -652
  69. package/test/serialization.test.js +0 -748
  70. package/test/setup.js +0 -27
  71. package/test/slug.test.js +0 -112
  72. package/test/soft-delete.test.js +0 -333
  73. package/test/validation.test.js +0 -1925
@@ -1,748 +0,0 @@
1
- import { createTestModel } from '../src/testing';
2
-
3
- describe('serialization', () => {
4
- describe('reserved fields', () => {
5
- it('should expose id', () => {
6
- const User = createTestModel();
7
- const user = new User();
8
- const data = user.toObject();
9
- expect(data.id).toBe(user.id);
10
- });
11
-
12
- it('should not expose _id or __v', () => {
13
- const User = createTestModel();
14
- const user = new User();
15
- const data = user.toObject();
16
- expect(data._id).toBeUndefined();
17
- expect(data.__v).toBeUndefined();
18
- });
19
-
20
- it('should not expose _id in nested array objects of mixed type', () => {
21
- const User = createTestModel({
22
- names: [
23
- {
24
- name: 'String',
25
- position: 'Number',
26
- },
27
- ],
28
- });
29
- const user = new User({
30
- names: [
31
- {
32
- name: 'Foo',
33
- position: 2,
34
- },
35
- ],
36
- });
37
- expect(user.toObject()).toEqual({
38
- id: user.id,
39
- names: [
40
- {
41
- name: 'Foo',
42
- position: 2,
43
- },
44
- ],
45
- });
46
- });
47
-
48
- it('should not expose _id in deeply nested array objects of mixed type', () => {
49
- const User = createTestModel({
50
- one: [
51
- {
52
- two: [
53
- {
54
- three: [
55
- {
56
- name: 'String',
57
- position: 'Number',
58
- },
59
- ],
60
- },
61
- ],
62
- },
63
- ],
64
- });
65
- const user = new User({
66
- one: [
67
- {
68
- two: [
69
- {
70
- three: [
71
- {
72
- name: 'Foo',
73
- position: 2,
74
- },
75
- ],
76
- },
77
- ],
78
- },
79
- ],
80
- });
81
- expect(user.toObject()).toEqual({
82
- id: user.id,
83
- one: [
84
- {
85
- two: [
86
- {
87
- three: [
88
- {
89
- name: 'Foo',
90
- position: 2,
91
- },
92
- ],
93
- },
94
- ],
95
- },
96
- ],
97
- });
98
- });
99
-
100
- it('should not expose fields with underscore', () => {
101
- const User = createTestModel({
102
- _private: 'String',
103
- });
104
- const user = new User();
105
- user._private = 'private';
106
-
107
- expect(user._private).toBe('private');
108
- const data = user.toObject();
109
- expect(data._private).toBeUndefined();
110
- });
111
- });
112
-
113
- describe('read scopes', () => {
114
- it('should deny all read access', () => {
115
- const User = createTestModel({
116
- password: {
117
- type: 'String',
118
- readAccess: 'none',
119
- },
120
- });
121
- const user = new User({
122
- password: 'fake password',
123
- });
124
- expect(user.password).toBe('fake password');
125
- expect(user.toObject().password).toBeUndefined();
126
- });
127
-
128
- it('should deny read access by scope', () => {
129
- const User = createTestModel({
130
- password: {
131
- type: 'String',
132
- readAccess: ['admin'],
133
- },
134
- });
135
- const user = new User({
136
- password: 'fake password',
137
- });
138
- expect(user.password).toBe('fake password');
139
- expect(user.toObject().password).toBeUndefined();
140
- });
141
-
142
- it('should allow read access by scope', () => {
143
- const User = createTestModel({
144
- password: {
145
- type: 'String',
146
- readAccess: ['admin'],
147
- },
148
- });
149
- const user = new User({
150
- password: 'fake password',
151
- });
152
- expect(user.password).toBe('fake password');
153
- expect(user.toObject({ scopes: ['admin'] }).password).toBe(
154
- 'fake password'
155
- );
156
- });
157
-
158
- it('should enforce self read access', () => {
159
- let data;
160
-
161
- const User = createTestModel({
162
- password: {
163
- type: 'String',
164
- readAccess: 'self',
165
- },
166
- });
167
- const user1 = new User({
168
- password: 'fake password',
169
- });
170
-
171
- data = user1.toObject({
172
- authUser: user1,
173
- });
174
- expect(data.password).toBe('fake password');
175
-
176
- const user2 = new User();
177
- data = user1.toObject({
178
- authUser: user2,
179
- });
180
- expect(data.password).toBeUndefined();
181
- });
182
-
183
- it('should allow owner read access', () => {
184
- let data;
185
-
186
- const User = createTestModel();
187
- const Shop = createTestModel({
188
- name: 'String',
189
- earnings: {
190
- type: 'Number',
191
- readAccess: 'owner',
192
- },
193
- owner: {
194
- type: 'ObjectId',
195
- ref: User.modelName,
196
- },
197
- });
198
- const user1 = new User();
199
- const shop = new Shop({
200
- earnings: 5000,
201
- owner: user1,
202
- });
203
-
204
- data = shop.toObject({
205
- authUser: user1,
206
- });
207
- expect(data.earnings).toBe(5000);
208
-
209
- const user2 = new User();
210
- data = shop.toObject({
211
- authUser: user2,
212
- });
213
- expect(data.earnings).toBeUndefined();
214
- });
215
-
216
- it('should allow user read access', () => {
217
- let data;
218
-
219
- const User = createTestModel();
220
- const Account = createTestModel({
221
- name: 'String',
222
- likes: {
223
- type: 'Number',
224
- readAccess: 'user',
225
- },
226
- user: {
227
- type: 'ObjectId',
228
- ref: User.modelName,
229
- },
230
- });
231
- const user1 = new User();
232
- const account = new Account({
233
- likes: 5000,
234
- user: user1,
235
- });
236
-
237
- data = account.toObject({
238
- authUser: user1,
239
- });
240
- expect(data.likes).toBe(5000);
241
-
242
- const user2 = new User();
243
- data = account.toObject({
244
- authUser: user2,
245
- });
246
- expect(data.likes).toBeUndefined();
247
- });
248
-
249
- it('should not error if document cannot be checked', async () => {
250
- const User = createTestModel({
251
- name: 'String',
252
- age: {
253
- type: 'Number',
254
- readAccess: 'self',
255
- },
256
- });
257
- const user = new User({
258
- name: 'Bob',
259
- age: 30,
260
- });
261
- expect(user.toObject()).toEqual({
262
- id: user.id,
263
- name: 'Bob',
264
- });
265
- });
266
-
267
- it('should allow string shortcut for scopes', () => {
268
- const User = createTestModel({
269
- password: {
270
- type: 'String',
271
- readAccess: ['admin'],
272
- },
273
- });
274
- const user = new User({
275
- password: 'fake password',
276
- });
277
- expect(user.password).toBe('fake password');
278
- expect(user.toObject({ scope: 'admin' }).password).toBe('fake password');
279
- });
280
-
281
- it('should allow read access to all', () => {
282
- const User = createTestModel({
283
- password: {
284
- type: 'String',
285
- readAccess: 'all',
286
- },
287
- });
288
- const user = new User({
289
- password: 'fake password',
290
- });
291
- expect(user.password).toBe('fake password');
292
- expect(user.toObject().password).toBe('fake password');
293
- });
294
-
295
- it('should not expose private array fields', () => {
296
- const User = createTestModel({
297
- tags: [
298
- {
299
- type: 'String',
300
- readAccess: 'none',
301
- },
302
- ],
303
- });
304
- const user = new User({
305
- tags: ['one', 'two'],
306
- });
307
-
308
- expect(user.tags).toEqual(['one', 'two']);
309
-
310
- const data = user.toObject();
311
- expect(data.tags).toBeUndefined();
312
- });
313
-
314
- it('should not expose private array fields with real syntax', () => {
315
- const User = createTestModel({
316
- tags: {
317
- type: ['String'],
318
- readAccess: 'none',
319
- },
320
- });
321
- const user = new User({
322
- tags: ['one', 'two'],
323
- });
324
-
325
- expect(user.tags).toEqual(['one', 'two']);
326
-
327
- const data = user.toObject();
328
- expect(data.tags).toBeUndefined();
329
- });
330
-
331
- it('should not expose deeply nested private fields', () => {
332
- const User = createTestModel({
333
- one: {
334
- two: {
335
- three: {
336
- name: 'String',
337
- age: {
338
- type: 'Number',
339
- readAccess: 'none',
340
- },
341
- },
342
- },
343
- },
344
- });
345
- const user = new User({
346
- one: {
347
- two: {
348
- three: {
349
- name: 'Harry',
350
- age: 21,
351
- },
352
- },
353
- },
354
- });
355
-
356
- const data = user.toObject();
357
- expect(data).toEqual({
358
- id: user.id,
359
- one: {
360
- two: {
361
- three: {
362
- name: 'Harry',
363
- },
364
- },
365
- },
366
- });
367
- });
368
-
369
- it('should not expose private fields deeply nested in arrays', () => {
370
- const User = createTestModel({
371
- one: [
372
- {
373
- two: [
374
- {
375
- three: [
376
- {
377
- name: 'String',
378
- age: {
379
- type: 'Number',
380
- readAccess: 'none',
381
- },
382
- },
383
- ],
384
- },
385
- ],
386
- },
387
- ],
388
- });
389
- const user = new User({
390
- one: [
391
- {
392
- two: [
393
- {
394
- three: [
395
- {
396
- name: 'Harry',
397
- age: 21,
398
- },
399
- ],
400
- },
401
- ],
402
- },
403
- ],
404
- });
405
-
406
- expect(user.toObject()).toEqual({
407
- id: user.id,
408
- one: [
409
- {
410
- two: [
411
- {
412
- three: [
413
- {
414
- name: 'Harry',
415
- },
416
- ],
417
- },
418
- ],
419
- },
420
- ],
421
- });
422
- });
423
-
424
- it('should serialize identically with toObject', () => {
425
- const User = createTestModel({
426
- secret: {
427
- type: 'String',
428
- readAccess: 'none',
429
- },
430
- });
431
- const user = new User({
432
- secret: 'foo',
433
- });
434
- const data = user.toObject();
435
- expect(data.id).toBe(user.id);
436
- expect(data._id).toBeUndefined();
437
- expect(data.__v).toBeUndefined();
438
- expect(data.secret).toBeUndefined();
439
- });
440
-
441
- it('should allow access to private fields with options on toJSON', () => {
442
- const User = createTestModel({
443
- secret: {
444
- type: 'String',
445
- readAccess: ['admin'],
446
- },
447
- });
448
- const user = new User({
449
- secret: 'foo',
450
- });
451
- const data = user.toJSON({
452
- scopes: ['admin'],
453
- });
454
- expect(data.id).toBe(user.id);
455
- expect(data._id).toBeUndefined();
456
- expect(data.__v).toBeUndefined();
457
- expect(data.secret).toBe('foo');
458
- });
459
-
460
- it('should allow access to private fields with options on toObject', () => {
461
- const User = createTestModel({
462
- secret: {
463
- type: 'String',
464
- readAccess: ['admin'],
465
- },
466
- });
467
- const user = new User({
468
- secret: 'foo',
469
- });
470
- const data = user.toObject({
471
- scopes: ['admin'],
472
- });
473
- expect(data.id).toBe(user.id);
474
- expect(data._id).toBeUndefined();
475
- expect(data.__v).toBeUndefined();
476
- expect(data.secret).toBe('foo');
477
- });
478
-
479
- it('should mark access on nested objects', async () => {
480
- const User = createTestModel({
481
- login: {
482
- type: 'Object',
483
- readAccess: ['admin'],
484
- attributes: {
485
- password: 'String',
486
- attempts: 'Number',
487
- },
488
- },
489
- });
490
- const user = new User({
491
- login: {
492
- password: 'password',
493
- attempts: 10,
494
- },
495
- });
496
- expect(user.login.password).toBe('password');
497
- expect(user.login.attempts).toBe(10);
498
- expect(user.toObject().login).toBeUndefined();
499
- });
500
-
501
- it('should deny access on nested objects', async () => {
502
- const User = createTestModel({
503
- terms: {
504
- readAccess: {
505
- type: 'String',
506
- default: 'none',
507
- },
508
- service: 'Boolean',
509
- privacy: 'Boolean',
510
- },
511
- });
512
- const user = new User({
513
- terms: {
514
- service: true,
515
- privacy: true,
516
- },
517
- });
518
- expect(user.terms).toEqual({
519
- service: true,
520
- privacy: true,
521
- readAccess: 'none',
522
- });
523
- expect(user.toObject().terms).toBeUndefined();
524
- });
525
-
526
- it('should enforce complex access', async () => {
527
- let data;
528
- const User = createTestModel({
529
- profile: {
530
- accounts: [
531
- {
532
- name: 'String',
533
- profits: {
534
- type: 'Number',
535
- readAccess: ['admin', 'self', 'foo'],
536
- },
537
- },
538
- ],
539
- },
540
- });
541
- const user = new User({
542
- profile: {
543
- accounts: [
544
- {
545
- name: 'Account 1',
546
- profits: 5000,
547
- },
548
- {
549
- name: 'Account 2',
550
- profits: 20,
551
- },
552
- ],
553
- },
554
- });
555
-
556
- data = user.toObject({
557
- authUser: user,
558
- });
559
- expect(data).toMatchObject({
560
- profile: {
561
- accounts: [
562
- {
563
- name: 'Account 1',
564
- profits: 5000,
565
- },
566
- {
567
- name: 'Account 2',
568
- profits: 20,
569
- },
570
- ],
571
- },
572
- });
573
-
574
- data = user.toObject({
575
- scope: 'admin',
576
- authUser: user,
577
- });
578
- expect(data).toMatchObject({
579
- profile: {
580
- accounts: [
581
- {
582
- name: 'Account 1',
583
- profits: 5000,
584
- },
585
- {
586
- name: 'Account 2',
587
- profits: 20,
588
- },
589
- ],
590
- },
591
- });
592
-
593
- data = user.toObject({
594
- scope: 'foo',
595
- authUser: user,
596
- });
597
- expect(data).toMatchObject({
598
- profile: {
599
- accounts: [
600
- {
601
- name: 'Account 1',
602
- profits: 5000,
603
- },
604
- {
605
- name: 'Account 2',
606
- profits: 20,
607
- },
608
- ],
609
- },
610
- });
611
-
612
- data = user.toObject({
613
- scope: 'bar',
614
- authUser: user,
615
- });
616
- expect(data).toMatchObject({
617
- profile: {
618
- accounts: [
619
- {
620
- name: 'Account 1',
621
- },
622
- {
623
- name: 'Account 2',
624
- },
625
- ],
626
- },
627
- });
628
- });
629
-
630
- it('should deny read access on extended array', () => {
631
- const User = createTestModel({
632
- tokens: {
633
- type: 'Array',
634
- readAccess: 'none',
635
- attributes: {
636
- name: 'String',
637
- },
638
- },
639
- });
640
- const user = new User({
641
- tokens: [
642
- {
643
- name: 'foo',
644
- },
645
- ],
646
- });
647
- expect(user.tokens).toMatchObject([
648
- {
649
- name: 'foo',
650
- },
651
- ]);
652
- expect(user.toObject().tokens).toBeUndefined();
653
- });
654
-
655
- it('should deny access to array field with explicit typedef', async () => {
656
- const User = createTestModel({
657
- tokens: {
658
- type: [
659
- {
660
- type: 'String',
661
- validate: 'email',
662
- },
663
- ],
664
- readAccess: 'none',
665
- },
666
- });
667
- const user = new User({
668
- tokens: ['token'],
669
- });
670
- expect(user.tokens).toEqual(['token']);
671
- expect(user.toObject().tokens).toBeUndefined();
672
- });
673
- });
674
-
675
- it('should not serialize nested array object ids', async () => {
676
- const User = createTestModel({
677
- foo: [
678
- {
679
- bar: [
680
- {
681
- name: 'String',
682
- },
683
- ],
684
- },
685
- ],
686
- });
687
- const user = new User({
688
- foo: [
689
- {
690
- bar: [
691
- {
692
- name: 'wut',
693
- },
694
- ],
695
- },
696
- ],
697
- });
698
- expect(user.toObject()).toEqual({
699
- id: user.id,
700
- foo: [
701
- {
702
- bar: [
703
- {
704
- name: 'wut',
705
- },
706
- ],
707
- },
708
- ],
709
- });
710
- });
711
-
712
- it('should serialize id on nested field with type', async () => {
713
- const User = createTestModel({
714
- foo: {
715
- type: {
716
- type: 'String',
717
- required: true,
718
- },
719
- bar: [
720
- {
721
- name: 'String',
722
- },
723
- ],
724
- },
725
- });
726
- const user = new User({
727
- foo: {
728
- type: 'foo type',
729
- bar: [
730
- {
731
- name: 'name',
732
- },
733
- ],
734
- },
735
- });
736
- expect(user.toObject()).toEqual({
737
- id: user.id,
738
- foo: {
739
- type: 'foo type',
740
- bar: [
741
- {
742
- name: 'name',
743
- },
744
- ],
745
- },
746
- });
747
- });
748
- });