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

feat: adds "required" option functionality

e22m4u 2 недель назад
Родитель
Сommit
3f14eedec9

+ 207 - 71
dist/cjs/index.cjs

@@ -2170,19 +2170,96 @@ var init_model_definition_utils = __esm({
   }
   }
 });
 });
 
 
+// src/definition/model/properties/required-property-validator.js
+var import_js_service8, import_js_empty_values2, _RequiredPropertyValidator, RequiredPropertyValidator;
+var init_required_property_validator = __esm({
+  "src/definition/model/properties/required-property-validator.js"() {
+    "use strict";
+    init_data_type();
+    import_js_service8 = require("@e22m4u/js-service");
+    import_js_empty_values2 = require("@e22m4u/js-empty-values");
+    init_errors();
+    init_model_definition_utils();
+    _RequiredPropertyValidator = class _RequiredPropertyValidator extends import_js_service8.Service {
+      /**
+       * Validate.
+       *
+       * @param {string} modelName
+       * @param {object} modelData
+       * @param {boolean} [isPartial]
+       */
+      validate(modelName, modelData, isPartial = false) {
+        if (!modelName || typeof modelName !== "string") {
+          throw new InvalidArgumentError(
+            'Parameter "modelName" must be a non-empty String, but %v was given.',
+            modelName
+          );
+        }
+        if (!modelData || typeof modelData !== "object" || Array.isArray(modelData)) {
+          throw new InvalidArgumentError(
+            "Data of the model %v should be an Object, but %v was given.",
+            modelName,
+            modelData
+          );
+        }
+        if (typeof isPartial !== "boolean") {
+          throw new InvalidArgumentError(
+            'Parameter "isPartial" must be a Boolean, but %v was given.',
+            isPartial
+          );
+        }
+        const propDefs = this.getService(
+          ModelDefinitionUtils
+        ).getPropertiesDefinitionInBaseModelHierarchy(modelName);
+        const propNames = Object.keys(isPartial ? modelData : propDefs);
+        const blankValuesService = this.getService(import_js_empty_values2.BlankValuesService);
+        for (const propName of propNames) {
+          const propDef = propDefs[propName];
+          if (!propDef || typeof propDef !== "object") {
+            continue;
+          }
+          const propValue = modelData[propName];
+          if (propDef.required) {
+            const propType = propDef.type || DataType.ANY;
+            if (blankValuesService.isBlankOf(propType, propValue)) {
+              throw new InvalidArgumentError(
+                "Property %v of the model %v is required, but %v was given.",
+                propName,
+                modelName,
+                propValue
+              );
+            }
+          }
+          if (propDef.type === DataType.OBJECT && propDef.model && propValue !== null && typeof propValue === "object" && !Array.isArray(propValue)) {
+            this.validate(propDef.model, propValue);
+          } else if (propDef.type === DataType.ARRAY && propDef.itemType === DataType.OBJECT && propDef.itemModel && Array.isArray(propValue)) {
+            propValue.forEach((itemData) => {
+              if (itemData !== null && typeof itemData === "object" && !Array.isArray(itemData)) {
+                this.validate(propDef.itemModel, itemData);
+              }
+            });
+          }
+        }
+      }
+    };
+    __name(_RequiredPropertyValidator, "RequiredPropertyValidator");
+    RequiredPropertyValidator = _RequiredPropertyValidator;
+  }
+});
+
 // src/definition/model/properties/property-uniqueness-validator.js
 // src/definition/model/properties/property-uniqueness-validator.js
