@e22m4u/js-repository 0.4.0 → 0.5.1

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 (88) hide show
  1. package/README.md +569 -80
  2. package/dist/cjs/index.cjs +319 -270
  3. package/package.json +1 -1
  4. package/src/adapter/adapter-loader.js +1 -1
  5. package/src/adapter/adapter-loader.spec.js +1 -1
  6. package/src/adapter/decorator/data-sanitizing-decorator.js +1 -1
  7. package/src/adapter/decorator/data-transformation-decorator.js +1 -1
  8. package/src/adapter/decorator/data-validation-decorator.js +1 -1
  9. package/src/adapter/decorator/default-values-decorator.js +1 -1
  10. package/src/adapter/decorator/fields-filtering-decorator.js +1 -1
  11. package/src/adapter/decorator/inclusion-decorator.js +1 -1
  12. package/src/adapter/decorator/property-uniqueness-decorator.js +1 -1
  13. package/src/definition/datasource/datasource-definition-validator.js +3 -3
  14. package/src/definition/datasource/datasource-definition-validator.spec.js +3 -3
  15. package/src/definition/model/model-data-sanitizer.js +2 -2
  16. package/src/definition/model/model-data-transformer.js +71 -13
  17. package/src/definition/model/model-data-transformer.spec.js +2073 -407
  18. package/src/definition/model/model-data-validator.js +125 -37
  19. package/src/definition/model/model-data-validator.spec.js +2138 -440
  20. package/src/definition/model/model-definition-utils.js +5 -5
  21. package/src/definition/model/model-definition-utils.spec.js +7 -7
  22. package/src/definition/model/model-definition-validator.js +7 -7
  23. package/src/definition/model/model-definition-validator.spec.js +10 -7
  24. package/src/definition/model/properties/properties-definition-validator.js +129 -54
  25. package/src/definition/model/properties/properties-definition-validator.spec.js +85 -25
  26. package/src/definition/model/properties/property-definition.d.ts +3 -3
  27. package/src/definition/model/properties/property-transformer/builtin/index.d.ts +0 -1
  28. package/src/definition/model/properties/property-transformer/builtin/index.js +0 -1
  29. package/src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.d.ts +1 -1
  30. package/src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.js +1 -1
  31. package/src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.spec.js +1 -1
  32. package/src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.d.ts +1 -1
  33. package/src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.js +1 -1
  34. package/src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.spec.js +1 -1
  35. package/src/definition/model/properties/property-transformer/builtin/trim-transformer.js +1 -1
  36. package/src/definition/model/properties/property-transformer/builtin/trim-transformer.spec.js +1 -1
  37. package/src/definition/model/properties/property-transformer/property-transformer-registry.js +2 -4
  38. package/src/definition/model/properties/property-transformer/property-transformer-registry.spec.js +2 -4
  39. package/src/definition/model/properties/property-transformer/property-transformer.d.ts +2 -2
  40. package/src/definition/model/properties/property-uniqueness-validator.js +4 -4
  41. package/src/definition/model/properties/property-uniqueness-validator.spec.js +4 -4
  42. package/src/definition/model/properties/property-validator/builtin/max-length-validator.d.ts +1 -1
  43. package/src/definition/model/properties/property-validator/builtin/max-length-validator.js +2 -2
  44. package/src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js +2 -2
  45. package/src/definition/model/properties/property-validator/builtin/min-length-validator.d.ts +1 -1
  46. package/src/definition/model/properties/property-validator/builtin/min-length-validator.js +2 -2
  47. package/src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js +2 -2
  48. package/src/definition/model/properties/property-validator/builtin/regexp-validator.d.ts +1 -1
  49. package/src/definition/model/properties/property-validator/builtin/regexp-validator.js +2 -2
  50. package/src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js +2 -2
  51. package/src/definition/model/properties/property-validator/property-validator-registry.js +2 -2
  52. package/src/definition/model/properties/property-validator/property-validator-registry.spec.js +2 -2
  53. package/src/definition/model/properties/property-validator/property-validator.d.ts +2 -2
  54. package/src/definition/model/relations/relation-definition.d.ts +21 -21
  55. package/src/definition/model/relations/relations-definition-validator.js +23 -23
  56. package/src/definition/model/relations/relations-definition-validator.spec.js +24 -24
  57. package/src/errors/invalid-operator-value-error.js +1 -1
  58. package/src/errors/invalid-operator-value-error.spec.js +1 -1
  59. package/src/filter/fields-clause-tool.js +5 -5
  60. package/src/filter/fields-clause-tool.spec.js +16 -16
  61. package/src/filter/include-clause-tool.js +6 -6
  62. package/src/filter/include-clause-tool.spec.js +2 -2
  63. package/src/filter/operator-clause-tool.js +13 -13
  64. package/src/filter/operator-clause-tool.spec.js +13 -13
  65. package/src/filter/order-clause-tool.js +3 -3
  66. package/src/filter/order-clause-tool.spec.js +4 -4
  67. package/src/filter/slice-clause-tool.js +5 -5
  68. package/src/filter/slice-clause-tool.spec.js +5 -5
  69. package/src/filter/where-clause-tool.js +4 -4
  70. package/src/filter/where-clause-tool.spec.js +3 -3
  71. package/src/relations/belongs-to-resolver.js +14 -14
  72. package/src/relations/belongs-to-resolver.spec.js +14 -14
  73. package/src/relations/has-many-resolver.js +22 -22
  74. package/src/relations/has-many-resolver.spec.js +23 -23
  75. package/src/relations/has-one-resolver.js +22 -22
  76. package/src/relations/has-one-resolver.spec.js +23 -23
  77. package/src/relations/references-many-resolver.js +7 -7
  78. package/src/relations/references-many-resolver.spec.js +7 -7
  79. package/src/repository/repository-registry.js +1 -1
  80. package/src/utils/exclude-object-keys.js +1 -1
  81. package/src/utils/exclude-object-keys.spec.js +1 -1
  82. package/src/utils/model-name-to-model-key.js +1 -1
  83. package/src/utils/model-name-to-model-key.spec.js +7 -7
  84. package/src/utils/select-object-keys.js +3 -3
  85. package/src/utils/select-object-keys.spec.js +3 -3
  86. package/src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.d.ts +0 -6
  87. package/src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.js +0 -22
  88. package/src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.spec.js +0 -41
