diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/ParsedStmtOp.java b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/ParsedStmtOp.java index 469cb92ef..e07f283b2 100644 --- a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/ParsedStmtOp.java +++ b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/ParsedStmtOp.java @@ -20,7 +20,7 @@ import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate; import io.nosqlbench.nb.api.config.params.Element; import io.nosqlbench.nb.api.config.params.NBParams; import io.nosqlbench.virtdata.core.templates.BindPoint; -import io.nosqlbench.virtdata.core.templates.ParsedTemplate; +import io.nosqlbench.virtdata.core.templates.ParsedStringTemplate; import java.util.List; import java.util.Map; @@ -30,7 +30,7 @@ import java.util.function.Function; public class ParsedStmtOp { private final OpTemplate optpl; - private final ParsedTemplate parsed; + private final ParsedStringTemplate parsed; /** * Construct a new ParsedStatement from the provided stmtDef and anchor token. @@ -40,7 +40,7 @@ public class ParsedStmtOp { public ParsedStmtOp(OpTemplate optpl) { this.optpl = optpl; String transformed = getStmt(); - parsed = new ParsedTemplate(transformed, optpl.getBindings()); + parsed = new ParsedStringTemplate(transformed, optpl.getBindings()); } public ParsedStmtOp orError() { diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/yaml/OpTemplate.java b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/yaml/OpTemplate.java index 6b637749a..3e45ae73d 100644 --- a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/yaml/OpTemplate.java +++ b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityconfig/yaml/OpTemplate.java @@ -23,7 +23,7 @@ import io.nosqlbench.nb.api.config.params.Element; import io.nosqlbench.nb.api.config.params.NBParams; import io.nosqlbench.nb.api.config.standard.NBTypeConverter; import io.nosqlbench.nb.api.errors.OpConfigError; -import io.nosqlbench.virtdata.core.templates.ParsedTemplate; +import io.nosqlbench.virtdata.core.templates.ParsedStringTemplate; import java.util.LinkedHashMap; import java.util.Map; @@ -271,9 +271,9 @@ public abstract class OpTemplate implements Tagged { * Parse the statement for anchors and return a richer view of the StmtDef which * is simpler to use for most statement configuration needs. * - * @return an optional {@link ParsedTemplate} + * @return an optional {@link ParsedStringTemplate} */ - public Optional getParsed(Function... rewriters) { + public Optional getParsed(Function... rewriters) { Optional os = getStmt(); return os.map(s -> { String result = s; @@ -281,11 +281,11 @@ public abstract class OpTemplate implements Tagged { result = rewriter.apply(result); } return result; - }).map(s -> new ParsedTemplate(s,getBindings())); + }).map(s -> new ParsedStringTemplate(s,getBindings())); } - public Optional getParsed() { - return getStmt().map(s -> new ParsedTemplate(s, getBindings())); + public Optional getParsed() { + return getStmt().map(s -> new ParsedStringTemplate(s, getBindings())); } public abstract Optional> getOp(); diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/VariableCapture.java b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/VariableCapture.java index c25749a89..658f6d015 100644 --- a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/VariableCapture.java +++ b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/VariableCapture.java @@ -16,7 +16,7 @@ package io.nosqlbench.engine.api.activityimpl.uniform.flowtypes; -import io.nosqlbench.virtdata.core.templates.ParsedTemplate; +import io.nosqlbench.virtdata.core.templates.ParsedStringTemplate; import java.util.Map; @@ -24,7 +24,7 @@ import java.util.Map; * If an op implements VariableCapture, then it is known to be able to * extract variables from its result. Generally speaking, this should * be implemented within an Op according to the standard format - * of {@link ParsedTemplate#getCaptures()}. Any op implementing + * of {@link ParsedStringTemplate#getCaptures()}. Any op implementing * this should use the interface below to support interop between adapters * and to allow for auto documentation tha the feature is supported for * a given adapter. diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/CommandTemplate.java b/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/CommandTemplate.java index 6bbe8389c..ede0d778a 100644 --- a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/CommandTemplate.java +++ b/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/CommandTemplate.java @@ -20,7 +20,7 @@ import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate; import io.nosqlbench.nb.api.config.params.ParamsParser; import io.nosqlbench.nb.api.errors.BasicError; import io.nosqlbench.virtdata.core.bindings.BindingsTemplate; -import io.nosqlbench.virtdata.core.templates.ParsedTemplate; +import io.nosqlbench.virtdata.core.templates.ParsedStringTemplate; import io.nosqlbench.virtdata.core.templates.StringBindings; import io.nosqlbench.virtdata.core.templates.StringBindingsTemplate; import org.apache.logging.log4j.Logger; @@ -160,7 +160,7 @@ public class CommandTemplate { cmd.putAll(params); cmd.forEach((param, value) -> { - ParsedTemplate paramTemplate = new ParsedTemplate(value, bindings); + ParsedStringTemplate paramTemplate = new ParsedStringTemplate(value, bindings); if (paramTemplate.getBindPoints().size() > 0) { BindingsTemplate paramBindings = new BindingsTemplate(paramTemplate.getBindPoints()); StringBindings paramStringBindings = new StringBindingsTemplate(value, paramBindings).resolve(); diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedOp.java b/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedOp.java index 5ebfc03bb..1a9cf3cb9 100644 --- a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedOp.java +++ b/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedOp.java @@ -26,7 +26,7 @@ import io.nosqlbench.nb.api.config.standard.NBConfiguration; import io.nosqlbench.nb.api.errors.OpConfigError; import io.nosqlbench.virtdata.core.templates.BindPoint; import io.nosqlbench.virtdata.core.templates.CapturePoint; -import io.nosqlbench.virtdata.core.templates.ParsedTemplate; +import io.nosqlbench.virtdata.core.templates.ParsedStringTemplate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -47,31 +47,40 @@ import java.util.function.LongFunction; * For some drivers or protocols, the primary user interface is a statement format or grammar. For CQL or SQL, * the most natural way of describing templates for operations is in that native format. For others, an operation * template may look like a block of JSON or a HTTP request. All these forms are supported. In order to deal with - * the variety, there are some normative rules about how they must be structured, both for the user, and for the - * driver developer. + * the variety, there is a set of detailed rules for how the workload definitions are transformed into driver + * operations for a native driver. The high-level flow is: + *
    + *
  1. Op Template Form
  2. + *
  3. Normalized Data Structure
  4. + *
  5. ParsedOp Form
  6. + *
+ * + * The specifications govern how the raw op template form is transformed into the normalized data structure. + * This documentation focuses on the API provided by the last form, this class, although peripheral awareness + * of the first two forms will certainly help for any advanced scenarios. You can find detailed examples and + * specification tests under the workload_definition folder of the adapters-api module. + *

+ * * *

Op Template Parsing

*
    - *
  1. All op templates are parsed into an internal normalized structure which contains: + *
  2. Rule #1: All op templates are parsed into an internal normalized structure which contains: *
      - *
    1. A map of op payload fields, which can be either of: + *
    2. A map of op fields, which can consist of: *
        *
      1. static field values
      2. - *
      3. dynamic field values
      4. + *
      5. dynamic field values, the actual value of which can only be known for a given cycle
      6. *
      - *
    3. - * Further, when the type of an op template is specified as a string, it is auto de-sugared into a map of a single - * op payload field named 'stmt'.
    4. * - *
    5. op params
    6. - *
    7. When neither 'op' nor 'params' is specified, all root-level op fields are put into the op payload.
    8. - *
    9. When either 'op' or 'params' is specified, all other root-level fields are automatically put into the other.
    10. - *
    11. When both 'op' and 'params' is specified, any other root-level fields causes an error to be thrown.
    12. - *
    - * +*
  3. Access to auxiliary configuration values like activity parameters. These can back-fill + * when values aren't present in the direct static or dynamic op fields.
  4. * * + *
  5. Rule #2: When asking for a dynamic parameter, static parameters may be automatically promoted to functional form as a back-fill.
  6. + *
  7. Rule #3: When asking for static parameters, config parameters may automatically be promoted as a back-fill.
  8. *
+ * The net effect of these rules is that the NoSQLBench driver developer may safely use functional forms to access data + * in the op template, or may decide that certain op fields must only be provided in a static way per operation. *

* *

Distinguishing Op Payload from Op Config

@@ -106,27 +115,28 @@ import java.util.function.LongFunction; * * All of these are considered valid constructions, and all of them may actually achieve the same result. * This looks like an undesirable problem, but it serves to simplify things for users in one specific way: It allows - * them to be a bit ambiguous, within guidelines, and still have a valid workload definition. + * them to be a vague, within guidelines, and still have a valid workload definition. * The NoSQLBench runtime does a non-trivial amount of processing on the op template to * ensure that it can conform to an unambiguous normalized internal structure on your behalf. * This is needed because of how awful YAML is as a user configuration language in spite of its - * ubiquity in practice. + * ubiquity in practice. The basic design guideline for these forms is that the op template must + * mean what a reasonable user would assume without looking at any documentation. * *
*

Design Invariants

* - - * - *

The above rules imply invariants, which are made explicit here. Most of these are provided for automatically by {@link ParsedOp}.

+ *

The above rules imply invariants, which are made explicit here. {@link ParsedOp}.

* *

    * *
  • You may not use an op field name or parameter name for more than one purpose. *
      - *
    • Treat all parameters supported by a drier adapter and it's op fields as a globally shared namespace, even if it is not. + *
    • Treat all parameters supported by a driver adapter and it's op fields as a globally shared namespace, even if it is not. * This avoids creating any confusion about what a parameter can be used for and how to use it for the right thing in the right place. * For example, you may not use the parameter name `socket` in an op template to mean one thing and then use it - * at the driver adapter level to mean something different. + * at the driver adapter level to mean something different. However, if the meaning is congruent, a driver developer + * may choose to support some cross-cutting parameters at the activity level. These allowances are explicit, + * however, as each driver dictates what it will allow as activity parameters. *
    • *
    *
  • @@ -135,7 +145,8 @@ import java.util.function.LongFunction; *
      *
    • IF a name is valid as an op field, it must also be valid as such when specified in op params.
    • *
    • If a name is valid as an op field, it must also be valid as such when specified in activity params, within the scope of {@link ParsedOp}
    • - *
    • When an op field is found via op params or activity params, it may be dynamic.
    • + *
    • When an op field is found via op params or activity params, it may NOT be dynamic. If dynamic values are intended to be provided + * at a common layer in the workload, then bindings support this already.
    • *
    * * @@ -160,6 +171,9 @@ import java.util.function.LongFunction; * the local op payload field takes precedence. *
      *
    • This is an extension of the param override rules which say that the closest (most local) value to an operation is the one that takes precedence.
    • + *
    • In practice, there will be no conflicts between direct static and dynamic fields, but there will be possibly between + * static or dynamic fields and parameters and activity params. If a user wants to promote an activity param as an override to existing op fields, + * template variables allow for this to happen gracefully. Otherwise, the order of precedence is 1) op fields 2) op params 3) activity params.
    • *
    * *
