This is an automated email from the ASF dual-hosted git repository.

apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new c2ec9c94e7 IGNITE-21428 Add WITH params for zone (#3374)
c2ec9c94e7 is described below

commit c2ec9c94e7e53baa483711d0cef3dfd1188dbb34
Author: Vadim Pakhnushev <8614891+valep...@users.noreply.github.com>
AuthorDate: Thu Mar 14 18:56:20 2024 +0300

    IGNITE-21428 Add WITH params for zone (#3374)
---
 .../apache/ignite/catalog/annotations/Zone.java    |  46 +++-
 .../catalog/definitions/ColumnDefinition.java      |   5 +
 .../catalog/definitions/TableDefinition.java       |  10 +-
 .../ignite/catalog/definitions/ZoneDefinition.java | 206 ++++++++++++++-
 .../ignite/internal/catalog/ItCatalogDslTest.java  |   9 +-
 .../catalog/sql/CreateFromAnnotationsImpl.java     |  25 +-
 .../catalog/sql/CreateFromDefinitionImpl.java      |  24 +-
 .../internal/catalog/sql/CreateZoneImpl.java       |  61 ++++-
 .../ignite/internal/catalog/sql/WithOption.java    |  24 ++
 .../catalog/sql/CreateFromAnnotationsTest.java     |  42 ++-
 .../catalog/sql/CreateFromDefinitionTest.java      |  10 +-
 .../catalog/sql/InvalidDefinitionTest.java         | 291 ++++++++++++++-------
 12 files changed, 617 insertions(+), 136 deletions(-)

diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java 
b/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java
index fd9589d86c..fc8cff04c1 100644
--- a/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java
+++ b/modules/api/src/main/java/org/apache/ignite/catalog/annotations/Zone.java
@@ -52,9 +52,51 @@ public @interface Zone {
     int replicas() default -1;
 
     /**
-     * Engine name.
+     * Affinity function.
      *
-     * @return Engine name.
+     * @return Affinity function.
+     */
+    String affinityFunction() default "";
+
+    /**
+     * Timeout in seconds between node added or node left topology event 
itself and data nodes switch.
+     *
+     * @return Timeout.
+     */
+    int dataNodesAutoAdjust() default -1;
+
+    /**
+     * Timeout in seconds between node added topology event itself and data 
nodes switch.
+     *
+     * @return Timeout.
+     */
+    int dataNodesAutoAdjustScaleUp() default -1;
+
+    /**
+     * Timeout in seconds between node left topology event itself and data 
nodes switch.
+     *
+     * @return Timeout.
+     */
+    int dataNodesAutoAdjustScaleDown() default -1;
+
+    /**
+     * Nodes filter.
+     *
+     * @return Nodes filter.
+     */
+    String filter() default "";
+
+    /**
+     * The storage engine name.
+     *
+     * @return The storage engine name.
      */
     ZoneEngine engine() default ZoneEngine.DEFAULT;
+
+    /**
+     * Data region name within the storage engine.
+     *
+     * @return Data region name.
+     */
+    String dataRegion() default "";
 }
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
index facebb6ff2..af32f8dd92 100644
--- 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ColumnDefinition.java
@@ -49,6 +49,7 @@ public class ColumnDefinition {
         if (name.isBlank()) {
             throw new IllegalArgumentException("Column name must not be 
blank.");
         }
+        Objects.requireNonNull(type, "Column type must not be null.");
 
         return new ColumnDefinition(name, type, null);
     }
@@ -65,6 +66,10 @@ public class ColumnDefinition {
         if (name.isBlank()) {
             throw new IllegalArgumentException("Column name must not be 
blank.");
         }
+        Objects.requireNonNull(definition, "Column definition must not be 
null.");
+        if (definition.isBlank()) {
+            throw new IllegalArgumentException("Column definition must not be 
blank.");
+        }
 
         return new ColumnDefinition(name, null, definition);
     }
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
index 7ac92cbf60..abf9d4651e 100644
--- 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/TableDefinition.java
@@ -247,6 +247,11 @@ public class TableDefinition {
         }
 
         Builder tableName(String name) {
+            Objects.requireNonNull(name, "Table name must not be null.");
+            if (name.isBlank()) {
+                throw new IllegalArgumentException("Table name must not be 
blank.");
+            }
+
             this.tableName = name;
             return this;
         }
@@ -495,11 +500,6 @@ public class TableDefinition {
          * @return Table definition.
          */
         public TableDefinition build() {
-            Objects.requireNonNull(tableName, "Table name must not be null.");
-            if (tableName.isBlank()) {
-                throw new IllegalArgumentException("Table name must not be 
blank.");
-            }
-
             return new TableDefinition(
                     tableName,
                     schemaName,
diff --git 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
index 227e753ba6..aa4260f0c9 100644
--- 
a/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
+++ 
b/modules/api/src/main/java/org/apache/ignite/catalog/definitions/ZoneDefinition.java
@@ -32,20 +32,44 @@ public class ZoneDefinition {
 
     private final Integer replicas;
 
+    private final String affinity;
+
+    private final Integer dataNodesAutoAdjust;
+
+    private final Integer dataNodesAutoAdjustScaleUp;
+
+    private final Integer dataNodesAutoAdjustScaleDown;
+
+    private final String filter;
+
     private final ZoneEngine engine;
 
+    private final String dataRegion;
+
     private ZoneDefinition(
             String zoneName,
             boolean ifNotExists,
             Integer partitions,
             Integer replicas,
-            ZoneEngine engine
+            String affinity,
+            Integer dataNodesAutoAdjust,
+            Integer dataNodesAutoAdjustScaleUp,
+            Integer dataNodesAutoAdjustScaleDown,
+            String filter,
+            ZoneEngine engine,
+            String dataRegion
     ) {
         this.zoneName = zoneName;
         this.ifNotExists = ifNotExists;
         this.partitions = partitions;
         this.replicas = replicas;
+        this.affinity = affinity;
+        this.dataNodesAutoAdjust = dataNodesAutoAdjust;
+        this.dataNodesAutoAdjustScaleUp = dataNodesAutoAdjustScaleUp;
+        this.dataNodesAutoAdjustScaleDown = dataNodesAutoAdjustScaleDown;
+        this.filter = filter;
         this.engine = engine;
+        this.dataRegion = dataRegion;
     }
 
     /**
@@ -94,6 +118,51 @@ public class ZoneDefinition {
         return replicas;
     }
 
+    /**
+     * Returns affinity function.
+     *
+     * @return Affinity function.
+     */
+    public String affinityFunction() {
+        return affinity;
+    }
+
+    /**
+     * Returns timeout in seconds between node added or node left topology 
event itself and data nodes switch.
+     *
+     * @return Timeout.
+     */
+    public Integer dataNodesAutoAdjust() {
+        return dataNodesAutoAdjust;
+    }
+
+    /**
+     * Returns timeout in seconds between node added topology event itself and 
data nodes switch.
+     *
+     * @return Timeout.
+     */
+    public Integer dataNodesAutoAdjustScaleUp() {
+        return dataNodesAutoAdjustScaleUp;
+    }
+
+    /**
+     * Returns timeout in seconds between node left topology event itself and 
data nodes switch.
+     *
+     * @return Timeout.
+     */
+    public Integer dataNodesAutoAdjustScaleDown() {
+        return dataNodesAutoAdjustScaleDown;
+    }
+
+    /**
+     * Returns nodes filter.
+     *
+     * @return Nodes filter.
+     */
+    public String filter() {
+        return filter;
+    }
+
     /**
      * Returns the storage engine name.
      *
@@ -103,6 +172,15 @@ public class ZoneDefinition {
         return engine;
     }
 
+    /**
+     * Returns the data region name within the storage engine.
+     *
+     * @return Data region name.
+     */
+    public String dataRegion() {
+        return dataRegion;
+    }
+
     /**
      * Returns new builder using this definition.
      *
@@ -124,8 +202,20 @@ public class ZoneDefinition {
 
         private Integer replicas;
 
+        private String affinity;
+
+        private Integer dataNodesAutoAdjust;
+
+        private Integer dataNodesAutoAdjustScaleUp;
+
+        private Integer dataNodesAutoAdjustScaleDown;
+
+        private String filter;
+
         private ZoneEngine engine = ZoneEngine.DEFAULT;
 
+        private String dataRegion;
+
         private Builder() {}
 
         private Builder(ZoneDefinition definition) {
@@ -133,7 +223,13 @@ public class ZoneDefinition {
             ifNotExists = definition.ifNotExists;
             partitions = definition.partitions;
             replicas = definition.replicas;
+            affinity = definition.affinity;
+            dataNodesAutoAdjust = definition.dataNodesAutoAdjust;
+            dataNodesAutoAdjustScaleUp = definition.dataNodesAutoAdjustScaleUp;
+            dataNodesAutoAdjustScaleDown = 
definition.dataNodesAutoAdjustScaleDown;
+            filter = definition.filter;
             engine = definition.engine;
+            dataRegion = definition.dataRegion;
         }
 
         /**
@@ -143,6 +239,11 @@ public class ZoneDefinition {
          * @return This builder instance.
          */
         Builder zoneName(String zoneName) {
+            Objects.requireNonNull(zoneName, "Zone name must not be null.");
+            if (zoneName.isBlank()) {
+                throw new IllegalArgumentException("Zone name must not be 
blank.");
+            }
+
             this.zoneName = zoneName;
             return this;
         }
@@ -183,6 +284,80 @@ public class ZoneDefinition {
             return this;
         }
 
+        /**
+         * Sets the affinity function.
+         *
+         * @param affinity Affinity function.
+         * @return This builder instance.
+         */
+        public Builder affinity(String affinity) {
+            Objects.requireNonNull(affinity, "Affinity function must not be 
null.");
+            if (affinity.isBlank()) {
+                throw new IllegalArgumentException("Affinity function must not 
be blank.");
+            }
+
+            this.affinity = affinity;
+            return this;
+        }
+
+        /**
+         * Sets timeout in seconds between node added or node left topology 
event itself and data nodes switch.
+         *
+         * @param adjust Timeout.
+         * @return This builder instance.
+         */
+        public Builder dataNodesAutoAdjust(Integer adjust) {
+            Objects.requireNonNull(
+                    adjust,
+                    "Timeout between node added or node left topology event 
itself and data nodes switch must not be null."
+            );
+
+            this.dataNodesAutoAdjust = adjust;
+            return this;
+        }
+
+        /**
+         * Sets timeout in seconds between node added topology event itself 
and data nodes switch.
+         *
+         * @param adjust Timeout.
+         * @return This builder instance.
+         */
+        public Builder dataNodesAutoAdjustScaleUp(Integer adjust) {
+            Objects.requireNonNull(adjust, "Timeout between node added 
topology event itself and data nodes switch must not be null.");
+
+            this.dataNodesAutoAdjustScaleUp = adjust;
+            return this;
+        }
+
+        /**
+         * Sets timeout in seconds between node left topology event itself and 
data nodes switch.
+         *
+         * @param adjust Timeout.
+         * @return This builder instance.
+         */
+        public Builder dataNodesAutoAdjustScaleDown(Integer adjust) {
+            Objects.requireNonNull(adjust, "Timeout between node left topology 
event itself and data nodes switch must not be null.");
+
+            this.dataNodesAutoAdjustScaleDown = adjust;
+            return this;
+        }
+
+        /**
+         * Sets nodes filter.
+         *
+         * @param filter Nodes filter.
+         * @return This builder instance.
+         */
+        public Builder filter(String filter) {
+            Objects.requireNonNull(filter, "Filter must not be null.");
+            if (filter.isBlank()) {
+                throw new IllegalArgumentException("Filter must not be 
blank.");
+            }
+
+            this.filter = filter;
+            return this;
+        }
+
         /**
          * Sets the storage engine name.
          *
@@ -196,23 +371,40 @@ public class ZoneDefinition {
             return this;
         }
 
+        /**
+         * Sets the data region name within the storage engine.
+         *
+         * @param dataRegion Data region name within the storage engine.
+         * @return This builder instance.
+         */
+        public Builder dataRegion(String dataRegion) {
+            Objects.requireNonNull(dataRegion, "Data region must not be 
null.");
+            if (dataRegion.isBlank()) {
+                throw new IllegalArgumentException("Data region must not be 
blank.");
+            }
+
+            this.dataRegion = dataRegion;
+            return this;
+        }
+
         /**
          * Builds the zone definition.
          *
          * @return Zone definition.
          */
         public ZoneDefinition build() {
-            Objects.requireNonNull(zoneName, "Zone name must not be null.");
-            if (zoneName.isBlank()) {
-                throw new IllegalArgumentException("Zone name must not be 
blank.");
-            }
-
             return new ZoneDefinition(
                     zoneName,
                     ifNotExists,
                     partitions,
                     replicas,
-                    engine
+                    affinity,
+                    dataNodesAutoAdjust,
+                    dataNodesAutoAdjustScaleUp,
+                    dataNodesAutoAdjustScaleDown,
+                    filter,
+                    engine,
+                    dataRegion
             );
         }
     }
diff --git 
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
index 5cd8af58a8..cfd590cb42 100644
--- 
a/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
+++ 
b/modules/catalog-dsl/src/integrationTest/java/org/apache/ignite/internal/catalog/ItCatalogDslTest.java
@@ -29,6 +29,7 @@ import java.util.UUID;
 import org.apache.ignite.catalog.ColumnType;
 import org.apache.ignite.catalog.IgniteCatalog;
 import org.apache.ignite.catalog.SortOrder;
+import org.apache.ignite.catalog.ZoneEngine;
 import org.apache.ignite.catalog.annotations.Column;
 import org.apache.ignite.catalog.annotations.ColumnRef;
 import org.apache.ignite.catalog.annotations.Id;
@@ -72,7 +73,13 @@ class ItCatalogDslTest extends 
ClusterPerClassIntegrationTest {
     @Test
     void zoneCreateAndDropByDefinition() {
         // Given zone definition
-        ZoneDefinition zoneDefinition = 
ZoneDefinition.builder(ZONE_NAME).build();
+        ZoneDefinition zoneDefinition = ZoneDefinition.builder(ZONE_NAME)
+                .affinity("affinity")
+                .dataNodesAutoAdjust(1)
+                .filter("filter")
+                .engine(ZoneEngine.AIMEM)
+                .dataRegion("dataRegion")
+                .build();
 
         // When create zone from definition
         catalog().createZone(zoneDefinition).execute();
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
index c0427aae8d..836d3b135a 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsImpl.java
@@ -111,7 +111,6 @@ class CreateFromAnnotationsImpl extends 
AbstractCatalogQuery {
             String zoneName = zone.value().isEmpty() ? zoneRef.getSimpleName() 
: zone.value();
             createTable.zone(zoneName);
             createZone.name(zoneName);
-            createZone.engine(zone.engine());
             if (zone.partitions() > 0) {
                 createZone.partitions(zone.partitions());
             }
@@ -119,7 +118,29 @@ class CreateFromAnnotationsImpl extends 
AbstractCatalogQuery {
                 createZone.replicas(zone.replicas());
             }
 
-            // TODO https://issues.apache.org/jira/browse/IGNITE-21428
+            if (!zone.affinityFunction().isEmpty()) {
+                createZone.affinity(zone.affinityFunction());
+            }
+
+            if (zone.dataNodesAutoAdjust() > 0) {
+                createZone.dataNodesAutoAdjust(zone.dataNodesAutoAdjust());
+            }
+            if (zone.dataNodesAutoAdjustScaleUp() > 0) {
+                
createZone.dataNodesAutoAdjustScaleUp(zone.dataNodesAutoAdjustScaleUp());
+            }
+            if (zone.dataNodesAutoAdjustScaleDown() > 0) {
+                
createZone.dataNodesAutoAdjustScaleDown(zone.dataNodesAutoAdjustScaleDown());
+            }
+
+            if (!zone.filter().isEmpty()) {
+                createZone.filter(zone.filter());
+            }
+
+            createZone.engine(zone.engine());
+
+            if (!zone.dataRegion().isEmpty()) {
+                createZone.dataRegion(zone.dataRegion());
+            }
         }
     }
 
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
index b62035bc69..472bda3bfd 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionImpl.java
@@ -55,9 +55,31 @@ class CreateFromDefinitionImpl extends AbstractCatalogQuery {
         if (isGreaterThanZero(def.replicas())) {
             createZone.replicas(def.replicas());
         }
+
+        if (!StringUtils.nullOrBlank(def.affinityFunction())) {
+            createZone.affinity(def.affinityFunction());
+        }
+
+        if (isGreaterThanZero(def.dataNodesAutoAdjust())) {
+            createZone.dataNodesAutoAdjust(def.dataNodesAutoAdjust());
+        }
+        if (isGreaterThanZero(def.dataNodesAutoAdjustScaleUp())) {
+            
createZone.dataNodesAutoAdjustScaleUp(def.dataNodesAutoAdjustScaleUp());
+        }
+        if (isGreaterThanZero(def.dataNodesAutoAdjustScaleDown())) {
+            
createZone.dataNodesAutoAdjustScaleDown(def.dataNodesAutoAdjustScaleDown());
+        }
+
+        if (!StringUtils.nullOrBlank(def.filter())) {
+            createZone.filter(def.filter());
+        }
+
         createZone.engine(def.engine());
 
-        // TODO https://issues.apache.org/jira/browse/IGNITE-21428
+        if (!StringUtils.nullOrBlank(def.dataRegion())) {
+            createZone.dataRegion(def.dataRegion());
+        }
+
         return this;
     }
 
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
index 21de7464bc..1b3899a091 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/CreateZoneImpl.java
@@ -45,7 +45,7 @@ class CreateZoneImpl extends AbstractCatalogQuery {
     }
 
     CreateZoneImpl name(String... names) {
-        Objects.requireNonNull(names, "Zone name must not be null");
+        Objects.requireNonNull(names, "Zone name must not be null.");
 
         this.zoneName = new Name(names);
         return this;
@@ -56,25 +56,70 @@ class CreateZoneImpl extends AbstractCatalogQuery {
         return this;
     }
 
-    CreateZoneImpl engine(ZoneEngine engine) {
-        this.engine = engine;
-        return this;
-    }
-
     CreateZoneImpl replicas(Integer n) {
-        Objects.requireNonNull(n, "Replicas count must not be null");
+        Objects.requireNonNull(n, "Replicas count must not be null.");
 
         withOptions.add(WithOption.replicas(n));
         return this;
     }
 
     CreateZoneImpl partitions(Integer n) {
-        Objects.requireNonNull(n, "Partitions must not be null");
+        Objects.requireNonNull(n, "Partitions must not be null.");
 
         withOptions.add(WithOption.partitions(n));
         return this;
     }
 
+    CreateZoneImpl affinity(String affinity) {
+        Objects.requireNonNull(affinity, "Affinity function must not be 
null.");
+
+        withOptions.add(WithOption.affinity(affinity));
+        return this;
+    }
+
+    CreateZoneImpl dataNodesAutoAdjust(Integer adjust) {
+        Objects.requireNonNull(
+                adjust,
+                "Timeout between node added or node left topology event itself 
and data nodes switch must not be null."
+        );
+
+        withOptions.add(WithOption.dataNodesAutoAdjust(adjust));
+        return this;
+    }
+
+    CreateZoneImpl dataNodesAutoAdjustScaleUp(Integer adjust) {
+        Objects.requireNonNull(adjust, "Timeout between node added topology 
event itself and data nodes switch must not be null.");
+
+        withOptions.add(WithOption.dataNodesAutoAdjustScaleUp(adjust));
+        return this;
+    }
+
+    CreateZoneImpl dataNodesAutoAdjustScaleDown(Integer adjust) {
+        Objects.requireNonNull(adjust, "Timeout between node left topology 
event itself and data nodes switch must not be null.");
+
+        withOptions.add(WithOption.dataNodesAutoAdjustScaleDown(adjust));
+        return this;
+    }
+
+    CreateZoneImpl filter(String filter) {
+        Objects.requireNonNull(filter, "Filter must not be null.");
+
+        withOptions.add(WithOption.filter(filter));
+        return this;
+    }
+
+    CreateZoneImpl engine(ZoneEngine engine) {
+        this.engine = engine;
+        return this;
+    }
+
+    CreateZoneImpl dataRegion(String dataRegion) {
+        Objects.requireNonNull(dataRegion, "Data region must not be null.");
+
+        withOptions.add(WithOption.dataRegion(dataRegion));
+        return this;
+    }
+
     @Override
     protected void accept(QueryContext ctx) {
         ctx.sql("CREATE ZONE ");
diff --git 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java
 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java
index b4a64811c8..d6d5355835 100644
--- 
a/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java
+++ 
b/modules/catalog-dsl/src/main/java/org/apache/ignite/internal/catalog/sql/WithOption.java
@@ -39,6 +39,30 @@ class WithOption extends QueryPart {
         return new WithOption("REPLICAS", replicas);
     }
 
+    public static WithOption affinity(String affinity) {
+        return new WithOption("AFFINITY_FUNCTION", affinity);
+    }
+
+    public static WithOption dataNodesAutoAdjust(Integer adjust) {
+        return new WithOption("DATA_NODES_AUTO_ADJUST", adjust);
+    }
+
+    public static WithOption dataNodesAutoAdjustScaleUp(Integer adjust) {
+        return new WithOption("DATA_NODES_AUTO_ADJUST_SCALE_UP", adjust);
+    }
+
+    public static WithOption dataNodesAutoAdjustScaleDown(Integer adjust) {
+        return new WithOption("DATA_NODES_AUTO_ADJUST_SCALE_DOWN", adjust);
+    }
+
+    public static WithOption filter(String filter) {
+        return new WithOption("DATA_NODES_FILTER", filter);
+    }
+
+    public static WithOption dataRegion(String dataRegion) {
+        return new WithOption("DATAREGION", dataRegion);
+    }
+
     @Override
     protected void accept(QueryContext ctx) {
         ctx.sql(name).sql("=");
diff --git 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
index 32fa879300..cd50f1008a 100644
--- 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
+++ 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromAnnotationsTest.java
@@ -63,9 +63,15 @@ class CreateFromAnnotationsTest {
     void testDefinitionCompatibility() {
         ZoneDefinition zoneDefinition = ZoneDefinition.builder("zone_test")
                 .ifNotExists()
-                .engine(ZoneEngine.AIMEM)
                 .partitions(1)
                 .replicas(3)
+                .affinity("affinity")
+                .dataNodesAutoAdjust(1)
+                .dataNodesAutoAdjustScaleDown(2)
+                .dataNodesAutoAdjustScaleUp(3)
+                .filter("filter")
+                .engine(ZoneEngine.AIMEM)
+                .dataRegion("dataRegion")
                 .build();
         String sqlZoneFromDefinition = new CreateFromDefinitionImpl(null, 
Options.DEFAULT).from(zoneDefinition).toSqlString();
 
@@ -88,7 +94,9 @@ class CreateFromAnnotationsTest {
         // primitive/boxed key class is a primary key with default name 'id'
         assertThat(
                 createTable().processKeyValueClasses(Integer.class, 
PojoValue.class).toSqlString(),
-                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3;"
+                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity',"
+                        + " DATA_NODES_AUTO_ADJUST=1, 
DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2,"
+                        + " DATA_NODES_FILTER='filter', 
DATAREGION='dataRegion';"
                         + "CREATE TABLE IF NOT EXISTS pojo_value_test (id int, 
f_name varchar, l_name varchar, str varchar,"
                         + " PRIMARY KEY (id)) COLOCATE BY (id, id_str) WITH 
PRIMARY_ZONE='ZONE_TEST';"
                         + "CREATE INDEX IF NOT EXISTS ix_pojo ON 
pojo_value_test (f_name, l_name desc);")
@@ -100,7 +108,9 @@ class CreateFromAnnotationsTest {
         // primitive/boxed key class is a primary key with default name 'id'
         assertThat(
                 createTableQuoted().processKeyValueClasses(Integer.class, 
PojoValue.class).toSqlString(),
-                is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3;"
+                is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity',"
+                        + " DATA_NODES_AUTO_ADJUST=1, 
DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2,"
+                        + " DATA_NODES_FILTER='filter', 
DATAREGION='dataRegion';"
                         + "CREATE TABLE IF NOT EXISTS \"pojo_value_test\" 
(\"id\" int, \"f_name\" varchar, \"l_name\" varchar,"
                         + " \"str\" varchar, PRIMARY KEY (\"id\")) COLOCATE BY 
(\"id\", \"id_str\") WITH PRIMARY_ZONE='ZONE_TEST';"
                         + "CREATE INDEX IF NOT EXISTS \"ix_pojo\" ON 
\"pojo_value_test\" (\"f_name\", \"l_name\" desc);")
@@ -112,7 +122,9 @@ class CreateFromAnnotationsTest {
         // key class fields (annotated only) is a composite primary keys
         assertThat(
                 createTable().processKeyValueClasses(PojoKey.class, 
PojoValue.class).toSqlString(),
-                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3;"
+                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity',"
+                        + " DATA_NODES_AUTO_ADJUST=1, 
DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2,"
+                        + " DATA_NODES_FILTER='filter', 
DATAREGION='dataRegion';"
                         + "CREATE TABLE IF NOT EXISTS pojo_value_test (id int, 
id_str varchar(20), f_name varchar, l_name varchar,"
                         + " str varchar, PRIMARY KEY (id, id_str)) COLOCATE BY 
(id, id_str) WITH PRIMARY_ZONE='ZONE_TEST';"
                         + "CREATE INDEX IF NOT EXISTS ix_pojo ON 
pojo_value_test (f_name, l_name desc);")
@@ -124,7 +136,9 @@ class CreateFromAnnotationsTest {
         // key class fields (annotated only) is a composite primary keys
         assertThat(
                 createTableQuoted().processKeyValueClasses(PojoKey.class, 
PojoValue.class).toSqlString(),
-                is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3;"
+                is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity',"
+                        + " DATA_NODES_AUTO_ADJUST=1, 
DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2,"
+                        + " DATA_NODES_FILTER='filter', 
DATAREGION='dataRegion';"
                         + "CREATE TABLE IF NOT EXISTS \"pojo_value_test\" 
(\"id\" int, \"id_str\" varchar(20), \"f_name\" varchar,"
                         + " \"l_name\" varchar, \"str\" varchar, PRIMARY KEY 
(\"id\", \"id_str\")) COLOCATE BY (\"id\", \"id_str\")"
                         + " WITH PRIMARY_ZONE='ZONE_TEST';"
@@ -136,7 +150,9 @@ class CreateFromAnnotationsTest {
     void createFromRecordClass() {
         assertThat(
                 createTable().processRecordClass(Pojo.class).toSqlString(),
-                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3;"
+                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity',"
+                        + " DATA_NODES_AUTO_ADJUST=1, 
DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2,"
+                        + " DATA_NODES_FILTER='filter', 
DATAREGION='dataRegion';"
                         + "CREATE TABLE IF NOT EXISTS pojo_test (id int, 
id_str varchar(20), f_name varchar(20) not null default 'a',"
                         + " l_name varchar, str varchar, PRIMARY KEY (id, 
id_str))"
                         + " COLOCATE BY (id, id_str) WITH 
PRIMARY_ZONE='ZONE_TEST';"
@@ -148,7 +164,9 @@ class CreateFromAnnotationsTest {
     void createFromRecordClassQuoted() {
         assertThat(
                 
createTableQuoted().processRecordClass(Pojo.class).toSqlString(),
-                is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3;"
+                is("CREATE ZONE IF NOT EXISTS \"zone_test\" ENGINE AIMEM WITH 
PARTITIONS=1, REPLICAS=3, AFFINITY_FUNCTION='affinity',"
+                        + " DATA_NODES_AUTO_ADJUST=1, 
DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2,"
+                        + " DATA_NODES_FILTER='filter', 
DATAREGION='dataRegion';"
                         + "CREATE TABLE IF NOT EXISTS \"pojo_test\" (\"id\" 
int, \"id_str\" varchar(20),"
                         + " \"f_name\" varchar(20) not null default 'a', 
\"l_name\" varchar, \"str\" varchar,"
                         + " PRIMARY KEY (\"id\", \"id_str\")) COLOCATE BY 
(\"id\", \"id_str\") WITH PRIMARY_ZONE='ZONE_TEST';"
@@ -203,9 +221,15 @@ class CreateFromAnnotationsTest {
 
     @Zone(
             value = "zone_test",
-            replicas = 3,
             partitions = 1,
-            engine = ZoneEngine.AIMEM
+            replicas = 3,
+            affinityFunction = "affinity",
+            dataNodesAutoAdjust = 1,
+            dataNodesAutoAdjustScaleDown = 2,
+            dataNodesAutoAdjustScaleUp = 3,
+            filter = "filter",
+            engine = ZoneEngine.AIMEM,
+            dataRegion = "dataRegion"
     )
     private static class ZoneTest {}
 
diff --git 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java
 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java
index 3d9219ff93..f0d5ee01ad 100644
--- 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java
+++ 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/CreateFromDefinitionTest.java
@@ -52,12 +52,20 @@ class CreateFromDefinitionTest {
                 .ifNotExists()
                 .partitions(3)
                 .replicas(3)
+                .affinity("affinity")
+                .dataNodesAutoAdjust(1)
+                .dataNodesAutoAdjustScaleDown(2)
+                .dataNodesAutoAdjustScaleUp(3)
+                .filter("filter")
                 .engine(ZoneEngine.AIMEM)
+                .dataRegion("dataRegion")
                 .build();
 
         assertThat(
                 createZone(zone),
-                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=3, REPLICAS=3;")
+                is("CREATE ZONE IF NOT EXISTS zone_test ENGINE AIMEM WITH 
PARTITIONS=3, REPLICAS=3, AFFINITY_FUNCTION='affinity',"
+                        + " DATA_NODES_AUTO_ADJUST=1, 
DATA_NODES_AUTO_ADJUST_SCALE_UP=3, DATA_NODES_AUTO_ADJUST_SCALE_DOWN=2,"
+                        + " DATA_NODES_FILTER='filter', 
DATAREGION='dataRegion';")
         );
     }
 
diff --git 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java
 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java
index 26e7222fa7..b6e0b3b443 100644
--- 
a/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java
+++ 
b/modules/catalog-dsl/src/test/java/org/apache/ignite/internal/catalog/sql/InvalidDefinitionTest.java
@@ -17,184 +17,275 @@
 
 package org.apache.ignite.internal.catalog.sql;
 
+import static java.util.Collections.singletonList;
+import static org.apache.ignite.catalog.ColumnType.INTEGER;
+import static org.apache.ignite.catalog.IndexType.DEFAULT;
 import static 
org.apache.ignite.internal.testframework.IgniteTestUtils.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 
-import java.util.Collections;
-import java.util.List;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
 import org.apache.ignite.catalog.ColumnSorted;
-import org.apache.ignite.catalog.ColumnType;
-import org.apache.ignite.catalog.IndexType;
+import org.apache.ignite.catalog.ZoneEngine;
 import org.apache.ignite.catalog.definitions.ColumnDefinition;
 import org.apache.ignite.catalog.definitions.TableDefinition;
-import org.apache.ignite.catalog.definitions.TableDefinition.Builder;
 import org.apache.ignite.catalog.definitions.ZoneDefinition;
 import org.junit.jupiter.api.Test;
 
 @SuppressWarnings("ThrowableNotThrown")
 class InvalidDefinitionTest {
     @Test
-    void zone() {
-        assertThrows(NullPointerException.class,
-                () -> ZoneDefinition.builder(null).build(),
-                "Zone name must not be null.");
+    void zoneName() {
+        assertNullOrBlank(ZoneDefinition::builder, "zone", "Zone name");
+    }
 
-        assertThrows(IllegalArgumentException.class,
-                () -> ZoneDefinition.builder("").build(),
-                "Zone name must not be blank.");
+    @Test
+    void zone() {
+        assertZoneBuilderNull(ZoneDefinition.Builder::engine, 
ZoneEngine.AIMEM, "Engine");
+        assertZoneBuilderNull(ZoneDefinition.Builder::partitions, 1, "Number 
of partitions");
+        assertZoneBuilderNull(ZoneDefinition.Builder::replicas, 1, "Number of 
replicas");
 
-        assertThrows(NullPointerException.class,
-                () -> ZoneDefinition.builder("zone").engine(null).build(),
-                "Engine must not be null.");
+        assertZoneBuilderNullOrBlank(ZoneDefinition.Builder::affinity, "a", 
"Affinity function");
 
-        assertThrows(NullPointerException.class,
-                () -> ZoneDefinition.builder("zone").partitions(null).build(),
-                "Number of partitions must not be null.");
+        assertZoneBuilderNull(ZoneDefinition.Builder::dataNodesAutoAdjust, 1,
+                "Timeout between node added or node left topology event itself 
and data nodes switch");
+        
assertZoneBuilderNull(ZoneDefinition.Builder::dataNodesAutoAdjustScaleUp, 1,
+                "Timeout between node added topology event itself and data 
nodes switch");
+        
assertZoneBuilderNull(ZoneDefinition.Builder::dataNodesAutoAdjustScaleDown, 1,
+                "Timeout between node left topology event itself and data 
nodes switch");
 
-        assertThrows(NullPointerException.class,
-                () -> ZoneDefinition.builder("zone").replicas(null).build(),
-                "Number of replicas must not be null.");
+        assertZoneBuilderNullOrBlank(ZoneDefinition.Builder::filter, "f", 
"Filter");
+        assertZoneBuilderNullOrBlank(ZoneDefinition.Builder::dataRegion, "r", 
"Data region");
     }
 
     @Test
-    void names() {
-        assertThrows(NullPointerException.class,
-                () -> TableDefinition.builder(null).build(),
-                "Table name must not be null.");
-
-        assertThrows(IllegalArgumentException.class,
-                () -> TableDefinition.builder("").build(),
-                "Table name must not be blank.");
+    void tableName() {
+        assertNullOrBlank(TableDefinition::builder, "table", "Table name");
     }
 
     @Test
     void columns() {
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().columns((ColumnDefinition[]) 
null).build(),
-                "Columns array must not be null.");
+        ColumnDefinition validColumn = ColumnDefinition.column("id", INTEGER);
 
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().columns((List<ColumnDefinition>) 
null).build(),
-                "Columns list must not be null.");
+        assertTableBuilderNull(TableDefinition.Builder::columns, new 
ColumnDefinition[]{validColumn}, "Columns array");
+        assertTableBuilderNull(TableDefinition.Builder::columns, 
singletonList(validColumn), "Columns list");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().columns((ColumnDefinition) null).build(),
+                () -> tableBuilder().columns(new ColumnDefinition[]{null}),
                 "Column must not be null.");
 
         assertThrows(NullPointerException.class,
-                () -> 
tableBuilder().columns(Collections.singletonList(null)).build(),
+                () -> tableBuilder().columns(singletonList(null)),
                 "Column must not be null.");
     }
 
     @Test
     void colocateBy() {
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().colocateBy((String[]) null).build(),
-                "Colocation columns array must not be null.");
-
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().colocateBy((List<String>) null).build(),
-                "Colocation columns list must not be null.");
+        assertTableBuilderNull(TableDefinition.Builder::colocateBy, new 
String[]{"id"}, "Colocation columns array");
+        assertTableBuilderNull(TableDefinition.Builder::colocateBy, 
singletonList("id"), "Colocation columns list");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().colocateBy((String) null).build(),
+                () -> tableBuilder().colocateBy(new String[]{null}),
                 "Colocation column must not be null.");
 
         assertThrows(NullPointerException.class,
-                () -> 
tableBuilder().colocateBy(Collections.singletonList(null)).build(),
+                () -> tableBuilder().colocateBy(singletonList(null)),
                 "Colocation column must not be null.");
     }
 
     @Test
     void primaryKey() {
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey((String[]) null).build(),
-                "Primary key columns array must not be null.");
+        assertTableBuilderNull(TableDefinition.Builder::primaryKey, new 
String[]{"id"}, "Primary key columns array");
 
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey((String) null).build(),
-                "Primary key column must not be null.");
+        ColumnSorted validColumn = ColumnSorted.column("id");
 
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey(null, (ColumnSorted[]) 
null).build(),
-                "Primary key index type must not be null.");
+        assertTableBuilderNull((builder, columns) -> 
builder.primaryKey(DEFAULT, columns), new ColumnSorted[]{validColumn},
+                "Primary key columns array");
+        assertTableBuilderNull((builder, columns) -> 
builder.primaryKey(DEFAULT, columns), singletonList(validColumn),
+                "Primary key columns list");
 
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey(IndexType.DEFAULT, 
(ColumnSorted[]) null).build(),
-                "Primary key columns array must not be null.");
+        assertTableBuilderNull((builder, type) -> builder.primaryKey(type, new 
ColumnSorted[]{validColumn}), DEFAULT,
+                "Primary key index type");
+        assertTableBuilderNull((builder, type) -> builder.primaryKey(type, 
singletonList(validColumn)), DEFAULT,
+                "Primary key index type");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey(IndexType.DEFAULT, 
(ColumnSorted) null).build(),
+                () -> tableBuilder().primaryKey((String) null),
                 "Primary key column must not be null.");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey(null, List.of()).build(),
-                "Primary key index type must not be null.");
-
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey(IndexType.DEFAULT, 
(List<ColumnSorted>) null).build(),
-                "Primary key columns list must not be null.");
+                () -> tableBuilder().primaryKey(DEFAULT, new 
ColumnSorted[]{null}),
+                "Primary key column must not be null.");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().primaryKey(IndexType.DEFAULT, 
Collections.singletonList(null)).build(),
+                () -> tableBuilder().primaryKey(DEFAULT, singletonList(null)),
                 "Primary key column must not be null.");
     }
 
     @Test
     void index() {
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().index((String[]) null).build(),
-                "Index columns array must not be null.");
+        assertTableBuilderNull(TableDefinition.Builder::index, new 
String[]{"id"}, "Index columns array");
 
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().index((String) null).build(),
-                "Index column must not be null.");
+        ColumnSorted validColumn = ColumnSorted.column("id");
 
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().index(null, null, (ColumnSorted[]) 
null).build(),
-                "Index type must not be null.");
+        assertTableBuilderNull((builder, columns) -> builder.index(null, 
DEFAULT, columns), new ColumnSorted[]{validColumn},
+                "Index columns array");
+        assertTableBuilderNull((builder, columns) -> builder.index(null, 
DEFAULT, columns), singletonList(validColumn),
+                "Index columns list");
 
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().index(null, IndexType.DEFAULT, 
(ColumnSorted[]) null).build(),
-                "Index columns array must not be null.");
+        assertTableBuilderNull((builder, type) -> builder.index(null, type, 
new ColumnSorted[]{validColumn}), DEFAULT,
+                "Index type");
+        assertTableBuilderNull((builder, type) -> builder.index(null, type, 
singletonList(validColumn)), DEFAULT,
+                "Index type");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().index(null, IndexType.DEFAULT, 
(ColumnSorted) null).build(),
+                () -> tableBuilder().index(new String[]{null}),
                 "Index column must not be null.");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().index(null, null, List.of()).build(),
-                "Index type must not be null.");
-
-        assertThrows(NullPointerException.class,
-                () -> tableBuilder().index(null, IndexType.DEFAULT, 
(List<ColumnSorted>) null).build(),
-                "Index columns list must not be null.");
+                () -> tableBuilder().index(null, DEFAULT, new 
ColumnSorted[]{null}),
+                "Index column must not be null.");
 
         assertThrows(NullPointerException.class,
-                () -> tableBuilder().index(null, IndexType.DEFAULT, 
Collections.singletonList(null)).build(),
+                () -> tableBuilder().index(null, DEFAULT, singletonList(null)),
                 "Index column must not be null.");
     }
 
     @Test
     void columnDefinition() {
-        assertThrows(NullPointerException.class,
-                () -> ColumnDefinition.column(null, (ColumnType<?>) null),
-                "Column name must not be null.");
+        assertNullOrBlank(name -> ColumnDefinition.column(name, INTEGER), 
"column", "Column name");
+        assertNullOrBlank(name -> ColumnDefinition.column(name, "definition"), 
"column", "Column name");
+
+        assertNull(type -> ColumnDefinition.column("column", type), INTEGER, 
"Column type");
+        assertNullOrBlank(def -> ColumnDefinition.column("column", def), 
"definition", "Column definition");
+    }
 
-        assertThrows(IllegalArgumentException.class,
-                () -> ColumnDefinition.column("", (ColumnType<?>) null),
-                "Column name must not be blank.");
+    private static ZoneDefinition.Builder zoneBuilder() {
+        return ZoneDefinition.builder("zone");
+    }
 
-        assertThrows(NullPointerException.class,
-                () -> ColumnDefinition.column(null, (String) null),
-                "Column name must not be null.");
+    private static TableDefinition.Builder tableBuilder() {
+        return TableDefinition.builder("table");
+    }
+
+    /**
+     * Checks that the {@link ZoneDefinition.Builder} method throws {@link 
NullPointerException} when passed a null value and doesn't throw
+     * when passed a valid value.
+     *
+     * @param builderConsumer Function which takes a builder as a first 
argument and a tested value as a second argument.
+     * @param validValue Valid value.
+     * @param messageFragment Fragment of the error text in the exception.
+     * @param <T> Type of the second argument.
+     */
+    private static <T> void assertZoneBuilderNull(
+            BiConsumer<ZoneDefinition.Builder, T> builderConsumer,
+            T validValue,
+            String messageFragment
+    ) {
+        assertNull(InvalidDefinitionTest::zoneBuilder, builderConsumer, 
validValue, messageFragment);
+    }
 
-        assertThrows(IllegalArgumentException.class,
-                () -> ColumnDefinition.column("", (String) null),
-                "Column name must not be blank.");
+    /**
+     * Checks that the {@link ZoneDefinition.Builder} method throws {@link 
NullPointerException} when passed a null string,
+     * {@link IllegalArgumentException} when passed a blank string and doesn't 
throw when passed a valid value.
+     *
+     * @param builderConsumer Function which takes a builder as a first 
argument and a tested value as a second argument.
+     * @param validValue Valid value.
+     * @param messageFragment Fragment of the error text in the exception.
+     */
+    private static void assertZoneBuilderNullOrBlank(
+            BiConsumer<ZoneDefinition.Builder, String> builderConsumer,
+            String validValue,
+            String messageFragment
+    ) {
+        assertNull(InvalidDefinitionTest::zoneBuilder, builderConsumer, 
validValue, messageFragment);
+        assertBlank(InvalidDefinitionTest::zoneBuilder, builderConsumer, 
messageFragment);
+    }
 
+    /**
+     * Checks that the {@link TableDefinition.Builder} method throws {@link 
NullPointerException} when passed a null value and doesn't throw
+     * when passed a valid value.
+     *
+     * @param builderConsumer Function which takes a builder as a first 
argument and a tested value as a second argument.
+     * @param validValue Valid value.
+     * @param messageFragment Fragment of the error text in the exception.
+     * @param <T> Type of the second argument.
+     */
+    private static <T> void assertTableBuilderNull(
+            BiConsumer<TableDefinition.Builder, T> builderConsumer,
+            T validValue,
+            String messageFragment
+    ) {
+        assertNull(InvalidDefinitionTest::tableBuilder, builderConsumer, 
validValue, messageFragment);
     }
 
-    private static Builder tableBuilder() {
-        return TableDefinition.builder("table");
+    /**
+     * Checks that the function throws {@link NullPointerException} when 
passed a null value as a second argument and doesn't throw when
+     * passed a valid value.
+     *
+     * @param firstArgumentSupplier Supplier of first argument.
+     * @param testedFunction Function to test.
+     * @param validValue Valid value.
+     * @param messageFragment Fragment of the error text in the exception.
+     * @param <T> First argument type.
+     * @param <U> Second argument type.
+     */
+    private static <T, U> void assertNull(
+            Supplier<T> firstArgumentSupplier,
+            BiConsumer<T, U> testedFunction,
+            U validValue,
+            String messageFragment
+    ) {
+        assertThrows(
+                NullPointerException.class,
+                () -> testedFunction.accept(firstArgumentSupplier.get(), null),
+                messageFragment + " must not be null."
+        );
+        assertDoesNotThrow(() -> 
testedFunction.accept(firstArgumentSupplier.get(), validValue));
+    }
+
+    /**
+     * Checks that the function throws {@link NullPointerException} when 
passed a null value and doesn't throw when passed a valid value.
+     *
+     * @param consumer Function to test.
+     * @param validValue Valid value.
+     * @param messageFragment Fragment of the error text in the exception.
+     */
+    private static <T> void assertNull(Consumer<T> consumer, T validValue, 
String messageFragment) {
+        assertNull(() -> null, (unused, value) -> consumer.accept(value), 
validValue, messageFragment);
+    }
+
+    /**
+     * Checks that the function throws {@link NullPointerException} when 
passed a null string, {@link IllegalArgumentException} when passed
+     * a blank string and doesn't throw when passed a valid value.
+     *
+     * @param consumer Function to test.
+     * @param validValue Valid value.
+     * @param messageFragment Fragment of the error text in the exception.
+     */
+    private static void assertNullOrBlank(Consumer<String> consumer, String 
validValue, String messageFragment) {
+        assertNull(() -> null, (unused, value) -> consumer.accept(value), 
validValue, messageFragment);
+        assertBlank(() -> null, (unused, value) -> consumer.accept(value), 
messageFragment);
+    }
+
+    /**
+     * Checks that the function throws {@link IllegalArgumentException} when 
passed a blank string as a second argument.
+     *
+     * @param firstArgumentSupplier Supplier of first argument.
+     * @param testedFunction Function to test.
+     * @param messageFragment Fragment of the error text in the exception.
+     * @param <T> First argument type.
+     */
+    private static <T> void assertBlank(
+            Supplier<T> firstArgumentSupplier,
+            BiConsumer<T, String> testedFunction,
+            String messageFragment
+    ) {
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> testedFunction.accept(firstArgumentSupplier.get(), ""),
+                messageFragment + " must not be blank."
+        );
     }
 }

Reply via email to