Skip to content

Commit 422422a

Browse files
[7.x][ML] Reuse SourceDestValidator for data frame analytics (#50841) (#50850)
This commit removes validation logic of source and dest indices for data frame analytics and replaces it with using the common `SourceDestValidator` class which is already used by transforms. This way the validations and their messages become consistent while we reduce code. This means that where these validations fail the error messages will be slightly different for data frame analytics. Backport of #50841
1 parent 7e68989 commit 422422a

File tree

18 files changed

+256
-385
lines changed

18 files changed

+256
-385
lines changed

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/common/validation/SourceDestValidator.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ public final class SourceDestValidator {
4949

5050
// messages
5151
public static final String SOURCE_INDEX_MISSING = "Source index [{0}] does not exist";
52-
public static final String SOURCE_LOWERCASE = "Source index [{0}] must be lowercase";
5352
public static final String DEST_IN_SOURCE = "Destination index [{0}] is included in source expression [{1}]";
5453
public static final String DEST_LOWERCASE = "Destination index [{0}] must be lowercase";
5554
public static final String NEEDS_REMOTE_CLUSTER_SEARCH = "Source index is configured with a remote index pattern(s) [{0}]"
@@ -62,6 +61,7 @@ public final class SourceDestValidator {
6261
+ "alias [{0}], at least a [{1}] license is required, found license [{2}]";
6362
public static final String REMOTE_CLUSTER_LICENSE_INACTIVE = "License check failed for remote cluster "
6463
+ "alias [{0}], license is not active";
64+
public static final String REMOTE_SOURCE_INDICES_NOT_SUPPORTED = "remote source indices are not supported";
6565

6666
// workaround for 7.x: remoteClusterAliases does not throw
6767
private static final ClusterNameExpressionResolver clusterNameExpressionResolver = new ClusterNameExpressionResolver();
@@ -222,7 +222,7 @@ private void resolveLocalAndRemoteSource() {
222222
}
223223
}
224224

225-
interface SourceDestValidation {
225+
public interface SourceDestValidation {
226226
void validate(Context context, ActionListener<Context> listener);
227227
}
228228

@@ -234,18 +234,7 @@ interface SourceDestValidation {
234234
public static final SourceDestValidation REMOTE_SOURCE_VALIDATION = new RemoteSourceEnabledAndRemoteLicenseValidation();
235235
public static final SourceDestValidation DESTINATION_IN_SOURCE_VALIDATION = new DestinationInSourceValidation();
236236
public static final SourceDestValidation DESTINATION_SINGLE_INDEX_VALIDATION = new DestinationSingleIndexValidation();
237-
238-
// set of default validation collections, if you want to automatically benefit from new validators, use those
239-
public static final List<SourceDestValidation> PREVIEW_VALIDATIONS = Arrays.asList(SOURCE_MISSING_VALIDATION, REMOTE_SOURCE_VALIDATION);
240-
241-
public static final List<SourceDestValidation> ALL_VALIDATIONS = Arrays.asList(
242-
SOURCE_MISSING_VALIDATION,
243-
REMOTE_SOURCE_VALIDATION,
244-
DESTINATION_IN_SOURCE_VALIDATION,
245-
DESTINATION_SINGLE_INDEX_VALIDATION
246-
);
247-
248-
public static final List<SourceDestValidation> NON_DEFERABLE_VALIDATIONS = Arrays.asList(DESTINATION_SINGLE_INDEX_VALIDATION);
237+
public static final SourceDestValidation REMOTE_SOURCE_NOT_SUPPORTED_VALIDATION = new RemoteSourceNotSupportedValidation();
249238

250239
/**
251240
* Create a new Source Dest Validator
@@ -305,10 +294,11 @@ public void validate(
305294
}
306295
}, listener::onFailure);
307296

297+
// We traverse the validations in reverse order as we chain the listeners from back to front
308298
for (int i = validations.size() - 1; i >= 0; i--) {
309-
final SourceDestValidation validation = validations.get(i);
299+
SourceDestValidation validation = validations.get(i);
310300
final ActionListener<Context> previousValidationListener = validationListener;
311-
validationListener = ActionListener.wrap(c -> { validation.validate(c, previousValidationListener); }, listener::onFailure);
301+
validationListener = ActionListener.wrap(c -> validation.validate(c, previousValidationListener), listener::onFailure);
312302
}
313303

314304
validationListener.onResponse(context);
@@ -433,13 +423,13 @@ public void validate(Context context, ActionListener<Context> listener) {
433423
return;
434424
}
435425

436-
if (context.resolvedSource.contains(destIndex)) {
426+
if (context.resolveSource().contains(destIndex)) {
437427
context.addValidationError(DEST_IN_SOURCE, destIndex, Strings.arrayToCommaDelimitedString(context.getSource()));
438428
listener.onResponse(context);
439429
return;
440430
}
441431

442-
if (context.resolvedSource.contains(context.resolveDest())) {
432+
if (context.resolveSource().contains(context.resolveDest())) {
443433
context.addValidationError(
444434
DEST_IN_SOURCE,
445435
context.resolveDest(),
@@ -460,6 +450,17 @@ public void validate(Context context, ActionListener<Context> listener) {
460450
}
461451
}
462452

453+
static class RemoteSourceNotSupportedValidation implements SourceDestValidation {
454+
455+
@Override
456+
public void validate(Context context, ActionListener<Context> listener) {
457+
if (context.resolveRemoteSource().isEmpty() == false) {
458+
context.addValidationError(REMOTE_SOURCE_INDICES_NOT_SUPPORTED);
459+
}
460+
listener.onResponse(context);
461+
}
462+
}
463+
463464
private static String getMessage(String message, Object... args) {
464465
return new MessageFormat(message, Locale.ROOT).format(args);
465466
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/action/PutDataFrameAnalyticsAction.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
import org.elasticsearch.common.xcontent.ToXContentObject;
1919
import org.elasticsearch.common.xcontent.XContentBuilder;
2020
import org.elasticsearch.common.xcontent.XContentParser;
21+
import org.elasticsearch.xpack.core.common.validation.SourceDestValidator;
2122
import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsConfig;
2223
import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsSource;
2324
import org.elasticsearch.xpack.core.ml.job.messages.Messages;
25+
import org.elasticsearch.xpack.core.ml.utils.MlStrings;
2426

2527
import java.io.IOException;
2628
import java.util.Objects;
@@ -90,14 +92,29 @@ public DataFrameAnalyticsConfig getConfig() {
9092
@Override
9193
public ActionRequestValidationException validate() {
9294
ActionRequestValidationException error = null;
95+
error = checkConfigIdIsValid(config, error);
96+
error = SourceDestValidator.validateRequest(error, config.getDest().getIndex());
9397
error = checkNoIncludedAnalyzedFieldsAreExcludedBySourceFiltering(config, error);
9498
return error;
9599
}
96100

101+
private ActionRequestValidationException checkConfigIdIsValid(DataFrameAnalyticsConfig config,
102+
ActionRequestValidationException error) {
103+
if (MlStrings.isValidId(config.getId()) == false) {
104+
error = ValidateActions.addValidationError(Messages.getMessage(Messages.INVALID_ID, DataFrameAnalyticsConfig.ID,
105+
config.getId()), error);
106+
}
107+
if (!MlStrings.hasValidLengthForId(config.getId())) {
108+
error = ValidateActions.addValidationError(Messages.getMessage(Messages.ID_TOO_LONG, DataFrameAnalyticsConfig.ID,
109+
config.getId(), MlStrings.ID_LENGTH_LIMIT), error);
110+
}
111+
return error;
112+
}
113+
97114
private ActionRequestValidationException checkNoIncludedAnalyzedFieldsAreExcludedBySourceFiltering(
98115
DataFrameAnalyticsConfig config, ActionRequestValidationException error) {
99116
if (config.getAnalyzedFields() == null) {
100-
return null;
117+
return error;
101118
}
102119
for (String analyzedInclude : config.getAnalyzedFields().includes()) {
103120
if (config.getSource().isFieldExcluded(analyzedInclude)) {
@@ -107,7 +124,7 @@ private ActionRequestValidationException checkNoIncludedAnalyzedFieldsAreExclude
107124
+ DataFrameAnalyticsSource._SOURCE.getPreferredName() + "]", error);
108125
}
109126
}
110-
return null;
127+
return error;
111128
}
112129

113130
@Override

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/DataFrameAnalyticsDest.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,11 @@
1313
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
1414
import org.elasticsearch.common.xcontent.ToXContentObject;
1515
import org.elasticsearch.common.xcontent.XContentBuilder;
16-
import org.elasticsearch.indices.InvalidIndexNameException;
1716
import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
1817

1918
import java.io.IOException;
20-
import java.util.Locale;
2119
import java.util.Objects;
2220

23-
import static org.elasticsearch.cluster.metadata.MetaDataCreateIndexService.validateIndexOrAliasName;
24-
2521
public class DataFrameAnalyticsDest implements Writeable, ToXContentObject {
2622

2723
public static final ParseField INDEX = new ParseField("index");
@@ -94,13 +90,4 @@ public String getIndex() {
9490
public String getResultsField() {
9591
return resultsField;
9692
}
97-
98-
public void validate() {
99-
if (index != null) {
100-
validateIndexOrAliasName(index, InvalidIndexNameException::new);
101-
if (index.toLowerCase(Locale.ROOT).equals(index) == false) {
102-
throw new InvalidIndexNameException(index, "dest.index must be lowercase");
103-
}
104-
}
105-
}
10693
}

0 commit comments

Comments
 (0)