@@ -241,7 +255,7 @@ import java.util.function.LongFunction; * } * *

This example combines the previous ones with structure and dynamic values. Both field1 and field2 are dynamic, - * since each contains some dynamic value or template within. When field1 is accessed within a cycle, that cycles value + * since each contains some dynamic value or template within. When field1 is accessed within a cycle, that cycle's value * will be used as the seed to generate equivalent structures with all the literal and dynamic elements inserted as * the template implies. As before, direct binding references like {@code {binding4}} will be inserted into the * structure with whatever type the binding definition produces, so if you want strings in them, ensure that you @@ -366,6 +380,10 @@ public class ParsedOp implements LongFunction>, StaticFieldReader return tmap.isStatic(field); } + private boolean isConfig(String fieldname) { + return tmap.isConfig(fieldname); + } + public boolean isStatic(String field, Class type) { return tmap.isStatic(field, type); } @@ -406,13 +424,8 @@ public class ParsedOp implements LongFunction>, StaticFieldReader return tmap.getStaticValue(field); } -// public Optional getStmtAsTemplate() { -// return _opTemplate.getParsed(); -// } - - - public Optional getAsTemplate(String fieldname) { - return this.tmap.getAsTemplate(fieldname); + public Optional getAsTemplate(String fieldname) { + return this.tmap.getAsStringTemplate(fieldname); } /** @@ -517,7 +530,7 @@ public class ParsedOp implements LongFunction>, StaticFieldReader * @return a set of names which are defined, whether in static fields or dynamic fields */ public Set getDefinedNames() { - return tmap.getDefinedNames(); + return tmap.getOpFieldNames(); } /** @@ -736,7 +749,18 @@ public class ParsedOp implements LongFunction>, StaticFieldReader return getOptionalEnumFromField(enumClass,fieldName).orElse(defaultEnum); } - public LongFunction enhance( + /** + *

