[Hotfix] Fix dynamic form item show by other item bug (#131)
* Fix dynamic form item show by other item bug
diff --git a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thirdparty/framework/SeaTunnelOptionRuleWrapper.java b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thirdparty/framework/SeaTunnelOptionRuleWrapper.java
index 7a0bfb6..4f5e7c6 100644
--- a/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thirdparty/framework/SeaTunnelOptionRuleWrapper.java
+++ b/seatunnel-server/seatunnel-app/src/main/java/org/apache/seatunnel/app/thirdparty/framework/SeaTunnelOptionRuleWrapper.java
@@ -22,6 +22,7 @@
import org.apache.seatunnel.api.configuration.util.OptionRule;
import org.apache.seatunnel.api.configuration.util.RequiredOption;
import org.apache.seatunnel.app.dynamicforms.AbstractFormOption;
+import org.apache.seatunnel.app.dynamicforms.Constants;
import org.apache.seatunnel.app.dynamicforms.FormLocale;
import org.apache.seatunnel.app.dynamicforms.FormOptionBuilder;
import org.apache.seatunnel.app.dynamicforms.FormStructure;
@@ -40,6 +41,8 @@
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.seatunnel.app.common.SeaTunnelConnectorI18n.CONNECTOR_I18N_CONFIG_EN;
@@ -74,20 +77,14 @@
@NonNull String name) {
FormLocale locale = new FormLocale();
List<AbstractFormOption> optionFormOptions = wrapperOptionOptions(name, optionList, locale);
- List<List<AbstractFormOption>> requiredFormOptions =
+ List<AbstractFormOption> requiredFormOptions =
wrapperRequiredOptions(name, requiredList, locale);
FormStructureBuilder formStructureBuilder = FormStructure.builder().name(name);
if (!CollectionUtils.isEmpty(requiredFormOptions)) {
- requiredFormOptions.forEach(
- list -> {
- if (CollectionUtils.isEmpty(list)) {
- return;
- }
-
- formStructureBuilder.addFormOption(list.toArray(new AbstractFormOption[1]));
- });
+ formStructureBuilder.addFormOption(
+ requiredFormOptions.toArray(new AbstractFormOption[1]));
}
if (!CollectionUtils.isEmpty(optionFormOptions)) {
@@ -110,167 +107,177 @@
.collect(Collectors.toList());
}
- private static List<List<AbstractFormOption>> wrapperRequiredOptions(
+ private static List<AbstractFormOption> wrapperRequiredOptions(
@NonNull String connectorName,
@NonNull List<RequiredOption> requiredList,
FormLocale locale) {
- List<List<AbstractFormOption>> formOptionsList =
- requiredList.stream()
- .map(
+ List<AbstractFormOption> result = new ArrayList<>();
+ requiredList.forEach(
+ requiredOptions -> {
+ if (requiredOptions instanceof RequiredOption.AbsolutelyRequiredOptions) {
+ RequiredOption.AbsolutelyRequiredOptions absolutelyRequiredOptions =
+ (RequiredOption.AbsolutelyRequiredOptions) requiredOptions;
+ absolutelyRequiredOptions
+ .getRequiredOption()
+ .forEach(
+ option -> {
+ AbstractFormOption requiredFormItem = null;
+ for (AbstractFormOption formItem : result) {
+ if (formItem.getField().equals(option.key())) {
+ requiredFormItem = formItem;
+ break;
+ }
+ }
+
+ if (requiredFormItem == null) {
+ requiredFormItem =
+ wrapperToFormOption(
+ connectorName, option, locale);
+ result.add(requiredFormItem);
+ }
+
+ if (requiredFormItem.getValidate() == null) {
+ requiredFormItem.withValidate(
+ ValidateBuilder.builder()
+ .nonEmptyValidateBuilder()
+ .nonEmptyValidate());
+ }
+ });
+ } else if (requiredOptions instanceof RequiredOption.BundledRequiredOptions) {
+ List<Option<?>> bundledRequiredOptions =
+ ((RequiredOption.BundledRequiredOptions) requiredOptions)
+ .getRequiredOption();
+ List<String> bundledFields =
+ bundledRequiredOptions.stream()
+ .map(requiredOption -> requiredOption.key())
+ .collect(Collectors.toList());
+
+ bundledRequiredOptions.forEach(
option -> {
- if (option
- instanceof RequiredOption.AbsolutelyRequiredOptions) {
- List<AbstractFormOption> collect =
- ((RequiredOption.AbsolutelyRequiredOptions) option)
- .getRequiredOption().stream()
- .map(
- requiredOption -> {
- return wrapperToFormOption(
- connectorName,
- requiredOption,
- locale)
- .withValidate(
- ValidateBuilder
- .builder()
- .nonEmptyValidateBuilder()
- .nonEmptyValidate());
- })
- .collect(Collectors.toList());
- return collect;
- }
-
- if (option instanceof RequiredOption.BundledRequiredOptions) {
- List<Option<?>> bundledRequiredOptions =
- ((RequiredOption.BundledRequiredOptions) option)
- .getRequiredOption();
- List<String> bundledFields =
- bundledRequiredOptions.stream()
- .map(requiredOption -> requiredOption.key())
- .collect(Collectors.toList());
-
- List<AbstractFormOption> collect =
- bundledRequiredOptions.stream()
- .map(
- requiredOption -> {
- AbstractFormOption
- bundledRequiredFormOption =
- wrapperToFormOption(
- connectorName,
- requiredOption,
- locale);
- bundledRequiredFormOption
- .withValidate(
- ValidateBuilder
- .builder()
- .unionNonEmptyValidateBuilder()
- .fields(
- bundledFields
- .toArray(
- new String
- [1]))
- .unionNonEmptyValidate());
- return bundledRequiredFormOption;
- })
- .collect(Collectors.toList());
- return collect;
- }
-
- if (option instanceof RequiredOption.ExclusiveRequiredOptions) {
- List<Option<?>> exclusiveOptions =
- ((RequiredOption.ExclusiveRequiredOptions) option)
- .getExclusiveOptions();
- List<String> exclusiveFields =
- exclusiveOptions.stream()
- .map(requiredOption -> requiredOption.key())
- .collect(Collectors.toList());
-
- List<AbstractFormOption> collect =
- exclusiveOptions.stream()
- .map(
- requiredOption -> {
- AbstractFormOption
- exclusiveRequiredFormOption =
- wrapperToFormOption(
- connectorName,
- requiredOption,
- locale)
- .withValidate(
- ValidateBuilder
- .builder()
- .mutuallyExclusiveValidateBuilder()
- .fields(
- exclusiveFields
- .toArray(
- new String
- [1]))
- .mutuallyExclusiveValidate());
- return exclusiveRequiredFormOption;
- })
- .collect(Collectors.toList());
- return collect;
- }
-
- if (option
- instanceof RequiredOption.ConditionalRequiredOptions) {
- RequiredOption.ConditionalRequiredOptions
- conditionalRequiredOptions =
- (RequiredOption.ConditionalRequiredOptions)
- option;
-
- // we only support one field to control a form option, so we
- // only need get condition key from the
- // first expression. And all expression is 'or' and every
- // condition have the same key.
- String conditionKey =
- conditionalRequiredOptions
- .getExpression()
- .getCondition()
- .getOption()
- .key();
- List<Object> expectValueList = new ArrayList<>();
- Expression expression =
- conditionalRequiredOptions.getExpression();
- expectValueList.add(
- expression
- .getCondition()
- .getExpectValue()
- .toString());
- while (expression.hasNext()) {
- expression = expression.getNext();
- expectValueList.add(
- expression
- .getCondition()
- .getExpectValue()
- .toString());
+ AbstractFormOption bundledRequiredFormOption = null;
+ for (AbstractFormOption formItem : result) {
+ if (formItem.getField().equals(option.key())) {
+ bundledRequiredFormOption = formItem;
+ break;
}
- List<AbstractFormOption> collect =
- conditionalRequiredOptions.getRequiredOption()
- .stream()
- .map(
- requiredOption -> {
- return wrapperToFormOption(
- connectorName,
- requiredOption,
- locale)
- .withShow(
- conditionKey,
- expectValueList)
- .withValidate(
- ValidateBuilder
- .builder()
- .nonEmptyValidateBuilder()
- .nonEmptyValidate());
- })
- .collect(Collectors.toList());
- return collect;
}
- throw new UnSupportWrapperException(
- connectorName, "Unknown", option.toString());
- })
- .collect(Collectors.toList());
+ if (bundledRequiredFormOption == null) {
+ bundledRequiredFormOption =
+ wrapperToFormOption(connectorName, option, locale);
+ result.add(bundledRequiredFormOption);
+ }
- return formOptionsList;
+ bundledRequiredFormOption.withValidate(
+ ValidateBuilder.builder()
+ .unionNonEmptyValidateBuilder()
+ .fields(bundledFields.toArray(new String[1]))
+ .unionNonEmptyValidate());
+ });
+ } else if (requiredOptions instanceof RequiredOption.ExclusiveRequiredOptions) {
+ List<Option<?>> exclusiveOptions =
+ ((RequiredOption.ExclusiveRequiredOptions) requiredOptions)
+ .getExclusiveOptions();
+ List<String> exclusiveFields =
+ exclusiveOptions.stream()
+ .map(requiredOption -> requiredOption.key())
+ .collect(Collectors.toList());
+
+ exclusiveOptions.forEach(
+ option -> {
+ AbstractFormOption exclusiveRequiredFormOption = null;
+ for (AbstractFormOption formItem : result) {
+ if (formItem.getField().equals(option.key())) {
+ exclusiveRequiredFormOption = formItem;
+ break;
+ }
+ }
+ if (exclusiveRequiredFormOption == null) {
+ exclusiveRequiredFormOption =
+ wrapperToFormOption(connectorName, option, locale);
+
+ result.add(exclusiveRequiredFormOption);
+ }
+
+ exclusiveRequiredFormOption.withValidate(
+ ValidateBuilder.builder()
+ .mutuallyExclusiveValidateBuilder()
+ .fields(exclusiveFields.toArray(new String[1]))
+ .mutuallyExclusiveValidate());
+ });
+ } else if (requiredOptions
+ instanceof RequiredOption.ConditionalRequiredOptions) {
+ RequiredOption.ConditionalRequiredOptions conditionalRequiredOptions =
+ (RequiredOption.ConditionalRequiredOptions) requiredOptions;
+
+ // we only support one field to control a form option, so we
+ // only need get condition key from the
+ // first expression. And all expression is 'or' and every
+ // condition have the same key.
+ String conditionKey =
+ conditionalRequiredOptions
+ .getExpression()
+ .getCondition()
+ .getOption()
+ .key();
+ List<Object> expectValueList = new ArrayList<>();
+ Expression expression = conditionalRequiredOptions.getExpression();
+ expectValueList.add(expression.getCondition().getExpectValue().toString());
+ while (expression.hasNext()) {
+ expression = expression.getNext();
+ expectValueList.add(
+ expression.getCondition().getExpectValue().toString());
+ }
+
+ conditionalRequiredOptions
+ .getRequiredOption()
+ .forEach(
+ option -> {
+ AbstractFormOption conditionalRequiredFormItem = null;
+ for (AbstractFormOption formItem : result) {
+ if (formItem.getField().equals(option.key())) {
+ conditionalRequiredFormItem = formItem;
+ break;
+ }
+ }
+
+ if (conditionalRequiredFormItem == null) {
+ conditionalRequiredFormItem =
+ wrapperToFormOption(
+ connectorName, option, locale);
+ result.add(conditionalRequiredFormItem);
+ }
+
+ if (conditionalRequiredFormItem.getShow() == null) {
+ conditionalRequiredFormItem.withShow(
+ conditionKey, expectValueList);
+ } else {
+ Map<String, Object> show =
+ conditionalRequiredFormItem.getShow();
+ String field =
+ show.get(Constants.SHOW_FIELD).toString();
+ if (field.equals(conditionKey)) {
+ Set values =
+ (Set) show.get(Constants.SHOW_VALUE);
+ values.addAll(expectValueList);
+ } else {
+ throw new UnSupportWrapperException(
+ connectorName,
+ conditionalRequiredFormItem.getLabel(),
+ "Only support show by one field");
+ }
+ }
+
+ if (conditionalRequiredFormItem.getValidate() == null) {
+ conditionalRequiredFormItem.withValidate(
+ ValidateBuilder.builder()
+ .nonEmptyValidateBuilder()
+ .nonEmptyValidate());
+ }
+ });
+ }
+ });
+ return result;
}
private static AbstractFormOption wrapperToFormOption(
diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java
index bc3e65c..2f829b5 100644
--- a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java
+++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/AbstractFormOption.java
@@ -27,8 +27,10 @@
import lombok.NonNull;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
@Data
public abstract class AbstractFormOption<T extends AbstractFormOption, V extends AbstractValidate> {
@@ -75,8 +77,9 @@
this.show = new HashMap<>();
}
+ Set<Object> valueSet = new HashSet<>(values);
this.show.put(Constants.SHOW_FIELD, field);
- this.show.put(Constants.SHOW_VALUE, values);
+ this.show.put(Constants.SHOW_VALUE, valueSet);
return (T) this;
}
diff --git a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java
index 74080b6..532c369 100644
--- a/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java
+++ b/seatunnel-server/seatunnel-dynamicform/src/main/java/org/apache/seatunnel/app/dynamicforms/FormStructureValidate.java
@@ -42,11 +42,13 @@
List<String> showErrorList = validateShow(formStructure);
List<String> unionNonErrorList = validateUnionNonEmpty(formStructure);
List<String> exclusiveErrorList = validateMutuallyExclusive(formStructure);
+ List<String> duplicateFormItemErrorList = validateDuplicateFormItem(formStructure);
apiErrorList.addAll(localeErrorList);
apiErrorList.addAll(showErrorList);
apiErrorList.addAll(unionNonErrorList);
apiErrorList.addAll(exclusiveErrorList);
+ apiErrorList.addAll(duplicateFormItemErrorList);
if (apiErrorList.size() > 0) {
throw new FormStructureValidateException(formStructure.getName(), apiErrorList);
@@ -267,4 +269,28 @@
return errorMessageList;
}
+
+ public static List<String> validateDuplicateFormItem(@NonNull FormStructure formStructure) {
+ List fieldList = new ArrayList();
+ List errorFieldList = new ArrayList();
+ List errorMessage = new ArrayList();
+ formStructure
+ .getForms()
+ .forEach(
+ form -> {
+ if (fieldList.contains(form.getField())) {
+ errorFieldList.add(form.getField());
+ } else {
+ fieldList.add(form.getField());
+ }
+ });
+ if (errorFieldList.size() > 0) {
+ errorMessage.add(
+ String.format(
+ "DuplicateFormItemValidate failed, Duplicate form items %s",
+ errorFieldList));
+ }
+
+ return errorMessage;
+ }
}