Skip to content

Commit

Permalink
[feature][api] Option support fallback keys (apache#4685)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashulin authored Apr 28, 2023
1 parent fa11e4a commit b491020
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public interface CommonOptions {
Options.key("factory")
.stringType()
.noDefaultValue()
.withDescription("Identifier of the SPI factory class.");
.withDescription("Identifier of the SPI factory class.")
.withFallbackKeys("plugin_name");

Option<String> PLUGIN_NAME =
Options.key("plugin_name")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

import org.apache.seatunnel.shade.com.fasterxml.jackson.core.type.TypeReference;

import lombok.Getter;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class Option<T> {
Expand All @@ -34,10 +39,13 @@ public class Option<T> {
/** The description for this option. */
String description = "";

@Getter private final List<String> fallbackKeys;

public Option(String key, TypeReference<T> typeReference, T defaultValue) {
this.key = key;
this.typeReference = typeReference;
this.defaultValue = defaultValue;
this.fallbackKeys = new ArrayList<>();
}

public String key() {
Expand All @@ -61,6 +69,11 @@ public Option<T> withDescription(String description) {
return this;
}

public Option<T> withFallbackKeys(String... fallbackKeys) {
this.fallbackKeys.addAll(Arrays.asList(fallbackKeys));
return this;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
Expand All @@ -71,16 +84,18 @@ public boolean equals(Object obj) {
}
Option<?> that = (Option<?>) obj;
return Objects.equals(this.key, that.key)
&& Objects.equals(this.defaultValue, that.defaultValue);
&& Objects.equals(this.defaultValue, that.defaultValue)
&& Objects.equals(this.fallbackKeys, that.fallbackKeys);
}

@Override
public int hashCode() {
return Objects.hash(this.key, this.defaultValue);
return Objects.hash(this.key, this.defaultValue, this.fallbackKeys);
}

@Override
public String toString() {
return String.format("Key: '%s', default: %s", key, defaultValue);
return String.format(
"Key: '%s', default: %s (fallback keys: %s)", key, defaultValue, fallbackKeys);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import org.apache.seatunnel.shade.com.typesafe.config.Config;
import org.apache.seatunnel.shade.com.typesafe.config.ConfigRenderOptions;

import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -34,6 +36,7 @@
import static org.apache.seatunnel.api.configuration.util.ConfigUtil.flatteningMap;
import static org.apache.seatunnel.api.configuration.util.ConfigUtil.treeMap;

@Slf4j
public class ReadonlyConfig implements Serializable {
private static final long serialVersionUID = 1L;
private static final ObjectMapper JACKSON_MAPPER = new ObjectMapper();
Expand Down Expand Up @@ -89,23 +92,40 @@ public <T> Optional<T> getOptional(Option<T> option) {
if (option == null) {
throw new NullPointerException("Option not be null.");
}
String[] keys = option.key().split("\\.");
Object value = getValue(option.key());
if (value == null) {
for (String fallbackKey : option.getFallbackKeys()) {
value = getValue(fallbackKey);
if (value != null) {
log.info(
"Config uses fallback configuration key '{}' instead of key '{}'",
fallbackKey,
option.key());
break;
}
}
}
if (value == null) {
return Optional.empty();
}
return Optional.of(convertValue(value, option));
}

private Object getValue(String key) {
String[] keys = key.split("\\.");
Map<String, Object> data = this.confData;
Object value = null;
for (int i = 0; i < keys.length; i++) {
value = data.get(keys[i]);
if (i < keys.length - 1) {
if (!(value instanceof Map)) {
return Optional.empty();
return null;
} else {
data = (Map<String, Object>) value;
}
}
}
if (value == null) {
return Optional.empty();
}
return Optional.of(convertValue(value, option));
return value;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,8 @@ public static <T> T convertValue(Object rawValue, Option<T> option) {
((ParameterizedType) typeReference.getType()).getRawType())) {
try {
log.warn(
String.format(
"Option '%s' is a List, and it is recommended to configure it as [\"string1\",\"string2\"]; we will only use ',' to split the String into a list.",
option.key()));
"Option '{}' is a List, and it is recommended to configure it as [\"string1\",\"string2\"]; we will only use ',' to split the String into a list.",
option.key());
return (T)
convertToList(
rawValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,12 @@ public static List<CatalogTable> getCatalogTables(Config config, ClassLoader cla
ReadonlyConfig readonlyConfig = ReadonlyConfig.fromConfig(config);
Map<String, String> catalogOptions =
readonlyConfig.getOptional(CatalogOptions.CATALOG_OPTIONS).orElse(new HashMap<>());
// TODO: fallback key
String factoryId =
catalogOptions.getOrDefault(
CommonOptions.FACTORY_ID.key(),
readonlyConfig.get(CommonOptions.PLUGIN_NAME));

Map<String, Object> catalogAllOptions = new HashMap<>();
catalogAllOptions.putAll(readonlyConfig.toMap());
catalogAllOptions.putAll(catalogOptions);
ReadonlyConfig catalogConfig = ReadonlyConfig.fromMap(catalogAllOptions);

String factoryId = catalogConfig.get(CommonOptions.FACTORY_ID);
// Highest priority: specified schema
Map<String, String> schemaMap = readonlyConfig.get(CatalogTableUtil.SCHEMA);
if (schemaMap != null && schemaMap.size() > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,11 @@ public void testEquals() {
Assertions.assertEquals(
TEST_MODE,
Options.key("option.mode").enumType(TestMode.class).defaultValue(TestMode.LATEST));
Assertions.assertEquals(
TEST_NUM.withFallbackKeys("option.numeric"),
Options.key("option.num")
.intType()
.defaultValue(100)
.withFallbackKeys("option.numeric"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,19 @@ public void testNumericListOption() {
.listType(Double.class)
.noDefaultValue()));
}

@Test
public void testFallbackKey() {
Map<String, Object> map = new HashMap<>();
map.put("user", "ashulin");
final Option<String> usernameOption =
Options.key("username").stringType().noDefaultValue().withFallbackKeys("user");
ReadonlyConfig readonlyConfig = ReadonlyConfig.fromMap(map);
Assertions.assertEquals("ashulin", readonlyConfig.get(usernameOption));
Assertions.assertNull(
readonlyConfig.get(Options.key("username").stringType().noDefaultValue()));
map.put("username", "ark");
readonlyConfig = ReadonlyConfig.fromMap(map);
Assertions.assertEquals("ark", readonlyConfig.get(usernameOption));
}
}

0 comments on commit b491020

Please sign in to comment.