Enhance a {@link Function} with another required named field or function combiner OR a default value.

+ * @param func The base function + * @param field The field name to derive the named enhancer function from + * @param type The type of the field value + * @param defaultFe The default value of the field, if none is provided + * @param combiner A {@link BiFunction} which applies the field or function combiner to the base function + * @param The base function result type + * @param The enhancer function result type + * @return an enhanced function + */ + public LongFunction enhanceDefaultFunc( LongFunction func, String field, Class type, @@ -750,23 +774,66 @@ public class ParsedOp implements LongFunction>, StaticFieldReader } - public Optional> enhance( - Optional> func, + /** + *

Enhance a {@link Function} with a named required function, or throw an error.

+ * @param func The base function + * @param field The field name to derive the named enhancer function from + * @param type The type of the field value + * @param combiner A {@link BiFunction} which applies the field or function combiner to the base function + * @param The base function result type + * @param The enhancer function result type + * @return a version of the base function, optionally enhanced + */ + public LongFunction enhanceFunc( + LongFunction func, String field, Class type, - FE defaultFe, BiFunction combiner ) { - if (func.isEmpty()) { - return func; - } - LongFunction fieldEnhancerFunc = getAsFunctionOr(field, defaultFe); - LongFunction faLongFunction = func.get(); - LongFunction lfa = l -> combiner.apply(faLongFunction.apply(l),fieldEnhancerFunc.apply(l)); - return Optional.of(lfa); + LongFunction fieldEnhancerFunc = getAsRequiredFunction(field, type); + LongFunction lfa = l -> combiner.apply(func.apply(l),fieldEnhancerFunc.apply(l)); + return lfa; } - public Optional> enhance( + /** + *

Enhance a {@link Function} with a named optional function IFF it exists.

+ * @param func The base function + * @param field The field name to derive the named enhancer function from + * @param type The type of the field value + * @param combiner A {@link BiFunction} which applies the field or function combiner to the base function + * @param The base function result type + * @param The enhancer function result type + * @return a version of the base function, optionally enhanced + */ + public LongFunction enhanceFuncOptionally( + LongFunction func, + String field, + Class type, + BiFunction combiner + ) { + Optional> fieldEnhancerFunc = getAsOptionalFunction(field, type); + if (fieldEnhancerFunc.isEmpty()) { + return func; + } + LongFunction feLongFunction = fieldEnhancerFunc.get(); + LongFunction lfa = l -> combiner.apply(func.apply(l),feLongFunction.apply(l)); + return lfa; + } + + + /** + *

Enhance an {@link Optional} {@link Function} with an optional named field or value combiner, + * IFF both functions are defined.

+ * + * @param func The base function + * @param field The field name to derive the named enhancer function from + * @param type The type of the field value + * @param combiner A {@link BiFunction} which applies the field or function combiner to the base function + * @param The base function result type + * @param The enhancer function result type + * @return the enhanced optional function + */ + public Optional> enhanceOptionalFuncOptionally( Optional> func, String field, Class type, @@ -782,21 +849,48 @@ public class ParsedOp implements LongFunction>, StaticFieldReader return Optional.of(lfa); } - public LongFunction enhance( - LongFunction func, + /** + *

Enhance an {@link Optional} {@link Function} with a named field or function combiner OR a default value, + * IFF the base function is present.

+ * + *

Create a required function for the specified field and default value, IFF the + * main function is present. The base type of the function remains the same, and if present, + * will be extended with the required field value or function in the provided combiner.

+ * @param func The base function + * @param field The field name to derive the named enhancer function from + * @param type The type of the field value + * @param defaultFe The default value of the field, if none is provided + * @param combiner A {@link BiFunction} which applies the field or function combiner to the base function + * @param The base function result type + * @param The enhancer function result type + * @return the enhanced optional base function + */ + public Optional> enhanceOptionalDefaultFunc( + Optional> func, String field, Class type, + FE defaultFe, BiFunction combiner ) { - Optional> fieldEnhancerFunc = getAsOptionalFunction(field, type); - if (fieldEnhancerFunc.isEmpty()) { + if (func.isEmpty()) { return func; } - LongFunction feLongFunction = fieldEnhancerFunc.get(); - LongFunction lfa = l -> combiner.apply(func.apply(l),feLongFunction.apply(l)); - return lfa; + LongFunction fieldEnhancerFunc = getAsFunctionOr(field, defaultFe); + LongFunction faLongFunction = func.get(); + LongFunction lfa = l -> combiner.apply(faLongFunction.apply(l),fieldEnhancerFunc.apply(l)); + return Optional.of(lfa); } + /** + *

Enhance a {@link Function} with an optional enum function IFF it is defined.

+ * @param func The base function + * @param field The field name to derive the named enhancer function from + * @param type The type of the field value + * @param combiner A {@link BiFunction} which applies the field or function combiner to the base function + * @param The base function result type + * @param The enhancer function result type + * @return an (optionally) enhanced base function + */ public > LongFunction enhanceEnum( LongFunction func, String field, @@ -815,4 +909,9 @@ public class ParsedOp implements LongFunction>, StaticFieldReader public Map parseStaticCmdMap(String key, String mainField) { return tmap.parseStaticCmdMap(key, mainField); } + + @Override + public String toString() { + return this.tmap.toString(); + } } diff --git a/adapters-api/src/test/java/io/nosqlbench/engine/api/activityconfig/yaml/ParsedStmtOpTest.java b/adapters-api/src/test/java/io/nosqlbench/engine/api/activityconfig/yaml/ParsedStmtOpTest.java index cfe47b816..cfee08f47 100644 --- a/adapters-api/src/test/java/io/nosqlbench/engine/api/activityconfig/yaml/ParsedStmtOpTest.java +++ b/adapters-api/src/test/java/io/nosqlbench/engine/api/activityconfig/yaml/ParsedStmtOpTest.java @@ -17,7 +17,7 @@ package io.nosqlbench.engine.api.activityconfig.yaml; import io.nosqlbench.engine.api.activityconfig.StatementsLoader; -import io.nosqlbench.virtdata.core.templates.ParsedTemplate; +import io.nosqlbench.virtdata.core.templates.ParsedStringTemplate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.BeforeAll; @@ -38,13 +38,13 @@ public class ParsedStmtOpTest { public void testBasicParser() { StmtsBlock block0 = doclist.getStmtDocs().get(0).getBlocks().get(0); OpTemplate stmtDef0 = block0.getOps().get(0); - ParsedTemplate parsed0 = stmtDef0.getParsed().orElseThrow(); + ParsedStringTemplate parsed0 = stmtDef0.getParsed().orElseThrow(); assertThat(parsed0.getMissing()).containsExactly("delta"); assertThat(parsed0.hasError()).isTrue(); StmtsBlock block1 = doclist.getStmtDocs().get(0).getBlocks().get(1); OpTemplate stmtDef1 = block1.getOps().get(0); - ParsedTemplate parsed1 = stmtDef1.getParsed().orElseThrow(); + ParsedStringTemplate parsed1 = stmtDef1.getParsed().orElseThrow(); assertThat(parsed1.getMissing()).containsExactly(); assertThat(parsed1.hasError()).isFalse(); } @@ -54,12 +54,12 @@ public class ParsedStmtOpTest { StmtsBlock block2 = doclist.getStmtDocs().get(0).getBlocks().get(2); OpTemplate stmtDef0 = block2.getOps().get(0); - ParsedTemplate parsed0 = stmtDef0.getParsed().orElseThrow(); + ParsedStringTemplate parsed0 = stmtDef0.getParsed().orElseThrow(); assertThat(parsed0.getMissing()).isEmpty(); assertThat(parsed0.hasError()).isFalse(); OpTemplate stmtDef1 = block2.getOps().get(1); - ParsedTemplate parsed1 = stmtDef1.getParsed().orElseThrow(); + ParsedStringTemplate parsed1 = stmtDef1.getParsed().orElseThrow(); assertThat(parsed1.getMissing()).isEmpty(); assertThat(parsed1.hasError()).isFalse(); } diff --git a/adapters-api/src/test/java/io/nosqlbench/engine/api/templating/ParsedOpTest.java b/adapters-api/src/test/java/io/nosqlbench/engine/api/templating/ParsedOpTest.java index 03da107ca..3ffbc29da 100644 --- a/adapters-api/src/test/java/io/nosqlbench/engine/api/templating/ParsedOpTest.java +++ b/adapters-api/src/test/java/io/nosqlbench/engine/api/templating/ParsedOpTest.java @@ -16,8 +16,12 @@ package io.nosqlbench.engine.api.templating; +import io.nosqlbench.engine.api.activityconfig.StatementsLoader; import io.nosqlbench.engine.api.activityconfig.yaml.OpData; +import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate; +import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList; import io.nosqlbench.nb.api.config.standard.ConfigModel; +import io.nosqlbench.nb.api.config.standard.NBConfiguration; import io.nosqlbench.nb.api.config.standard.Param; import org.junit.jupiter.api.Test; @@ -49,9 +53,43 @@ public class ParsedOpTest { .apply(Map.of()) ); + @Test + public void testFieldDelegationFromDynamicToStaticToConfig() { + NBConfiguration cfg = ConfigModel.of(ParsedOpTest.class) + .add(Param.defaultTo("puppy", "dog")) + .add(Param.required("surname", String.class)) + .asReadOnly().apply(Map.of("surname", "yes")); + + String opt = """ + ops: + op1: + d1: "{{NumberNameToString()}}" + s1: "static-one" + params: + ps1: "param-one" + """; + StmtsDocList stmtsDocs = StatementsLoader.loadString(opt, cfg.getMap()); + assertThat(stmtsDocs.getStmts().size()).isEqualTo(1); + OpTemplate opTemplate = stmtsDocs.getStmts().get(0); + ParsedOp parsedOp = new ParsedOp(opTemplate, cfg); + + assertThat(parsedOp.getAsFunctionOr("d1","invalid").apply(1L)).isEqualTo("one"); + assertThat(parsedOp.getAsFunctionOr("s1","invalid").apply(1L)).isEqualTo("static-one"); + assertThat(parsedOp.getAsFunctionOr("ps1","invalid").apply(1L)).isEqualTo("param-one"); + assertThat(parsedOp.getAsFunctionOr("puppy","invalid").apply(1L)).isEqualTo("dog"); + assertThat(parsedOp.getAsFunctionOr("surname","invalid").apply(1L)).isEqualTo("yes"); + + } + + @Test + public void testStaticParamsPromoteToDynamicParams() { + + } + + @Test public void testSubMapTemplates() { - ParsedOp pop = new ParsedOp( + ParsedOp parsedOp = new ParsedOp( new OpData().applyFields(Map.of( "op", Map.of( "field1-literal", "literalvalue1", @@ -72,12 +110,16 @@ public class ParsedOpTest { .asReadOnly() .apply(Map.of()) ); - LongFunction f1 = pop.getAsRequiredFunction("field1-literal"); - LongFunction f2 = pop.getAsRequiredFunction("field2-object"); - LongFunction f3 = pop.getAsRequiredFunction("field3-template"); - LongFunction f4 = pop.getAsRequiredFunction("field4-map-template",Map.class); - LongFunction f5 = pop.getAsRequiredFunction("field5-map-literal",Map.class); - System.out.println("woo"); + LongFunction f1 = parsedOp.getAsRequiredFunction("field1-literal"); + LongFunction f2 = parsedOp.getAsRequiredFunction("field2-object"); + LongFunction f3 = parsedOp.getAsRequiredFunction("field3-template"); + LongFunction f4 = parsedOp.getAsRequiredFunction("field4-map-template",Map.class); + LongFunction f5 = parsedOp.getAsRequiredFunction("field5-map-literal",Map.class); + assertThat(f1.apply(1)).isNotNull(); + assertThat(f2.apply(2)).isNotNull(); + assertThat(f3.apply(3)).isNotNull(); + assertThat(f4.apply(4)).isNotNull(); + assertThat(f5.apply(5)).isNotNull(); }