allow param defs to expand config models

This commit is contained in:
Jonathan Shook 2022-06-09 15:01:35 -05:00
parent d1541f0dd2
commit 0631f4aa05
3 changed files with 84 additions and 26 deletions

View File

@ -22,6 +22,7 @@ import io.nosqlbench.nb.api.errors.BasicError;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class ConfigModel implements NBConfigModel { public class ConfigModel implements NBConfigModel {
@ -29,6 +30,7 @@ public class ConfigModel implements NBConfigModel {
private final List<Param<?>> params = new ArrayList<>(); private final List<Param<?>> params = new ArrayList<>();
private Param<?> lastAdded = null; private Param<?> lastAdded = null;
private final Class<?> ofType; private final Class<?> ofType;
private final List<NBConfigModelExpander> expanders = new ArrayList<>();
private ConfigModel(Class<?> ofType, Param<?>... params) { private ConfigModel(Class<?> ofType, Param<?>... params) {
this.ofType = ofType; this.ofType = ofType;
@ -40,6 +42,7 @@ public class ConfigModel implements NBConfigModel {
public static ConfigModel of(Class<?> ofType, Param<?>... params) { public static ConfigModel of(Class<?> ofType, Param<?>... params) {
return new ConfigModel(ofType, params); return new ConfigModel(ofType, params);
} }
public static ConfigModel of(Class<?> ofType) { public static ConfigModel of(Class<?> ofType) {
return new ConfigModel(ofType); return new ConfigModel(ofType);
} }
@ -47,7 +50,7 @@ public class ConfigModel implements NBConfigModel {
public static NBConfiguration defacto(ActivityDef def) { public static NBConfiguration defacto(ActivityDef def) {
ConfigModel configModel = new ConfigModel(Object.class); ConfigModel configModel = new ConfigModel(Object.class);
for (Map.Entry<String, Object> entry : def.getParams().entrySet()) { for (Map.Entry<String, Object> entry : def.getParams().entrySet()) {
configModel.add(Param.defaultTo(entry.getKey(),entry.getValue().toString())); configModel.add(Param.defaultTo(entry.getKey(), entry.getValue().toString()));
} }
return configModel.apply(def.getParams()); return configModel.apply(def.getParams());
} }
@ -61,6 +64,13 @@ public class ConfigModel implements NBConfigModel {
return this; return this;
} }
/**
* Add a param that, when present in a runtime configuration, will cause the config
* model to be expanded dynamically. This is for scenarios in which you have external
* configurable resources or templates which contain their own models that can
* only be known at runtime.
*/
public NBConfigModel asReadOnly() { public NBConfigModel asReadOnly() {
return this; return this;
} }
@ -140,14 +150,19 @@ public class ConfigModel implements NBConfigModel {
@Override @Override
public NBConfiguration extractConfig(Map<String, ?> sharedConfig) { public NBConfiguration extractConfig(Map<String, ?> sharedConfig) {
NBConfiguration matchedConfig = matchConfig(sharedConfig);
matchedConfig.getMap().keySet().forEach(sharedConfig::remove);
return new NBConfiguration(this, matchedConfig.getMap());
}
@Override
public NBConfiguration matchConfig(Map<String, ?> sharedConfig) {
LinkedHashMap<String, Object> extracted = new LinkedHashMap<>(); LinkedHashMap<String, Object> extracted = new LinkedHashMap<>();
for (String providedCfgField : sharedConfig.keySet()) { for (String providedCfgField : sharedConfig.keySet()) {
if (getNamedParams().containsKey(providedCfgField)) { if (getNamedParams().containsKey(providedCfgField)) {
extracted.put(providedCfgField, sharedConfig.get(providedCfgField)); extracted.put(providedCfgField, sharedConfig.get(providedCfgField));
} }
} }
extracted.keySet().forEach(sharedConfig::remove);
return new NBConfiguration(this, extracted); return new NBConfiguration(this, extracted);
} }
@ -156,6 +171,11 @@ public class ConfigModel implements NBConfigModel {
return extractConfig(cfg.getMap()); return extractConfig(cfg.getMap());
} }
@Override
public NBConfiguration matchConfig(NBConfiguration cfg) {
return matchConfig(cfg.getMap());
}
private void assertDistinctSynonyms(Map<String, ?> config) { private void assertDistinctSynonyms(Map<String, ?> config) {
List<String> names = new ArrayList<>(); List<String> names = new ArrayList<>();
for (Param<?> param : getParams()) { for (Param<?> param : getParams()) {
@ -173,10 +193,12 @@ public class ConfigModel implements NBConfigModel {
@Override @Override
public NBConfiguration apply(Map<String, ?> config) { public NBConfiguration apply(Map<String, ?> config) {
ConfigModel expanded = expand(this, config);
assertValidConfig(config); assertValidConfig(config);
LinkedHashMap<String, Object> validConfig = new LinkedHashMap<>(); LinkedHashMap<String, Object> validConfig = new LinkedHashMap<>();
for (Param<?> param : params) { for (Param<?> param : expanded.params) {
Class<?> type = param.getType(); Class<?> type = param.getType();
List<String> found = new ArrayList<>(); List<String> found = new ArrayList<>();
String activename = null; String activename = null;
@ -188,7 +210,7 @@ public class ConfigModel implements NBConfigModel {
break; break;
} }
} }
if (activename==null) { if (activename == null) {
activename = param.getNames().get(0); activename = param.getNames().get(0);
} }
if (cval == null && param.isRequired()) { if (cval == null && param.isRequired()) {
@ -205,9 +227,28 @@ public class ConfigModel implements NBConfigModel {
@Override @Override
public void assertValidConfig(Map<String, ?> config) { public void assertValidConfig(Map<String, ?> config) {
assertRequiredFields(config); ConfigModel expanded = expand(this, config);
assertNoExtraneousFields(config); expanded.assertRequiredFields(config);
assertDistinctSynonyms(config); expanded.assertNoExtraneousFields(config);
expanded.assertDistinctSynonyms(config);
}
private ConfigModel expand(ConfigModel configModel, Map<String, ?> config) {
List<Param<?>> expanders = configModel.params.stream()
.filter(p -> p.getExpander()!=null).toList();
for (Param<?> expandingParameter : expanders) {
for (String name : expandingParameter.getNames()) {
if (config.containsKey(name)) {
Object triggeringValue = config.get(name);
expandingParameter.validate(triggeringValue);
NBConfigModelExpander expander = expandingParameter.getExpander();
NBConfigModel newModel = expander.apply(triggeringValue);
configModel = configModel.add(newModel);
}
}
}
return configModel;
} }
@Override @Override
@ -274,4 +315,14 @@ public class ConfigModel implements NBConfigModel {
} }
return this; return this;
} }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[").append(
params.stream().map(p -> p.getNames().get(0)).collect(Collectors.joining(",")))
.append("]");
return sb.toString();
}
} }

View File

@ -14,12 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
package io.nosqlbench.adapter.diag.optasks; package io.nosqlbench.nb.api.config.standard;
import io.nosqlbench.nb.api.config.standard.NBConfigurable; public interface NBConfigModelExpander {
NBConfigModel apply(Object value);
import java.util.Map;
import java.util.function.BiFunction;
public interface DiagOpTask extends BiFunction<Long,Map<String,Object>, Map<String,Object>>, NBConfigurable {
} }

View File

@ -34,19 +34,22 @@ public class Param<T> {
private final T defaultValue; private final T defaultValue;
public boolean required; public boolean required;
private Pattern regex; private Pattern regex;
private final NBConfigModelExpander expander;
public Param( public Param(
List<String> names, List<String> names,
Class<? extends T> type, Class<? extends T> type,
String description, String description,
boolean required, boolean required,
T defaultValue T defaultValue,
NBConfigModelExpander expander
) { ) {
this.names = names; this.names = names;
this.type = type; this.type = type;
this.description = description; this.description = description;
this.required = required; this.required = required;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
this.expander = expander;
} }
/** /**
@ -78,7 +81,7 @@ public class Param<T> {
* @param <V> Generic type for inference. * @param <V> Generic type for inference.
*/ */
public static <V> Param<V> optional(List<String> names, Class<V> type) { public static <V> Param<V> optional(List<String> names, Class<V> type) {
return new Param<V>(names, type, null, false, null); return new Param<V>(names, type, null, false, null, null);
} }
/** /**
@ -92,7 +95,7 @@ public class Param<T> {
* @param <V> Generic type for inference. * @param <V> Generic type for inference.
*/ */
public static <V> Param<V> optional(List<String> names, Class<V> type, String description) { public static <V> Param<V> optional(List<String> names, Class<V> type, String description) {
return new Param<V>(names, type, description, false, null); return new Param<V>(names, type, description, false, null, null);
} }
@ -106,7 +109,7 @@ public class Param<T> {
* @param <V> Generic type for inference. * @param <V> Generic type for inference.
*/ */
public static <V> Param<V> optional(String name, Class<V> type) { public static <V> Param<V> optional(String name, Class<V> type) {
return new Param<V>(List.of(name), type, null, false, null); return new Param<V>(List.of(name), type, null, false, null, null);
} }
/** /**
@ -120,7 +123,7 @@ public class Param<T> {
* @param <V> Generic type for inference. * @param <V> Generic type for inference.
*/ */
public static <V> Param<V> optional(String name, Class<V> type, String description) { public static <V> Param<V> optional(String name, Class<V> type, String description) {
return new Param<V>(List.of(name), type, description, false, null); return new Param<V>(List.of(name), type, description, false, null, null);
} }
/** /**
@ -133,7 +136,7 @@ public class Param<T> {
* @return * @return
*/ */
public static <V> Param<V> defaultTo(String name, V defaultValue) { public static <V> Param<V> defaultTo(String name, V defaultValue) {
return new Param<V>(List.of(name), (Class<V>) defaultValue.getClass(), null, true, defaultValue); return new Param<V>(List.of(name), (Class<V>) defaultValue.getClass(), null, true, defaultValue, null);
} }
/** /**
@ -146,7 +149,7 @@ public class Param<T> {
* @return * @return
*/ */
public static <V> Param<V> defaultTo(String name, V defaultValue, String description) { public static <V> Param<V> defaultTo(String name, V defaultValue, String description) {
return new Param<V>(List.of(name), (Class<V>) defaultValue.getClass(), description, true, defaultValue); return new Param<V>(List.of(name), (Class<V>) defaultValue.getClass(), description, true, defaultValue, null);
} }
/** /**
@ -159,15 +162,15 @@ public class Param<T> {
* @return * @return
*/ */
public static <V> Param<V> defaultTo(List<String> names, V defaultValue) { public static <V> Param<V> defaultTo(List<String> names, V defaultValue) {
return new Param<V>(names, (Class<V>) defaultValue.getClass(), null, true, defaultValue); return new Param<V>(names, (Class<V>) defaultValue.getClass(), null, true, defaultValue, null);
} }
public static <V> Param<V> required(String name, Class<V> type) { public static <V> Param<V> required(String name, Class<V> type) {
return new Param<V>(List.of(name), type, null, true, null); return new Param<V>(List.of(name), type, null, true, null, null);
} }
public static <V> Param<V> required(List<String> names, Class<V> type) { public static <V> Param<V> required(List<String> names, Class<V> type) {
return new Param<V>(names, type, null, true, null); return new Param<V>(names, type, null, true, null, null);
} }
@ -228,7 +231,6 @@ public class Param<T> {
public CheckResult<T> validate(Object value) { public CheckResult<T> validate(Object value) {
if (value == null) { if (value == null) {
if (isRequired()) { if (isRequired()) {
return CheckResult.INVALID(this, null, "Value is null but " + this.getNames() + " is required"); return CheckResult.INVALID(this, null, "Value is null but " + this.getNames() + " is required");
@ -255,6 +257,15 @@ public class Param<T> {
return CheckResult.VALID(this, value, "All validators passed for field '" + getNames() + "'"); return CheckResult.VALID(this, value, "All validators passed for field '" + getNames() + "'");
} }
public NBConfigModelExpander getExpander() {
return this.expander;
}
public Param<T> expand(NBConfigModelExpander expander) {
return new Param<>(names, type, description, required, defaultValue, expander);
}
public final static class CheckResult<T> { public final static class CheckResult<T> {
public final Param<T> element; public final Param<T> element;
public final Object value; public final Object value;