Skip to content

Commit

Permalink
[Bug][Seatunnel-web] Escape seatunnel-web placeholders (#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
arshadmohammad authored Oct 12, 2024
1 parent 2515e2b commit ee399d2
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 13 deletions.
4 changes: 2 additions & 2 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ sh build.sh code
#### 2.4 配置应用程序并运行SeaTunnel Web后端服务器

1. 编辑 `seatunnel-server/seatunnel-app/src/main/resources/application.yml` 写数据库连接信息
2. 编辑 `apache-seatunnel-web-${project.version}/conf/application.yml` 文件,填写jwt.secretKey密钥,例如:https://github.com/apache/seatunnel(注意不要太短)。
2. 编辑 `apache-seatunnel-web-${project.version}/conf/application.yml` 文件,填写jwt.secretKey密钥,例如:https://github.com/apache/seatunnel (注意不要太短)。

![img.png](docs/images/application_config.png)

Expand Down Expand Up @@ -173,7 +173,7 @@ tar -zxvf apache-seatunnel-web-${project.version}.tar.gz
#### 3.5 配置应用并运行 SeaTunnel Web 后端服务

* 编辑 `apache-seatunnel-web-${project.version}/conf/application.yml` 在文件中填写数据库连接信息和数据服务接口相关信息。
* 编辑 `apache-seatunnel-web-${project.version}/conf/application.yml` 文件,填写jwt.secretKey密钥,例如:https://github.com/apache/seatunnel(注意不要太短)。
* 编辑 `apache-seatunnel-web-${project.version}/conf/application.yml` 文件,填写jwt.secretKey密钥,例如:https://github.com/apache/seatunnel (注意不要太短)。

![image](docs/images/application_config.png)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.seatunnel.app.dynamicforms.FormOptionBuilder;
import org.apache.seatunnel.app.dynamicforms.FormStructure;
import org.apache.seatunnel.app.dynamicforms.FormStructureBuilder;
import org.apache.seatunnel.app.dynamicforms.PlaceholderUtil;
import org.apache.seatunnel.app.dynamicforms.validate.ValidateBuilder;
import org.apache.seatunnel.common.constants.PluginType;

Expand Down Expand Up @@ -97,6 +98,17 @@ public static FormStructure wrapper(
return FormOptionSort.sortFormStructure(formStructureBuilder.build());
}

private static Object getDefaultValue(Option<?> option) {
Object defValue = option.defaultValue();
if (defValue == null) {
return null;
}
if (String.class.equals(option.typeReference().getType())) {
return PlaceholderUtil.escapePlaceholders(defValue.toString());
}
return defValue;
}

private static List<AbstractFormOption> wrapperOptionOptions(
@NonNull String connectorName, @NonNull List<Option<?>> optionList, FormLocale locale) {
return optionList.stream()
Expand Down Expand Up @@ -401,10 +413,7 @@ private static AbstractFormOption selectInput(
AbstractFormOption abstractFormOption =
staticSelectOptionBuilder
.formStaticSelectOption()
.withDefaultValue(
option.defaultValue() == null
? null
: option.defaultValue().toString());
.withDefaultValue(getDefaultValue(option));

String placeholderI18nOptionKey = i18nOptionKey + "_description";
if (enableLabelI18n(connectorName, placeholderI18nOptionKey, locale)) {
Expand Down Expand Up @@ -457,7 +466,7 @@ private static AbstractFormOption textInput(
builder.withField(option.key())
.inputOptionBuilder()
.formTextInputOption()
.withDefaultValue(option.defaultValue());
.withDefaultValue(getDefaultValue(option));
if (enableLabelI18n(connectorName, placeholderI18nOptionKey, locale)) {
abstractFormOption = abstractFormOption.withI18nPlaceholder(placeholderI18nOptionKey);
} else {
Expand All @@ -484,7 +493,7 @@ private static AbstractFormOption textareaInput(
.inputOptionBuilder()
.formTextareaInputOption()
.withClearable()
.withDefaultValue(option.defaultValue());
.withDefaultValue(getDefaultValue(option));
if (enableLabelI18n(connectorName, placeholderI18nOptionKey, locale)) {
abstractFormOption = abstractFormOption.withI18nPlaceholder(placeholderI18nOptionKey);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public class JobUtils {

// The maximum length of the job execution error message, 4KB
private static final int ERROR_MESSAGE_MAX_LENGTH = 4096;
private static final Pattern placeholderPattern = Pattern.compile("\\$\\{(\\w+)(?::(.*?))?\\}");
private static final Pattern placeholderPattern =
Pattern.compile("(\\\\{0,2})\\$\\{(\\w+)(?::(.*?))?\\}");

public static String getJobInstanceErrorMessage(String message) {
if (message == null) {
Expand Down Expand Up @@ -75,18 +76,26 @@ public static String replaceJobConfigPlaceholders(
(jobExecParam != null && jobExecParam.getPlaceholderValues() != null)
? jobExecParam.getPlaceholderValues()
: Collections.emptyMap();

Matcher matcher = placeholderPattern.matcher(jobConfigString);
StringBuffer result = new StringBuffer();

while (matcher.find()) {
String placeholderName = matcher.group(1);
String replacement = placeholderValues.getOrDefault(placeholderName, matcher.group(2));
String escapeCharacter = matcher.group(1);
String placeholderName = matcher.group(2);

if (escapeCharacter != null && !escapeCharacter.isEmpty()) {
String withoutEscape =
matcher.group().replace("\\\\${", "${").replace("\\${", "${");
matcher.appendReplacement(result, Matcher.quoteReplacement(withoutEscape));
// remove the escape character and continue
continue;
}
String replacement = placeholderValues.getOrDefault(placeholderName, matcher.group(3));
if (replacement == null) {
throw new SeatunnelException(
SeatunnelErrorEnum.JOB_NO_VALUE_FOUND_FOR_PLACEHOLDER, placeholderName);
}
matcher.appendReplacement(result, replacement);
matcher.appendReplacement(result, Matcher.quoteReplacement(replacement));
}

matcher.appendTail(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,22 @@ public void testParseConfigWithPlaceHolders() {
assertNotNull(config);
}

@Test
public void testEscapedPlaceholderValuesNotReplaced() {
String jobConfigContent =
"job.mode=\\${jobModeParam:BATCH}\ncheckpoint.interval=\\\\${checkParam:30}\njob.name=${jobNameParam}";
Map<String, String> paramValues = new HashMap<>();
paramValues.put("jobModeParam", "STREAMING");
paramValues.put("jobNameParam", "newJob");
JobExecParam jobExecParam = getJobExecParam(paramValues);

String expected =
"job.mode=${jobModeParam:BATCH}\ncheckpoint.interval=${checkParam:30}\njob.name=newJob";
String actual = JobUtils.replaceJobConfigPlaceholders(jobConfigContent, jobExecParam);

assertEquals(expected, actual);
}

private JobExecParam getJobExecParam(Map<String, String> paramValues) {
JobExecParam jobExecParam = new JobExecParam();
jobExecParam.setPlaceholderValues(paramValues);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* 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.seatunnel.app.dynamicforms;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PlaceholderUtil {

private static final Pattern placeholderPattern =
Pattern.compile("(?<!\\\\)\\$\\{(\\w+)(?::(.*?))?\\}");

/**
* To replace ${paramName:defaultValue} or ${paramName} with \${paramName:defaultValue} and
* \${paramName} respectively.
*
* @param input the input string
* @return the input string with placeholders escaped
*/
public static String escapePlaceholders(String input) {
Matcher matcher = placeholderPattern.matcher(input);
StringBuffer result = new StringBuffer();

while (matcher.find()) {
String placeholder = matcher.group();
matcher.appendReplacement(result, Matcher.quoteReplacement("\\" + placeholder));
}

matcher.appendTail(result);
return result.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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.seatunnel.app.dynamicforms;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class PlaceholderUtilTest {

@Test
public void testEscapePlaceholders() {
String input = "This is a test string with ${paramName:defaultValue} and ${paramName}.";
String expectedOutput =
"This is a test string with \\${paramName:defaultValue} and \\${paramName}.";
String actualOutput = PlaceholderUtil.escapePlaceholders(input);
assertEquals(expectedOutput, actualOutput);
}

@Test
public void testEscapePlaceholdersWithNoPlaceholders() {
String input = "This is a test string with no placeholders.";
String expectedOutput = "This is a test string with no placeholders.";
String actualOutput = PlaceholderUtil.escapePlaceholders(input);
assertEquals(expectedOutput, actualOutput);
}

@Test
public void testEscapePlaceholdersWithEscapedPlaceholders() {
String input = "This is a test string with \\${paramName:defaultValue} and \\${paramName}.";
String expectedOutput =
"This is a test string with \\${paramName:defaultValue} and \\${paramName}.";
String actualOutput = PlaceholderUtil.escapePlaceholders(input);
assertEquals(expectedOutput, actualOutput);
}

@Test
public void testEscapePlaceholdersWithMixedPlaceholders() {
String input =
"This is a test string with ${paramName:defaultValue}, \\${paramName}, and ${anotherParam}.";
String expectedOutput =
"This is a test string with \\${paramName:defaultValue}, \\${paramName}, and \\${anotherParam}.";
String actualOutput = PlaceholderUtil.escapePlaceholders(input);
assertEquals(expectedOutput, actualOutput);
}
}

0 comments on commit ee399d2

Please sign in to comment.