Skip to content

Commit

Permalink
Allow to use fn:filter() on previous pipeline elements
Browse files Browse the repository at this point in the history
Makes it possible to omit the first value in the fn:filter(1, 2, 3) function to apply the filter on the previous pipeline element, instead of a placeholder/hard value

Signed-off-by: David Schwilk <david.schwilk@bosch.io>
  • Loading branch information
DerSchwilk committed Jan 25, 2022
1 parent 7ba52ce commit ee65cd2
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,14 @@ public Signature getSignature() {
public PipelineElement apply(final PipelineElement value, final String paramsIncludingParentheses,
final ExpressionResolver expressionResolver) {

final Parameters parameters = parseAndResolve(paramsIncludingParentheses, expressionResolver);
final Parameters parameters = parseAndResolve(paramsIncludingParentheses, expressionResolver, value);

return value.onResolved(valueThatShouldBeFilteredConditionally -> {

final Optional<FilterFunction> rqlFunctionOpt =
FilterFunctions.fromName(parameters.getRqlFunction());
final boolean shouldKeepValue = rqlFunctionOpt
final boolean shouldKeepValue = parameters.getRqlFunction()
.map(rqlFunction -> applyRqlFunction(parameters, rqlFunction))
.orElse(false);


if (shouldKeepValue) {
return PipelineElement.resolved(valueThatShouldBeFilteredConditionally);
} else {
Expand All @@ -82,30 +79,64 @@ private static Boolean applyRqlFunction(final Parameters parameters, final Filte
}

private Parameters parseAndResolve(final String paramsIncludingParentheses,
final ExpressionResolver expressionResolver) {
final ExpressionResolver expressionResolver, final PipelineElement value) {

final Parameters result;
final List<PipelineElement> parameterElements =
PipelineFunctionParameterResolverFactory.forDoubleOrTripleStringOrPlaceholderParameter()
.apply(paramsIncludingParentheses, expressionResolver, this);
.apply(paramsIncludingParentheses, expressionResolver, this);

final PipelineElement firstElement = parameterElements.get(0);
final PipelineElement secondElement = parameterElements.get(1);
if (parameterElements.size() == 2) {
final Optional<FilterFunction> firstFilter = firstElement.toOptional().flatMap(FilterFunctions::fromName);
if (firstFilter.isPresent() && isExistsExistsFilter(firstElement, secondElement)) {
result = handleParameters(firstFilter.orElse(null), secondElement, value);
} else {
final Optional<FilterFunction> secondFilter =
getFilterFunctionOrThrow(secondElement, paramsIncludingParentheses);
result = handleParametersWithOmittedCompared(secondFilter.orElse(null), firstElement);
}
} else {
final Optional<FilterFunction> secondFilter =
getFilterFunctionOrThrow(secondElement, paramsIncludingParentheses);
final PipelineElement thirdElement = parameterElements.get(2);
result = handleParameters(secondFilter.orElse(null), thirdElement, firstElement);
}
return result;

final ParametersBuilder parametersBuilder = new ParametersBuilder();
}

final PipelineElement filterValueParamElement = parameterElements.get(0);
final String filterValueParam = filterValueParamElement.toOptional().orElse("");
parametersBuilder.withFilterValue(filterValueParam);
private static boolean isExistsExistsFilter(final PipelineElement firstElement,
final PipelineElement secondElement) {
final Optional<String> first = firstElement.toOptional();
final Optional<String> second = secondElement.toOptional();
return !(first.isPresent() && first.get().equals("exists") &&
second.isPresent() && second.get().equals("exists"));
}

final PipelineElement rqlFunctionParamElement = parameterElements.get(1);
final String rqlFunctionParam = rqlFunctionParamElement.toOptional().orElseThrow(() ->
PlaceholderFunctionSignatureInvalidException.newBuilder(paramsIncludingParentheses, this)
.build());
parametersBuilder.withRqlFunction(rqlFunctionParam);
private Optional<FilterFunction> getFilterFunctionOrThrow(final PipelineElement element, final String params) {
return FilterFunctions.fromName(element.toOptional().orElseThrow(() ->
PlaceholderFunctionSignatureInvalidException.newBuilder(params, this)
.build()));
}

if (parameterElements.size() > 2) {
final PipelineElement comparedValueParamElement = parameterElements.get(2);
final String comparedValueParam = comparedValueParamElement.toOptional().orElse("");
parametersBuilder.withComparedValue(comparedValueParam);
}
private static Parameters handleParameters(@Nullable final FilterFunction filter,
final PipelineElement comparedElement, final PipelineElement filterElement) {

final ParametersBuilder parametersBuilder = new ParametersBuilder();
parametersBuilder.withRqlFunction(filter);
parametersBuilder.withComparedValue(comparedElement.toOptional().orElse(""));
parametersBuilder.withFilterValue(filterElement.toOptional().orElse(""));
return parametersBuilder.build();
}

private static Parameters handleParametersWithOmittedCompared(@Nullable final FilterFunction filter,
final PipelineElement previousElement) {

final ParametersBuilder parametersBuilder = new ParametersBuilder();
parametersBuilder.withRqlFunction(filter);
parametersBuilder.withFilterValue(previousElement.toOptional().orElse(""));
return parametersBuilder.build();
}

Expand All @@ -114,7 +145,7 @@ private Parameters parseAndResolve(final String paramsIncludingParentheses,
*/
static final class FilterFunctionSignature implements Signature {

private static final FilterFunctionSignature INSTANCE = new FilterFunctionSignature();
private static final Signature INSTANCE = new FilterFunctionSignature();

private final ParameterDefinition<String> filterValueParam;
private final ParameterDefinition<String> rqlFunctionParam;
Expand Down Expand Up @@ -222,27 +253,30 @@ public String getDescription() {

@Immutable
private static final class Parameters {

private final String filterValue;
private final String rqlFunction;
@Nullable
private final FilterFunction rqlFunction;
@Nullable
private final String comparedValueParam;


private Parameters(
final String filterValue,
final String rqlFunction,
@Nullable final FilterFunction rqlFunction,
@Nullable final String comparedValueParam) {

this.filterValue = checkNotNull(filterValue, "filterValue");
this.rqlFunction = checkNotNull(rqlFunction, "rqlFuntion");
this.rqlFunction = rqlFunction;
this.comparedValueParam = comparedValueParam;
}

private String getFilterValue() {
return filterValue;
}

private String getRqlFunction() {
return rqlFunction;
private Optional<FilterFunction> getRqlFunction() {
return Optional.ofNullable(rqlFunction);
}

private Optional<String> getComparedValueParam() {
Expand All @@ -252,23 +286,21 @@ private Optional<String> getComparedValueParam() {
}

private static final class ParametersBuilder {

private String filterValue;
private String rqlFunction;
private FilterFunction rqlFunction;
private String comparedValue;

ParametersBuilder withFilterValue(final String filterValue) {
void withFilterValue(final String filterValue) {
this.filterValue = filterValue;
return this;
}

ParametersBuilder withRqlFunction(final String rqlFunction) {
void withRqlFunction(@Nullable final FilterFunction rqlFunction) {
this.rqlFunction = rqlFunction;
return this;
}

ParametersBuilder withComparedValue(@Nullable final String comparedValue) {
void withComparedValue(@Nullable final String comparedValue) {
this.comparedValue = comparedValue;
return this;
}

Parameters build() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,26 @@ public void eqSucceedsWithBothValuesUnresolved() {
assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

@Test
public void eqSucceedsWithTwoValues() {
final String params = String.format("(\"%s\",%s)", "eq", "header:some-header");

when(expressionResolver.resolveAsPipelineElement("header:some-header"))
.thenReturn(KNOWN_INPUT);

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

@Test
public void eqFailsWithTwoValues() {
final String params = String.format("(\"%s\",%s)", "eq", "header:some-header");

when(expressionResolver.resolveAsPipelineElement("header:some-header"))
.thenReturn(PipelineElement.resolved("test"));

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).isEmpty();
}

@Test
public void eqFailsWithOneUnresolvedValue() {
final String params = String.format("(%s,\"%s\",%s)", "header:reply-to", "eq", "header:some-header");
Expand All @@ -105,6 +125,26 @@ public void eqFailsWithOneUnresolvedValue() {
assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).isEmpty();
}

@Test
public void neSucceedsWithTwoValues() {
final String params = String.format("(\"%s\",%s)", "ne", "header:some-header");

when(expressionResolver.resolveAsPipelineElement("header:some-header"))
.thenReturn(PipelineElement.resolved("test"));

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

@Test
public void neFailsWithTwoValues() {
final String params = String.format("(\"%s\",%s)", "ne", "header:some-header");

when(expressionResolver.resolveAsPipelineElement("header:some-header"))
.thenReturn(KNOWN_INPUT);

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).isEmpty();
}

@Test
public void neFailsWithBothValuesUnresolved() {
when(expressionResolver.resolveAsPipelineElement("header:reply-to"))
Expand Down Expand Up @@ -165,6 +205,20 @@ public void filterSucceedsWithBothValuesUnresolved() {
assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

@Test
public void likeSucceedsWithTwoValues() {
final String params = String.format("(\"%s\",\"%s\")", "like", "*value");

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

@Test
public void likeFailsWithTwoValues() {
final String params = String.format("(\"%s\",\"%s\")", "like", "*elavue");

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).isEmpty();
}

@Test
public void filterFailsWithOneUnresolvedValue() {
final String params = String.format("(%s,\"%s\",%s)", "header:reply-to", "like", "header:some-header");
Expand Down Expand Up @@ -230,6 +284,43 @@ public void existsFalseFailsWithValueUnresolved() {
assertThat(underTest.apply(KNOWN_INPUT_BOOLEAN, params, expressionResolver)).contains(KNOWN_BOOLEAN);
}

@Test
public void existsSucceedsWithTwoValues() {
final String params = String.format("(\"%s\",%s)", "exists", "header:some-header");

when(expressionResolver.resolveAsPipelineElement("header:some-header"))
.thenReturn(PipelineElement.resolved("true"));

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

@Test
public void existsFailsWithTwoValues() {
final String params = String.format("(\"%s\",%s)", "exists", "header:some-header");

when(expressionResolver.resolveAsPipelineElement("header:some-header"))
.thenReturn(PipelineElement.resolved("false"));

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).isEmpty();
}

@Test
public void existsCustomElementSucceedsWithTwoValues() {
final String params = String.format("(%s,\"%s\")", "header:some-header", "exists");

when(expressionResolver.resolveAsPipelineElement("header:some-header"))
.thenReturn(PipelineElement.resolved("test"));

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

@Test
public void existsExistsSucceedsWithTwoValues() {
final String params = String.format("(\"%s\",\"%s\")", "exists", "exists");

assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver)).contains(KNOWN_VALUE);
}

private void testPatternMatching(final String arg, final String pattern, final boolean shouldMatch) {
final String params = String.format("('%s','like','%s')", arg, pattern);
assertThat(underTest.apply(KNOWN_INPUT, params, expressionResolver))
Expand Down

0 comments on commit ee65cd2

Please sign in to comment.