Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 108ae79

Browse files
committedMar 20, 2025·
modify based on comments
Signed-off-by: Ruirui Zhang <mariazrr@amazon.com>
1 parent c87cf21 commit 108ae79

File tree

5 files changed

+90
-41
lines changed

5 files changed

+90
-41
lines changed
 

‎server/src/main/java/org/opensearch/autotagging/Attribute.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
/**
1818
* Represents an attribute within the auto-tagging feature. Attributes define characteristics that can
19-
* be used for tagging and classification. Implementations Implementations must ensure that attributes
19+
* be used for tagging and classification. Implementations must ensure that attributes
2020
* are uniquely identifiable by their name. Attributes should be singletons and managed centrally to
2121
* avoid duplicates.
2222
*
@@ -25,11 +25,29 @@
2525
public interface Attribute extends Writeable {
2626
String getName();
2727

28+
/**
29+
* Ensure that `validateAttribute` is called in the constructor of attribute implementations
30+
* to prevent potential serialization issues.
31+
*/
32+
default void validateAttribute() {
33+
String name = getName();
34+
if (name == null || name.isEmpty()) {
35+
throw new IllegalArgumentException("Attribute name cannot be null or empty");
36+
}
37+
}
38+
2839
@Override
2940
default void writeTo(StreamOutput out) throws IOException {
3041
out.writeString(getName());
3142
}
3243

