Просмотр исходного кода

refactor: repalces empty values with blank values

e22m4u 2 недель назад
Родитель
Сommit
28fc449bb6

+ 101 - 133
README.md

@@ -15,8 +15,7 @@
 - [Источник данных](#источник-данных)
 - [Источник данных](#источник-данных)
 - [Модель](#модель)
 - [Модель](#модель)
 - [Свойства](#свойства)
 - [Свойства](#свойства)
-- [Пустые значения](#пустые-значения)
-  - [Переопределение пустых значений](#переопределение-пустых-значений)
+- [Пустые и незаполненные значения](#пустые-и-незаполненные-значения)
 - [Репозиторий](#репозиторий)
 - [Репозиторий](#репозиторий)
   - [create](#repositorycreate)
   - [create](#repositorycreate)
   - [replaceById](#repositoryreplacebyid)
   - [replaceById](#repositoryreplacebyid)
@@ -88,9 +87,9 @@ const {DatabaseSchema} = require('@e22m4u/js-repository');
 согласно определению модели, и встраивать связанные данные в результат
 согласно определению модели, и встраивать связанные данные в результат
 выборки.
 выборки.
 
 
-- *Источник данных* - определяет способ подключения к базе
-- *Модель* - описывает структуру документа и связи к другим моделям
-- *Репозиторий* - выполняет операции чтения и записи документов модели
+- *Источник данных* - определяет способ подключения к базе;
+- *Модель* - описывает структуру документа и связи к другим моделям;
+- *Репозиторий* - выполняет операции чтения и записи документов модели;
 
 
 ```mermaid
 ```mermaid
 flowchart TD
 flowchart TD
@@ -261,9 +260,9 @@ console.log(cityWithCountry);
 
 
 **Методы**
 **Методы**
 
 
-- `defineDatasource(datasourceDef: object): this` - добавить источник
-- `defineModel(modelDef: object): this` - добавить модель
-- `getRepository(modelName: string): Repository` - получить репозиторий
+- `defineDatasource(datasourceDef: object): this` - добавить источник;
+- `defineModel(modelDef: object): this` - добавить модель;
+- `getRepository(modelName: string): Repository` - получить репозиторий;
 
 
 **Примеры**
 **Примеры**
 
 
@@ -311,9 +310,9 @@ const productRep = dbs.getRepository('product');
 
 
 **Параметры**
 **Параметры**
 
 
-- `name: string` уникальное название
-- `adapter: string` выбранный адаптер
-- параметры адаптера (если имеются)
+- `name: string` уникальное название;
+- `adapter: string` выбранный адаптер;
+- параметры адаптера (если имеются);
 
 
 **Примеры**
 **Примеры**
 
 
@@ -346,12 +345,12 @@ dbs.defineDatasource({
 
 
 **Параметры**
 **Параметры**
 
 
-- `name: string` название модели (обязательно)
-- `base: string` название наследуемой модели
-- `tableName: string` название коллекции в базе
-- `datasource: string` выбранный источник данных
-- `properties: object` определения свойств (см. [Свойства](#Свойства))
-- `relations: object` определения связей (см. [Связи](#Связи))
+- `name: string` название модели (обязательно);
+- `base: string` название наследуемой модели;
+- `tableName: string` название коллекции в базе;
+- `datasource: string` выбранный источник данных;
+- `properties: object` определения свойств (см. [Свойства](#Свойства));
+- `relations: object` определения связей (см. [Связи](#Связи));
 
 
 **Примеры**
 **Примеры**
 
 
@@ -375,47 +374,46 @@ dbs.defineModel({
 
 
 **Тип данных**
 **Тип данных**
 
 
-- `DataType.ANY` разрешено любое значение
-- `DataType.STRING` только значение типа `string`
-- `DataType.NUMBER` только значение типа `number`
-- `DataType.BOOLEAN` только значение типа `boolean`
-- `DataType.ARRAY` только значение типа `array`
-- `DataType.OBJECT` только значение типа `object`
+- `DataType.ANY` разрешено любое значение;
+- `DataType.STRING` только значение типа `string`;
+- `DataType.NUMBER` только значение типа `number`;
+- `DataType.BOOLEAN` только значение типа `boolean`;
+- `DataType.ARRAY` только значение типа `array`;
+- `DataType.OBJECT` только значение типа `object`;
 
 
 **Параметры**
 **Параметры**
 
 
-- `type: string` тип допустимого значения (обязательно)
-- `itemType: string` тип элемента массива (для `type: 'array'`)
-- `model: string` модель объекта (для `type: 'object'`)
-- `primaryKey: boolean` объявить свойство первичным ключом
-- `columnName: string` переопределение названия колонки
-- `columnType: string` тип колонки (определяется адаптером)
-- `required: boolean` объявить свойство обязательным
-- `default: any` значение по умолчанию
-- `unique: boolean | string` проверять значение на уникальность
+- `type: string` тип допустимого значения (обязательно);
+- `itemType: string` тип элемента массива (для `type: 'array'`);
+- `model: string` модель объекта (для `type: 'object'`);
+- `primaryKey: boolean` объявить свойство первичным ключом;
+- `columnName: string` переопределение названия колонки;
+- `columnType: string` тип колонки (определяется адаптером);
+- `required: boolean` объявить свойство обязательным;
+- `default: any` значение по умолчанию;
+- `unique: boolean | string` проверять значение на уникальность;
 
 
 **Параметр `unique`**
 **Параметр `unique`**
 
 
-Если значением параметра `unique` является `true` или `'strict'`, то выполняется
-строгая проверка на уникальность. В этом режиме [пустые значения](#Пустые-значения)
-так же подлежат проверке, где `null` и `undefined` также считаются значениями,
-которые должны быть уникальными.
+Если значением параметра `unique` является `true` или `"strict"`, то выполняется
+строгая проверка на уникальность. В этом режиме любое значение данного свойства
+не может быть представлено более одного раза.
 
 
-Режим `'sparse'` проверяет только значения с полезной нагрузкой, исключая
-[пустые значения](#Пустые-значения), список которых отличается в зависимости
-от типа свойства. Например, для типа `string` пустым значением будет `undefined`,
-`null` и `''` (пустая строка).
+Режим `"sparse"` проверяет только значения с полезной нагрузкой, исключая
+[пустые значения](#пустые-значения), список которых отличается в зависимости
+от типа свойства. Например, для типа `string` пустым значением будет
+`undefined`, `null` и `""` (пустая строка).
 
 
-- `unique: true | 'strict'` строгая проверка на уникальность
-- `unique: 'sparse'` исключить из проверки [пустые значения](#Пустые-значения)
-- `unique: false | 'nonUnique'` не проверять на уникальность (по умолчанию)
+- `unique: true | 'strict'` строгая проверка на уникальность;
+- `unique: 'sparse'` исключить из проверки [пустые значения](#пустые-значения);
+- `unique: false | 'nonUnique'` не проверять на уникальность (по умолчанию);
 
 
 В качестве значений параметра `unique` можно использовать предопределенные
 В качестве значений параметра `unique` можно использовать предопределенные
 константы как эквивалент строковых значений `strict`, `sparse` и `nonUnique`.
 константы как эквивалент строковых значений `strict`, `sparse` и `nonUnique`.
 
 
-- `PropertyUniqueness.STRICT`
-- `PropertyUniqueness.SPARSE`
-- `PropertyUniqueness.NON_UNIQUE`
+- `PropertyUniqueness.STRICT`;
+- `PropertyUniqueness.SPARSE`;
+- `PropertyUniqueness.NON_UNIQUE`;
 
 
 **Примеры**
 **Примеры**
 
 
@@ -473,15 +471,26 @@ dbs.defineModel({
 });
 });
 ```
 ```
 
 
-## Пустые значения
+## Пустые и незаполненные значения
 
 
-Разные типы свойств имеют свои наборы пустых значений. Эти наборы используются
-для определения наличия полезной нагрузки в значении свойства. Например,
-параметр `default` в определении свойства устанавливает значение по умолчанию,
-только если входящее значение является пустым. Параметр `required` исключает
-пустые значения выбрасывая ошибку. А параметр `unique` в режиме `sparse`
-наоборот допускает дублирование пустых значений уникального свойства,
-поскольку они не участвуют в проверке.
+Библиотека использует внешний модуль
+[@e22m4u/js-empty-values](https://www.npmjs.com/package/@e22m4u/js-empty-values)
+централизованного управления категориями значений, не имеющих полезной нагрузки.
+Ниже приводится описание этих категорий и их назначение.
+
+- Пустые значения (*Empty Values*)
+  - управляются сервисом `EmptyValuesService`;
+  - используются параметром `unique` в режиме `"sparse"`;
+
+- Незаполненные значения (*Blank Values*)
+  - управляются сервисом `BlankValuesService`;
+  - используются параметром `required` и `default`;
+
+### Пустые значения
+
+Данная категория используется при проверке уникальности значения в режиме
+`sparse`, когда уникальность значений из этой категории игнорируется. Кроме
+пустой строки, сюда входят структурная пустота и ноль.
 
 
 | тип данных         | пустые значения            |
 | тип данных         | пустые значения            |
 |--------------------|----------------------------|
 |--------------------|----------------------------|
@@ -492,61 +501,26 @@ dbs.defineModel({
 | `DataType.ARRAY`   | `undefined`, `null`, `[]`  |
 | `DataType.ARRAY`   | `undefined`, `null`, `[]`  |
 | `DataType.OBJECT`  | `undefined`, `null`, `{}`  |
 | `DataType.OBJECT`  | `undefined`, `null`, `{}`  |
 
 
-### Переопределение пустых значений
-
-Набор пустых значений для любого типа данных можно переопределить. Управление
-этими наборами осуществляется через специальный сервис, который предоставляет
-модуль
-[@e22m4u/js-empty-values](https://www.npmjs.com/package/@e22m4u/js-empty-values)
-(не требует установки).
-
-**EmptyValuesService**
-
-Для переопределения пустых значений необходимо получить экземпляр класса
-`EmptyValuesService` из контейнера схемы и вызвать метод, который принимает
-тип данных и массив новых значений.
-
-Интерфейс:
-
-```ts
-class EmptyValuesService {
-  /**
-   * Установить пустые значения
-   * для определенного типа данных.
-   * 
-   * @param dataType    Тип данных.
-   * @param emptyValues Массив новых пустых значений.
-   */
-  setEmptyValuesOf(
-    dataType: DataType,
-    emptyValues: unknown[],
-  ): this;
-}
-```
-
-**Пример**
-
-Если свойство с типом `string` является обязательным, то попытка записи пустой
-строки приведет к ошибке. Следующий пример демонстрирует, как изменить данное
-поведение, оставив в качестве пустых значений только `undefined` и `null`.
-
-```js
-import {DataType} from '@e22m4u/js-repository';
-import {DatabaseSchema} from '@e22m4u/js-repository';
-import {EmptyValuesService} from '@e22m4u/js-empty-values';
-
-const dbs = new DatabaseSchema();
+### Незаполненные значения
 
 
-// получение сервиса для работы с пустыми значениями
-const emptyValuesService = dbs.getService(EmptyValuesService);
+Вторая категория имеет более строгий набор критериев для определения отсутствия
+полезной нагрузки. Значения этой категории вызывают ошибку при записи свойств
+с флагом `required`. Также категория применяется при определении необходимости
+использования значения по умолчанию вместо исходного значения свойства.
 
 
-// переопределение пустых значений для типа DataType.STRING
-emptyValuesService.setEmptyValuesOf(DataType.STRING, [undefined, null]);
-```
+| тип данных         | незаполненные значения     |
+|--------------------|----------------------------|
+| `DataType.ANY`     | *значения остальных типов* |
+| `DataType.STRING`  | `undefined`, `null`, `""`  |
+| `DataType.NUMBER`  | `undefined`, `null`        |
+| `DataType.BOOLEAN` | `undefined`, `null`        |
+| `DataType.ARRAY`   | `undefined`, `null`        |
+| `DataType.OBJECT`  | `undefined`, `null`        |
 
 
-Теперь пустая строка будет успешно проходить проверку для обязательных свойств
-с типом `string`, а также не будет заменяться на значение по умолчанию
-для необязательных свойств модели.
+Наборы пустых и незаполненных значений каждого типа можно переопределить
+через соответствующие сервисы модуля
+[@e22m4u/js-empty-values](https://www.npmjs.com/package/@e22m4u/js-empty-values)
+(не требует установки).
 
 
 ## Репозиторий
 ## Репозиторий
 
 
@@ -570,10 +544,10 @@ emptyValuesService.setEmptyValuesOf(DataType.STRING, [undefined, null]);
 
 
 **Аргументы**
 **Аргументы**
 
 
-- `id: number|string` идентификатор (первичный ключ)
-- `data: object` данные документа (используется при записи)
-- `where: object` условия фильтрации (см. [Фильтрация](#Фильтрация))
-- `filter: object` параметры выборки (см. [Фильтрация](#Фильтрация))
+- `id: number|string` идентификатор (первичный ключ);
+- `data: object` данные документа (используется при записи);
+- `where: object` условия фильтрации (см. [Фильтрация](#Фильтрация));
+- `filter: object` параметры выборки (см. [Фильтрация](#Фильтрация));
 
 
 **Получение репозитория**
 **Получение репозитория**
 
 
@@ -1083,26 +1057,26 @@ const news = await newsRepository.find({
 операторов сравнения.
 операторов сравнения.
 
 
 - [Поиск по значению](#поиск-по-значению-сокращенная-форма)
 - [Поиск по значению](#поиск-по-значению-сокращенная-форма)
-- [`eq`](#eq-строгое-равенство) (строгое равенство)
-- [`neq`](#neq-неравенство) (неравенство)
-- [`gt`](#gt-больше-чем) (больше чем)
-- [`lt`](#lt-меньше-чем) (меньше чем)
-- [`gte`](#gte-больше-или-равно) (больше или равно)
-- [`lte`](#lte-меньше-или-равно) (меньше или равно)
-- [`inq`](#inq-в-списке) (в списке)
-- [`nin`](#nin-не-в-списке) (не в списке)
-- [`between`](#between-диапазон) (диапазон)
-- [`exists`](#exists-наличие-свойства) (наличие свойства)
-- [`like`](#like-шаблон) (шаблон)
-- [`nlike`](#nlike-исключающий-шаблон) (исключающий шаблон)
-- [`ilike`](#ilike-регистронезависимый-шаблон) (регистронезависимый шаблон)
-- [`nilike`](#nilike-регистронезависимый-шаблон-исключения) (регистронезависимый шаблон исключения)
-- [`regexp`](#regexp-регулярное-выражение) (регулярное выражение)
+- [`eq`](#eq-строгое-равенство) строгое равенство;
+- [`neq`](#neq-неравенство) неравенство;
+- [`gt`](#gt-больше-чем) больше чем;
+- [`lt`](#lt-меньше-чем) меньше чем;
+- [`gte`](#gte-больше-или-равно) больше или равно;
+- [`lte`](#lte-меньше-или-равно) меньше или равно;
+- [`inq`](#inq-в-списке) в списке;
+- [`nin`](#nin-не-в-списке) не в списке;
+- [`between`](#between-диапазон) диапазон;
+- [`exists`](#exists-наличие-свойства) наличие свойства;
+- [`like`](#like-шаблон) *SQL*-подобный шаблон;
+- [`nlike`](#nlike-исключающий-шаблон) исключающий шаблон;
+- [`ilike`](#ilike-регистронезависимый-шаблон) регистронезависимый шаблон;
+- [`nilike`](#nilike-регистронезависимый-шаблон-исключения) регистронезависимый шаблон исключения;
+- [`regexp`](#regexp-регулярное-выражение) регулярное выражение;
 
 
 Условия можно объединять логическими операторами:
 Условия можно объединять логическими операторами:
 
 
-- [`and`](#and-логическое-и) (логическое И)
-- [`or`](#or-логическое-или) (логическое ИЛИ)
+- [`and`](#and-логическое-и) логическое *И*;
+- [`or`](#or-логическое-или) логическое *ИЛИ*;
 
 
 #### Поиск по значению (сокращенная форма)
 #### Поиск по значению (сокращенная форма)
 
 
@@ -2141,12 +2115,6 @@ const city: City = await cityRep.create({
 });
 });
 ```
 ```
 
 
-Для определения моделей с помощью TypeScript классов,
-рекомендуется использовать специальную версию данного модуля
-[@e22m4u/ts-repository](https://www.npmjs.com/package/@e22m4u/ts-repository),
-поставляемую с набором TypeScript декораторов и дополнительных
-инструментов для работы в TypeScript окружении.
-
 ## Тесты
 ## Тесты
 
 
 ```bash
 ```bash

+ 3 - 3
dist/cjs/index.cjs

@@ -1876,13 +1876,13 @@ var init_model_definition_utils = __esm({
         const propDefs = this.getPropertiesDefinitionInBaseModelHierarchy(modelName);
         const propDefs = this.getPropertiesDefinitionInBaseModelHierarchy(modelName);
         const propNames = onlyProvidedProperties ? Object.keys(modelData) : Object.keys(propDefs);
         const propNames = onlyProvidedProperties ? Object.keys(modelData) : Object.keys(propDefs);
         const extendedData = cloneDeep(modelData);
         const extendedData = cloneDeep(modelData);
-        const emptyValuesService = this.getService(import_js_empty_values.EmptyValuesService);
+        const blankValuesService = this.getService(import_js_empty_values.BlankValuesService);
         propNames.forEach((propName) => {
         propNames.forEach((propName) => {
           const propDef = propDefs[propName];
           const propDef = propDefs[propName];
           const propValue = extendedData[propName];
           const propValue = extendedData[propName];
           const propType = propDef != null ? this.getDataTypeFromPropertyDefinition(propDef) : DataType.ANY;
           const propType = propDef != null ? this.getDataTypeFromPropertyDefinition(propDef) : DataType.ANY;
-          const isEmpty = emptyValuesService.isEmptyOf(propType, propValue);
-          if (!isEmpty) return;
+          const isBlank = blankValuesService.isBlankOf(propType, propValue);
+          if (!isBlank) return;
           if (propDef && typeof propDef === "object" && propDef.default !== void 0) {
           if (propDef && typeof propDef === "object" && propDef.default !== void 0) {
             extendedData[propName] = this.getDefaultPropertyValue(
             extendedData[propName] = this.getDefaultPropertyValue(
               modelName,
               modelName,

+ 3 - 3
package.json

@@ -39,13 +39,13 @@
     "prepare": "husky"
     "prepare": "husky"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@e22m4u/js-empty-values": "~0.3.2",
+    "@e22m4u/js-empty-values": "~0.4.0",
     "@e22m4u/js-format": "~0.3.2",
     "@e22m4u/js-format": "~0.3.2",
     "@e22m4u/js-service": "~0.5.1"
     "@e22m4u/js-service": "~0.5.1"
   },
   },
   "devDependencies": {
   "devDependencies": {
-    "@commitlint/cli": "~20.2.0",
-    "@commitlint/config-conventional": "~20.2.0",
+    "@commitlint/cli": "~20.3.0",
+    "@commitlint/config-conventional": "~20.3.0",
     "@e22m4u/js-spy": "~0.3.5",
     "@e22m4u/js-spy": "~0.3.5",
     "@types/chai": "~5.2.3",
     "@types/chai": "~5.2.3",
     "@types/chai-as-promised": "~8.0.2",
     "@types/chai-as-promised": "~8.0.2",

+ 4 - 4
src/definition/model/model-definition-utils.js

@@ -1,6 +1,6 @@
 import {Service} from '@e22m4u/js-service';
 import {Service} from '@e22m4u/js-service';
 import {DataType} from './properties/index.js';
 import {DataType} from './properties/index.js';
-import {EmptyValuesService} from '@e22m4u/js-empty-values';
+import {BlankValuesService} from '@e22m4u/js-empty-values';
 import {InvalidArgumentError} from '../../errors/index.js';
 import {InvalidArgumentError} from '../../errors/index.js';
 import {DefinitionRegistry} from '../definition-registry.js';
 import {DefinitionRegistry} from '../definition-registry.js';
 import {cloneDeep, excludeObjectKeys} from '../../utils/index.js';
 import {cloneDeep, excludeObjectKeys} from '../../utils/index.js';
@@ -139,7 +139,7 @@ export class ModelDefinitionUtils extends Service {
       ? Object.keys(modelData)
       ? Object.keys(modelData)
       : Object.keys(propDefs);
       : Object.keys(propDefs);
     const extendedData = cloneDeep(modelData);
     const extendedData = cloneDeep(modelData);
-    const emptyValuesService = this.getService(EmptyValuesService);
+    const blankValuesService = this.getService(BlankValuesService);
     propNames.forEach(propName => {
     propNames.forEach(propName => {
       const propDef = propDefs[propName];
       const propDef = propDefs[propName];
       const propValue = extendedData[propName];
       const propValue = extendedData[propName];
@@ -147,8 +147,8 @@ export class ModelDefinitionUtils extends Service {
         propDef != null
         propDef != null
           ? this.getDataTypeFromPropertyDefinition(propDef)
           ? this.getDataTypeFromPropertyDefinition(propDef)
           : DataType.ANY;
           : DataType.ANY;
-      const isEmpty = emptyValuesService.isEmptyOf(propType, propValue);
-      if (!isEmpty) return;
+      const isBlank = blankValuesService.isBlankOf(propType, propValue);
+      if (!isBlank) return;
       if (
       if (
         propDef &&
         propDef &&
         typeof propDef === 'object' &&
         typeof propDef === 'object' &&

+ 5 - 5
src/definition/model/model-definition-utils.spec.js

@@ -4,7 +4,7 @@ import {createSandbox} from '@e22m4u/js-spy';
 import {DataType} from './properties/index.js';
 import {DataType} from './properties/index.js';
 import {RelationType} from './relations/index.js';
 import {RelationType} from './relations/index.js';
 import {DatabaseSchema} from '../../database-schema.js';
 import {DatabaseSchema} from '../../database-schema.js';
-import {EmptyValuesService} from '@e22m4u/js-empty-values';
+import {BlankValuesService} from '@e22m4u/js-empty-values';
 import {InvalidArgumentError} from '../../errors/index.js';
 import {InvalidArgumentError} from '../../errors/index.js';
 
 
 import {
 import {
@@ -480,7 +480,7 @@ describe('ModelDefinitionUtils', function () {
       expect(result).to.be.eql({foo: 'string'});
       expect(result).to.be.eql({foo: 'string'});
     });
     });
 
 
-    it('sets a default value if a property has an empty value', function () {
+    it('sets a default value if a property has a blank value', function () {
       const dbs = new DatabaseSchema();
       const dbs = new DatabaseSchema();
       dbs.defineModel({
       dbs.defineModel({
         name: 'model',
         name: 'model',
@@ -492,11 +492,11 @@ describe('ModelDefinitionUtils', function () {
         },
         },
       });
       });
       dbs
       dbs
-        .getService(EmptyValuesService)
-        .setEmptyValuesOf(DataType.STRING, ['empty']);
+        .getService(BlankValuesService)
+        .setBlankValuesOf(DataType.STRING, ['blank']);
       const result = dbs
       const result = dbs
         .getService(ModelDefinitionUtils)
         .getService(ModelDefinitionUtils)
-        .setDefaultValuesToEmptyProperties('model', {foo: 'empty'});
+        .setDefaultValuesToEmptyProperties('model', {foo: 'blank'});
       expect(result).to.be.eql({foo: 'placeholder'});
       expect(result).to.be.eql({foo: 'placeholder'});
     });
     });