-var import_js_service8, import_js_empty_values2, _PropertyUniquenessValidator, PropertyUniquenessValidator;
+var import_js_service9, import_js_empty_values3, _PropertyUniquenessValidator, PropertyUniquenessValidator;
 var init_property_uniqueness_validator = __esm({
 var init_property_uniqueness_validator = __esm({
   "src/definition/model/properties/property-uniqueness-validator.js"() {
   "src/definition/model/properties/property-uniqueness-validator.js"() {
     "use strict";
     "use strict";
     init_data_type();
     init_data_type();
-    import_js_service8 = require("@e22m4u/js-service");
+    import_js_service9 = require("@e22m4u/js-service");
     init_utils();
     init_utils();
-    import_js_empty_values2 = require("@e22m4u/js-empty-values");
+    import_js_empty_values3 = require("@e22m4u/js-empty-values");
     init_property_uniqueness();
     init_property_uniqueness();
     init_errors();
     init_errors();
     init_model_definition_utils();
     init_model_definition_utils();
-    _PropertyUniquenessValidator = class _PropertyUniquenessValidator extends import_js_service8.Service {
+    _PropertyUniquenessValidator = class _PropertyUniquenessValidator extends import_js_service9.Service {
       /**
       /**
        * Validate.
        * Validate.
        *
        *
@@ -2230,7 +2307,7 @@ var init_property_uniqueness_validator = __esm({
           propValue
           propValue
         ), "createError");
         ), "createError");
         let willBeReplaced = void 0;
         let willBeReplaced = void 0;
-        const emptyValuesService = this.getService(import_js_empty_values2.EmptyValuesService);
+        const emptyValuesService = this.getService(import_js_empty_values3.EmptyValuesService);
         for (const propName of propNames) {
         for (const propName of propNames) {
           const propDef = propDefs[propName];
           const propDef = propDefs[propName];
           if (!propDef || typeof propDef === "string" || !propDef.unique || propDef.unique === PropertyUniqueness.NON_UNIQUE) {
           if (!propDef || typeof propDef === "string" || !propDef.unique || propDef.unique === PropertyUniqueness.NON_UNIQUE) {
@@ -2291,14 +2368,14 @@ var init_property_uniqueness_validator = __esm({
 });
 });
 
 
 // src/definition/model/properties/primary-keys-definition-validator.js
 // src/definition/model/properties/primary-keys-definition-validator.js
-var import_js_service9, _PrimaryKeysDefinitionValidator, PrimaryKeysDefinitionValidator;
+var import_js_service10, _PrimaryKeysDefinitionValidator, PrimaryKeysDefinitionValidator;
 var init_primary_keys_definition_validator = __esm({
 var init_primary_keys_definition_validator = __esm({
   "src/definition/model/properties/primary-keys-definition-validator.js"() {
   "src/definition/model/properties/primary-keys-definition-validator.js"() {
     "use strict";
     "use strict";
-    import_js_service9 = require("@e22m4u/js-service");
+    import_js_service10 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_model_definition_utils();
     init_model_definition_utils();
-    _PrimaryKeysDefinitionValidator = class _PrimaryKeysDefinitionValidator extends import_js_service9.Service {
+    _PrimaryKeysDefinitionValidator = class _PrimaryKeysDefinitionValidator extends import_js_service10.Service {
       /**
       /**
        * Validate.
        * Validate.
        *
        *
@@ -2343,17 +2420,17 @@ var init_primary_keys_definition_validator = __esm({
 });
 });
 
 
 // src/definition/model/properties/properties-definition-validator.js
 // src/definition/model/properties/properties-definition-validator.js
-var import_js_service10, _PropertiesDefinitionValidator, PropertiesDefinitionValidator;
+var import_js_service11, _PropertiesDefinitionValidator, PropertiesDefinitionValidator;
 var init_properties_definition_validator = __esm({
 var init_properties_definition_validator = __esm({
   "src/definition/model/properties/properties-definition-validator.js"() {
   "src/definition/model/properties/properties-definition-validator.js"() {
     "use strict";
     "use strict";
-    import_js_service10 = require("@e22m4u/js-service");
+    import_js_service11 = require("@e22m4u/js-service");
     init_data_type();
     init_data_type();
     init_utils();
     init_utils();
     init_property_uniqueness();
     init_property_uniqueness();
     init_errors();
     init_errors();
     init_primary_keys_definition_validator();
     init_primary_keys_definition_validator();
-    _PropertiesDefinitionValidator = class _PropertiesDefinitionValidator extends import_js_service10.Service {
+    _PropertiesDefinitionValidator = class _PropertiesDefinitionValidator extends import_js_service11.Service {
       /**
       /**
        * Validate.
        * Validate.
        *
        *
@@ -2574,6 +2651,7 @@ var init_properties = __esm({
     init_data_type();
     init_data_type();
     init_property_definition();
     init_property_definition();
     init_property_uniqueness();
     init_property_uniqueness();
+    init_required_property_validator();
     init_property_uniqueness_validator();
     init_property_uniqueness_validator();
     init_properties_definition_validator();
     init_properties_definition_validator();
     init_primary_keys_definition_validator();
     init_primary_keys_definition_validator();
@@ -2588,14 +2666,14 @@ var init_model_definition = __esm({
 });
 });
 
 
 // src/definition/model/model-data-sanitizer.js
 // src/definition/model/model-data-sanitizer.js
-var import_js_service11, _ModelDataSanitizer, ModelDataSanitizer;
+var import_js_service12, _ModelDataSanitizer, ModelDataSanitizer;
 var init_model_data_sanitizer = __esm({
 var init_model_data_sanitizer = __esm({
   "src/definition/model/model-data-sanitizer.js"() {
   "src/definition/model/model-data-sanitizer.js"() {
     "use strict";
     "use strict";
-    import_js_service11 = require("@e22m4u/js-service");
+    import_js_service12 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_model_definition_utils();
     init_model_definition_utils();
-    _ModelDataSanitizer = class _ModelDataSanitizer extends import_js_service11.Service {
+    _ModelDataSanitizer = class _ModelDataSanitizer extends import_js_service12.Service {
       /**
       /**
        * Validate.
        * Validate.
        *
        *
@@ -2625,15 +2703,15 @@ var init_model_data_sanitizer = __esm({
 });
 });
 
 
 // src/definition/model/model-definition-validator.js
 // src/definition/model/model-definition-validator.js
-var import_js_service12, _ModelDefinitionValidator, ModelDefinitionValidator;
+var import_js_service13, _ModelDefinitionValidator, ModelDefinitionValidator;
 var init_model_definition_validator = __esm({
 var init_model_definition_validator = __esm({
   "src/definition/model/model-definition-validator.js"() {
   "src/definition/model/model-definition-validator.js"() {
     "use strict";
     "use strict";
-    import_js_service12 = require("@e22m4u/js-service");
+    import_js_service13 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_relations();
     init_relations();
     init_properties();
     init_properties();
-    _ModelDefinitionValidator = class _ModelDefinitionValidator extends import_js_service12.Service {
+    _ModelDefinitionValidator = class _ModelDefinitionValidator extends import_js_service13.Service {
       /**
       /**
        * Validate.
        * Validate.
        *
        *
@@ -2715,13 +2793,13 @@ var init_model = __esm({
 });
 });
 
 
 // src/definition/datasource/datasource-definition-validator.js
 // src/definition/datasource/datasource-definition-validator.js
-var import_js_service13, _DatasourceDefinitionValidator, DatasourceDefinitionValidator;
+var import_js_service14, _DatasourceDefinitionValidator, DatasourceDefinitionValidator;
 var init_datasource_definition_validator = __esm({
 var init_datasource_definition_validator = __esm({
   "src/definition/datasource/datasource-definition-validator.js"() {
   "src/definition/datasource/datasource-definition-validator.js"() {
     "use strict";
     "use strict";
-    import_js_service13 = require("@e22m4u/js-service");
+    import_js_service14 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
-    _DatasourceDefinitionValidator = class _DatasourceDefinitionValidator extends import_js_service13.Service {
+    _DatasourceDefinitionValidator = class _DatasourceDefinitionValidator extends import_js_service14.Service {
       /**
       /**
        * Validate.
        * Validate.
        *
        *
@@ -2770,15 +2848,15 @@ var init_definition = __esm({
 });
 });
 
 
 // src/filter/fields-clause-tool.js
 // src/filter/fields-clause-tool.js
-var import_js_service14, _FieldsClauseTool, FieldsClauseTool;
+var import_js_service15, _FieldsClauseTool, FieldsClauseTool;
 var init_fields_clause_tool = __esm({
 var init_fields_clause_tool = __esm({
   "src/filter/fields-clause-tool.js"() {
   "src/filter/fields-clause-tool.js"() {
     "use strict";
     "use strict";
-    import_js_service14 = require("@e22m4u/js-service");
+    import_js_service15 = require("@e22m4u/js-service");
     init_utils();
     init_utils();
     init_errors();
     init_errors();
     init_definition();
     init_definition();
-    _FieldsClauseTool = class _FieldsClauseTool extends import_js_service14.Service {
+    _FieldsClauseTool = class _FieldsClauseTool extends import_js_service15.Service {
       /**
       /**
        * Filter.
        * Filter.
        *
        *
@@ -2862,15 +2940,15 @@ var init_fields_clause_tool = __esm({
 });
 });
 
 
 // src/adapter/decorator/inclusion-decorator.js
 // src/adapter/decorator/inclusion-decorator.js
-var import_js_service15, _InclusionDecorator, InclusionDecorator;
+var import_js_service16, _InclusionDecorator, InclusionDecorator;
 var init_inclusion_decorator = __esm({
 var init_inclusion_decorator = __esm({
   "src/adapter/decorator/inclusion-decorator.js"() {
   "src/adapter/decorator/inclusion-decorator.js"() {
     "use strict";
     "use strict";
     init_adapter();
     init_adapter();
-    import_js_service15 = require("@e22m4u/js-service");
+    import_js_service16 = require("@e22m4u/js-service");
     init_filter();
     init_filter();
     init_errors();
     init_errors();
-    _InclusionDecorator = class _InclusionDecorator extends import_js_service15.Service {
+    _InclusionDecorator = class _InclusionDecorator extends import_js_service16.Service {
       /**
       /**
        * Decorate.
        * Decorate.
        *
        *
@@ -2951,15 +3029,15 @@ var init_inclusion_decorator = __esm({
 });
 });
 
 
 // src/adapter/decorator/default-values-decorator.js
 // src/adapter/decorator/default-values-decorator.js
-var import_js_service16, _DefaultValuesDecorator, DefaultValuesDecorator;
+var import_js_service17, _DefaultValuesDecorator, DefaultValuesDecorator;
 var init_default_values_decorator = __esm({
 var init_default_values_decorator = __esm({
   "src/adapter/decorator/default-values-decorator.js"() {
   "src/adapter/decorator/default-values-decorator.js"() {
     "use strict";
     "use strict";
     init_adapter();
     init_adapter();
-    import_js_service16 = require("@e22m4u/js-service");
+    import_js_service17 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_definition();
     init_definition();
-    _DefaultValuesDecorator = class _DefaultValuesDecorator extends import_js_service16.Service {
+    _DefaultValuesDecorator = class _DefaultValuesDecorator extends import_js_service17.Service {
       /**
       /**
        * Decorate.
        * Decorate.
        *
        *
@@ -3016,15 +3094,15 @@ var init_default_values_decorator = __esm({
 });
 });
 
 
 // src/adapter/decorator/data-sanitizing-decorator.js
 // src/adapter/decorator/data-sanitizing-decorator.js
-var import_js_service17, _DataSanitizingDecorator, DataSanitizingDecorator;
+var import_js_service18, _DataSanitizingDecorator, DataSanitizingDecorator;
 var init_data_sanitizing_decorator = __esm({
 var init_data_sanitizing_decorator = __esm({
   "src/adapter/decorator/data-sanitizing-decorator.js"() {
   "src/adapter/decorator/data-sanitizing-decorator.js"() {
     "use strict";
     "use strict";
     init_adapter();
     init_adapter();
-    import_js_service17 = require("@e22m4u/js-service");
+    import_js_service18 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_definition();
     init_definition();
-    _DataSanitizingDecorator = class _DataSanitizingDecorator extends import_js_service17.Service {
+    _DataSanitizingDecorator = class _DataSanitizingDecorator extends import_js_service18.Service {
       /**
       /**
        * Decorate.
        * Decorate.
        *
        *
@@ -3071,15 +3149,15 @@ var init_data_sanitizing_decorator = __esm({
 });
 });
 
 
 // src/adapter/decorator/fields-filtering-decorator.js
 // src/adapter/decorator/fields-filtering-decorator.js
-var import_js_service18, _FieldsFilteringDecorator, FieldsFilteringDecorator;
+var import_js_service19, _FieldsFilteringDecorator, FieldsFilteringDecorator;
 var init_fields_filtering_decorator = __esm({
 var init_fields_filtering_decorator = __esm({
   "src/adapter/decorator/fields-filtering-decorator.js"() {
   "src/adapter/decorator/fields-filtering-decorator.js"() {
     "use strict";
     "use strict";
     init_adapter();
     init_adapter();
-    import_js_service18 = require("@e22m4u/js-service");
+    import_js_service19 = require("@e22m4u/js-service");
     init_filter();
     init_filter();
     init_errors();
     init_errors();
-    _FieldsFilteringDecorator = class _FieldsFilteringDecorator extends import_js_service18.Service {
+    _FieldsFilteringDecorator = class _FieldsFilteringDecorator extends import_js_service19.Service {
       /**
       /**
        * Decorate.
        * Decorate.
        *
        *
@@ -3153,16 +3231,70 @@ var init_fields_filtering_decorator = __esm({
   }
   }
 });
 });
 
 
+// src/adapter/decorator/required-property-decorator.js
+var import_js_service20, _RequiredPropertyDecorator, RequiredPropertyDecorator;
+var init_required_property_decorator = __esm({
+  "src/adapter/decorator/required-property-decorator.js"() {
+    "use strict";
+    init_adapter();
+    import_js_service20 = require("@e22m4u/js-service");
+    init_errors();
+    init_definition();
+    _RequiredPropertyDecorator = class _RequiredPropertyDecorator extends import_js_service20.Service {
+      /**
+       * Decorate.
+       *
+       * @param {Adapter} adapter
+       */
+      decorate(adapter) {
+        if (!adapter || !(adapter instanceof Adapter))
+          throw new InvalidArgumentError(
+            "The first argument of RequiredPropertyDecorator.decorate should be an Adapter instance, but %v was given.",
+            adapter
+          );
+        const validator = this.getService(RequiredPropertyValidator);
+        const create = adapter.create;
+        adapter.create = async function(modelName, modelData, filter) {
+          validator.validate(modelName, modelData);
+          return create.call(this, modelName, modelData, filter);
+        };
+        const replaceById = adapter.replaceById;
+        adapter.replaceById = async function(modelName, id, modelData, filter) {
+          validator.validate(modelName, modelData);
+          return replaceById.call(this, modelName, id, modelData, filter);
+        };
+        const replaceOrCreate = adapter.replaceOrCreate;
+        adapter.replaceOrCreate = async function(modelName, modelData, filter) {
+          validator.validate(modelName, modelData);
+          return replaceOrCreate.call(this, modelName, modelData, filter);
+        };
+        const patch = adapter.patch;
+        adapter.patch = async function(modelName, modelData, where) {
+          validator.validate(modelName, modelData, true);
+          return patch.call(this, modelName, modelData, where);
+        };
+        const patchById = adapter.patchById;
+        adapter.patchById = async function(modelName, id, modelData, filter) {
+          validator.validate(modelName, modelData, true);
+          return patchById.call(this, modelName, id, modelData, filter);
+        };
+      }
+    };
+    __name(_RequiredPropertyDecorator, "RequiredPropertyDecorator");
+    RequiredPropertyDecorator = _RequiredPropertyDecorator;
+  }
+});
+
 // src/adapter/decorator/property-uniqueness-decorator.js
 // src/adapter/decorator/property-uniqueness-decorator.js
-var import_js_service19, _PropertyUniquenessDecorator, PropertyUniquenessDecorator;
+var import_js_service21, _PropertyUniquenessDecorator, PropertyUniquenessDecorator;
 var init_property_uniqueness_decorator = __esm({
 var init_property_uniqueness_decorator = __esm({
   "src/adapter/decorator/property-uniqueness-decorator.js"() {
   "src/adapter/decorator/property-uniqueness-decorator.js"() {
     "use strict";
     "use strict";
     init_adapter();
     init_adapter();
-    import_js_service19 = require("@e22m4u/js-service");
+    import_js_service21 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_definition();
     init_definition();
-    _PropertyUniquenessDecorator = class _PropertyUniquenessDecorator extends import_js_service19.Service {
+    _PropertyUniquenessDecorator = class _PropertyUniquenessDecorator extends import_js_service21.Service {
       /**
       /**
        * Decorate.
        * Decorate.
        *
        *
@@ -3237,20 +3369,21 @@ var init_decorator = __esm({
     init_default_values_decorator();
     init_default_values_decorator();
     init_data_sanitizing_decorator();
     init_data_sanitizing_decorator();
     init_fields_filtering_decorator();
     init_fields_filtering_decorator();
+    init_required_property_decorator();
     init_property_uniqueness_decorator();
     init_property_uniqueness_decorator();
   }
   }
 });
 });
 
 
 // src/adapter/adapter.js
 // src/adapter/adapter.js
-var import_js_service20, ADAPTER_CLASS_NAME, _Adapter, Adapter;
+var import_js_service22, ADAPTER_CLASS_NAME, _Adapter, Adapter;
 var init_adapter = __esm({
 var init_adapter = __esm({
   "src/adapter/adapter.js"() {
   "src/adapter/adapter.js"() {
     "use strict";
     "use strict";
-    import_js_service20 = require("@e22m4u/js-service");
+    import_js_service22 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_decorator();
     init_decorator();
     ADAPTER_CLASS_NAME = "Adapter";
     ADAPTER_CLASS_NAME = "Adapter";
-    _Adapter = class _Adapter extends import_js_service20.Service {
+    _Adapter = class _Adapter extends import_js_service22.Service {
       /**
       /**
        * Settings.
        * Settings.
        *
        *
@@ -3277,6 +3410,7 @@ var init_adapter = __esm({
         if (this.constructor !== _Adapter) {
         if (this.constructor !== _Adapter) {
           this.getService(DataSanitizingDecorator).decorate(this);
           this.getService(DataSanitizingDecorator).decorate(this);
           this.getService(DefaultValuesDecorator).decorate(this);
           this.getService(DefaultValuesDecorator).decorate(this);
+          this.getService(RequiredPropertyDecorator).decorate(this);
           this.getService(PropertyUniquenessDecorator).decorate(this);
           this.getService(PropertyUniquenessDecorator).decorate(this);
           this.getService(FieldsFilteringDecorator).decorate(this);
           this.getService(FieldsFilteringDecorator).decorate(this);
           this.getService(InclusionDecorator).decorate(this);
           this.getService(InclusionDecorator).decorate(this);
@@ -3440,7 +3574,7 @@ var init_adapter = __esm({
      *
      *
      * @type {string[]}
      * @type {string[]}
      */
      */
-    __publicField(_Adapter, "kinds", [...import_js_service20.Service.kinds, ADAPTER_CLASS_NAME]);
+    __publicField(_Adapter, "kinds", [...import_js_service22.Service.kinds, ADAPTER_CLASS_NAME]);
     Adapter = _Adapter;
     Adapter = _Adapter;
   }
   }
 });
 });
@@ -3866,15 +4000,15 @@ function findAdapterCtorInModule(module2) {
   }
   }
   return adapterCtor;
   return adapterCtor;
 }
 }
-var import_js_service21, _AdapterLoader, AdapterLoader;
+var import_js_service23, _AdapterLoader, AdapterLoader;
 var init_adapter_loader = __esm({
 var init_adapter_loader = __esm({
   "src/adapter/adapter-loader.js"() {
   "src/adapter/adapter-loader.js"() {
     "use strict";
     "use strict";
-    import_js_service21 = require("@e22m4u/js-service");
+    import_js_service23 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_adapter();
     init_adapter();
     init_();
     init_();
-    _AdapterLoader = class _AdapterLoader extends import_js_service21.Service {
+    _AdapterLoader = class _AdapterLoader extends import_js_service23.Service {
       /**
       /**
        * Load by name.
        * Load by name.
        *
        *
@@ -3915,15 +4049,15 @@ var init_adapter_loader = __esm({
 });
 });
 
 
 // src/adapter/adapter-registry.js
 // src/adapter/adapter-registry.js
-var import_js_service22, _AdapterRegistry, AdapterRegistry;
+var import_js_service24, _AdapterRegistry, AdapterRegistry;
 var init_adapter_registry = __esm({
 var init_adapter_registry = __esm({
   "src/adapter/adapter-registry.js"() {
   "src/adapter/adapter-registry.js"() {
     "use strict";
     "use strict";
     init_adapter();
     init_adapter();
-    import_js_service22 = require("@e22m4u/js-service");
+    import_js_service24 = require("@e22m4u/js-service");
     init_adapter_loader();
     init_adapter_loader();
     init_definition();
     init_definition();
-    _AdapterRegistry = class _AdapterRegistry extends import_js_service22.Service {
+    _AdapterRegistry = class _AdapterRegistry extends import_js_service24.Service {
       /**
       /**
        * Adapters.
        * Adapters.
        *
        *
@@ -3965,15 +4099,15 @@ var init_adapter2 = __esm({
 });
 });
 
 
 // src/repository/repository.js
 // src/repository/repository.js
-var import_js_service23, _Repository, Repository;
+var import_js_service25, _Repository, Repository;
 var init_repository = __esm({
 var init_repository = __esm({
   "src/repository/repository.js"() {
   "src/repository/repository.js"() {
     "use strict";
     "use strict";
-    import_js_service23 = require("@e22m4u/js-service");
+    import_js_service25 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_definition();
     init_definition();
     init_adapter2();
     init_adapter2();
-    _Repository = class _Repository extends import_js_service23.Service {
+    _Repository = class _Repository extends import_js_service25.Service {
       /**
       /**
        * Model name.
        * Model name.
        *
        *
@@ -4167,15 +4301,15 @@ var init_repository = __esm({
 });
 });
 
 
 // src/repository/repository-registry.js
 // src/repository/repository-registry.js
-var import_js_service24, _RepositoryRegistry, RepositoryRegistry;
+var import_js_service26, _RepositoryRegistry, RepositoryRegistry;
 var init_repository_registry = __esm({
 var init_repository_registry = __esm({
   "src/repository/repository-registry.js"() {
   "src/repository/repository-registry.js"() {
     "use strict";
     "use strict";
-    import_js_service24 = require("@e22m4u/js-service");
+    import_js_service26 = require("@e22m4u/js-service");
     init_repository();
     init_repository();
     init_utils();
     init_utils();
     init_errors();
     init_errors();
-    _RepositoryRegistry = class _RepositoryRegistry extends import_js_service24.Service {
+    _RepositoryRegistry = class _RepositoryRegistry extends import_js_service26.Service {
       /**
       /**
        * Repositories.
        * Repositories.
        *
        *
@@ -4233,16 +4367,16 @@ var init_repository2 = __esm({
 });
 });
 
 
 // src/relations/has-one-resolver.js
 // src/relations/has-one-resolver.js
-var import_js_service25, _HasOneResolver, HasOneResolver;
+var import_js_service27, _HasOneResolver, HasOneResolver;
 var init_has_one_resolver = __esm({
 var init_has_one_resolver = __esm({
   "src/relations/has-one-resolver.js"() {
   "src/relations/has-one-resolver.js"() {
     "use strict";
     "use strict";
-    import_js_service25 = require("@e22m4u/js-service");
+    import_js_service27 = require("@e22m4u/js-service");
     init_utils();
     init_utils();
     init_errors();
     init_errors();
     init_repository2();
     init_repository2();
     init_definition();
     init_definition();
-    _HasOneResolver = class _HasOneResolver extends import_js_service25.Service {
+    _HasOneResolver = class _HasOneResolver extends import_js_service27.Service {
       /**
       /**
        * Include to.
        * Include to.
        *
        *
@@ -4487,16 +4621,16 @@ var init_has_one_resolver = __esm({
 });
 });
 
 
 // src/relations/has-many-resolver.js
 // src/relations/has-many-resolver.js
-var import_js_service26, _HasManyResolver, HasManyResolver;
+var import_js_service28, _HasManyResolver, HasManyResolver;
 var init_has_many_resolver = __esm({
 var init_has_many_resolver = __esm({
   "src/relations/has-many-resolver.js"() {
   "src/relations/has-many-resolver.js"() {
     "use strict";
     "use strict";
-    import_js_service26 = require("@e22m4u/js-service");
+    import_js_service28 = require("@e22m4u/js-service");
     init_utils();
     init_utils();
     init_errors();
     init_errors();
     init_repository2();
     init_repository2();
     init_definition();
     init_definition();
-    _HasManyResolver = class _HasManyResolver extends import_js_service26.Service {
+    _HasManyResolver = class _HasManyResolver extends import_js_service28.Service {
       /**
       /**
        * Include to.
        * Include to.
        *
        *
@@ -4751,16 +4885,16 @@ var init_has_many_resolver = __esm({
 });
 });
 
 
 // src/relations/belongs-to-resolver.js
 // src/relations/belongs-to-resolver.js
-var import_js_service27, _BelongsToResolver, BelongsToResolver;
+var import_js_service29, _BelongsToResolver, BelongsToResolver;
 var init_belongs_to_resolver = __esm({
 var init_belongs_to_resolver = __esm({
   "src/relations/belongs-to-resolver.js"() {
   "src/relations/belongs-to-resolver.js"() {
     "use strict";
     "use strict";
-    import_js_service27 = require("@e22m4u/js-service");
+    import_js_service29 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_utils();
     init_utils();
     init_repository2();
     init_repository2();
     init_definition();
     init_definition();
-    _BelongsToResolver = class _BelongsToResolver extends import_js_service27.Service {
+    _BelongsToResolver = class _BelongsToResolver extends import_js_service29.Service {
       /**
       /**
        * Include to.
        * Include to.
        *
        *
@@ -4958,16 +5092,16 @@ var init_belongs_to_resolver = __esm({
 });
 });
 
 
 // src/relations/references-many-resolver.js
 // src/relations/references-many-resolver.js
-var import_js_service28, _ReferencesManyResolver, ReferencesManyResolver;
+var import_js_service30, _ReferencesManyResolver, ReferencesManyResolver;
 var init_references_many_resolver = __esm({
 var init_references_many_resolver = __esm({
   "src/relations/references-many-resolver.js"() {
   "src/relations/references-many-resolver.js"() {
     "use strict";
     "use strict";
-    import_js_service28 = require("@e22m4u/js-service");
+    import_js_service30 = require("@e22m4u/js-service");
     init_errors();
     init_errors();
     init_utils();
     init_utils();
     init_repository2();
     init_repository2();
     init_definition();
     init_definition();
-    _ReferencesManyResolver = class _ReferencesManyResolver extends import_js_service28.Service {
+    _ReferencesManyResolver = class _ReferencesManyResolver extends import_js_service30.Service {
       /**
       /**
        * Include to.
        * Include to.
        *
        *
@@ -5069,11 +5203,11 @@ var init_relations2 = __esm({
 });
 });
 
 
 // src/filter/include-clause-tool.js
 // src/filter/include-clause-tool.js
-var import_js_service29, _IncludeClauseTool, IncludeClauseTool;
+var import_js_service31, _IncludeClauseTool, IncludeClauseTool;
 var init_include_clause_tool = __esm({
 var init_include_clause_tool = __esm({
   "src/filter/include-clause-tool.js"() {
   "src/filter/include-clause-tool.js"() {
     "use strict";
     "use strict";
-    import_js_service29 = require("@e22m4u/js-service");
+    import_js_service31 = require("@e22m4u/js-service");
     init_where_clause_tool();
     init_where_clause_tool();
     init_order_clause_tool();
     init_order_clause_tool();
     init_slice_clause_tool();
     init_slice_clause_tool();
@@ -5081,7 +5215,7 @@ var init_include_clause_tool = __esm({
     init_fields_clause_tool();
     init_fields_clause_tool();
     init_definition();
     init_definition();
     init_relations2();
     init_relations2();
-    _IncludeClauseTool = class _IncludeClauseTool extends import_js_service29.Service {
+    _IncludeClauseTool = class _IncludeClauseTool extends import_js_service31.Service {
       /**
       /**
        * Include to.
        * Include to.
        *
        *
@@ -5451,6 +5585,7 @@ __export(index_exports, {
   RelationsDefinitionValidator: () => RelationsDefinitionValidator,
   RelationsDefinitionValidator: () => RelationsDefinitionValidator,
   Repository: () => Repository,
   Repository: () => Repository,
   RepositoryRegistry: () => RepositoryRegistry,
   RepositoryRegistry: () => RepositoryRegistry,
+  RequiredPropertyValidator: () => RequiredPropertyValidator,
   SliceClauseTool: () => SliceClauseTool,
   SliceClauseTool: () => SliceClauseTool,
   WhereClauseTool: () => WhereClauseTool,
   WhereClauseTool: () => WhereClauseTool,
   capitalize: () => capitalize,
   capitalize: () => capitalize,
@@ -5473,10 +5608,10 @@ init_filter();
 init_adapter2();
 init_adapter2();
 
 
 // src/database-schema.js
 // src/database-schema.js
-var import_js_service30 = require("@e22m4u/js-service");
+var import_js_service32 = require("@e22m4u/js-service");
 init_definition();
 init_definition();
 init_repository2();
 init_repository2();
-var _DatabaseSchema = class _DatabaseSchema extends import_js_service30.Service {
+var _DatabaseSchema = class _DatabaseSchema extends import_js_service32.Service {
   /**
   /**
    * Define datasource.
    * Define datasource.
    *
    *
@@ -5547,6 +5682,7 @@ init_repository2();
   RelationsDefinitionValidator,
   RelationsDefinitionValidator,
   Repository,
   Repository,
   RepositoryRegistry,
   RepositoryRegistry,
+  RequiredPropertyValidator,
   SliceClauseTool,
   SliceClauseTool,
   WhereClauseTool,
   WhereClauseTool,
   capitalize,
   capitalize,

+ 2 - 0
src/adapter/adapter.js

@@ -8,6 +8,7 @@ import {
   DefaultValuesDecorator,
   DefaultValuesDecorator,
   DataSanitizingDecorator,
   DataSanitizingDecorator,
   FieldsFilteringDecorator,
   FieldsFilteringDecorator,
+  RequiredPropertyDecorator,
   PropertyUniquenessDecorator,
   PropertyUniquenessDecorator,
 } from './decorator/index.js';
 } from './decorator/index.js';
 
 
@@ -58,6 +59,7 @@ export class Adapter extends Service {
     if (this.constructor !== Adapter) {
     if (this.constructor !== Adapter) {
       this.getService(DataSanitizingDecorator).decorate(this);
       this.getService(DataSanitizingDecorator).decorate(this);
       this.getService(DefaultValuesDecorator).decorate(this);
       this.getService(DefaultValuesDecorator).decorate(this);
+      this.getService(RequiredPropertyDecorator).decorate(this);
       this.getService(PropertyUniquenessDecorator).decorate(this);
       this.getService(PropertyUniquenessDecorator).decorate(this);
       this.getService(FieldsFilteringDecorator).decorate(this);
       this.getService(FieldsFilteringDecorator).decorate(this);
       this.getService(InclusionDecorator).decorate(this);
       this.getService(InclusionDecorator).decorate(this);

+ 39 - 41
src/adapter/adapter.spec.js

@@ -9,13 +9,14 @@ import {
   DefaultValuesDecorator,
   DefaultValuesDecorator,
   DataSanitizingDecorator,
   DataSanitizingDecorator,
   FieldsFilteringDecorator,
   FieldsFilteringDecorator,
+  RequiredPropertyDecorator,
   PropertyUniquenessDecorator,
   PropertyUniquenessDecorator,
 } from './decorator/index.js';
 } from './decorator/index.js';
 
 
 const sandbox = createSandbox();
 const sandbox = createSandbox();
 
 
 describe('Adapter', function () {
 describe('Adapter', function () {
-  it('exposes static property "kinds"', function () {
+  it('should expose the static property "kinds"', function () {
     const kinds = [...Service.kinds, ADAPTER_CLASS_NAME];
     const kinds = [...Service.kinds, ADAPTER_CLASS_NAME];
     expect(Adapter.kinds).to.be.eql(kinds);
     expect(Adapter.kinds).to.be.eql(kinds);
     const MyAdapter = class extends Adapter {};
     const MyAdapter = class extends Adapter {};
@@ -27,12 +28,12 @@ describe('Adapter', function () {
       sandbox.restore();
       sandbox.restore();
     });
     });
 
 
-    it('inherits from the Service class', function () {
+    it('should extend the Service class', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       expect(adapter).to.be.instanceof(Service);
       expect(adapter).to.be.instanceof(Service);
     });
     });
 
 
-    it('sets given service container and settings', function () {
+    it('should set given service container and settings', function () {
       const container = new ServiceContainer();
       const container = new ServiceContainer();
       const settings = {};
       const settings = {};
       const adapter = new Adapter(container, settings);
       const adapter = new Adapter(container, settings);
@@ -40,47 +41,36 @@ describe('Adapter', function () {
       expect(adapter._settings).to.be.eq(settings);
       expect(adapter._settings).to.be.eq(settings);
     });
     });
 
 
-    it('decorates only extended adapter', function () {
+    it('should decorate only when the instance inherits the Adapter class', function () {
+      const decCtors = [
+        DataSanitizingDecorator,
+        DefaultValuesDecorator,
+        RequiredPropertyDecorator,
+        PropertyUniquenessDecorator,
+        FieldsFilteringDecorator,
+        InclusionDecorator,
+      ];
       const dbs = new DatabaseSchema();
       const dbs = new DatabaseSchema();
-      const dec1 = dbs.getService(DataSanitizingDecorator);
-      const dec2 = dbs.getService(DefaultValuesDecorator);
-      const dec3 = dbs.getService(PropertyUniquenessDecorator);
-      const dec4 = dbs.getService(FieldsFilteringDecorator);
-      const dec5 = dbs.getService(InclusionDecorator);
+      const decs = decCtors.map(ctor => dbs.getService(ctor));
       const order = [];
       const order = [];
-      const decorate = function (ctx) {
-        expect(ctx).to.be.instanceof(Adapter);
+      const decorate = function (...args) {
+        expect(args[0]).to.be.instanceof(Adapter);
+        expect(args).to.have.length(1);
         order.push(this);
         order.push(this);
       };
       };
-      sandbox.on(dec1, 'decorate', decorate);
-      sandbox.on(dec2, 'decorate', decorate);
-      sandbox.on(dec3, 'decorate', decorate);
-      sandbox.on(dec4, 'decorate', decorate);
-      sandbox.on(dec5, 'decorate', decorate);
+      decs.forEach(dec => sandbox.on(dec, 'decorate', decorate));
       new Adapter(dbs.container);
       new Adapter(dbs.container);
       expect(order).to.be.empty;
       expect(order).to.be.empty;
-      expect(dec1.decorate).to.be.not.called;
-      expect(dec2.decorate).to.be.not.called;
-      expect(dec3.decorate).to.be.not.called;
-      expect(dec4.decorate).to.be.not.called;
-      expect(dec5.decorate).to.be.not.called;
+      decs.forEach(dec => expect(dec.decorate).to.be.not.called);
       class ExtendedAdapter extends Adapter {}
       class ExtendedAdapter extends Adapter {}
       new ExtendedAdapter(dbs.container);
       new ExtendedAdapter(dbs.container);
-      expect(order[0]).to.be.eql(dec1);
-      expect(order[1]).to.be.eql(dec2);
-      expect(order[2]).to.be.eql(dec3);
-      expect(order[3]).to.be.eql(dec4);
-      expect(order[4]).to.be.eql(dec5);
-      expect(dec1.decorate).to.be.called.once;
-      expect(dec2.decorate).to.be.called.once;
-      expect(dec3.decorate).to.be.called.once;
-      expect(dec4.decorate).to.be.called.once;
-      expect(dec5.decorate).to.be.called.once;
+      decs.forEach((dec, index) => expect(order[index]).to.be.eq(dec));
+      decs.forEach(dec => expect(dec.decorate).to.be.called.once);
     });
     });
   });
   });
 
 
   describe('create', function () {
   describe('create', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.create();
       const throwable = () => adapter.create();
       expect(throwable).to.throw('Adapter.create is not implemented.');
       expect(throwable).to.throw('Adapter.create is not implemented.');
@@ -88,7 +78,7 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('replaceById', function () {
   describe('replaceById', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.replaceById();
       const throwable = () => adapter.replaceById();
       expect(throwable).to.throw('Adapter.replaceById is not implemented.');
       expect(throwable).to.throw('Adapter.replaceById is not implemented.');
@@ -96,15 +86,23 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('replaceOrCreate', function () {
   describe('replaceOrCreate', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.replaceOrCreate();
       const throwable = () => adapter.replaceOrCreate();
       expect(throwable).to.throw('Adapter.replaceOrCreate is not implemented.');
       expect(throwable).to.throw('Adapter.replaceOrCreate is not implemented.');
     });
     });
   });
   });
 
 
+  describe('patch', function () {
+    it('should throw the "Not implemented"', function () {
+      const adapter = new Adapter();
+      const throwable = () => adapter.patch();
+      expect(throwable).to.throw('Adapter.patch is not implemented.');
+    });
+  });
+
   describe('patchById', function () {
   describe('patchById', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.patchById();
       const throwable = () => adapter.patchById();
       expect(throwable).to.throw('Adapter.patchById is not implemented.');
       expect(throwable).to.throw('Adapter.patchById is not implemented.');
@@ -112,7 +110,7 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('find', function () {
   describe('find', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.find();
       const throwable = () => adapter.find();
       expect(throwable).to.throw('Adapter.find is not implemented.');
       expect(throwable).to.throw('Adapter.find is not implemented.');
@@ -120,7 +118,7 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('findById', function () {
   describe('findById', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.findById();
       const throwable = () => adapter.findById();
       expect(throwable).to.throw('Adapter.findById is not implemented.');
       expect(throwable).to.throw('Adapter.findById is not implemented.');
@@ -128,7 +126,7 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('delete', function () {
   describe('delete', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.delete();
       const throwable = () => adapter.delete();
       expect(throwable).to.throw('Adapter.delete is not implemented.');
       expect(throwable).to.throw('Adapter.delete is not implemented.');
@@ -136,7 +134,7 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('deleteById', function () {
   describe('deleteById', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.deleteById();
       const throwable = () => adapter.deleteById();
       expect(throwable).to.throw('Adapter.deleteById is not implemented.');
       expect(throwable).to.throw('Adapter.deleteById is not implemented.');
@@ -144,7 +142,7 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('exists', function () {
   describe('exists', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.exists();
       const throwable = () => adapter.exists();
       expect(throwable).to.throw('Adapter.exists is not implemented.');
       expect(throwable).to.throw('Adapter.exists is not implemented.');
@@ -152,7 +150,7 @@ describe('Adapter', function () {
   });
   });
 
 
   describe('count', function () {
   describe('count', function () {
-    it('throws the "Not implemented"', function () {
+    it('should throw the "Not implemented"', function () {
       const adapter = new Adapter();
       const adapter = new Adapter();
       const throwable = () => adapter.count();
       const throwable = () => adapter.count();
       expect(throwable).to.throw('Adapter.count is not implemented.');
       expect(throwable).to.throw('Adapter.count is not implemented.');

+ 1 - 0
src/adapter/decorator/index.d.ts

@@ -2,4 +2,5 @@ export * from './inclusion-decorator.js';
 export * from './default-values-decorator.js';
 export * from './default-values-decorator.js';
 export * from './data-sanitizing-decorator.js';
 export * from './data-sanitizing-decorator.js';
 export * from './fields-filtering-decorator.js';
 export * from './fields-filtering-decorator.js';
+export * from './required-property-decorator.js';
 export * from './property-uniqueness-decorator.js';
 export * from './property-uniqueness-decorator.js';

+ 1 - 0
src/adapter/decorator/index.js

@@ -2,4 +2,5 @@ export * from './inclusion-decorator.js';
 export * from './default-values-decorator.js';
 export * from './default-values-decorator.js';
 export * from './data-sanitizing-decorator.js';
 export * from './data-sanitizing-decorator.js';
 export * from './fields-filtering-decorator.js';
 export * from './fields-filtering-decorator.js';
+export * from './required-property-decorator.js';
 export * from './property-uniqueness-decorator.js';
 export * from './property-uniqueness-decorator.js';

+ 14 - 0
src/adapter/decorator/required-property-decorator.d.ts

@@ -0,0 +1,14 @@
+import {Adapter} from '../adapter.js';
+import {Service} from '@e22m4u/js-service';
+
+/**
+ * Required property decorator.
+ */
+export declare class RequiredPropertyDecorator extends Service {
+  /**
+   * Decorate.
+   *
+   * @param adapter
+   */
+  decorate(adapter: Adapter): void;
+}

+ 54 - 0
src/adapter/decorator/required-property-decorator.js

@@ -0,0 +1,54 @@
+import {Adapter} from '../adapter.js';
+import {Service} from '@e22m4u/js-service';
+import {InvalidArgumentError} from '../../errors/index.js';
+import {RequiredPropertyValidator} from '../../definition/index.js';
+
+/**
+ * Required property decorator.
+ */
+export class RequiredPropertyDecorator extends Service {
+  /**
+   * Decorate.
+   *
+   * @param {Adapter} adapter
+   */
+  decorate(adapter) {
+    if (!adapter || !(adapter instanceof Adapter))
+      throw new InvalidArgumentError(
+        'The first argument of RequiredPropertyDecorator.decorate should be ' +
+          'an Adapter instance, but %v was given.',
+        adapter,
+      );
+    const validator = this.getService(RequiredPropertyValidator);
+
+    const create = adapter.create;
+    adapter.create = async function (modelName, modelData, filter) {
+      validator.validate(modelName, modelData);
+      return create.call(this, modelName, modelData, filter);
+    };
+
+    const replaceById = adapter.replaceById;
+    adapter.replaceById = async function (modelName, id, modelData, filter) {
+      validator.validate(modelName, modelData);
+      return replaceById.call(this, modelName, id, modelData, filter);
+    };
+
+    const replaceOrCreate = adapter.replaceOrCreate;
+    adapter.replaceOrCreate = async function (modelName, modelData, filter) {
+      validator.validate(modelName, modelData);
+      return replaceOrCreate.call(this, modelName, modelData, filter);
+    };
+
+    const patch = adapter.patch;
+    adapter.patch = async function (modelName, modelData, where) {
+      validator.validate(modelName, modelData, true);
+      return patch.call(this, modelName, modelData, where);
+    };
+
+    const patchById = adapter.patchById;
+    adapter.patchById = async function (modelName, id, modelData, filter) {
+      validator.validate(modelName, modelData, true);
+      return patchById.call(this, modelName, id, modelData, filter);
+    };
+  }
+}

+ 105 - 0
src/adapter/decorator/required-property-decorator.spec.js

@@ -0,0 +1,105 @@
+import {expect} from 'chai';
+import {Adapter} from '../adapter.js';
+import {createSandbox} from '@e22m4u/js-spy';
+import {DatabaseSchema} from '../../database-schema.js';
+import {RequiredPropertyValidator} from '../../definition/index.js';
+
+const dbs = new DatabaseSchema();
+dbs.defineModel({name: 'model'});
+
+class TestAdapter extends Adapter {
+  // eslint-disable-next-line no-unused-vars
+  create(modelName, modelData, filter = undefined) {
+    return Promise.resolve(modelData);
+  }
+
+  // eslint-disable-next-line no-unused-vars
+  replaceById(modelName, id, modelData, filter = undefined) {
+    return Promise.resolve(modelData);
+  }
+
+  // eslint-disable-next-line no-unused-vars
+  replaceOrCreate(modelName, modelData, filter = undefined) {
+    return Promise.resolve(modelData);
+  }
+
+  // eslint-disable-next-line no-unused-vars
+  patch(modelName, modelData, where = undefined) {
+    return Promise.resolve(modelData);
+  }
+
+  // eslint-disable-next-line no-unused-vars
+  patchById(modelName, id, modelData, filter = undefined) {
+    return Promise.resolve(modelData);
+  }
+}
+
+const A = dbs.getService(TestAdapter);
+const V = dbs.getService(RequiredPropertyValidator);
+const sandbox = createSandbox();
+
+describe('RequiredPropertyDecorator', function () {
+  afterEach(function () {
+    sandbox.restore();
+  });
+
+  it('overrides the "create" method and validates a given data', async function () {
+    const data = {kind: 'data'};
+    sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
+      expect(modelName).to.be.eq('model');
+      expect(modelData).to.be.eql(data);
+      expect(isPartial).to.be.false;
+    });
+    const res = await A.create('model', data);
+    expect(res).to.be.eql(data);
+    expect(V.validate).to.be.called.once;
+  });
+
+  it('overrides the "replaceById" method and validates a given data', async function () {
+    const data = {kind: 'data'};
+    sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
+      expect(modelName).to.be.eq('model');
+      expect(modelData).to.be.eql(data);
+      expect(isPartial).to.be.false;
+    });
+    const res = await A.replaceById('model', 1, data);
+    expect(res).to.be.eql(data);
+    expect(V.validate).to.be.called.once;
+  });
+
+  it('overrides the "replaceOrCreate" method and validates a given data', async function () {
+    const data = {kind: 'data'};
+    sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
+      expect(modelName).to.be.eq('model');
+      expect(modelData).to.be.eql(data);
+      expect(isPartial).to.be.false;
+    });
+    const res = await A.replaceOrCreate('model', data);
+    expect(res).to.be.eql(data);
+    expect(V.validate).to.be.called.once;
+  });
+
+  it('overrides the "patch" method and validates a given data', async function () {
+    const data = {kind: 'data'};
+    sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
+      expect(modelName).to.be.eq('model');
+      expect(modelData).to.be.eql(data);
+      expect(isPartial).to.be.true;
+    });
+    const res = await A.patch('model', data);
+    expect(res).to.be.eql(data);
+    expect(V.validate).to.be.called.once;
+  });
+
+  it('overrides the "patchById" method and validates a given data', async function () {
+    const data = {kind: 'data'};
+    sandbox.on(V, 'validate', (modelName, modelData, isPartial = false) => {
+      expect(modelName).to.be.eq('model');
+      expect(modelData).to.be.eql(data);
+      expect(isPartial).to.be.true;
+    });
+    const res = await A.patchById('model', 1, data);
+    expect(res).to.be.eql(data);
+    expect(V.validate).to.be.called.once;
+  });
+});

+ 1 - 0
src/definition/model/properties/index.d.ts

@@ -1,6 +1,7 @@
 export * from './data-type.js';
 export * from './data-type.js';
 export * from './property-definition.js';
 export * from './property-definition.js';
 export * from './property-uniqueness.js';
 export * from './property-uniqueness.js';
+export * from './required-property-validator.js';
 export * from './property-uniqueness-validator.js';
 export * from './property-uniqueness-validator.js';
 export * from './properties-definition-validator.js';
 export * from './properties-definition-validator.js';
 export * from './primary-keys-definition-validator.js';
 export * from './primary-keys-definition-validator.js';

+ 1 - 0
src/definition/model/properties/index.js

@@ -1,6 +1,7 @@
 export * from './data-type.js';
 export * from './data-type.js';
 export * from './property-definition.js';
 export * from './property-definition.js';
 export * from './property-uniqueness.js';
 export * from './property-uniqueness.js';
+export * from './required-property-validator.js';
 export * from './property-uniqueness-validator.js';
 export * from './property-uniqueness-validator.js';
 export * from './properties-definition-validator.js';
 export * from './properties-definition-validator.js';
 export * from './primary-keys-definition-validator.js';
 export * from './primary-keys-definition-validator.js';

+ 15 - 0
src/definition/model/properties/required-property-validator.d.ts

@@ -0,0 +1,15 @@
+import {Service} from '@e22m4u/js-service';
+
+/**
+ * Required property validator.
+ */
+export class RequiredPropertyValidator extends Service {
+  /**
+   * Validate.
+   *
+   * @param modelName
+   * @param modelData
+   * @param isPartial
+   */
+  validate(modelName: string, modelData: object, isPartial?: boolean): void;
+}

+ 95 - 0
src/definition/model/properties/required-property-validator.js

@@ -0,0 +1,95 @@
+import {DataType} from './data-type.js';
+import {Service} from '@e22m4u/js-service';
+import {BlankValuesService} from '@e22m4u/js-empty-values';
+import {InvalidArgumentError} from '../../../errors/index.js';
+import {ModelDefinitionUtils} from '../model-definition-utils.js';
+
+/**
+ * Required property validator.
+ */
+export class RequiredPropertyValidator extends Service {
+  /**
+   * Validate.
+   *
+   * @param {string} modelName
+   * @param {object} modelData
+   * @param {boolean} [isPartial]
+   */
+  validate(modelName, modelData, isPartial = false) {
+    if (!modelName || typeof modelName !== 'string') {
+      throw new InvalidArgumentError(
+        'Parameter "modelName" must be a non-empty String, but %v was given.',
+        modelName,
+      );
+    }
+    if (
+      !modelData ||
+      typeof modelData !== 'object' ||
+      Array.isArray(modelData)
+    ) {
+      throw new InvalidArgumentError(
+        'Data of the model %v should be an Object, but %v was given.',
+        modelName,
+        modelData,
+      );
+    }
+    if (typeof isPartial !== 'boolean') {
+      throw new InvalidArgumentError(
+        'Parameter "isPartial" must be a Boolean, but %v was given.',
+        isPartial,
+      );
+    }
+    const propDefs =
+      this.getService(
+        ModelDefinitionUtils,
+      ).getPropertiesDefinitionInBaseModelHierarchy(modelName);
+    const propNames = Object.keys(isPartial ? modelData : propDefs);
+    const blankValuesService = this.getService(BlankValuesService);
+    for (const propName of propNames) {
+      const propDef = propDefs[propName];
+      if (!propDef || typeof propDef !== 'object') {
+        continue;
+      }
+      // проверка основного значения
+      const propValue = modelData[propName];
+      if (propDef.required) {
+        const propType = propDef.type || DataType.ANY;
+        if (blankValuesService.isBlankOf(propType, propValue)) {
+          throw new InvalidArgumentError(
+            'Property %v of the model %v is required, but %v was given.',
+            propName,
+            modelName,
+            propValue,
+          );
+        }
+      }
+      // проверка вложенного объекта
+      if (
+        propDef.type === DataType.OBJECT &&
+        propDef.model &&
+        propValue !== null &&
+        typeof propValue === 'object' &&
+        !Array.isArray(propValue)
+      ) {
+        this.validate(propDef.model, propValue);
+      }
+      // проверка массива объектов
+      else if (
+        propDef.type === DataType.ARRAY &&
+        propDef.itemType === DataType.OBJECT &&
+        propDef.itemModel &&
+        Array.isArray(propValue)
+      ) {
+        propValue.forEach(itemData => {
+          if (
+            itemData !== null &&
+            typeof itemData === 'object' &&
+            !Array.isArray(itemData)
+          ) {
+            this.validate(propDef.itemModel, itemData);
+          }
+        });
+      }
+    }
+  }
+}

+ 583 - 0
src/definition/model/properties/required-property-validator.spec.js

@@ -0,0 +1,583 @@
+import {expect} from 'chai';
+import {format} from '@e22m4u/js-format';
+import {DatabaseSchema} from '../../../database-schema.js';
+import {RequiredPropertyValidator} from './required-property-validator.js';
+import {DataType} from './data-type.js';
+import {BlankValuesService} from '@e22m4u/js-empty-values';
+
+describe('RequiredPropertyValidator', function () {
+  describe('validate', function () {
+    it('should require the parameter "modelName" to be a non-empty String', function () {
+      const dbs = new DatabaseSchema();
+      const S = dbs.getService(RequiredPropertyValidator);
+      dbs.defineModel({name: 'model'});
+      const throwable = v => () => S.validate(v, {});
+      const error = s =>
+        format(
+          'Parameter "modelName" must be a non-empty String, but %s was given.',
+          s,
+        );
+      expect(throwable('')).to.throw(error('""'));
+      expect(throwable(10)).to.throw(error('10'));
+      expect(throwable(0)).to.throw(error('0'));
+      expect(throwable(true)).to.throw(error('true'));
+      expect(throwable(false)).to.throw(error('false'));
+      expect(throwable([])).to.throw(error('Array'));
+      expect(throwable({})).to.throw(error('Object'));
+      expect(throwable(undefined)).to.throw(error('undefined'));
+      expect(throwable(null)).to.throw(error('null'));
+      throwable('model')();
+    });
+
+    it('should require the parameter "modelData" to be an Object', function () {
+      const dbs = new DatabaseSchema();
+      const S = dbs.getService(RequiredPropertyValidator);
+      dbs.defineModel({name: 'model'});
+      const throwable = v => () => S.validate('model', v);
+      const error = s =>
+        format(
+          'Data of the model "model" should be an Object, but %s was given.',
+          s,
+        );
+      expect(throwable('str')).to.throw(error('"str"'));
+      expect(throwable('')).to.throw(error('""'));
+      expect(throwable(10)).to.throw(error('10'));
+      expect(throwable(0)).to.throw(error('0'));
+      expect(throwable(true)).to.throw(error('true'));
+      expect(throwable(false)).to.throw(error('false'));
+      expect(throwable([])).to.throw(error('Array'));
+      expect(throwable(undefined)).to.throw(error('undefined'));
+      expect(throwable(null)).to.throw(error('null'));
+      throwable({})();
+    });
+
+    it('should require the parameter "isPartial" to be an Object', function () {
+      const dbs = new DatabaseSchema();
+      const S = dbs.getService(RequiredPropertyValidator);
+      dbs.defineModel({name: 'model'});
+      const throwable = v => () => S.validate('model', {}, v);
+      const error = s =>
+        format('Parameter "isPartial" must be a Boolean, but %s was given.', s);
+      expect(throwable('str')).to.throw(error('"str"'));
+      expect(throwable('')).to.throw(error('""'));
+      expect(throwable(10)).to.throw(error('10'));
+      expect(throwable(0)).to.throw(error('0'));
+      expect(throwable([])).to.throw(error('Array'));
+      expect(throwable({})).to.throw(error('Object'));
+      expect(throwable(null)).to.throw(error('null'));
+      throwable(true)();
+      throwable(false)();
+      throwable(undefined)();
+    });
+
+    it('should not throw an error if no properties in the model definition', function () {
+      const dbs = new DatabaseSchema();
+      const S = dbs.getService(RequiredPropertyValidator);
+      dbs.defineModel({name: 'model'});
+      S.validate('model', {foo: 'bar', baz: undefined});
+    });
+
+    it('should not throw an error if the property definition in short form', function () {
+      const dbs = new DatabaseSchema();
+      const S = dbs.getService(RequiredPropertyValidator);
+      dbs.defineModel({name: 'model', properties: {foo: DataType.STRING}});
+      S.validate('model', {foo: 'bar'});
+    });
+
+    it('should not throw an error if a required property is not blank', function () {
+      const dbs = new DatabaseSchema();
+      const S = dbs.getService(RequiredPropertyValidator);
+      const blankValues = S.getService(BlankValuesService);
+      blankValues.setBlankValues([undefined]);
+      dbs.defineModel({
+        name: 'model',
+        properties: {
+          foo: {
+            type: DataType.STRING,
+            required: true,
+          },
+        },
+      });
+      S.validate('model', {foo: 'bar'});
+    });
+
+    it('should throw an error if a required property is blank', function () {
+      const dbs = new DatabaseSchema();
+      const S = dbs.getService(RequiredPropertyValidator);
+      const blankValues = S.getService(BlankValuesService);
+      blankValues.setBlankValues([undefined]);
+      dbs.defineModel({
+        name: 'model',
+        properties: {
+          foo: {
+            type: DataType.STRING,
+            required: true,
+          },
+        },
+      });
+      const throwable = () => S.validate('model', {foo: undefined});
+      expect(throwable).to.throw(
+        'Property "foo" of the model "model" is required, ' +
+          'but undefined was given.',
+      );
+    });
+
+    describe('embedded model', function () {
+      it('should not throw an error if no data is provided for an embedded model', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            embedded: {
+              type: DataType.OBJECT,
+              model: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.validate('modelA', {embedded: undefined});
+      });
+
+      it('should throw an error if an embedded model is required but not provided', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            embedded: {
+              type: DataType.OBJECT,
+              model: 'modelB',
+              required: true,
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        const throwable = () => S.validate('modelA', {embedded: undefined});
+        expect(throwable).to.throw(
+          'Property "embedded" of the model "modelA" is required, ' +
+            'but undefined was given.',
+        );
+      });
+
+      it('should allow a model data to have properties without a specified schema', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            embedded: {
+              type: DataType.OBJECT,
+              model: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.validate('modelA', {embedded: {bar: 'baz', qux: undefined}});
+      });
+
+      it('should allow omit a model data when its model has a required property', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            embedded: {
+              type: DataType.OBJECT,
+              model: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        S.validate('modelA', {embedded: undefined});
+      });
+
+      it('should allow omit an optional property for an embedded model', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            embedded: {
+              type: DataType.OBJECT,
+              model: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.validate('modelA', {embedded: {}});
+      });
+
+      it('should throw an error if a required property is not provided', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            embedded: {
+              type: DataType.OBJECT,
+              model: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        const throwable = () => S.validate('modelA', {embedded: {}});
+        expect(throwable).to.throw(
+          'Property "foo" of the model "modelB" is required, ' +
+            'but undefined was given.',
+        );
+      });
+    });
+
+    describe('object array', function () {
+      it('should allow omit an optional array', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.validate('modelA', {});
+      });
+
+      it('should allow a required array to be empty', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+              required: true,
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.validate('modelA', {array: []});
+      });
+
+      it('should allow omit an optional array even if the item model has a required property', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        S.validate('modelA', {});
+      });
+
+      it('should allow an empty array even if the item model has a required property', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        S.validate('modelA', {});
+      });
+
+      it('should throw an error when a required array is not provided', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+              required: true,
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        const throwable = () => S.validate('modelA', {});
+        expect(throwable).to.throw(
+          'Property "array" of the model "modelA" is required, ' +
+            'but undefined was given.',
+        );
+      });
+
+      it('should allow omit an optional property of the item model', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+              required: true,
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.validate('modelA', {array: [{}]});
+      });
+
+      it('should allow an item date to have properties without a specified schema', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+              required: true,
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.validate('modelA', {array: [{bar: 'baz', qux: undefined}]});
+      });
+    });
+
+    describe('isPartial', function () {
+      it('should throw an error if a required property is blank', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        const throwable = () => S.validate('model', {foo: undefined}, true);
+        expect(throwable).to.throw(
+          'Property "foo" of the model "model" is required, ' +
+            'but undefined was given.',
+        );
+      });
+
+      it('should not validate a required but not provided properties', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        S.validate('model', {}, true);
+      });
+
+      it('should validate not provided properties of an embedded model', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            embedded: {
+              type: DataType.OBJECT,
+              model: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        const throwable = () => S.validate('modelA', {embedded: {}}, true);
+        expect(throwable).to.throw(
+          'Property "foo" of the model "modelB" is required, ' +
+            'but undefined was given.',
+        );
+      });
+
+      it('should validate not provided properties of an item model', function () {
+        const dbs = new DatabaseSchema();
+        const S = dbs.getService(RequiredPropertyValidator);
+        const blankValues = S.getService(BlankValuesService);
+        blankValues.setBlankValues([undefined]);
+        dbs.defineModel({
+          name: 'modelA',
+          properties: {
+            array: {
+              type: DataType.ARRAY,
+              itemType: DataType.OBJECT,
+              itemModel: 'modelB',
+            },
+          },
+        });
+        dbs.defineModel({
+          name: 'modelB',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        const throwable = () => S.validate('modelA', {array: [{}]}, true);
+        expect(throwable).to.throw(
+          'Property "foo" of the model "modelB" is required, ' +
+            'but undefined was given.',
+        );
+      });
+    });
+  });
+});