This is an automated email from the ASF dual-hosted git repository.
terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new 86c1519e4d6 Introduce Apache BVal rule configuration validation
(#39880)
86c1519e4d6 is described below
commit 86c1519e4d6deaf68e18ee47196e81da2e114f62
Author: Liang Zhang <[email protected]>
AuthorDate: Thu Sep 17 23:15:56 2026 +0800
Introduce Apache BVal rule configuration validation (#39880)
* Require automatic review and Git commit commands
* Introduce Apache BVal rule configuration validation
* Introduce Apache BVal rule configuration validation
* Introduce Apache BVal rule configuration validation
---
infra/common/pom.xml | 9 +++
.../rule/validator/RuleConfigurationValidator.java | 44 ++++++++-----
.../builder/database/DatabaseRulesBuilder.java | 6 +-
.../rule/builder/global/GlobalRulesBuilder.java | 6 +-
.../validator/RuleConfigurationValidatorTest.java | 72 ++++++++++++++++++++++
.../builder/database/DatabaseRulesBuilderTest.java | 18 ++++++
.../fixture/FixtureDatabaseRuleConfiguration.java | 7 +++
.../fixture/FixtureGlobalRuleConfiguration.java | 7 +++
.../ToggleFixtureDatabaseRuleConfiguration.java | 13 +++-
.../builder/global/GlobalRulesBuilderTest.java | 18 ++++++
...YamlRuntimeDatabaseConfigurationsValidator.java | 8 +--
mcp/pom.xml | 17 -----
mcp/support/pom.xml | 4 +-
.../configuration/MCPConfigurationValidator.java | 29 +++++++--
.../MCPCompletionTargetDescriptorValidator.java | 2 +
.../MCPResourceNavigationDescriptorValidator.java | 4 ++
.../yaml/YamlMCPCompletionTargetDescriptor.java | 2 -
.../yaml/YamlMCPResourceNavigationDescriptor.java | 3 -
.../validator/MCPPromptGuidanceMetaValidator.java | 4 +-
.../UniqueMCPPromptArgumentNamesValidator.java | 9 +--
...MCPCompletionTargetDescriptorValidatorTest.java | 12 +++-
.../MCPDescriptorCatalogYamlSwapperTest.java | 22 -------
...PResourceNavigationDescriptorValidatorTest.java | 31 ++++++++--
.../rule/DatabaseRuleConfigurationManager.java | 2 +
.../manager/rule/GlobalConfigurationManager.java | 2 +
.../manager/resource/StorageUnitManagerTest.java | 9 ++-
.../rule/DatabaseRuleConfigurationManagerTest.java | 19 ++++++
.../rule/GlobalConfigurationManagerTest.java | 17 +++++
pom.xml | 6 ++
29 files changed, 310 insertions(+), 92 deletions(-)
diff --git a/infra/common/pom.xml b/infra/common/pom.xml
index d2bef8e0a00..79f8d3ccd9a 100644
--- a/infra/common/pom.xml
+++ b/infra/common/pom.xml
@@ -48,6 +48,15 @@
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>jakarta.validation</groupId>
+ <artifactId>jakarta.validation-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-jsr</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-test-infra-framework</artifactId>
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/configuration/MCPConfigurationValidator.java
b/infra/common/src/main/java/org/apache/shardingsphere/infra/config/rule/validator/RuleConfigurationValidator.java
similarity index 50%
copy from
mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/configuration/MCPConfigurationValidator.java
copy to
infra/common/src/main/java/org/apache/shardingsphere/infra/config/rule/validator/RuleConfigurationValidator.java
index 77135342abc..6a0e229b192 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/configuration/MCPConfigurationValidator.java
+++
b/infra/common/src/main/java/org/apache/shardingsphere/infra/config/rule/validator/RuleConfigurationValidator.java
@@ -15,47 +15,57 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.mcp.support.configuration;
+package org.apache.shardingsphere.infra.config.rule.validator;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import org.apache.bval.jsr.ApacheValidationProvider;
+import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
-import org.hibernate.validator.HibernateValidator;
-import
org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
+import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
/**
- * MCP configuration validator.
+ * Rule configuration validator.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public final class MCPConfigurationValidator {
+public final class RuleConfigurationValidator {
- private static final Validator VALIDATOR =
Validation.byProvider(HibernateValidator.class)
- .configure().messageInterpolator(new
ParameterMessageInterpolator()).buildValidatorFactory().getValidator();
+ private static final Validator VALIDATOR =
Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory().getValidator();
/**
- * Validate MCP configuration.
+ * Validate rule configurations.
*
- * @param config configuration
- * @param configName configuration name
+ * @param ruleConfigs rule configurations
*/
- public static void validate(final Object config, final String configName) {
- ShardingSpherePreconditions.checkNotNull(config, () -> new
IllegalArgumentException(String.format("%s cannot be null.", configName)));
- Set<ConstraintViolation<Object>> violations =
VALIDATOR.validate(config);
- ShardingSpherePreconditions.checkMustEmpty(violations, () -> new
IllegalArgumentException(violations.stream()
- .map(each -> formatViolation(configName,
each)).sorted().collect(Collectors.joining("; "))));
+ public static void validate(final Collection<RuleConfiguration>
ruleConfigs) {
+ for (RuleConfiguration each : ruleConfigs) {
+ validate(each);
+ }
+ }
+
+ /**
+ * Validate rule configuration.
+ *
+ * @param ruleConfig rule configuration
+ */
+ public static void validate(final RuleConfiguration ruleConfig) {
+ Set<ConstraintViolation<RuleConfiguration>> violations =
VALIDATOR.validate(ruleConfig);
+ ShardingSpherePreconditions.checkMustEmpty(violations, () -> new
InvalidRuleConfigurationException(
+ ruleConfig.getClass().getSimpleName(),
violations.stream().map(RuleConfigurationValidator::formatViolation).sorted().collect(Collectors.joining(";
"))));
}
- private static String formatViolation(final String configName, final
ConstraintViolation<Object> violation) {
+ private static String formatViolation(final
ConstraintViolation<RuleConfiguration> violation) {
if (violation.getPropertyPath().toString().isEmpty()) {
return violation.getMessage();
}
String message = violation.getMessage();
- return String.format("%s property `%s` %s%s", configName,
violation.getPropertyPath(), message, message.endsWith(".") ? "" : ".");
+ return String.format("Property `%s` %s%s",
violation.getPropertyPath(), message, message.endsWith(".") ? "" : ".");
}
}
diff --git
a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java
b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java
index 40abab78286..2113f3f5dc0 100644
---
a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java
+++
b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilder.java
@@ -27,6 +27,7 @@ import
org.apache.shardingsphere.infra.config.rule.checker.DatabaseRuleConfigura
import
org.apache.shardingsphere.infra.config.rule.function.DistributedRuleConfiguration;
import
org.apache.shardingsphere.infra.config.rule.function.EnhancedRuleConfiguration;
import
org.apache.shardingsphere.infra.config.rule.scope.DatabaseRuleConfiguration;
+import
org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
import org.apache.shardingsphere.infra.instance.ComputeNodeInstanceContext;
import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
@@ -63,8 +64,10 @@ public final class DatabaseRulesBuilder {
@SuppressWarnings({"unchecked", "rawtypes"})
public static Collection<ShardingSphereRule> build(final String
databaseName, final DatabaseType protocolType, final DatabaseConfiguration
databaseConfig,
final
ComputeNodeInstanceContext computeNodeInstanceContext, final ResourceMetaData
resourceMetaData) {
+ Map<RuleConfiguration, DatabaseRuleBuilder> ruleBuilderMap =
getRuleBuilderMap(databaseConfig);
+ RuleConfigurationValidator.validate(ruleBuilderMap.keySet());
Collection<ShardingSphereRule> result = new LinkedList<>();
- for (Entry<RuleConfiguration, DatabaseRuleBuilder> entry :
getRuleBuilderMap(databaseConfig).entrySet()) {
+ for (Entry<RuleConfiguration, DatabaseRuleBuilder> entry :
ruleBuilderMap.entrySet()) {
DatabaseRuleConfigurationChecker configChecker =
OrderedSPILoader.getServicesByClass(
DatabaseRuleConfigurationChecker.class,
Collections.singleton(entry.getKey().getClass())).get(entry.getKey().getClass());
if (null != configChecker) {
@@ -90,6 +93,7 @@ public final class DatabaseRulesBuilder {
@SuppressWarnings({"unchecked", "rawtypes"})
public static ShardingSphereRule build(final String databaseName, final
DatabaseType protocolType, final Collection<ShardingSphereRule> rules, final
RuleConfiguration ruleConfig,
final ComputeNodeInstanceContext
computeNodeInstanceContext, final ResourceMetaData resourceMetaData) {
+ RuleConfigurationValidator.validate(ruleConfig);
DatabaseRuleBuilder databaseRuleBuilder =
OrderedSPILoader.getServices(DatabaseRuleBuilder.class,
Collections.singleton(ruleConfig)).get(ruleConfig);
DatabaseRuleConfigurationChecker configChecker =
OrderedSPILoader.getServicesByClass(DatabaseRuleConfigurationChecker.class,
Collections.singleton(ruleConfig.getClass())).get(ruleConfig.getClass());
diff --git
a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilder.java
b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilder.java
index 0a28e081dc4..0a9c41b5c4d 100644
---
a/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilder.java
+++
b/infra/common/src/main/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilder.java
@@ -21,6 +21,7 @@ import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
+import
org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
import org.apache.shardingsphere.infra.spi.type.ordered.OrderedSPILoader;
@@ -50,8 +51,10 @@ public final class GlobalRulesBuilder {
@SuppressWarnings({"unchecked", "rawtypes"})
public static Collection<ShardingSphereRule> buildRules(final
Collection<RuleConfiguration> globalRuleConfigs,
final
Collection<ShardingSphereDatabase> databases, final ConfigurationProperties
props) {
+ Map<RuleConfiguration, GlobalRuleBuilder> ruleBuilderMap =
getRuleBuilderMap(globalRuleConfigs);
+ RuleConfigurationValidator.validate(ruleBuilderMap.keySet());
Collection<ShardingSphereRule> result = new LinkedList<>();
- for (Entry<RuleConfiguration, GlobalRuleBuilder> entry :
getRuleBuilderMap(globalRuleConfigs).entrySet()) {
+ for (Entry<RuleConfiguration, GlobalRuleBuilder> entry :
ruleBuilderMap.entrySet()) {
result.add(entry.getValue().build(entry.getKey(), databases,
props));
}
return result;
@@ -89,6 +92,7 @@ public final class GlobalRulesBuilder {
*/
@SuppressWarnings("unchecked")
public static Collection<ShardingSphereRule> buildSingleRules(final
RuleConfiguration globalRuleConfig, final Collection<ShardingSphereDatabase>
databases, final ConfigurationProperties props) {
+ RuleConfigurationValidator.validate(globalRuleConfig);
return OrderedSPILoader.getServices(GlobalRuleBuilder.class,
Collections.singleton(globalRuleConfig)).entrySet()
.stream().map(each -> each.getValue().build(each.getKey(),
databases, props)).collect(Collectors.toList());
}
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/config/rule/validator/RuleConfigurationValidatorTest.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/config/rule/validator/RuleConfigurationValidatorTest.java
new file mode 100644
index 00000000000..9fbc864c17a
--- /dev/null
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/config/rule/validator/RuleConfigurationValidatorTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.infra.config.rule.validator;
+
+import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
+import org.junit.jupiter.api.Test;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import java.util.Arrays;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class RuleConfigurationValidatorTest {
+
+ @Test
+ void assertValidateRuleConfiguration() {
+ assertDoesNotThrow(() -> RuleConfigurationValidator.validate(new
FixtureRuleConfiguration("fixture")));
+ }
+
+ @Test
+ void assertValidateRuleConfigurationWithViolation() {
+ InvalidRuleConfigurationException actual =
assertThrows(InvalidRuleConfigurationException.class,
+ () -> RuleConfigurationValidator.validate(new
FixtureRuleConfiguration("")));
+ assertThat(actual.getMessage(), is("Invalid 'FixtureRuleConfiguration'
rule, error message is: Property `nested.name` is required."));
+ }
+
+ @Test
+ void assertValidateRuleConfigurationsWithViolation() {
+ assertThrows(InvalidRuleConfigurationException.class,
+ () -> RuleConfigurationValidator.validate(Arrays.asList(new
FixtureRuleConfiguration("fixture"), new FixtureRuleConfiguration(""))));
+ }
+
+ private static final class FixtureRuleConfiguration implements
RuleConfiguration {
+
+ @Valid
+ private final NestedConfiguration nested;
+
+ private FixtureRuleConfiguration(final String name) {
+ nested = new NestedConfiguration(name);
+ }
+ }
+
+ private static final class NestedConfiguration {
+
+ @NotBlank(message = "is required")
+ private final String name;
+
+ private NestedConfiguration(final String name) {
+ this.name = name;
+ }
+ }
+}
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java
index e826de16d97..454aedcc19e 100644
---
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/database/DatabaseRulesBuilderTest.java
@@ -20,6 +20,7 @@ package org.apache.shardingsphere.infra.rule.builder.database;
import
org.apache.shardingsphere.infra.config.database.impl.DataSourceProvidedDatabaseConfiguration;
import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
import
org.apache.shardingsphere.infra.config.rule.checker.DatabaseRuleConfigurationChecker;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
import org.apache.shardingsphere.infra.fixture.FixtureRule;
import org.apache.shardingsphere.infra.fixture.FixtureRuleConfiguration;
import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
@@ -43,6 +44,7 @@ import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyCollection;
import static org.mockito.ArgumentMatchers.argThat;
@@ -88,11 +90,27 @@ class DatabaseRulesBuilderTest {
}
}
+ @Test
+ void assertBuildWithInvalidRuleConfiguration() {
+ ToggleFixtureDatabaseRuleConfiguration ruleConfig = new
ToggleFixtureDatabaseRuleConfiguration(false);
+ ruleConfig.setName("");
+ assertThrows(InvalidRuleConfigurationException.class, () ->
DatabaseRulesBuilder.build("foo_db", null,
+ new
DataSourceProvidedDatabaseConfiguration(Collections.emptyMap(),
Collections.singleton(ruleConfig)), null, EMPTY_RESOURCE_META_DATA));
+ }
+
@Test
void assertBuildSingleRule() {
assertThat(DatabaseRulesBuilder.build("foo_db", null,
Collections.emptyList(), new FixtureDatabaseRuleConfiguration(), null,
EMPTY_RESOURCE_META_DATA), isA(FixtureRule.class));
}
+ @Test
+ void assertBuildSingleRuleWithInvalidRuleConfiguration() {
+ FixtureDatabaseRuleConfiguration ruleConfig = new
FixtureDatabaseRuleConfiguration();
+ ruleConfig.setName("");
+ assertThrows(InvalidRuleConfigurationException.class,
+ () -> DatabaseRulesBuilder.build("foo_db", null,
Collections.emptyList(), ruleConfig, null, EMPTY_RESOURCE_META_DATA));
+ }
+
@Test
@SuppressWarnings("unchecked")
void assertBuildSingleRuleWithChecker() {
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureDatabaseRuleConfiguration.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureDatabaseRuleConfiguration.java
index 50e7f4df814..b1936509e2e 100644
---
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureDatabaseRuleConfiguration.java
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureDatabaseRuleConfiguration.java
@@ -17,7 +17,14 @@
package org.apache.shardingsphere.infra.rule.builder.fixture;
+import lombok.Setter;
import
org.apache.shardingsphere.infra.config.rule.scope.DatabaseRuleConfiguration;
+import javax.validation.constraints.NotBlank;
+
public final class FixtureDatabaseRuleConfiguration implements
DatabaseRuleConfiguration {
+
+ @Setter
+ @NotBlank(message = "is required")
+ private String name = "fixture";
}
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureGlobalRuleConfiguration.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureGlobalRuleConfiguration.java
index 653e775527c..61d2d64d3f7 100644
---
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureGlobalRuleConfiguration.java
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/FixtureGlobalRuleConfiguration.java
@@ -17,7 +17,14 @@
package org.apache.shardingsphere.infra.rule.builder.fixture;
+import lombok.Setter;
import
org.apache.shardingsphere.infra.config.rule.scope.GlobalRuleConfiguration;
+import javax.validation.constraints.NotBlank;
+
public final class FixtureGlobalRuleConfiguration implements
GlobalRuleConfiguration {
+
+ @Setter
+ @NotBlank(message = "is required")
+ private String name = "fixture";
}
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/ToggleFixtureDatabaseRuleConfiguration.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/ToggleFixtureDatabaseRuleConfiguration.java
index d4b260a24fd..76f27fc6bd1 100644
---
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/ToggleFixtureDatabaseRuleConfiguration.java
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/fixture/ToggleFixtureDatabaseRuleConfiguration.java
@@ -17,15 +17,24 @@
package org.apache.shardingsphere.infra.rule.builder.fixture;
-import lombok.AllArgsConstructor;
import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
import
org.apache.shardingsphere.infra.config.rule.function.EnhancedRuleConfiguration;
import
org.apache.shardingsphere.infra.config.rule.scope.DatabaseRuleConfiguration;
-@AllArgsConstructor
+import javax.validation.constraints.AssertFalse;
+import javax.validation.constraints.NotBlank;
+
+@RequiredArgsConstructor
public final class ToggleFixtureDatabaseRuleConfiguration implements
DatabaseRuleConfiguration, EnhancedRuleConfiguration {
@Getter
+ @AssertFalse(message = "must be false")
private final boolean empty;
+ @Setter
+ @NotBlank(message = "is required")
+ private String name = "fixture";
+
}
diff --git
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilderTest.java
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilderTest.java
index 77009b04b50..8ccf0326eea 100644
---
a/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilderTest.java
+++
b/infra/common/src/test/java/org/apache/shardingsphere/infra/rule/builder/global/GlobalRulesBuilderTest.java
@@ -19,6 +19,7 @@ package org.apache.shardingsphere.infra.rule.builder.global;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabaseFactory;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
@@ -34,6 +35,7 @@ import java.util.Properties;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
class GlobalRulesBuilderTest {
@@ -45,12 +47,28 @@ class GlobalRulesBuilderTest {
assertThat(rules.iterator().next(), isA(FixtureGlobalRule.class));
}
+ @Test
+ void assertBuildRulesWithInvalidRuleConfiguration() {
+ FixtureGlobalRuleConfiguration ruleConfig = new
FixtureGlobalRuleConfiguration();
+ ruleConfig.setName("");
+ assertThrows(InvalidRuleConfigurationException.class,
+ () ->
GlobalRulesBuilder.buildRules(Collections.singletonList(ruleConfig),
Collections.singleton(buildDatabase()), mock()));
+ }
+
@Test
void assertBuildSingleRules() {
Collection<ShardingSphereRule> rules =
GlobalRulesBuilder.buildSingleRules(new FixtureGlobalRuleConfiguration(),
Collections.singleton(buildDatabase()), mock());
assertThat(rules.size(), is(1));
}
+ @Test
+ void assertBuildSingleRulesWithInvalidRuleConfiguration() {
+ FixtureGlobalRuleConfiguration ruleConfig = new
FixtureGlobalRuleConfiguration();
+ ruleConfig.setName("");
+ assertThrows(InvalidRuleConfigurationException.class,
+ () -> GlobalRulesBuilder.buildSingleRules(ruleConfig,
Collections.singleton(buildDatabase()), mock()));
+ }
+
private ShardingSphereDatabase buildDatabase() {
return ShardingSphereDatabaseFactory.create("foo_db",
TypedSPILoader.getService(DatabaseType.class, "FIXTURE"), new
ConfigurationProperties(new Properties()));
}
diff --git
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java
index 9df166968e4..b2ecbf3e2af 100644
---
a/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java
+++
b/mcp/bootstrap/src/main/java/org/apache/shardingsphere/mcp/bootstrap/config/yaml/validator/YamlRuntimeDatabaseConfigurationsValidator.java
@@ -27,16 +27,16 @@ import java.util.Map.Entry;
/**
* YAML runtime database configurations validator.
*/
-public final class YamlRuntimeDatabaseConfigurationsValidator implements
ConstraintValidator<ValidYamlRuntimeDatabaseConfigurations, Map<String,
YamlRuntimeDatabaseConfiguration>> {
+public final class YamlRuntimeDatabaseConfigurationsValidator implements
ConstraintValidator<ValidYamlRuntimeDatabaseConfigurations, Map<?, ?>> {
@Override
- public boolean isValid(final Map<String, YamlRuntimeDatabaseConfiguration>
value, final ConstraintValidatorContext context) {
+ public boolean isValid(final Map<?, ?> value, final
ConstraintValidatorContext context) {
if (null == value) {
return true;
}
boolean result = true;
- for (Entry<String, YamlRuntimeDatabaseConfiguration> entry :
value.entrySet()) {
- result = validateRuntimeDatabase(entry.getKey(), entry.getValue(),
context) && result;
+ for (Entry<?, ?> entry : value.entrySet()) {
+ result = validateRuntimeDatabase((String) entry.getKey(),
(YamlRuntimeDatabaseConfiguration) entry.getValue(), context) && result;
}
return result;
}
diff --git a/mcp/pom.xml b/mcp/pom.xml
index 30b92edef73..fe96c44b44d 100644
--- a/mcp/pom.xml
+++ b/mcp/pom.xml
@@ -41,22 +41,5 @@
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.release>${java.version}</maven.compiler.release>
- <hibernate-validator.version>6.2.5.Final</hibernate-validator.version>
- <jakarta-validation-api.version>2.0.2</jakarta-validation-api.version>
</properties>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>jakarta.validation</groupId>
- <artifactId>jakarta.validation-api</artifactId>
- <version>${jakarta-validation-api.version}</version>
- </dependency>
- <dependency>
- <groupId>org.hibernate.validator</groupId>
- <artifactId>hibernate-validator</artifactId>
- <version>${hibernate-validator.version}</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
</project>
diff --git a/mcp/support/pom.xml b/mcp/support/pom.xml
index 720b3686e05..abd04b31090 100644
--- a/mcp/support/pom.xml
+++ b/mcp/support/pom.xml
@@ -67,8 +67,8 @@
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
- <groupId>org.hibernate.validator</groupId>
- <artifactId>hibernate-validator</artifactId>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-jsr</artifactId>
</dependency>
</dependencies>
</project>
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/configuration/MCPConfigurationValidator.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/configuration/MCPConfigurationValidator.java
index 77135342abc..6fc38a701d2 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/configuration/MCPConfigurationValidator.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/configuration/MCPConfigurationValidator.java
@@ -19,13 +19,15 @@ package org.apache.shardingsphere.mcp.support.configuration;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import org.apache.bval.jsr.ApacheValidationProvider;
import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
-import org.hibernate.validator.HibernateValidator;
-import
org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
import javax.validation.ConstraintViolation;
+import javax.validation.ElementKind;
+import javax.validation.Path;
import javax.validation.Validation;
import javax.validation.Validator;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -35,8 +37,7 @@ import java.util.stream.Collectors;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class MCPConfigurationValidator {
- private static final Validator VALIDATOR =
Validation.byProvider(HibernateValidator.class)
- .configure().messageInterpolator(new
ParameterMessageInterpolator()).buildValidatorFactory().getValidator();
+ private static final Validator VALIDATOR =
Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory().getValidator();
/**
* Validate MCP configuration.
@@ -48,7 +49,7 @@ public final class MCPConfigurationValidator {
ShardingSpherePreconditions.checkNotNull(config, () -> new
IllegalArgumentException(String.format("%s cannot be null.", configName)));
Set<ConstraintViolation<Object>> violations =
VALIDATOR.validate(config);
ShardingSpherePreconditions.checkMustEmpty(violations, () -> new
IllegalArgumentException(violations.stream()
- .map(each -> formatViolation(configName,
each)).sorted().collect(Collectors.joining("; "))));
+ .map(each -> formatViolation(configName,
each)).distinct().sorted().collect(Collectors.joining("; "))));
}
private static String formatViolation(final String configName, final
ConstraintViolation<Object> violation) {
@@ -56,6 +57,22 @@ public final class MCPConfigurationValidator {
return violation.getMessage();
}
String message = violation.getMessage();
- return String.format("%s property `%s` %s%s", configName,
violation.getPropertyPath(), message, message.endsWith(".") ? "" : ".");
+ return String.format("%s property `%s` %s%s", configName,
formatPropertyPath(violation.getPropertyPath()), message, message.endsWith(".")
? "" : ".");
+ }
+
+ private static String formatPropertyPath(final Path propertyPath) {
+ String result = propertyPath.toString();
+ for (Path.Node each : propertyPath) {
+ if (ElementKind.CONTAINER_ELEMENT != each.getKind()) {
+ continue;
+ }
+ Path.ContainerElementNode containerElementNode =
each.as(Path.ContainerElementNode.class);
+ if (!Map.class.equals(containerElementNode.getContainerClass())) {
+ continue;
+ }
+ String node = String.format("[%s].%s", each.getKey(),
each.getName());
+ result = result.replace(node, String.format("<%s>%s", 0 ==
containerElementNode.getTypeArgumentIndex() ? "K" : "V", node));
+ }
+ return result;
}
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidator.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidator.java
index bc1664c0f1c..602dfa27ac9 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidator.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidator.java
@@ -55,6 +55,8 @@ public final class MCPCompletionTargetDescriptorValidator {
Map<String, MCPResourceDescriptor> resourceDescriptors =
resources.stream().collect(Collectors.toMap(MCPResourceDescriptor::getUriTemplate,
each -> each));
Map<String, MCPCompletionTargetDescriptor> registered = new
LinkedHashMap<>(descriptors.size(), 1F);
for (MCPCompletionTargetDescriptor each : descriptors) {
+ ShardingSpherePreconditions.checkState(each.getArguments().size()
== new HashSet<>(each.getArguments()).size(), () -> new IllegalStateException(
+ String.format("Completion target `%s:%s` must not contain
duplicate arguments.", each.getReferenceType(), each.getReference())));
validateCompletionReference(each, promptNames,
resourceDescriptors.keySet());
validatePromptCompletionArguments(each, promptArguments);
validateResourceCompletionArguments(each, resourceDescriptors);
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidator.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidator.java
index c8f5eb2b512..e430429b10f 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidator.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidator.java
@@ -48,6 +48,10 @@ public final class MCPResourceNavigationDescriptorValidator {
() -> new IllegalStateException(String.format("Resource
navigation references unknown source `%s`.", each.getFrom())));
ShardingSpherePreconditions.checkState(publicIdentifiers.contains(each.getTo()),
() -> new IllegalStateException(String.format("Resource
navigation references unknown target `%s`.", each.getTo())));
+
ShardingSpherePreconditions.checkState(each.getRequiredArguments().size() ==
new HashSet<>(each.getRequiredArguments()).size(), () -> new
IllegalStateException(
+ String.format("Resource navigation `%s` to `%s` must not
contain duplicate required arguments.", each.getFrom(), each.getTo())));
+
ShardingSpherePreconditions.checkState(each.getCarriedArguments().size() == new
HashSet<>(each.getCarriedArguments()).size(), () -> new IllegalStateException(
+ String.format("Resource navigation `%s` to `%s` must not
contain duplicate carried arguments.", each.getFrom(), each.getTo())));
ShardingSpherePreconditions.checkState(registered.add(each.getFrom() + "->" +
each.getTo()),
() -> new IllegalStateException(String.format("Duplicate
MCP resource navigation `%s` to `%s`.", each.getFrom(), each.getTo())));
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPCompletionTargetDescriptor.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPCompletionTargetDescriptor.java
index c0059e77300..8ccdcfd920c 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPCompletionTargetDescriptor.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPCompletionTargetDescriptor.java
@@ -20,7 +20,6 @@ package org.apache.shardingsphere.mcp.support.descriptor.yaml;
import lombok.Getter;
import lombok.Setter;
import
org.apache.shardingsphere.mcp.support.descriptor.yaml.validator.MCPMetadataKey;
-import org.hibernate.validator.constraints.UniqueElements;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotBlank;
@@ -48,7 +47,6 @@ public final class YamlMCPCompletionTargetDescriptor {
private String reference;
@NotEmpty(message = "is required")
- @UniqueElements(message = "must not contain duplicate values")
private Collection<@NotBlank(message = "is required") String> arguments =
new LinkedList<>();
@Max(value = 100, message = "must not exceed 100")
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPResourceNavigationDescriptor.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPResourceNavigationDescriptor.java
index 0a020a42366..cb23b6eb6a9 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPResourceNavigationDescriptor.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/YamlMCPResourceNavigationDescriptor.java
@@ -20,7 +20,6 @@ package org.apache.shardingsphere.mcp.support.descriptor.yaml;
import lombok.Getter;
import lombok.Setter;
import
org.apache.shardingsphere.mcp.support.descriptor.yaml.validator.MCPDescription;
-import org.hibernate.validator.constraints.UniqueElements;
import javax.validation.constraints.NotBlank;
import java.util.Collection;
@@ -39,10 +38,8 @@ public final class YamlMCPResourceNavigationDescriptor {
@NotBlank(message = "is required")
private String to;
- @UniqueElements(message = "must not contain duplicate values")
private Collection<@NotBlank(message = "is required") String>
requiredArguments = new LinkedList<>();
- @UniqueElements(message = "must not contain duplicate values")
private Collection<@NotBlank(message = "is required") String>
carriedArguments = new LinkedList<>();
@MCPDescription
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/MCPPromptGuidanceMetaValidator.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/MCPPromptGuidanceMetaValidator.java
index 8ac0f1a2c1b..9ea7ae57fe8 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/MCPPromptGuidanceMetaValidator.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/MCPPromptGuidanceMetaValidator.java
@@ -27,10 +27,10 @@ import java.util.Map;
/**
* MCP prompt guidance metadata validator.
*/
-public final class MCPPromptGuidanceMetaValidator implements
ConstraintValidator<MCPPromptGuidanceMeta, Map<String, Object>> {
+public final class MCPPromptGuidanceMetaValidator implements
ConstraintValidator<MCPPromptGuidanceMeta, Map<?, ?>> {
@Override
- public boolean isValid(final Map<String, Object> value, final
ConstraintValidatorContext context) {
+ public boolean isValid(final Map<?, ?> value, final
ConstraintValidatorContext context) {
if (null == value) {
return true;
}
diff --git
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/UniqueMCPPromptArgumentNamesValidator.java
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/UniqueMCPPromptArgumentNamesValidator.java
index 6b365777cea..4217242e5aa 100644
---
a/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/UniqueMCPPromptArgumentNamesValidator.java
+++
b/mcp/support/src/main/java/org/apache/shardingsphere/mcp/support/descriptor/yaml/validator/UniqueMCPPromptArgumentNamesValidator.java
@@ -28,16 +28,17 @@ import java.util.Set;
/**
* Unique MCP prompt argument names validator.
*/
-public final class UniqueMCPPromptArgumentNamesValidator implements
ConstraintValidator<UniqueMCPPromptArgumentNames,
Collection<YamlMCPPromptArgumentDescriptor>> {
+public final class UniqueMCPPromptArgumentNamesValidator implements
ConstraintValidator<UniqueMCPPromptArgumentNames, Collection<?>> {
@Override
- public boolean isValid(final Collection<YamlMCPPromptArgumentDescriptor>
value, final ConstraintValidatorContext context) {
+ public boolean isValid(final Collection<?> value, final
ConstraintValidatorContext context) {
if (null == value) {
return true;
}
Set<String> names = new HashSet<>(value.size(), 1F);
- for (YamlMCPPromptArgumentDescriptor each : value) {
- if (null != each && null != each.getName() &&
!each.getName().isBlank() && !names.add(each.getName())) {
+ for (Object each : value) {
+ YamlMCPPromptArgumentDescriptor argument =
(YamlMCPPromptArgumentDescriptor) each;
+ if (null != argument && null != argument.getName() &&
!argument.getName().isBlank() && !names.add(argument.getName())) {
return false;
}
}
diff --git
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidatorTest.java
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidatorTest.java
index 1c5808cab50..783e362fdc5 100644
---
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidatorTest.java
+++
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPCompletionTargetDescriptorValidatorTest.java
@@ -32,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
class MCPCompletionTargetDescriptorValidatorTest {
@Test
- void assertValidate() {
+ void assertValidateWithUndeclaredContextArgument() {
MCPPromptDescriptor prompt = new MCPPromptDescriptor("test_prompt",
"Test Prompt", "Guide the model through a test prompt.",
List.of(new MCPPromptArgumentDescriptor("database",
"Database", "Logical database.", false)), Map.of());
MCPCompletionTargetDescriptor completion = new
MCPCompletionTargetDescriptor("prompt", "test_prompt", List.of("database"), 50,
@@ -41,4 +41,14 @@ class MCPCompletionTargetDescriptorValidatorTest {
() ->
MCPCompletionTargetDescriptorValidator.validate(List.of(completion),
List.of(prompt), List.of()));
assertThat(actual.getMessage(), is("Completion target
`prompt:test_prompt` context argument `tenant` for `database` is not declared
by the target."));
}
+
+ @Test
+ void assertValidateWithDuplicateArguments() {
+ MCPPromptDescriptor prompt = new MCPPromptDescriptor("test_prompt",
"Test Prompt", "Guide the model through a test prompt.",
+ List.of(new MCPPromptArgumentDescriptor("database",
"Database", "Logical database.", false)), Map.of());
+ MCPCompletionTargetDescriptor completion = new
MCPCompletionTargetDescriptor("prompt", "test_prompt", List.of("database",
"database"), 50, Map.of());
+ IllegalStateException actual =
assertThrows(IllegalStateException.class,
+ () ->
MCPCompletionTargetDescriptorValidator.validate(List.of(completion),
List.of(prompt), List.of()));
+ assertThat(actual.getMessage(), is("Completion target
`prompt:test_prompt` must not contain duplicate arguments."));
+ }
}
diff --git
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogYamlSwapperTest.java
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogYamlSwapperTest.java
index 95534f7da38..7bb9eb005bf 100644
---
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogYamlSwapperTest.java
+++
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPDescriptorCatalogYamlSwapperTest.java
@@ -24,7 +24,6 @@ import
org.apache.shardingsphere.mcp.support.descriptor.yaml.YamlMCPPromptBindin
import
org.apache.shardingsphere.mcp.support.descriptor.yaml.YamlMCPPromptDescriptor;
import
org.apache.shardingsphere.mcp.support.descriptor.yaml.YamlMCPResourceAnnotations;
import
org.apache.shardingsphere.mcp.support.descriptor.yaml.YamlMCPResourceDescriptor;
-import
org.apache.shardingsphere.mcp.support.descriptor.yaml.YamlMCPResourceNavigationDescriptor;
import
org.apache.shardingsphere.mcp.support.descriptor.yaml.YamlMCPToolAnnotations;
import
org.apache.shardingsphere.mcp.support.descriptor.yaml.YamlMCPToolDescriptor;
import org.junit.jupiter.api.Test;
@@ -127,27 +126,6 @@ class MCPDescriptorCatalogYamlSwapperTest {
assertYamlValidationError(yamlCatalog, "MCP descriptor catalog
property `completionTargets[0].maxValues` must not exceed 100.");
}
- @Test
- void assertSwapRejectsDuplicateCompletionArguments() {
- YamlMCPCompletionTargetDescriptor yamlCompletionTarget =
createYamlCompletionTargetDescriptor();
- yamlCompletionTarget.setArguments(List.of("database", "database"));
- YamlMCPDescriptorCatalog yamlCatalog = new YamlMCPDescriptorCatalog();
- yamlCatalog.setCompletionTargets(List.of(yamlCompletionTarget));
- assertYamlValidationError(yamlCatalog, "MCP descriptor catalog
property `completionTargets[0].arguments` must not contain duplicate values.");
- }
-
- @Test
- void assertSwapRejectsDuplicateResourceNavigationArguments() {
- YamlMCPResourceNavigationDescriptor yamlResourceNavigation = new
YamlMCPResourceNavigationDescriptor();
- yamlResourceNavigation.setFrom("database_gateway_search_metadata");
- yamlResourceNavigation.setTo("shardingsphere://capabilities");
- yamlResourceNavigation.setDescription("Navigate from metadata search
to capabilities.");
- yamlResourceNavigation.setRequiredArguments(List.of("database",
"database"));
- YamlMCPDescriptorCatalog yamlCatalog = new YamlMCPDescriptorCatalog();
- yamlCatalog.setResourceNavigation(List.of(yamlResourceNavigation));
- assertYamlValidationError(yamlCatalog, "MCP descriptor catalog
property `resourceNavigation[0].requiredArguments` must not contain duplicate
values.");
- }
-
@Test
void assertSwapRejectsUnnamespacedMetadataKey() {
YamlMCPResourceDescriptor yamlResource =
createYamlResourceDescriptor();
diff --git
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidatorTest.java
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidatorTest.java
index 2d237ec20c8..2bb573fca7c 100644
---
a/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidatorTest.java
+++
b/mcp/support/src/test/java/org/apache/shardingsphere/mcp/support/descriptor/MCPResourceNavigationDescriptorValidatorTest.java
@@ -31,17 +31,38 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
class MCPResourceNavigationDescriptorValidatorTest {
@Test
- void assertValidate() {
+ void assertValidateWithUnknownSource() {
MCPResourceNavigationDescriptor navigation = new
MCPResourceNavigationDescriptor("missing", "database_gateway_test_tool",
List.of(), List.of(), "Read test tool.");
+ IllegalStateException actual =
assertThrows(IllegalStateException.class,
+ () ->
MCPResourceNavigationDescriptorValidator.validate(List.of(navigation),
createCatalog()));
+ assertThat(actual.getMessage(), is("Resource navigation references
unknown source `missing`."));
+ }
+
+ @Test
+ void assertValidateWithDuplicateRequiredArguments() {
+ MCPResourceNavigationDescriptor navigation = new
MCPResourceNavigationDescriptor("database_gateway_test_tool",
"database_gateway_test_tool",
+ List.of("database", "database"), List.of(), "Read test tool.");
+ IllegalStateException actual =
assertThrows(IllegalStateException.class,
+ () ->
MCPResourceNavigationDescriptorValidator.validate(List.of(navigation),
createCatalog()));
+ assertThat(actual.getMessage(), is("Resource navigation
`database_gateway_test_tool` to `database_gateway_test_tool` must not contain
duplicate required arguments."));
+ }
+
+ @Test
+ void assertValidateWithDuplicateCarriedArguments() {
+ MCPResourceNavigationDescriptor navigation = new
MCPResourceNavigationDescriptor("database_gateway_test_tool",
"database_gateway_test_tool",
+ List.of(), List.of("database", "database"), "Read test tool.");
+ IllegalStateException actual =
assertThrows(IllegalStateException.class,
+ () ->
MCPResourceNavigationDescriptorValidator.validate(List.of(navigation),
createCatalog()));
+ assertThat(actual.getMessage(), is("Resource navigation
`database_gateway_test_tool` to `database_gateway_test_tool` must not contain
duplicate carried arguments."));
+ }
+
+ private MCPDescriptorCatalog createCatalog() {
MCPToolDescriptor toolDescriptor = new
MCPToolDescriptor("database_gateway_test_tool", "Test Tool", "Run a test tool.",
Map.of("type", "object", "properties", Map.of("query",
Map.of("type", "string", "description", "Query.")),
"required", List.of("query"), "additionalProperties",
false),
Map.of("type", "object", "properties", Map.of("status",
Map.of("type", "string", "description", "Status.")), "examples",
List.of(Map.of("status", "ok"))),
MCPToolAnnotations.builder().title("Test
Tool").readOnlyHint(true).destructiveHint(false).idempotentHint(true).openWorldHint(true).build(),
Map.of());
- MCPDescriptorCatalog catalog = new MCPDescriptorCatalog(new
MCPProtocolDescriptorCatalog(List.of(), List.of(), List.of(toolDescriptor),
List.of()),
+ return new MCPDescriptorCatalog(new
MCPProtocolDescriptorCatalog(List.of(), List.of(), List.of(toolDescriptor),
List.of()),
new MCPShardingSphereDescriptorCatalog(List.of(), List.of(),
List.of(), List.of(), List.of()));
- IllegalStateException actual =
assertThrows(IllegalStateException.class,
- () ->
MCPResourceNavigationDescriptorValidator.validate(List.of(navigation),
catalog));
- assertThat(actual.getMessage(), is("Resource navigation references
unknown source `missing`."));
}
}
diff --git
a/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManager.java
b/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManager.java
index fecb43e7639..0b286ea4823 100644
---
a/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManager.java
+++
b/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManager.java
@@ -22,6 +22,7 @@ import lombok.SneakyThrows;
import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
import
org.apache.shardingsphere.infra.config.rule.checker.DatabaseRuleConfigurationEmptyChecker;
import
org.apache.shardingsphere.infra.config.rule.scope.DatabaseRuleConfiguration;
+import
org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
import org.apache.shardingsphere.infra.instance.ComputeNodeInstanceContext;
import org.apache.shardingsphere.infra.rule.PartialRuleUpdateSupported;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
@@ -62,6 +63,7 @@ public final class DatabaseRuleConfigurationManager {
refreshMetadata(databaseName, ruleConfig, false, rules);
return;
}
+ RuleConfigurationValidator.validate(ruleConfig);
Optional<ShardingSphereRule> toBeChangedRule =
rules.stream().filter(each ->
each.getConfiguration().getClass().equals(ruleConfig.getClass())).findFirst();
if (toBeChangedRule.isPresent() && toBeChangedRule.get() instanceof
PartialRuleUpdateSupported) {
boolean needRefreshSchemas = ((PartialRuleUpdateSupported)
toBeChangedRule.get()).partialUpdate(ruleConfig);
diff --git
a/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManager.java
b/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManager.java
index d32362a165c..51580f14b95 100644
---
a/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManager.java
+++
b/mode/core/src/main/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManager.java
@@ -24,6 +24,7 @@ import
org.apache.shardingsphere.infra.config.props.MetadataIdentifierCaseSensit
import
org.apache.shardingsphere.infra.config.props.temporary.TemporaryConfigurationProperties;
import
org.apache.shardingsphere.infra.config.props.temporary.TemporaryConfigurationPropertyKey;
import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
+import
org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
import org.apache.shardingsphere.infra.rule.builder.global.GlobalRulesBuilder;
@@ -55,6 +56,7 @@ public final class GlobalConfigurationManager {
if (null == ruleConfig) {
return;
}
+ RuleConfigurationValidator.validate(ruleConfig);
Collection<ShardingSphereRule> rules = removeGlobalRule(ruleConfig,
metaDataContexts.getMetaData().getGlobalRuleMetaData().getRules());
rules.addAll(GlobalRulesBuilder.buildSingleRules(ruleConfig,
metaDataContexts.getMetaData().getAllDatabases(),
metaDataContexts.getMetaData().getProps()));
metaDataContexts.getMetaData().getGlobalRuleMetaData().getRules().clear();
diff --git
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/resource/StorageUnitManagerTest.java
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/resource/StorageUnitManagerTest.java
index 8c9a267aaa7..bafe3c8c14d 100644
---
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/resource/StorageUnitManagerTest.java
+++
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/resource/StorageUnitManagerTest.java
@@ -77,7 +77,8 @@ class StorageUnitManagerTest {
SwitchingResource switchingResource = new
SwitchingResource(Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyList(), Collections.emptyMap());
when(resourceSwitchManager.switchByRegisterStorageUnit(any(ResourceMetaData.class),
any(Map.class), anyBoolean())).thenReturn(switchingResource);
ShardingSphereDatabase reloadDatabase =
mock(ShardingSphereDatabase.class);
-
when(reloadDatabase.getAllSchemas()).thenReturn(Collections.singleton(new
ShardingSphereSchema("foo_schema", mock(DatabaseType.class))));
+ ShardingSphereSchema schema = new ShardingSphereSchema("foo_schema",
mock(DatabaseType.class));
+
when(reloadDatabase.getAllSchemas()).thenReturn(Collections.singleton(schema));
when(reloadDatabase.getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
"FIXTURE"));
MetaDataContexts reloadMetaDataContexts = mock(MetaDataContexts.class,
RETURNS_DEEP_STUBS);
when(reloadMetaDataContexts.getMetaData().getDatabase(DATABASE_NAME)).thenReturn(reloadDatabase);
@@ -132,7 +133,8 @@ class StorageUnitManagerTest {
MetaDataContexts reloadMetaDataContexts = mock(MetaDataContexts.class,
RETURNS_DEEP_STUBS);
when(reloadMetaDataContexts.getMetaData().getDatabase(DATABASE_NAME)).thenReturn(reloadDatabase);
when(reloadMetaDataContexts.getMetaData().getProps()).thenReturn(new
ConfigurationProperties(new Properties()));
-
when(reloadDatabase.getAllSchemas()).thenReturn(Collections.singleton(new
ShardingSphereSchema("foo_schema", mock(DatabaseType.class))));
+ ShardingSphereSchema schema = new ShardingSphereSchema("foo_schema",
mock(DatabaseType.class));
+
when(reloadDatabase.getAllSchemas()).thenReturn(Collections.singleton(schema));
try (
MockedConstruction<MetaDataContextsFactory> ignored =
mockConstruction(MetaDataContextsFactory.class,
(mock, context) ->
when(mock.createBySwitchResource(DATABASE_NAME, switchingResource,
metaDataContexts)).thenReturn(reloadMetaDataContexts))) {
@@ -164,7 +166,8 @@ class StorageUnitManagerTest {
MetaDataContexts reloadMetaDataContexts = mock(MetaDataContexts.class,
RETURNS_DEEP_STUBS);
when(reloadMetaDataContexts.getMetaData().getDatabase(DATABASE_NAME)).thenReturn(reloadDatabase);
when(reloadMetaDataContexts.getMetaData().getProps()).thenReturn(new
ConfigurationProperties(new Properties()));
-
when(reloadDatabase.getAllSchemas()).thenReturn(Collections.singleton(new
ShardingSphereSchema("foo_schema", mock(DatabaseType.class))));
+ ShardingSphereSchema schema = new ShardingSphereSchema("foo_schema",
mock(DatabaseType.class));
+
when(reloadDatabase.getAllSchemas()).thenReturn(Collections.singleton(schema));
try (
MockedConstruction<MetaDataContextsFactory> ignored =
mockConstruction(MetaDataContextsFactory.class,
(mock, context) ->
when(mock.createBySwitchResource(DATABASE_NAME, switchingResource,
metaDataContexts)).thenReturn(reloadMetaDataContexts))) {
diff --git
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManagerTest.java
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManagerTest.java
index a8a40bfa1d6..f9d10430885 100644
---
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManagerTest.java
+++
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/DatabaseRuleConfigurationManagerTest.java
@@ -20,6 +20,8 @@ package org.apache.shardingsphere.mode.metadata.manager.rule;
import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
import
org.apache.shardingsphere.infra.config.rule.checker.DatabaseRuleConfigurationEmptyChecker;
import
org.apache.shardingsphere.infra.config.rule.scope.DatabaseRuleConfiguration;
+import
org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
import org.apache.shardingsphere.infra.rule.PartialRuleUpdateSupported;
import org.apache.shardingsphere.infra.rule.ShardingSphereRule;
@@ -32,6 +34,7 @@ import
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockS
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
import java.io.Serializable;
import java.sql.SQLException;
@@ -49,6 +52,7 @@ import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -89,6 +93,21 @@ class DatabaseRuleConfigurationManagerTest {
}
}
+ @Test
+ void assertRefreshWithInvalidRuleConfiguration() {
+ RuleConfiguration ruleConfig = mock(RuleConfiguration.class);
+ ShardingSphereRule partialRule = mock(ShardingSphereRule.class,
withSettings().extraInterfaces(PartialRuleUpdateSupported.class));
+ MetaDataContexts metaDataContexts = mock(MetaDataContexts.class,
RETURNS_DEEP_STUBS);
+
when(metaDataContexts.getMetaData().getDatabase(DATABASE_NAME).getRuleMetaData()).thenReturn(new
RuleMetaData(Collections.singleton(partialRule)));
+ DatabaseRuleConfigurationManager manager = new
DatabaseRuleConfigurationManager(metaDataContexts, mock(), mock());
+ try (MockedStatic<RuleConfigurationValidator> mockedValidator =
mockStatic(RuleConfigurationValidator.class)) {
+ mockedValidator.when(() ->
RuleConfigurationValidator.validate(ruleConfig)).thenThrow(new
InvalidRuleConfigurationException("fixture", "invalid"));
+ assertThrows(InvalidRuleConfigurationException.class, () ->
manager.refresh(DATABASE_NAME, ruleConfig));
+ }
+ verify((PartialRuleUpdateSupported) partialRule,
never()).partialUpdate(ruleConfig);
+ verify(metaDataContexts, never()).update(any(MetaDataContexts.class));
+ }
+
@Test
void assertRefreshWithPartialUpdateSkipMetadata() throws SQLException {
DatabaseRuleConfiguration ruleConfig =
mockDatabaseRuleConfiguration(false);
diff --git
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManagerTest.java
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManagerTest.java
index 9074b5a18bf..0bed0e2d0f8 100644
---
a/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManagerTest.java
+++
b/mode/core/src/test/java/org/apache/shardingsphere/mode/metadata/manager/rule/GlobalConfigurationManagerTest.java
@@ -22,6 +22,8 @@ import
org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
import
org.apache.shardingsphere.infra.config.props.temporary.TemporaryConfigurationProperties;
import
org.apache.shardingsphere.infra.config.props.temporary.TemporaryConfigurationPropertyKey;
import org.apache.shardingsphere.infra.config.rule.RuleConfiguration;
+import
org.apache.shardingsphere.infra.config.rule.validator.RuleConfigurationValidator;
+import
org.apache.shardingsphere.infra.exception.kernel.metadata.rule.InvalidRuleConfigurationException;
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
@@ -39,6 +41,7 @@ import
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExt
import
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
import java.io.Serializable;
import java.util.Arrays;
@@ -56,6 +59,7 @@ import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -75,6 +79,19 @@ class GlobalConfigurationManagerTest {
verify(metaDataContexts,
never()).update(any(ShardingSphereMetaData.class),
any(MetaDataPersistFacade.class));
}
+ @Test
+ void assertAlterGlobalRuleConfigurationWithInvalidConfiguration() {
+ RuleConfiguration ruleConfig = mock(RuleConfiguration.class);
+ MetaDataContexts metaDataContexts = mock(MetaDataContexts.class);
+ GlobalConfigurationManager manager = new
GlobalConfigurationManager(metaDataContexts, mock(MetaDataPersistFacade.class));
+ try (MockedStatic<RuleConfigurationValidator> mockedValidator =
mockStatic(RuleConfigurationValidator.class)) {
+ mockedValidator.when(() ->
RuleConfigurationValidator.validate(ruleConfig)).thenThrow(new
InvalidRuleConfigurationException("fixture", "invalid"));
+ assertThrows(InvalidRuleConfigurationException.class, () ->
manager.alterGlobalRuleConfiguration(ruleConfig));
+ }
+ verify(metaDataContexts, never()).getMetaData();
+ verify(metaDataContexts,
never()).update(any(ShardingSphereMetaData.class),
any(MetaDataPersistFacade.class));
+ }
+
@Test
void assertAlterGlobalRuleConfigurationReplacesRules() {
RuleConfiguration newConfig = mock(RuleConfiguration.class,
withSettings().extraInterfaces(Serializable.class));
diff --git a/pom.xml b/pom.xml
index 729c37b5ffb..d49250ba1a6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -104,6 +104,7 @@
<bytebuddy.version>1.18.8</bytebuddy.version>
<jakarta.jakartaee-bom.version>8.0.0</jakarta.jakartaee-bom.version>
+ <bval.version>2.0.6</bval.version>
<netty.version>4.2.9.Final</netty.version>
<bouncycastle.version>1.84</bouncycastle.version>
@@ -311,6 +312,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.bval</groupId>
+ <artifactId>bval-jsr</artifactId>
+ <version>${bval.version}</version>
+ </dependency>
<dependency>
<groupId>org.freemarker</groupId>