Skip to content

Commit 3a4d4b7

Browse files
yiyuabcclaude
andcommitted
Add aggregation name validation matching REST API behavior
Apply the same aggregation name validation as REST API using AggregatorFactories.VALID_AGG_NAME pattern in the gRPC registry. This ensures aggregation names cannot contain '[', ']', or '>' characters, matching REST validation behavior exactly. Changes: - Add name validation in AggregationBuilderProtoConverterSpiRegistry.fromProto() - Remove outdated javadoc stating "Name validation is handled by the registry" from MinAggregationProtoUtils and MaxAggregationProtoUtils - Add comprehensive tests for invalid aggregation name validation Mirrors REST-side validation from AggregatorFactories#parseAggregators. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e37f084 commit 3a4d4b7

File tree

10 files changed

+63
-21
lines changed

10 files changed

+63
-21
lines changed

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/AggregationBuilderProtoConverterRegistryImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
/**
2222
* Registry for AggregationBuilderProtoConverter implementations.
23-
* Wraps the SPI registry and registers built-in converters (Min, Max).
2423
*/
2524
@Singleton
2625
public class AggregationBuilderProtoConverterRegistryImpl implements AggregationBuilderProtoConverterRegistry {

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/AggregationBuilderProtoConverterSpiRegistry.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import java.util.HashMap;
2222
import java.util.Map;
23+
import java.util.regex.Matcher;
2324

2425
/**
2526
* SPI registry for AggregationBuilderProtoConverter implementations.
@@ -51,6 +52,13 @@ public AggregationBuilder fromProto(String name, AggregationContainer container)
5152
throw new IllegalArgumentException("Aggregation name cannot be null or empty");
5253
}
5354

55+
Matcher validAggMatcher = AggregatorFactories.VALID_AGG_NAME.matcher(name);
56+
if (!validAggMatcher.matches()) {
57+
throw new IllegalArgumentException(
58+
"Invalid aggregation name [" + name + "]. Aggregation names can contain any character except '[', ']', and '>'"
59+
);
60+
}
61+
5462
AggregationContainer.AggregationContainerCase aggregationCase = container.getAggregationContainerCase();
5563
AggregationBuilderProtoConverter converter = converterMap.get(aggregationCase);
5664

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/metrics/MaxAggregationBuilderProtoConverter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
import org.opensearch.transport.grpc.spi.AggregationBuilderProtoConverter;
1313

1414
/**
15-
* Converter for Max aggregation. Delegates to {@link MaxAggregationProtoUtils}.
16-
* Mirrors REST parsing via {@link org.opensearch.search.aggregations.metrics.MaxAggregationBuilder#PARSER}.
15+
* Converter for Max aggregation.
1716
*/
1817
public class MaxAggregationBuilderProtoConverter implements AggregationBuilderProtoConverter {
1918

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/metrics/MaxAggregationProtoUtils.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
/**
1818
* Utility class for converting MaxAggregation Protocol Buffers to MaxAggregationBuilder.
19-
*
20-
* @see MaxAggregationBuilder#PARSER
2119
*/
2220
public class MaxAggregationProtoUtils {
2321

@@ -31,9 +29,7 @@ private MaxAggregationProtoUtils() {
3129
* <p>This method parallels the REST parsing logic in {@link MaxAggregationBuilder#PARSER},
3230
* processing fields in the exact same sequence to ensure consistent validation and behavior.
3331
*
34-
* <p>Name validation is handled by the registry before this method is called.
35-
*
36-
* @param name The name of the aggregation (validated by registry)
32+
* @param name The name of the aggregation
3733
* @param maxAggProto The Protocol Buffer MaxAggregation to convert
3834
* @return A configured MaxAggregationBuilder
3935
* @throws IllegalArgumentException if required fields are missing or validation fails

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/metrics/MinAggregationBuilderProtoConverter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
import org.opensearch.transport.grpc.spi.AggregationBuilderProtoConverter;
1313

1414
/**
15-
* Converter for Min aggregation. Delegates to {@link MinAggregationProtoUtils}.
16-
* Mirrors REST parsing via {@link org.opensearch.search.aggregations.metrics.MinAggregationBuilder#PARSER}.
15+
* Converter for Min aggregation.
1716
*/
1817
public class MinAggregationBuilderProtoConverter implements AggregationBuilderProtoConverter {
1918

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/metrics/MinAggregationProtoUtils.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
/**
1818
* Utility class for converting MinAggregation Protocol Buffers to MinAggregationBuilder.
19-
*
20-
* @see MinAggregationBuilder#PARSER
2119
*/
2220
public class MinAggregationProtoUtils {
2321

@@ -31,9 +29,7 @@ private MinAggregationProtoUtils() {
3129
* <p>This method parallels the REST parsing logic in {@link MinAggregationBuilder#PARSER},
3230
* processing fields in the exact same sequence to ensure consistent validation and behavior.
3331
*
34-
* <p>Name validation is handled by the registry before this method is called.
35-
*
36-
* @param name The name of the aggregation (validated by registry)
32+
* @param name The name of the aggregation
3733
* @param minAggProto The Protocol Buffer MinAggregation to convert
3834
* @return A configured MinAggregationBuilder
3935
* @throws IllegalArgumentException if required fields are missing or validation fails

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/support/ValuesSourceAggregationProtoUtils.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
/**
2020
* Utility class for parsing common fields from ValuesSource-based aggregation Protocol Buffer messages.
21-
* Mirrors {@link ValuesSourceAggregationBuilder#declareFields} using {@link ObjectParserProtoUtils}.
2221
*/
2322
public class ValuesSourceAggregationProtoUtils {
2423

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/aggregation/support/ValuesSourceProtoFields.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
/**
1313
* Wrapper for common ValuesSource aggregation fields extracted from proto messages.
1414
* Reduces parameter count in {@link ValuesSourceAggregationProtoUtils#declareFields}.
15-
*
16-
* <p>All fields use null to indicate "not set". The proto3 limitation (strings default to "",
17-
* enums default to first value) is handled at the proto boundary when constructing this wrapper.
1815
*/
1916
public class ValuesSourceProtoFields {
2017
private final String field;

modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/response/search/aggregation/metrics/package-info.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
/**
66
* Protocol Buffer utilities for metrics aggregation responses.
77
* Contains converters from OpenSearch internal metrics aggregation results to Protocol Buffer messages.
8-
* <p>
9-
* Metrics aggregations compute metrics (like min, max, avg, sum, cardinality) over a set of documents.
108
*
119
* @see org.opensearch.search.aggregations.metrics
1210
*/

modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/aggregation/AggregationBuilderProtoConverterRegistryTests.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,55 @@ public void testNestedAggregationsInfrastructure() {
288288
// exception.getMessage().contains("cannot accept sub-aggregations")
289289
// );
290290
}
291+
292+
/**
293+
* Test that invalid aggregation names are rejected.
294+
* Verifies that aggregation name validation matches REST API behavior.
295+
* See {@link org.opensearch.search.aggregations.AggregatorFactories#parseAggregators}.
296+
*/
297+
public void testInvalidAggregationNames() {
298+
AggregationBuilderProtoConverterRegistryImpl registry = new AggregationBuilderProtoConverterRegistryImpl();
299+
AggregationContainer container = AggregationContainer.newBuilder()
300+
.setMin(MinAggregation.newBuilder().setField("price").build())
301+
.build();
302+
303+
// Test name with '[' character
304+
IllegalArgumentException ex1 = expectThrows(
305+
IllegalArgumentException.class,
306+
() -> registry.fromProto("invalid[name", container)
307+
);
308+
assertTrue(
309+
"Exception should mention invalid aggregation name",
310+
ex1.getMessage().contains("Invalid aggregation name")
311+
);
312+
assertTrue(
313+
"Exception should mention forbidden characters",
314+
ex1.getMessage().contains("'[', ']', and '>'")
315+
);
316+
317+
// Test name with ']' character
318+
IllegalArgumentException ex2 = expectThrows(
319+
IllegalArgumentException.class,
320+
() -> registry.fromProto("invalid]name", container)
321+
);
322+
assertTrue(
323+
"Exception should mention invalid aggregation name",
324+
ex2.getMessage().contains("Invalid aggregation name")
325+
);
326+
327+
// Test name with '>' character
328+
IllegalArgumentException ex3 = expectThrows(
329+
IllegalArgumentException.class,
330+
() -> registry.fromProto("invalid>name", container)
331+
);
332+
assertTrue(
333+
"Exception should mention invalid aggregation name",
334+
ex3.getMessage().contains("Invalid aggregation name")
335+
);
336+
337+
// Test that valid names work fine
338+
AggregationBuilder validBuilder = registry.fromProto("valid_name-with.dots:and_more", container);
339+
assertNotNull("Valid aggregation name should be accepted", validBuilder);
340+
assertEquals("valid_name-with.dots:and_more", validBuilder.getName());
341+
}
291342
}

0 commit comments

Comments
 (0)