@e22m4u/js-repository 0.4.0 → 0.5.0

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 (86) hide show
  1. package/README.md +479 -61
  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-transformer/builtin/index.d.ts +0 -1
  27. package/src/definition/model/properties/property-transformer/builtin/index.js +0 -1
  28. package/src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.d.ts +1 -1
  29. package/src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.js +1 -1
  30. package/src/definition/model/properties/property-transformer/builtin/to-lower-case-transformer.spec.js +1 -1
  31. package/src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.d.ts +1 -1
  32. package/src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.js +1 -1
  33. package/src/definition/model/properties/property-transformer/builtin/to-upper-case-transformer.spec.js +1 -1
  34. package/src/definition/model/properties/property-transformer/builtin/trim-transformer.js +1 -1
  35. package/src/definition/model/properties/property-transformer/builtin/trim-transformer.spec.js +1 -1
  36. package/src/definition/model/properties/property-transformer/property-transformer-registry.js +2 -4
  37. package/src/definition/model/properties/property-transformer/property-transformer-registry.spec.js +2 -4
  38. package/src/definition/model/properties/property-transformer/property-transformer.d.ts +2 -2
  39. package/src/definition/model/properties/property-uniqueness-validator.js +4 -4
  40. package/src/definition/model/properties/property-uniqueness-validator.spec.js +4 -4
  41. package/src/definition/model/properties/property-validator/builtin/max-length-validator.d.ts +1 -1
  42. package/src/definition/model/properties/property-validator/builtin/max-length-validator.js +2 -2
  43. package/src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js +2 -2
  44. package/src/definition/model/properties/property-validator/builtin/min-length-validator.d.ts +1 -1
  45. package/src/definition/model/properties/property-validator/builtin/min-length-validator.js +2 -2
  46. package/src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js +2 -2
  47. package/src/definition/model/properties/property-validator/builtin/regexp-validator.d.ts +1 -1
  48. package/src/definition/model/properties/property-validator/builtin/regexp-validator.js +2 -2
  49. package/src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js +2 -2
  50. package/src/definition/model/properties/property-validator/property-validator-registry.js +2 -2
  51. package/src/definition/model/properties/property-validator/property-validator-registry.spec.js +2 -2
  52. package/src/definition/model/properties/property-validator/property-validator.d.ts +2 -2
  53. package/src/definition/model/relations/relations-definition-validator.js +23 -23
  54. package/src/definition/model/relations/relations-definition-validator.spec.js +24 -24
  55. package/src/errors/invalid-operator-value-error.js +1 -1
  56. package/src/errors/invalid-operator-value-error.spec.js +1 -1
  57. package/src/filter/fields-clause-tool.js +5 -5
  58. package/src/filter/fields-clause-tool.spec.js +16 -16
  59. package/src/filter/include-clause-tool.js +6 -6
  60. package/src/filter/include-clause-tool.spec.js +2 -2
  61. package/src/filter/operator-clause-tool.js +13 -13
  62. package/src/filter/operator-clause-tool.spec.js +13 -13
  63. package/src/filter/order-clause-tool.js +3 -3
  64. package/src/filter/order-clause-tool.spec.js +4 -4
  65. package/src/filter/slice-clause-tool.js +5 -5
  66. package/src/filter/slice-clause-tool.spec.js +5 -5
  67. package/src/filter/where-clause-tool.js +4 -4
  68. package/src/filter/where-clause-tool.spec.js +3 -3
  69. package/src/relations/belongs-to-resolver.js +14 -14
  70. package/src/relations/belongs-to-resolver.spec.js +14 -14
  71. package/src/relations/has-many-resolver.js +22 -22
  72. package/src/relations/has-many-resolver.spec.js +23 -23
  73. package/src/relations/has-one-resolver.js +22 -22
  74. package/src/relations/has-one-resolver.spec.js +23 -23
  75. package/src/relations/references-many-resolver.js +7 -7
  76. package/src/relations/references-many-resolver.spec.js +7 -7
  77. package/src/repository/repository-registry.js +1 -1
  78. package/src/utils/exclude-object-keys.js +1 -1
  79. package/src/utils/exclude-object-keys.spec.js +1 -1
  80. package/src/utils/model-name-to-model-key.js +1 -1
  81. package/src/utils/model-name-to-model-key.spec.js +7 -7
  82. package/src/utils/select-object-keys.js +3 -3
  83. package/src/utils/select-object-keys.spec.js +3 -3
  84. package/src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.d.ts +0 -6
  85. package/src/definition/model/properties/property-transformer/builtin/to-title-case-transformer.js +0 -22
  86. 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
  - [Фильтрация](#фильтрация)
@@ -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
+ функции.
407
479
 
408
- Регистрация пользовательского валидатора выполняется методом `addValidator`
409
- сервиса `PropertyValidatorRegistry`, который принимает новое название
410
- и функцию для проверки значения.
480
+ Регистрация глобального валидатора выполняется методом `addValidator` сервиса
481
+ `PropertyValidatorRegistry`, который принимает название валидатора и функцию
482
+ для проверки значения.
411
483
 
412
- **Пример**
484
+ **Примеры**
485
+
486
+ Регистрация глобального валидатора для проверки формата UUID.
413
487
 
414
488
  ```js
415
- // создание валидатора для запрета
416
- // всех символов кроме чисел
417
- const numericValidator = (input) => {
418
- return /^[0-9]+$/.test(String(input));
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
+ ```
551
+
552
+ Использование глобального валидатора в определении свойства.
553
+
554
+ ```js
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
+ ```
423
631
 
424
- // использование валидатора в определении
425
- // свойства "code" для новой модели
632
+ Использование анонимной функции-валидатора для проверки слага.
633
+
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,244 @@ dbs.defineModel({
436
649
 
437
650
  ## Трансформеры
438
651
 
439
- С помощью трансформеров производится модификация значений определенных
440
- полей перед записью в базу. Трансформеры позволяют указать какие изменения
441
- нужно производить с входящими данными. Исключением являются
442
- [пустые значения](#Пустые-значения), которые не подлежат трансформации.
652
+ Трансформеры используются для модификации значения свойства перед проверкой
653
+ типа и передачей данных в базу. Трансформеры позволяют автоматически очищать
654
+ или приводить данные к нужному формату.
655
+
656
+ ### Глобальные трансформеры
657
+
658
+ Модуль поставляется с набором глобальных трансформеров:
659
+
660
+ - `trim` удаление пробельных символов с начала и конца строки;
661
+ - `toUpperCase` перевод строки в верхний регистр;
662
+ - `toLowerCase` перевод строки в нижний регистр;
663
+
664
+ Трансформеры указанные ниже находятся в разработке:
443
665
 
444
- - `trim` удаление пробельных символов с начала и конца строки
445
- - `toUpperCase` перевод строки в верхний регистр
446
- - `toLowerCase` перевод строки в нижний регистр
447
- - `toTitleCase` перевод строки в регистр заголовка
666
+ - `cut` усечение строки или массива до указанной длины,
667
+ *параметр: `number` - максимальная длина;*
448
668
 
449
- **Пример**
669
+ - `truncate` усечение строки с добавлением троеточия,
670
+ *параметр: `number` - максимальная длина;*
450
671
 
451
- Трансформеры указываются в объявлении свойства модели параметром
452
- `transform`, который принимает название трансформера. Если требуется
453
- указать несколько названий, то используется массив. Если трансформер
454
- имеет настройки, то используется объект, где ключом является название
455
- трансформера, а значением его параметры.
672
+ - `capitalize` перевод первой буквы каждого слова в верхний регистр,
673
+ *параметр: `{firstWordOnly?: boolean}`;*
674
+
675
+ **Примеры**
676
+
677
+ Использование глобального трансформера.
456
678
 
457
679
  ```js
458
680
  dbs.defineModel({
459
681
  name: 'user',
460
682
  properties: {
461
- name: {
683
+ username: {
462
684
  type: DataType.STRING,
463
- transform: [ // трансформеры свойства "name"
464
- 'trim', // удалить пробелы в начале и конце строки
465
- 'toTitleCase', // перевод строки в регистр заголовка
685
+ transform: 'toLowerCase',
686
+ },
687
+ },
688
+ });
689
+ ```
690
+
691
+ Использование глобальных трансформеров в виде массива.
692
+
693
+ ```js
694
+ dbs.defineModel({
695
+ name: 'user',
696
+ properties: {
697
+ firstName: {
698
+ type: DataType.STRING,
699
+ transform: [
700
+ 'trim',
701
+ 'capitalize',
466
702
  ],
467
703
  },
468
704
  },
469
705
  });
470
706
  ```
471
707
 
708
+ Использование глобальных трансформеров с передачей аргументов.
709
+
710
+ ```js
711
+ dbs.defineModel({
712
+ name: 'article',
713
+ properties: {
714
+ annotation: {
715
+ type: DataType.STRING,
716
+ transform: {
717
+ truncate: 200,
718
+ capitalize: {firstWordOnly: true},
719
+ },
720
+ },
721
+ },
722
+ });
723
+ ```
724
+
725
+ Глобальные трансформеры без параметров могут принимать любые аргументы.
726
+
727
+ ```js
728
+ dbs.defineModel({
729
+ name: 'user',
730
+ properties: {
731
+ firstName: {
732
+ type: DataType.STRING,
733
+ transform: {
734
+ cut: 60,
735
+ // так как трансформер "trim" не имеет параметров,
736
+ // его определение допускает передачу любого значения
737
+ // в качестве аргумента
738
+ trim: true,
739
+ },
740
+ },
741
+ },
742
+ });
743
+ ```
744
+
745
+ ### Регистрация глобальных трансформеров
746
+
747
+ Трансформером является функция, которая принимает значение свойства и возвращает
748
+ новое значение. Функция может быть как синхронной, так и асинхронной (возвращать
749
+ `Promise`).
750
+
751
+ Регистрация глобального трансформера выполняется методом `addTransformer`
752
+ сервиса `PropertyTransformerRegistry`, который принимает название трансформера
753
+ и саму функцию.
754
+
755
+ **Примеры**
756
+
757
+ Регистрация глобального трансформера для удаления HTML-тегов.
758
+
759
+ ```js
760
+ import {PropertyTransformerRegistry} from '@e22m4u/js-repository';
761
+
762
+ // получение экземпляра сервиса
763
+ const ptr = dbs.get(PropertyTransformerRegistry);
764
+
765
+ // регистрация глобального трансформера "stripTags"
766
+ ptr.addTransformer('stripTags', (value, options, context) => {
767
+ // value - трансформируемое значение;
768
+ // options - настройки трансформера (если переданы);
769
+ // context - информация о свойстве;
770
+ console.log(context);
771
+ // {
772
+ // transformerName: 'stripTags',
773
+ // modelName: 'comment',
774
+ // propName: 'text'
775
+ // }
776
+
777
+ if (typeof value !== 'string')
778
+ return value; // возвращаем как есть, если не строка
779
+
780
+ return value.replace(/<[^>]*>?/gm, '');
781
+ });
782
+ ```
783
+
784
+ Использование глобального трансформера в определении модели.
785
+
786
+ ```js
787
+ dbs.defineModel({
788
+ name: 'comment',
789
+ properties: {
790
+ text: {
791
+ type: DataType.STRING,
792
+ transform: 'stripTags',
793
+ },
794
+ },
795
+ });
796
+ ```
797
+
798
+ ### Локальные трансформеры
799
+
800
+ Функция-трансформер может быть передана непосредственно в определении свойства без предварительной регистрации. Для этого достаточно передать функцию в параметр `transform` в качестве значения или элемента массива.
801
+
802
+ **Примеры**
803
+
804
+ Использование локального трансформера для нормализации имен.
805
+
806
+ ```js
807
+ // функция для нормализации имени
808
+ function normalizeName(value, options, context) {
809
+ // value - трансформируемое значение
810
+ // options - не используется
811
+ // context - информация о свойстве
812
+ if (!value || typeof value !== 'string') return value;
813
+ return value
814
+ .trim() // удаление пробелов в начале и конце
815
+ .toLowerCase() // перевод к нижнему регистру
816
+ .split(' ') // разделение на слова
817
+ // перевод к верхнему регистру первой буквы каждого слова
818
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
819
+ .join(' '); // сборка массива в строку
820
+ }
821
+
822
+ // определение модели "user"
823
+ dbs.defineModel({
824
+ name: 'user',
825
+ properties: {
826
+ firstName: {
827
+ type: DataType.STRING,
828
+ transform: normalizeName, // <=
829
+ },
830
+ lastName: {
831
+ type: DataType.STRING,
832
+ transform: normalizeName, // <=
833
+ },
834
+ },
835
+ });
836
+ ```
837
+
838
+ Использование локального асинхронного трансформера для хэширования пароля.
839
+
840
+ ```js
841
+ import * as bcrypt from 'bcrypt';
842
+
843
+ // асинхронная функция для хеширования значения
844
+ async function hash(value, options, context) {
845
+ // value - трансформируемое значение
846
+ // options - не используется
847
+ // context - информация о свойстве
848
+ console.log(context);
849
+ // {
850
+ // transformerName: 'hash',
851
+ // modelName: 'user',
852
+ // propName: 'password'
853
+ // }
854
+ const saltRounds = 10;
855
+ return bcrypt.hash(value, saltRounds);
856
+ }
857
+
858
+ // определение модели "user"
859
+ dbs.defineModel({
860
+ name: 'user',
861
+ properties: {
862
+ password: {
863
+ type: DataType.STRING,
864
+ transform: hash, // <=
865
+ // или
866
+ // transform: [hash, ...]
867
+ },
868
+ },
869
+ });
870
+ ```
871
+
872
+ Использование анонимной функции-трансформера для коррекции слага.
873
+
874
+ ```js
875
+ dbs.defineModel({
876
+ name: 'article',
877
+ properties: {
878
+ slug: {
879
+ type: DataType.STRING,
880
+ transform: (value) => {
881
+ if (typeof value !== 'string') return value;
882
+ return value.toLowerCase().replace(/\s+/g, '-');
883
+ },
884
+ },
885
+ },
886
+ });
887
+ ```
888
+
889
+
472
890
  ## Пустые значения
473
891
 
474
892
  Разные типы свойств имеют свои наборы пустых значений. Эти наборы
@@ -924,7 +1342,7 @@ dbs.defineModel({
924
1342
  name: 'city',
925
1343
  datasource: 'myDatasource',
926
1344
  properties: {
927
- title: DataType.STRING,
1345
+ name: DataType.STRING,
928
1346
  timeZone: DataType.STRING,
929
1347
  },
930
1348
  relations: {
@@ -938,7 +1356,7 @@ dbs.defineModel({
938
1356
  // определение интерфейса "city"
939
1357
  interface City {
940
1358
  id: number;
941
- title?: string;
1359
+ name?: string;
942
1360
  timeZone?: string;
943
1361
  countryId?: number;
944
1362
  country?: Country;