44+
/**
45+
* Retrieves an attribute from the given feature type based on its name.
46+
* Implementations of `FeatureType.getAttributeFromName` must be thread-safe as this method
47+
* may be called concurrently.
48+
* @param in - the {@link StreamInput} from which the attribute name is read
49+
* @param featureType - the FeatureType used to look up the attribute
50+
*/
3351
static Attribute from(StreamInput in, FeatureType featureType) throws IOException {
3452
String attributeName = in.readString();
3553
Attribute attribute = featureType.getAttributeFromName(attributeName);

‎server/src/main/java/org/opensearch/autotagging/AutoTaggingRegistry.java

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
* @opensearch.experimental
2222
*/
2323
public class AutoTaggingRegistry {
24+
/**
25+
* featureTypesRegistryMap should be concurrently readable but not concurrently writable.
26+
* The registration of FeatureType should only be done during boot-up.
27+
*/
2428
public static final Map<String, FeatureType> featureTypesRegistryMap = new HashMap<>();
2529
public static final int MAX_FEATURE_TYPE_NAME_LENGTH = 30;
2630

‎server/src/main/java/org/opensearch/autotagging/FeatureType.java

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ public interface FeatureType extends Writeable {
3333

3434
String getName();
3535

36+
/**
37+
* Returns the registry of allowed attributes for this feature type.
38+
* Implementations must ensure that access to this registry is thread-safe.
39+
*/
3640
Map<String, Attribute> getAllowedAttributesRegistry();
3741

3842
default int getMaxNumberOfValuesPerAttribute() {
@@ -49,6 +53,11 @@ default boolean isValidAttribute(Attribute attribute) {
4953
return getAllowedAttributesRegistry().containsValue(attribute);
5054
}
5155

56+
/**
57+
* Retrieves an attribute by its name from the allowed attributes' registry.
58+
* Implementations must ensure that this method is thread-safe.
59+
* @param name The name of the attribute.
60+
*/
5261
default Attribute getAttributeFromName(String name) {
5362
return getAllowedAttributesRegistry().get(name);
5463
}

‎server/src/main/java/org/opensearch/autotagging/Rule.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class Rule implements Writeable, ToXContentObject {
4343
private final Map<Attribute, Set<String>> attributeMap;
4444
private final String featureValue;
4545
private final String updatedAt;
46+
private RuleValidator ruleValidator;
4647
public static final String _ID_STRING = "_id";
4748
public static final String DESCRIPTION_STRING = "description";
4849
public static final String UPDATED_AT_STRING = "updated_at";
@@ -72,8 +73,8 @@ public Rule(StreamInput in) throws IOException {
7273
}
7374

7475
private void validateRule() {
75-
RuleValidator validator = new RuleValidator(description, attributeMap, featureValue, updatedAt, featureType);
76-
validator.validate();
76+
this.ruleValidator = new RuleValidator(description, attributeMap, featureValue, updatedAt, featureType);
77+
this.ruleValidator.validate();
7778
}
7879

7980
@Override

‎server/src/main/java/org/opensearch/autotagging/RuleValidator.java

+55-38
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import org.opensearch.common.ValidationException;
1212
import org.joda.time.Instant;
1313

14+
import java.util.ArrayList;
15+
import java.util.List;
1416
import java.util.Map;
1517
import java.util.Set;
1618

@@ -28,7 +30,6 @@ public class RuleValidator {
2830
private final String featureValue;
2931
private final String updatedAt;
3032
private final FeatureType featureType;
31-
private final ValidationException validationException = new ValidationException();
3233
public static final int MAX_DESCRIPTION_LENGTH = 256;
3334

3435
public RuleValidator(
@@ -46,90 +47,106 @@ public RuleValidator(
4647
}
4748

4849
public void validate() {
49-
validateStringFields();
50-
validateFeatureType();
51-
validateUpdatedAtEpoch();
52-
validateAttributeMap();
53-
if (!validationException.validationErrors().isEmpty()) {
50+
List<String> errorMessages = new ArrayList<>();
51+
errorMessages.addAll(validateStringFields());
52+
errorMessages.addAll(validateFeatureType());
53+
errorMessages.addAll(validateUpdatedAtEpoch());
54+
errorMessages.addAll(validateAttributeMap());
55+
if (!errorMessages.isEmpty()) {
56+
ValidationException validationException = new ValidationException();
57+
validationException.addValidationErrors(errorMessages);
5458
throw new IllegalArgumentException(validationException);
5559
}
5660
}
5761

58-
private void validateStringFields() {
59-
requireNonNullOrEmpty(description, "Rule description can't be null or empty");
60-
if (description != null && description.length() > MAX_DESCRIPTION_LENGTH) {
61-
validationException.addValidationError("Rule description cannot exceed " + MAX_DESCRIPTION_LENGTH + " characters.");
62+
private List<String> validateStringFields() {
63+
List<String> errors = new ArrayList<>();
64+
if (isNullOrEmpty(description)) {
65+
errors.add("Rule description can't be null or empty");
66+
} else if (description.length() > MAX_DESCRIPTION_LENGTH) {
67+
errors.add("Rule description cannot exceed " + MAX_DESCRIPTION_LENGTH + " characters.");
6268
}
63-
requireNonNullOrEmpty(featureValue, "Rule featureValue can't be null or empty");
64-
requireNonNullOrEmpty(updatedAt, "Rule update time can't be null or empty");
69+
if (isNullOrEmpty(featureValue)) {
70+
errors.add("Rule featureValue can't be null or empty");
71+
}
72+
if (isNullOrEmpty(updatedAt)) {
73+
errors.add("Rule update time can't be null or empty");
74+
}
75+
return errors;
6576
}
6677

67-
private void validateFeatureType() {
78+
private boolean isNullOrEmpty(String str) {
79+
return str == null || str.isEmpty();
80+
}
81+
82+
private List<String> validateFeatureType() {
6883
if (featureType == null) {
69-
validationException.addValidationError("Couldn't identify which feature the rule belongs to. Rule feature can't be null.");
84+
return List.of("Couldn't identify which feature the rule belongs to. Rule feature can't be null.");
7085
}
86+
return new ArrayList<>();
7187
}
7288

73-
private void validateUpdatedAtEpoch() {
89+
private List<String> validateUpdatedAtEpoch() {
7490
if (updatedAt != null && !isValid(Instant.parse(updatedAt).getMillis())) {
75-
validationException.addValidationError("Rule update time is not a valid epoch");
91+
return List.of("Rule update time is not a valid epoch");
7692
}
93+
return new ArrayList<>();
7794
}
7895

79-
private void validateAttributeMap() {
96+
private List<String> validateAttributeMap() {
97+
List<String> errors = new ArrayList<>();
8098
if (attributeMap == null || attributeMap.isEmpty()) {
81-
validationException.addValidationError("Rule should have at least 1 attribute requirement");
99+
errors.add("Rule should have at least 1 attribute requirement");
82100
}
83101

84102
if (attributeMap != null && featureType != null) {
85103
for (Map.Entry<Attribute, Set<String>> entry : attributeMap.entrySet()) {
86104
Attribute attribute = entry.getKey();
87105
Set<String> attributeValues = entry.getValue();
88-
validateAttributeExistence(attribute);
89-
validateMaxAttributeValues(attribute, attributeValues);
90-
validateAttributeValuesLength(attributeValues);
106+
errors.addAll(validateAttributeExistence(attribute));
107+
errors.addAll(validateMaxAttributeValues(attribute, attributeValues));
108+
errors.addAll(validateAttributeValuesLength(attributeValues));
91109
}
92110
}
111+
return errors;
93112
}
94113

95-
private void validateAttributeExistence(Attribute attribute) {
114+
private List<String> validateAttributeExistence(Attribute attribute) {
96115
if (featureType.getAttributeFromName(attribute.getName()) == null) {
97-
validationException.addValidationError(
98-
attribute.getName() + " is not a valid attribute within the " + featureType.getName() + " feature."
99-
);
116+
return List.of(attribute.getName() + " is not a valid attribute within the " + featureType.getName() + " feature.");
100117
}
118+
return new ArrayList<>();
101119
}
102120

103-
private void validateMaxAttributeValues(Attribute attribute, Set<String> attributeValues) {
121+
private List<String> validateMaxAttributeValues(Attribute attribute, Set<String> attributeValues) {
122+
List<String> errors = new ArrayList<>();
123+
String attributeName = attribute.getName();
124+
if (attributeValues.isEmpty()) {
125+
errors.add("Attribute values for " + attributeName + " cannot be empty.");
126+
}
104127
int maxSize = featureType.getMaxNumberOfValuesPerAttribute();
105128
int actualSize = attributeValues.size();
106129
if (actualSize > maxSize) {
107-
validationException.addValidationError(
130+
errors.add(
108131
"Each attribute can only have a maximum of "
109132
+ maxSize
110133
+ " values. The input attribute "
111-
+ attribute
134+
+ attributeName
112135
+ " has length "
113136
+ attributeValues.size()
114137
+ ", which exceeds this limit."
115138
);
116139
}
140+
return errors;
117141
}
118142

119-
private void validateAttributeValuesLength(Set<String> attributeValues) {
143+
private List<String> validateAttributeValuesLength(Set<String> attributeValues) {
120144
int maxValueLength = featureType.getMaxCharLengthPerAttributeValue();
121145
for (String attributeValue : attributeValues) {
122146
if (attributeValue.isEmpty() || attributeValue.length() > maxValueLength) {
123-
validationException.addValidationError(
124-
"Attribute value [" + attributeValue + "] is invalid (empty or exceeds " + maxValueLength + " characters)"
125-
);
147+
return List.of("Attribute value [" + attributeValue + "] is invalid (empty or exceeds " + maxValueLength + " characters)");
126148
}
127149
}
128-
}
129-
130-
private void requireNonNullOrEmpty(String value, String errorMessage) {
131-
if (value == null || value.isEmpty()) {
132-
validationException.addValidationError(errorMessage);
133-
}
150+
return new ArrayList<>();
134151
}
135152
}

0 commit comments

Comments
 (0)
Please sign in to comment.