package/README.md CHANGED
@@ -6,12 +6,18 @@
6
6
  - [Импорт](#импорт)
7
7
  - [Описание](#описание)
8
8
  - [Пример](#пример)
9
- - [Схема базы данных](#схема-базы-данных)
9
+ - [Схема](#схема)
10
10
  - [Источник данных](#источник-данных)
11
11
  - [Модель](#модель)
12
12
  - [Свойства](#свойства)
13
13
  - [Валидаторы](#валидаторы)
14
+ - [Глобальные валидаторы](#глобальные-валидаторы)
15
+ - [Регистрация глобальных валидаторов](#регистрация-глобальных-валидаторов)
16
+ - [Локальные валидаторы](#локальные-валидаторы)
14
17
  - [Трансформеры](#трансформеры)
18
+ - [Глобальные трансформеры](#глобальные-трансформеры)
19
+ - [Регистрация глобальных трансформеров](#регистрация-глобальных-трансформеров)
20
+ - [Локальные трансформеры](#локальные-трансформеры)
15
21
  - [Пустые значения](#пустые-значения)
16
22
  - [Репозиторий](#репозиторий)
17
23
  - [Фильтрация](#фильтрация)
@@ -27,7 +33,7 @@
27
33
  npm install @e22m4u/js-repository
28
34
  ```
29
35
 
30
- Опционально устанавливаем адаптер.
36
+ Опционально устанавливается нужный адаптер.
31
37
 
32
38
  | | описание |
33
39
  |-----------|--------------------------------------------------------------------------------------------------------------------------------|
@@ -129,10 +135,10 @@ dbs.defineModel({
129
135
  },
130
136
  })
131
137
 
132
- // получение репозитория модели "country"
138
+ // получение репозитория модели
133
139
  const countryRep = dbs.getRepository('country');
134
140
 
135
- // добавление нового документа в коллекцию "country"
141
+ // добавление нового документа в коллекцию
136
142
  const country = await countryRep.create({
137
143
  name: 'Russia',
138
144
  population: 143400000,
@@ -141,16 +147,15 @@ const country = await countryRep.create({
141
147
  // вывод нового документа
142
148
  console.log(country);
143
149
  // {
144
- // "id": 1,
145
- // "name": "Russia",
146
- // "population": 143400000,
150
+ // id: 1,
151
+ // name: 'Russia',
152
+ // population: 143400000,
147
153
  // }
148
154
  ```
149
155
 
150
- ## Схема базы данных
156
+ ## Схема
151
157
 
152
- Экземпляр класса `DatabaseSchema` хранит определения источников и моделей
153
- данных.
158
+ Экземпляр класса `DatabaseSchema` хранит определения источников данных и моделей.
154
159
 
155
160
  **Методы**
156
161
 
@@ -285,7 +290,7 @@ dbs.defineModel({
285
290
  - `columnType: string` тип колонки (определяется адаптером)
286
291
  - `required: boolean` объявить свойство обязательным
287
292
  - `default: any` значение по умолчанию
288
- - `validate: string | array | object` см. [Валидаторы](#Валидаторы)
293
+ - `validate: string | Function | array | object` см. [Валидаторы](#Валидаторы)
289
294
  - `unique: boolean | string` проверять значение на уникальность
290
295
 
291
296
  **Параметр `unique`**
@@ -341,7 +346,7 @@ dbs.defineModel({
341
346
  },
342
347
  code: {
343
348
  type: DataType.NUMBER, // тип свойства "number" (обязательно)
344
- unique: PropertyUniqueness.UNIQUE, // проверять уникальность
349
+ unique: PropertyUniqueness.STRICT, // проверять уникальность
345
350
  },
346
351
  },
347
352
  });
@@ -369,19 +374,64 @@ dbs.defineModel({
369
374
 
370
375
  ## Валидаторы
371
376
 
372
- Кроме проверки типа, дополнительные условия можно задать с помощью
373
- валидаторов, через которые будет проходить значение свойства перед
374
- записью в базу. Исключением являются [пустые значения](#Пустые-значения),
375
- которые не подлежат проверке.
377
+ Валидаторы используются для проверки значения свойства перед записью в базу.
378
+ Проверка значения валидатором выполняется сразу после проверки типа, указанного
379
+ в определении свойства модели. [Пустые значения](#пустые-значения) пропускают
380
+ проверку валидаторами, так как не имеют полезной нагрузки.
381
+
382
+ ### Глобальные валидаторы
383
+
384
+ Модуль поставляется с набором глобальных валидаторов:
385
+
386
+ - `regexp` проверка по регулярному выражению,
387
+ *параметр: `string | RegExp` - регулярное выражение;*
388
+
389
+ - `maxLength` максимальная длина строки или массива,
390
+ *параметр: `number` - максимальная длина;*
391
+
392
+ - `minLength` минимальная длина строки или массива,
393
+ *параметр: `number` - минимальная длина;*
394
+
395
+ Валидаторы указанные ниже находятся в разработке:
396
+
397
+ - `isLowerCase` проверка регистра (только прописные буквы);
398
+ - `isUpperCase` проверка регистра (только строчные буквы);
399
+ - `isEmail` проверка формата электронного адреса;
400
+
401
+ **Примеры**
402
+
403
+ Использование глобального валидатора.
404
+
405
+ ```js
406
+ dbs.defineModel({
407
+ name: 'user',
408
+ properties: {
409
+ email: {
410
+ type: DataType.STRING,
411
+ validate: 'isEmail',
412
+ },
413
+ },
414
+ });
415
+ ```
376
416
 
377
- - `minLength: number` минимальная длинна строки или массива
378
- - `maxLength: number` максимальная длинна строки или массива
379
- - `regexp: string | RegExp` проверка по регулярному выражению
417
+ Использование глобальных валидаторов в виде массива.
380
418
 
381
- **Пример**
419
+ ```js
420
+ dbs.defineModel({
421
+ name: 'user',
422
+ properties: {
423
+ email: {
424
+ type: DataType.STRING,
425
+ validate: [
426
+ 'isEmail',
427
+ 'isLowerCase',
428
+ ],
429
+ },
430
+ },
431
+ });
432
+ ```
382
433
 
383
- Валидаторы указываются в объявлении свойства модели параметром
384
- `validate`, который принимает объект с их названиями и настройками.
434
+ Использование глобальных валидаторов с передачей аргументов.
385
435
 
386
436
  ```js
387
437
  dbs.defineModel({
@@ -389,46 +439,209 @@ dbs.defineModel({
389
439
  properties: {
390
440
  name: {
391
441
  type: DataType.STRING,
392
- validate: { // валидаторы свойства "name"
393
- minLength: 2, // минимальная длинна строки
394
- maxLength: 24, // максимальная длинна строки
442
+ validate: {
443
+ minLength: 2,
444
+ maxLength: 24,
445
+ regexp: /^[a-zA-Z-']+$/,
446
+ },
447
+ },
448
+ },
449
+ });
450
+ ```
451
+
452
+ Глобальные валидаторы без параметров могут принимать любые аргументы.
453
+
454
+ ```js
455
+ dbs.defineModel({
456
+ name: 'user',
457
+ properties: {
458
+ email: {
459
+ type: DataType.STRING,
460
+ validate: {
461
+ maxLength: 100,
462
+ // так как валидатор "isEmail" не имеет параметров,
463
+ // его определение допускает передачу любого значения
464
+ // в качестве аргумента
465
+ isEmail: true,
395
466
  },
396
467
  },
397
468
  },
398
469
  });
399
470
  ```
400
471
 
401
- ### Пользовательские валидаторы
472
+ ### Регистрация глобальных валидаторов
402
473
 
403
474
  Валидатором является функция, в которую передается значение соответствующего
404
475
  поля перед записью в базу. Если во время проверки функция возвращает `false`,
405
476
  то выбрасывается стандартная ошибка. Подмена стандартной ошибки возможна
406
- с помощью выброса пользовательской ошибки непосредственно внутри функции.
477
+ с помощью выброса пользовательской ошибки непосредственно внутри проверяющей
478
+ функции.
479
+
480
+ Регистрация глобального валидатора выполняется методом `addValidator` сервиса
481
+ `PropertyValidatorRegistry`, который принимает название валидатора и функцию
482
+ для проверки значения.
483
+
484
+ **Примеры**
485
+
486
+ Регистрация глобального валидатора для проверки формата UUID.
407
487
 
408
- Регистрация пользовательского валидатора выполняется методом `addValidator`
409
- сервиса `PropertyValidatorRegistry`, который принимает новое название
410
- и функцию для проверки значения.
488
+ ```js
489
+ import {createError} from 'http-errors';
490
+ import {format} from '@e22m4u/js-format';
491
+ import {Errorf} from '@e22m4u/js-format';
492
+ import {PropertyValidatorRegistry} from '@e22m4u/js-repository';
493
+
494
+ // получение экземпляра сервиса
495
+ const pvr = dbs.get(PropertyValidatorRegistry);
496
+
497
+ // регулярные выражения для разных версий UUID
498
+ const uuidRegex = {
499
+ any: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
500
+ v4: /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
501
+ };
502
+
503
+ // регистрация глобального валидатора "isUuid",
504
+ // принимающего объект настроек со свойством "version"
505
+ pvr.addValidator('isUuid', (value, options, context) => {
506
+ // value - проверяемое значение;
507
+ // options - параметры валидатора;
508
+ // context - информация о проверяемом свойстве;
509
+ console.log(options);
510
+ // {
511
+ // version: 'v4'
512
+ // }
513
+ console.log(context);
514
+ // {
515
+ // validatorName: 'isUuid',
516
+ // modelName: 'device',
517
+ // propName: 'deviceId'
518
+ // }
519
+
520
+ // пустые значения не передаются в валидаторы
521
+ // (условие ниже никогда не сработает)
522
+ if (typeof value !== 'string') return false;
523
+ // поиск регулярного выражения для указанной
524
+ // версии UUID (из параметров валидатора)
525
+ const version = options?.version || 'any';
526
+ const regex = uuidRegex[version];
527
+ // если регулярное выражение не найдено,
528
+ // то выбрасывается внутренняя ошибка
529
+ if (!regex)
530
+ throw new Errorf(
531
+ 'Invalid UUID version %v specified for validator.',
532
+ version,
533
+ );
534
+ // при неудачной проверке выбрасывается
535
+ // ошибка 400 BadRequest
536
+ if (!regex.test(value)) {
537
+ const versionString = version !== 'any' ? ` (version ${version})` : '';
538
+ throw createError(400, format(
539
+ 'The property %v of the model %v must be a valid UUID%s.',
540
+ context.propName,
541
+ context.modelName,
542
+ versionString,
543
+ ));
544
+ }
545
+ // при успешной проверке возвращается true,
546
+ // в противном случае выбрасывается стандартная
547
+ // ошибка проверки
548
+ return true;
549
+ });
550
+ ```
411
551
 
412
- **Пример**
552
+ Использование глобального валидатора в определении свойства.
413
553
 
414
554
  ```js
415
- // создание валидатора для запрета
416
- // всех символов кроме чисел
417
- const numericValidator = (input) => {
418
- return /^[0-9]+$/.test(String(input));
555
+ // определение модели "device"
556
+ dbs.defineModel({
557
+ name: 'device',
558
+ properties: {
559
+ deviceId: {
560
+ type: DataType.STRING,
561
+ required: true,
562
+ validate: {
563
+ // значение {version: 'v4'} будет передаваться
564
+ // вторым аргументом в функцию-валидатор
565
+ isUuid: {version: 'v4'},
566
+ },
567
+ },
568
+ },
569
+ });
570
+ ```
571
+
572
+ ### Локальные валидаторы
573
+
574
+ Функция-валидатор может быть передана непосредственно в определении свойства
575
+ без предварительной регистрации. Для этого достаточно передать функцию
576
+ в параметр `validate` в качестве значения или элемента массива наряду
577
+ с другими валидаторами.
578
+
579
+ **Примеры**
580
+
581
+ Использование локального валидатора для проверки сложности пароля.
582
+
583
+ ```js
584
+ // валидатор `passwordStrength` проверяет сложность пароля
585
+ function passwordStrength(value, options, context) {
586
+ // value - проверяемое значение;
587
+ // options - не используется;
588
+ // context - информация о свойстве;
589
+ console.log(context);
590
+ // {
591
+ // validatorName: 'passwordStrength',
592
+ // modelName: 'user',
593
+ // propName: 'password'
594
+ // }
595
+ const errors = [];
596
+ if (value.length < 8)
597
+ errors.push('must be at least 8 characters long');
598
+ if (!/\d/.test(value))
599
+ errors.push('must contain at least one number');
600
+ if (!/[a-zA-Z]/.test(value))
601
+ errors.push('must contain at least one letter');
602
+ // если одно из условий сработало,
603
+ // то выбрасывается ошибка
604
+ if (errors.length > 0)
605
+ throw createError(400, format(
606
+ 'Value of the property %v of the model %v %s.',
607
+ context.propName,
608
+ context.modelName,
609
+ errors.join(', '),
610
+ ));
611
+ // при успешной проверке возвращается true,
612
+ // в противном случае выбрасывается стандартная
613
+ // ошибка проверки
614
+ return true;
419
615
  }
420
616
 
421
- // регистрация валидатора "numeric"
422
- dbs.get(PropertyValidatorRegistry).addValidator('numeric', numericValidator);
617
+ // определение модели "user"
618
+ dbs.defineModel({
619
+ name: 'user',
620
+ properties: {
621
+ password: {
622
+ type: DataType.STRING,
623
+ required: true,
624
+ validate: passwordStrength, // <=
625
+ // или
626
+ // validate: [passwordStrength, ...]
627
+ },
628
+ },
629
+ });
630
+ ```
631
+
632
+ Использование анонимной функции-валидатора для проверки слага.
423
633
 
424
- // использование валидатора в определении
425
- // свойства "code" для новой модели
634
+ ```js
635
+ // определение модели "article"
426
636
  dbs.defineModel({
427
- name: 'document',
637
+ name: 'article',
428
638
  properties: {
429
- code: {
639
+ slug: {
430
640
  type: DataType.STRING,
431
- validate: 'numeric',
641
+ validate: (value) => {
642
+ const re = /^[a-z0-9]+(-[a-z0-9]+)*$/;
643
+ return re.test(value);
644
+ },
432
645
  },
433
646
  },
434
647
  });
@@ -436,39 +649,245 @@ dbs.defineModel({
436
649
 
437
650
  ## Трансформеры
438
651
 
439
- С помощью трансформеров производится модификация значений определенных
440
- полей перед записью в базу. Трансформеры позволяют указать какие изменения
441
- нужно производить с входящими данными. Исключением являются
442
- [пустые значения](#Пустые-значения), которые не подлежат трансформации.
652
+ Трансформеры используются для модификации значения свойства перед проверкой
653
+ типа и передачей данных в базу. Трансформеры позволяют автоматически очищать
654
+ или приводить данные к нужному формату. [Пустые значения](#Пустые-значения)
655
+ не передаются в трансформеры, так как не имеют полезной нагрузки.
656
+
657
+ ### Глобальные трансформеры
658
+
659
+ Модуль поставляется с набором глобальных трансформеров:
660
+
661
+ - `trim` удаление пробельных символов с начала и конца строки;
662
+ - `toUpperCase` перевод строки в верхний регистр;
663
+ - `toLowerCase` перевод строки в нижний регистр;
664
+
665
+ Трансформеры указанные ниже находятся в разработке:
443
666
 
444
- - `trim` удаление пробельных символов с начала и конца строки
445
- - `toUpperCase` перевод строки в верхний регистр
446
- - `toLowerCase` перевод строки в нижний регистр
447
- - `toTitleCase` перевод строки в регистр заголовка
667
+ - `cut` усечение строки или массива до указанной длины,
668
+ *параметр: `number` - максимальная длина;*
448
669
 
449
- **Пример**
670
+ - `truncate` усечение строки с добавлением троеточия,
671
+ *параметр: `number` - максимальная длина;*
450
672
 
451
- Трансформеры указываются в объявлении свойства модели параметром
452
- `transform`, который принимает название трансформера. Если требуется
453
- указать несколько названий, то используется массив. Если трансформер
454
- имеет настройки, то используется объект, где ключом является название
455
- трансформера, а значением его параметры.
673
+ - `capitalize` перевод первой буквы каждого слова в верхний регистр,
674
+ *параметр: `{firstWordOnly?: boolean}`;*
675
+
676
+ **Примеры**
677
+
678
+ Использование глобального трансформера.
456
679
 
457
680
  ```js
458
681
  dbs.defineModel({
459
682
  name: 'user',
460
683
  properties: {
461
- name: {
684
+ username: {
462
685
  type: DataType.STRING,
463
- transform: [ // трансформеры свойства "name"
464
- 'trim', // удалить пробелы в начале и конце строки
465
- 'toTitleCase', // перевод строки в регистр заголовка
686
+ transform: 'toLowerCase',
687
+ },
688
+ },
689
+ });
690
+ ```
691
+
692
+ Использование глобальных трансформеров в виде массива.
693
+
694
+ ```js
695
+ dbs.defineModel({
696
+ name: 'user',
697
+ properties: {
698
+ firstName: {
699
+ type: DataType.STRING,
700
+ transform: [
701
+ 'trim',
702
+ 'capitalize',
466
703
  ],
467
704
  },
468
705
  },
469
706
  });
470
707
  ```
471
708
 
709
+ Использование глобальных трансформеров с передачей аргументов.
710
+
711
+ ```js
712
+ dbs.defineModel({
713
+ name: 'article',
714
+ properties: {
715
+ annotation: {
716
+ type: DataType.STRING,
717
+ transform: {
718
+ truncate: 200,
719
+ capitalize: {firstWordOnly: true},
720
+ },
721
+ },
722
+ },
723
+ });
724
+ ```
725
+
726
+ Глобальные трансформеры без параметров могут принимать любые аргументы.
727
+
728
+ ```js
729
+ dbs.defineModel({
730
+ name: 'user',
731
+ properties: {
732
+ firstName: {
733
+ type: DataType.STRING,
734
+ transform: {
735
+ cut: 60,
736
+ // так как трансформер "trim" не имеет параметров,
737
+ // его определение допускает передачу любого значения
738
+ // в качестве аргумента
739
+ trim: true,
740
+ },
741
+ },
742
+ },
743
+ });
744
+ ```
745
+
746
+ ### Регистрация глобальных трансформеров
747
+
748
+ Трансформером является функция, которая принимает значение свойства и возвращает
749
+ новое значение. Функция может быть как синхронной, так и асинхронной (возвращать
750
+ `Promise`).
751
+
752
+ Регистрация глобального трансформера выполняется методом `addTransformer`
753
+ сервиса `PropertyTransformerRegistry`, который принимает название трансформера
754
+ и саму функцию.
755
+
756
+ **Примеры**
757
+
758
+ Регистрация глобального трансформера для удаления HTML-тегов.
759
+
760
+ ```js
761
+ import {PropertyTransformerRegistry} from '@e22m4u/js-repository';
762
+
763
+ // получение экземпляра сервиса
764
+ const ptr = dbs.get(PropertyTransformerRegistry);
765
+
766
+ // регистрация глобального трансформера "stripTags"
767
+ ptr.addTransformer('stripTags', (value, options, context) => {
768
+ // value - трансформируемое значение;
769
+ // options - настройки трансформера (если переданы);
770
+ // context - информация о свойстве;
771
+ console.log(context);
772
+ // {
773
+ // transformerName: 'stripTags',
774
+ // modelName: 'comment',
775
+ // propName: 'text'
776
+ // }
777
+
778
+ if (typeof value !== 'string')
779
+ return value; // возвращаем как есть, если не строка
780
+
781
+ return value.replace(/<[^>]*>?/gm, '');
782
+ });
783
+ ```
784
+
785
+ Использование глобального трансформера в определении модели.
786
+
787
+ ```js
788
+ dbs.defineModel({
789
+ name: 'comment',
790
+ properties: {
791
+ text: {
792
+ type: DataType.STRING,
793
+ transform: 'stripTags',
794
+ },
795
+ },
796
+ });
797
+ ```
798
+
799
+ ### Локальные трансформеры
800
+
801
+ Функция-трансформер может быть передана непосредственно в определении свойства без предварительной регистрации. Для этого достаточно передать функцию в параметр `transform` в качестве значения или элемента массива.
802
+
803
+ **Примеры**
804
+
805
+ Использование локального трансформера для нормализации имен.
806
+
807
+ ```js
808
+ // функция для нормализации имени
809
+ function normalizeName(value, options, context) {
810
+ // value - трансформируемое значение
811
+ // options - не используется
812
+ // context - информация о свойстве
813
+ if (!value || typeof value !== 'string') return value;
814
+ return value
815
+ .trim() // удаление пробелов в начале и конце
816
+ .toLowerCase() // перевод к нижнему регистру
817
+ .split(' ') // разделение на слова
818
+ // перевод к верхнему регистру первой буквы каждого слова
819
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
820
+ .join(' '); // сборка массива в строку
821
+ }
822
+
823
+ // определение модели "user"
824
+ dbs.defineModel({
825
+ name: 'user',
826
+ properties: {
827
+ firstName: {
828
+ type: DataType.STRING,
829
+ transform: normalizeName, // <=
830
+ },
831
+ lastName: {
832
+ type: DataType.STRING,
833
+ transform: normalizeName, // <=
834
+ },
835
+ },
836
+ });
837
+ ```
838
+
839
+ Использование локального асинхронного трансформера для хэширования пароля.
840
+
841
+ ```js
842
+ import * as bcrypt from 'bcrypt';
843
+
844
+ // асинхронная функция для хеширования значения
845
+ async function hash(value, options, context) {
846
+ // value - трансформируемое значение
847
+ // options - не используется
848
+ // context - информация о свойстве
849
+ console.log(context);
850
+ // {
851
+ // transformerName: 'hash',
852
+ // modelName: 'user',
853
+ // propName: 'password'
854
+ // }
855
+ const saltRounds = 10;
856
+ return bcrypt.hash(value, saltRounds);
857
+ }
858
+
859
+ // определение модели "user"
860
+ dbs.defineModel({
861
+ name: 'user',
862
+ properties: {
863
+ password: {
864
+ type: DataType.STRING,
865
+ transform: hash, // <=
866
+ // или
867
+ // transform: [hash, ...]
868
+ },
869
+ },
870
+ });
871
+ ```
872
+
873
+ Использование анонимной функции-трансформера для коррекции слага.
874
+
875
+ ```js
876
+ dbs.defineModel({
877
+ name: 'article',
878
+ properties: {
879
+ slug: {
880
+ type: DataType.STRING,
881
+ transform: (value) => {
882
+ if (typeof value !== 'string') return value;
883
+ return value.toLowerCase().replace(/\s+/g, '-');
884
+ },
885
+ },
886
+ },
887
+ });
888
+ ```
889
+
890
+
472
891
  ## Пустые значения
473
892
 
474
893
  Разные типы свойств имеют свои наборы пустых значений. Эти наборы
@@ -718,31 +1137,101 @@ const res = await rep.find({
718
1137
 
719
1138
  ## Связи
720
1139
 
721
- Параметр `relations` находится в определении модели и принимает
722
- объект, ключ которого является названием связи, а значением объект
723
- с параметрами.
1140
+ Связи позволяют описывать отношения между моделями, что дает возможность
1141
+ автоматически встраивать связанные данные с помощью опции `include`
1142
+ в методах репозитория. Ниже приводится пример автоматического разрешения
1143
+ связи при использовании метода `findById`.
724
1144
 
725
- **Параметры**
1145
+ ```js
1146
+ // запрос документа коллекции "users",
1147
+ // включая связанные данные (role и city)
1148
+ const user = await userRep.findById(1, {
1149
+ include: ['role', 'city'],
1150
+ });
1151
+
1152
+ console.log(user);
1153
+ // {
1154
+ // id: 1,
1155
+ // name: 'John Doe',
1156
+ // roleId: 3,
1157
+ // role: {
1158
+ // id: 3,
1159
+ // name: 'Manager'
1160
+ // },
1161
+ // cityId: 24,
1162
+ // city: {
1163
+ // id: 24,
1164
+ // name: 'Moscow'
1165
+ // }
1166
+ // }
1167
+ ```
726
1168
 
727
- - `type: string` тип связи
728
- - `model: string` название целевой модели
729
- - `foreignKey: string` свойство текущей модели для идентификатора цели
730
- - `polymorphic: boolean|string` объявить связь полиморфной*
731
- - `discriminator: string` свойство текущей модели для названия целевой*
1169
+ ### Определение связи
1170
+
1171
+ Свойство `relations` в определении модели принимает объект, ключи которого
1172
+ являются названиями связей, а значения их параметрами. В дальнейшем название
1173
+ связи можно будет использовать в опции `include` методах репозитория.
1174
+
1175
+ ```js
1176
+ import {DataType} from '@e22m4u/js-repository';
1177
+ import {RelationType} from '@e22m4u/js-repository';
1178
+
1179
+ dbs.defineModel({
1180
+ name: 'user',
1181
+ datasource: 'memory',
1182
+ properties: {
1183
+ name: DataType.STRING,
1184
+ },
1185
+ relations: {
1186
+ // связь role -> параметры
1187
+ role: {
1188
+ type: RelationType.BELONGS_TO,
1189
+ model: 'role',
1190
+ },
1191
+ // связь city -> параметры
1192
+ city: {
1193
+ type: RelationType.BELONGS_TO,
1194
+ model: 'city',
1195
+ },
1196
+ },
1197
+ });
1198
+ ```
1199
+
1200
+ **Основные параметры**
1201
+
1202
+ - `type: string` тип связи (обязательно);
1203
+ - `model: string` название целевой модели (обязательно для некоторых типов);
1204
+ - `foreignKey: string` свойство текущей модели для идентификатора цели;
1205
+
1206
+ *i. Для типов Belongs To и References Many значение параметра `foreignKey`
1207
+ можно опустить, так как генерируется автоматически по названию связи.*
1208
+
1209
+ **Полиморфный режим**
1210
+
1211
+ - `polymorphic: boolean|string` объявление полиморфной связи;
1212
+ - `discriminator: string` свойство текущей модели для названия цели;
732
1213
 
733
1214
  *i. Полиморфный режим позволяет динамически определять целевую модель
734
1215
  по ее названию, которое хранит документ в свойстве-дискриминаторе.*
735
1216
 
736
- **Тип связи**
1217
+ **Типы связи**
737
1218
 
738
- - `belongsTo` - текущая модель содержит свойство для идентификатора цели
739
- - `hasOne` - обратная сторона `belongsTo` по принципу "один к одному"
740
- - `hasMany` - обратная сторона `belongsTo` по принципу "один ко многим"
741
- - `referencesMany` - документ содержит массив с идентификаторами целевой модели
1219
+ - `belongsTo` - текущая модель ссылается на целевую по идентификатору;
1220
+ - `hasOne` - обратная сторона `belongsTo` по принципу "один к одному";
1221
+ - `hasMany` - обратная сторона `belongsTo` по принципу "один ко многим";
1222
+ - `referencesMany` - модель ссылается через массив идентификаторов;
1223
+
1224
+ Параметр `type` в определении связи принимает строку с названием типа. Чтобы исключить опечатку, рекомендуется использовать константы объекта `RelationType`
1225
+ указанные ниже.
1226
+
1227
+ - `RelationType.BELONGS_TO`
1228
+ - `RelationType.HAS_ONE`
1229
+ - `RelationType.HAS_MANY`
1230
+ - `RelationType.REFERENCES_MANY`
742
1231
 
743
1232
  **Примеры**
744
1233
 
745
- Объявление связи `belongsTo`
1234
+ Объявление связи `belongsTo`.
746
1235
 
747
1236
  ```js
748
1237
  dbs.defineModel({
@@ -760,7 +1249,7 @@ dbs.defineModel({
760
1249
  });
761
1250
  ```
762
1251
 
763
- Объявление связи `hasMany`
1252
+ Объявление связи `hasMany`.
764
1253
 
765
1254
  ```js
766
1255
  dbs.defineModel({
@@ -775,7 +1264,7 @@ dbs.defineModel({
775
1264
  });
776
1265
  ```
777
1266
 
778
- Объявление связи `referencesMany`
1267
+ Объявление связи `referencesMany`.
779
1268
 
780
1269
  ```js
781
1270
  dbs.defineModel({
@@ -793,7 +1282,7 @@ dbs.defineModel({
793
1282
  });
794
1283
  ```
795
1284
 
796
- Полиморфная версия `belongsTo`
1285
+ Полиморфная версия `belongsTo`.
797
1286
 
798
1287
  ```js
799
1288
  dbs.defineModel({
@@ -924,7 +1413,7 @@ dbs.defineModel({
924
1413
  name: 'city',
925
1414
  datasource: 'myDatasource',
926
1415
  properties: {
927
- title: DataType.STRING,
1416
+ name: DataType.STRING,
928
1417
  timeZone: DataType.STRING,
929
1418
  },
930
1419
  relations: {
@@ -938,7 +1427,7 @@ dbs.defineModel({
938
1427
  // определение интерфейса "city"
939
1428
  interface City {
940
1429
  id: number;
941
- title?: string;
1430
+ name?: string;
942
1431
  timeZone?: string;
943
1432
  countryId?: number;
944
1433
  country?: Country;