diff --git a/src/main/java/com/hubspot/jinjava/features/BuiltInFeatures.java b/src/main/java/com/hubspot/jinjava/features/BuiltInFeatures.java index 7722c601e..87b34a6b8 100644 --- a/src/main/java/com/hubspot/jinjava/features/BuiltInFeatures.java +++ b/src/main/java/com/hubspot/jinjava/features/BuiltInFeatures.java @@ -8,4 +8,5 @@ public interface BuiltInFeatures { String IGNORE_NESTED_INTERPRETATION_PARSE_ERRORS = "IGNORE_NESTED_INTERPRETATION_PARSE_ERRORS"; String OUTPUT_UNDEFINED_VARIABLES_ERROR = "OUTPUT_UNDEFINED_VARIABLES_ERROR"; + String INTEGER_SET_TO_LONG_CONVERSION = "INTEGER_SET_TO_LONG_CONVERSION"; } diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/AbstractSetFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/AbstractSetFilter.java index 48306bacc..664f82760 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/AbstractSetFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/AbstractSetFilter.java @@ -1,5 +1,6 @@ package com.hubspot.jinjava.lib.filter; +import com.hubspot.jinjava.features.BuiltInFeatures; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.interpret.TemplateError; import com.hubspot.jinjava.interpret.TemplateSyntaxException; @@ -43,7 +44,27 @@ public Object filter( Set varSet = objectToSet(var); Set argSet = objectToSet(parseArgs(interpreter, args)); - attachMismatchedTypesWarning(interpreter, varSet, argSet); + if (!varSet.isEmpty() && !argSet.isEmpty()) { + Object oneVar = varSet.iterator().next(); + Object oneArg = argSet.iterator().next(); + + boolean featureActive = interpreter + .getConfig() + .getFeatures() + .isActive( + BuiltInFeatures.INTEGER_SET_TO_LONG_CONVERSION, + interpreter.getContext() + ); + if (featureActive) { + if (oneVar instanceof Integer && oneArg instanceof Long) { + varSet = convertIntegersToLongs(varSet); + } else if (oneArg instanceof Integer && oneVar instanceof Long) { + argSet = convertIntegersToLongs(argSet); + } + } + + attachMismatchedTypesWarning(interpreter, varSet, argSet, oneVar, oneArg); + } return filter(varSet, argSet); } @@ -55,17 +76,31 @@ protected void attachMismatchedTypesWarning( Set varSet, Set argSet ) { - boolean hasAtLeastOneSetEmpty = varSet.isEmpty() || argSet.isEmpty(); - if (hasAtLeastOneSetEmpty) { + if (varSet.isEmpty() || argSet.isEmpty()) { return; } + attachMismatchedTypesWarning( + interpreter, + varSet, + argSet, + varSet.iterator().next(), + argSet.iterator().next() + ); + } - boolean areMatchedElementTypes = getTypeOfSetElements(varSet) - .equals(getTypeOfSetElements(argSet)); - if (areMatchedElementTypes) { + private void attachMismatchedTypesWarning( + JinjavaInterpreter interpreter, + Set varSet, + Set argSet, + Object oneVarObj, + Object oneArgObj + ) { + if (getTypeOfSetElements(varSet).equals(getTypeOfSetElements(argSet))) { + return; + } + if (potentiallyConvertibleNumbers(oneVarObj, oneArgObj)) { return; } - interpreter.addError( new TemplateError( TemplateError.ErrorType.WARNING, @@ -84,6 +119,25 @@ protected void attachMismatchedTypesWarning( ); } + private boolean potentiallyConvertibleNumbers(Object oneVarObj, Object oneArgObj) { + return ( + (oneArgObj instanceof Integer && oneVarObj instanceof Long) || + (oneVarObj instanceof Integer && oneArgObj instanceof Long) + ); + } + + private Set convertIntegersToLongs(Set set) { + Set result = new LinkedHashSet<>(); + for (Object element : set) { + if (element instanceof Integer integer) { + result.add(integer.longValue()); + } else { + result.add(element); + } + } + return result; + } + private String getTypeOfSetElements(Set set) { return TypeFunction.type(set.iterator().next()); } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/AbstractSetFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/AbstractSetFilterTest.java index cc2455ffc..dde125123 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/AbstractSetFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/AbstractSetFilterTest.java @@ -3,9 +3,16 @@ import static org.assertj.core.api.Assertions.assertThat; import com.hubspot.jinjava.BaseJinjavaTest; +import com.hubspot.jinjava.Jinjava; +import com.hubspot.jinjava.features.BuiltInFeatures; +import com.hubspot.jinjava.features.FeatureConfig; +import com.hubspot.jinjava.features.FeatureStrategies; import com.hubspot.jinjava.interpret.JinjavaInterpreter; +import com.hubspot.jinjava.interpret.RenderResult; import com.hubspot.jinjava.interpret.TemplateError; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import org.junit.Before; import org.junit.Test; @@ -80,4 +87,53 @@ public void itThrowsWarningOnMismatchTypes() { "Mismatched Types: input set has elements of type 'long' but arg set has elements of type 'str'. Use |map filter to convert sets to the same type for filter to work correctly." ); } + + @Test + public void itDoesNotThrowWarningOnIntegerLongMismatch() { + JinjavaInterpreter interpreter = jinjava.newInterpreter(); + + Set varSet = concreteSetFilter.objectToSet(new Long[] { 1L, 2L, 3L }); + Set argSet = concreteSetFilter.objectToSet(new Integer[] { 1, 2, 3 }); + concreteSetFilter.attachMismatchedTypesWarning(interpreter, varSet, argSet); + + assertThat(interpreter.getErrors()).isEmpty(); + } + + @Test + public void itConvertsIntegerToLongWhenFeatureActive() { + Jinjava jinjavaWithFeature = new Jinjava( + BaseJinjavaTest + .newConfigBuilder() + .withFeatureConfig( + FeatureConfig + .newBuilder() + .add(BuiltInFeatures.INTEGER_SET_TO_LONG_CONVERSION, FeatureStrategies.ACTIVE) + .build() + ) + .build() + ); + + Map vars = new HashMap<>(); + vars.put("longList", new Long[] { 1L, 2L, 3L }); + vars.put("intList", new Integer[] { 2, 3, 4 }); + + String result = jinjavaWithFeature.render("{{ longList|intersect(intList) }}", vars); + + assertThat(result).isEqualTo("[2, 3]"); + } + + @Test + public void itDoesNotConvertWhenFeatureInactive() { + Map vars = new HashMap<>(); + vars.put("longList", new Long[] { 1L, 2L, 3L }); + vars.put("intList", new Integer[] { 2, 3, 4 }); + + RenderResult renderResult = jinjava.renderForResult( + "{{ longList|intersect(intList) }}", + vars + ); + + assertThat(renderResult.getOutput()).isEqualTo("[]"); + assertThat(renderResult.getErrors()).isEmpty(); + } }