From 32e5d0c930b135d7e5b6e78b697c296efdd4e5f5 Mon Sep 17 00:00:00 2001 From: ystrict Date: Mon, 19 Jun 2023 16:30:33 +0300 Subject: [PATCH 1/8] added array, set and "IN" support --- .../predicates/model/CollectionValue.java | 28 +++++++++++++++++ .../thunx/predicates/model/Comparison.java | 12 +++++++ .../predicates/model/FunctionExpression.java | 1 + .../thunx/predicates/model/Scalar.java | 7 +++++ .../model/ThunkExpressionVisitor.java | 3 ++ .../QuerySetToThunkExpressionConverter.java | 14 +++++++-- .../querydsl/QueryDslConvertingVisitor.java | 31 +++++++++++++------ .../visitor/reducer/ThunkReducerVisitor.java | 6 ++++ 8 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java new file mode 100644 index 00000000..7ebe1b3e --- /dev/null +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java @@ -0,0 +1,28 @@ +package com.contentgrid.thunx.predicates.model; + +import com.contentgrid.opa.rego.ast.Term; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import java.util.Collection; + +@EqualsAndHashCode +public class CollectionValue implements Scalar>> { + + @Getter + private final Collection> value; + + protected CollectionValue(Collection> value) { + this.value = value; + } + + @Override + public Class>> getResultType() { + return null; // TODO: return correct class + } + + @Override + public R accept(ThunkExpressionVisitor visitor, C context) { + return visitor.visit(this, context); + } +} diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Comparison.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Comparison.java index c251787d..22718675 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Comparison.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Comparison.java @@ -83,6 +83,18 @@ public static Comparison lessOrEquals(ThunkExpression left, ThunkExpression> terms) { + assertTermSizeIsTwo(terms); + return in(terms.get(0), terms.get(1)); + } + + public static Comparison in (ThunkExpression left, ThunkExpression right) { + if (right == null || ((CollectionValue) right).getValue().isEmpty()) { + return null; + } + return new Comparison(Operator.IN, left, right); + } + private static void assertTermSizeIsTwo(List> terms) { if (terms.size() == 2) { return; diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java index f9e3148e..19480017 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java @@ -36,6 +36,7 @@ enum Operator { GREATER_THAN_OR_EQUAL_TO("gte", Boolean.class, (FunctionExpressionFactory) Comparison::greaterOrEquals), LESS_THAN("lt", Boolean.class, (FunctionExpressionFactory) Comparison::less), LESS_THEN_OR_EQUAL_TO("lte", Boolean.class, (FunctionExpressionFactory) Comparison::lessOrEquals), + IN("internal.member_2", Boolean.class, (FunctionExpressionFactory) Comparison::in), // Logical operator AND("and", Boolean.class, (FunctionExpressionFactory) LogicalOperation::uncheckedConjunction), diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java index fd044c7e..7c37d7f2 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java @@ -1,6 +1,9 @@ package com.contentgrid.thunx.predicates.model; +import com.contentgrid.opa.rego.ast.Term; + import java.math.BigDecimal; +import java.util.Collection; public interface Scalar extends ThunkExpression { @@ -30,6 +33,10 @@ static BooleanValue of(boolean value) { return new BooleanValue(value); } + static CollectionValue of(Collection> value) { + return new CollectionValue(value); + } + static NullValue nullValue() { return NullValue.INSTANCE; } diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ThunkExpressionVisitor.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ThunkExpressionVisitor.java index 2d387ad9..82a778df 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ThunkExpressionVisitor.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ThunkExpressionVisitor.java @@ -6,5 +6,8 @@ public interface ThunkExpressionVisitor { T visit(FunctionExpression functionExpression, C context); T visit(SymbolicReference symbolicReference, C context); T visit(Variable variable, C context); + default T visit(CollectionValue collection, C context) { + throw new UnsupportedOperationException("Visit for CollectionValue is not yet implemented."); + } } diff --git a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java index 204c4d99..80a37c7c 100644 --- a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java +++ b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java @@ -11,6 +11,7 @@ import com.contentgrid.opa.rego.ast.Term.Null; import com.contentgrid.opa.rego.ast.Term.Numeric; import com.contentgrid.opa.rego.ast.Term.Ref; +import com.contentgrid.opa.rego.ast.Term.SetTerm; import com.contentgrid.opa.rego.ast.Term.Text; import com.contentgrid.opa.rego.ast.Term.Var; import com.contentgrid.thunx.predicates.model.Comparison; @@ -21,8 +22,10 @@ import com.contentgrid.thunx.predicates.model.SymbolicReference.StringPathElement; import com.contentgrid.thunx.predicates.model.ThunkExpression; import com.contentgrid.thunx.predicates.model.Variable; + import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; @@ -57,6 +60,7 @@ public ThunkExpression convert(Query query) { var expressions = query.stream() .map(this::convert) + .filter(Objects::nonNull) .peek(expr -> { if (!Boolean.class.isAssignableFrom(expr.getResultType())) { // there are non-boolean expressions in here ? @@ -92,7 +96,8 @@ static class PredicatesVisitor implements RegoVisitor> { Map.entry("gt", Comparison::greater), Map.entry("gte", Comparison::greaterOrEquals), Map.entry("lt", Comparison::less), - Map.entry("lte", Comparison::lessOrEquals) + Map.entry("lte", Comparison::lessOrEquals), + Map.entry("internal.member_2", Comparison::in) ); @Override @@ -279,7 +284,12 @@ public ThunkExpression visit(Null nullValue) { @Override public ThunkExpression visit(ArrayTerm arrayTerm) { - return null; + return Scalar.of(arrayTerm.getValue()); + } + + @Override + public ThunkExpression visit(SetTerm setTerm) { + return Scalar.of(setTerm.getValue()); } } diff --git a/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java b/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java index 01378f2e..e47beef6 100644 --- a/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java +++ b/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java @@ -1,5 +1,7 @@ package com.contentgrid.thunx.predicates.querydsl; +import com.contentgrid.opa.rego.ast.Term; +import com.contentgrid.thunx.predicates.model.CollectionValue; import com.contentgrid.thunx.predicates.model.FunctionExpression; import com.contentgrid.thunx.predicates.model.Scalar; import com.contentgrid.thunx.predicates.model.SymbolicReference; @@ -13,18 +15,19 @@ import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.PathBuilder; -import java.lang.reflect.Field; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; +import lombok.AccessLevel; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + import javax.persistence.Embedded; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; -import lombok.AccessLevel; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; @RequiredArgsConstructor(access = AccessLevel.PACKAGE) class QueryDslConvertingVisitor implements ThunkExpressionVisitor, QueryDslConversionContext> { @@ -70,6 +73,9 @@ public Expression visit(FunctionExpression function, QueryDslConversionCon case LESS_THAN: assertTwoTerms(terms); return Expressions.booleanOperation(Ops.LT, terms.toArray(new Expression[0])); + case IN: + assertTwoTerms(terms); + return ExpressionUtils.predicate(Ops.IN, terms.get(0), terms.get(1)); case OR: return ExpressionUtils.anyOf(terms.stream().map(term -> (Predicate) term).collect(Collectors.toList())); case AND: @@ -121,21 +127,21 @@ public Expression visit(SymbolicReference symbolicReference, QueryDslConversi * annotations */ private static void assertNotReferencingEntity(@NonNull SymbolicReference symbolicReference, - @NonNull PathBuilder builder) { + @NonNull PathBuilder builder) { var blacklist = Set.of(OneToOne.class, OneToMany.class, ManyToOne.class, ManyToMany.class, Embedded.class); var element = builder.getAnnotatedElement(); if (blacklist.stream().anyMatch(element::isAnnotationPresent)) { var msg = String.format("Cannot use `%s` as an expression, because it refers to a relation, not an attribute.", symbolicReference, - element instanceof Field ? ((Field)element).getType().getName() + " " + ((Field)element).getName() : element); + element instanceof Field ? ((Field) element).getType().getName() + " " + ((Field) element).getName() : element); throw new IllegalArgumentException(msg); } } private PathBuilder traversePath(SymbolicReference symbolicReference, PathBuilder builder, - String pathElement) { + String pathElement) { // we want to build a typed path, making sure the segments in the path are valid var property = this.accessStrategy.getProperty(builder.getType(), pathElement).orElseThrow(() -> { @@ -173,4 +179,9 @@ public Expression visit(Variable variable, QueryDslConversionContext context) // TODO could there be more variables available, than just the subject-path-builder ? throw new UnsupportedOperationException("converting variable to querydsl is not yet implemented"); } + + @Override + public Expression visit(CollectionValue collection, QueryDslConversionContext context) { + return Expressions.constant(collection.getValue().stream().map(Term.ScalarTerm::getValue).collect(Collectors.toList())); + } } diff --git a/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java b/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java index 932042bd..ebeafc19 100644 --- a/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java +++ b/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java @@ -1,5 +1,6 @@ package com.contentgrid.thunx.visitor.reducer; +import com.contentgrid.thunx.predicates.model.CollectionValue; import com.contentgrid.thunx.predicates.model.FunctionExpression; import com.contentgrid.thunx.predicates.model.FunctionExpression.Operator; import com.contentgrid.thunx.predicates.model.LogicalOperation; @@ -54,4 +55,9 @@ public ThunkExpression visit(SymbolicReference symbolicReference) { public ThunkExpression visit(Variable variable) { return variable; } + + @Override + public ThunkExpression visit(CollectionValue collection, Void context) { + return collection; + } } From ec25dd01aad0cfbb0dd07d08c1183c451b7fd1c7 Mon Sep 17 00:00:00 2001 From: ystrict Date: Mon, 26 Jun 2023 13:36:20 +0300 Subject: [PATCH 2/8] Added test and type optimizattion --- .../predicates/model/CollectionValue.java | 8 +++---- .../thunx/predicates/model/Scalar.java | 2 +- ...uerySetToThunkExpressionConverterTest.java | 21 +++++++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java index 7ebe1b3e..0ab16d49 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java @@ -7,17 +7,17 @@ import java.util.Collection; @EqualsAndHashCode -public class CollectionValue implements Scalar>> { +public class CollectionValue implements Scalar>> { @Getter - private final Collection> value; + private final Collection> value; - protected CollectionValue(Collection> value) { + protected CollectionValue(Collection> value) { this.value = value; } @Override - public Class>> getResultType() { + public Class>> getResultType() { return null; // TODO: return correct class } diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java index 7c37d7f2..12cfd8ea 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java @@ -33,7 +33,7 @@ static BooleanValue of(boolean value) { return new BooleanValue(value); } - static CollectionValue of(Collection> value) { + static CollectionValue of(Collection> value) { return new CollectionValue(value); } diff --git a/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java b/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java index 9c0bcb70..d219036f 100644 --- a/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java +++ b/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java @@ -11,6 +11,8 @@ import com.contentgrid.thunx.predicates.model.Scalar; import com.contentgrid.thunx.predicates.model.SymbolicReference; import java.util.List; +import java.util.Set; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -135,6 +137,25 @@ void less_or_equals() { )); } + @Test + void in() { + // input.entity.security in {4, 5} + var opaExpr = new Expression(0, List.of( + new Term.Ref(List.of(new Term.Var("internal"), new Term.Text("member_2"))), + new Term.Ref(List.of( + new Term.Var("input"), + new Term.Text("entity"), + new Term.Text("security") + )), + new Term.SetTerm(Set.of(new Term.Numeric(4), new Term.Numeric(5))) + )); + + assertThat(converter.convert(opaExpr)).isEqualTo(Comparison.in( + SymbolicReference.of("entity", path -> path.string("security")), + Scalar.of(Set.of(new Term.Numeric(4), new Term.Numeric(5))) + )); + } + } From 4df46bb8407cda18d8182a67dfe690c9e3990379 Mon Sep 17 00:00:00 2001 From: ystrict Date: Tue, 27 Jun 2023 12:43:40 +0300 Subject: [PATCH 3/8] updated opa-client version --- thunx-bom/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thunx-bom/build.gradle b/thunx-bom/build.gradle index 962aa764..0e9be370 100644 --- a/thunx-bom/build.gradle +++ b/thunx-bom/build.gradle @@ -17,7 +17,7 @@ dependencies { api project(':thunx-encoding-json') api project(':thunx-predicates-querydsl') - api 'com.contentgrid.opa-java-client:opa-async-java-client:0.4.0' + api 'com.contentgrid.opa-java-client:opa-async-java-client:0.4.1-SNAPSHOT' } } From d982522447531dbba0841ddeda3b77cd65d2c5af Mon Sep 17 00:00:00 2001 From: ystrict Date: Tue, 27 Jun 2023 15:36:38 +0300 Subject: [PATCH 4/8] Changed CollectionValue data structure --- .../predicates/model/CollectionValue.java | 18 ++-- .../thunx/predicates/model/Scalar.java | 4 +- .../QuerySetToThunkExpressionConverter.java | 85 ++++++++++++++++++- .../querydsl/QueryDslConvertingVisitor.java | 2 +- 4 files changed, 95 insertions(+), 14 deletions(-) diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java index 0ab16d49..fe970700 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java @@ -1,24 +1,26 @@ package com.contentgrid.thunx.predicates.model; -import com.contentgrid.opa.rego.ast.Term; import lombok.EqualsAndHashCode; -import lombok.Getter; import java.util.Collection; @EqualsAndHashCode -public class CollectionValue implements Scalar>> { +public class CollectionValue implements Scalar>> { - @Getter - private final Collection> value; + private final Collection> value; - protected CollectionValue(Collection> value) { + @Override + public Collection> getValue() { + return value; + } + + protected CollectionValue(Collection> value) { this.value = value; } @Override - public Class>> getResultType() { - return null; // TODO: return correct class + public Class>> getResultType() { + return null; } @Override diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java index 12cfd8ea..53e5afc6 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java @@ -1,7 +1,5 @@ package com.contentgrid.thunx.predicates.model; -import com.contentgrid.opa.rego.ast.Term; - import java.math.BigDecimal; import java.util.Collection; @@ -33,7 +31,7 @@ static BooleanValue of(boolean value) { return new BooleanValue(value); } - static CollectionValue of(Collection> value) { + static CollectionValue of(Collection> value) { return new CollectionValue(value); } diff --git a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java index 80a37c7c..c3d491a3 100644 --- a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java +++ b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java @@ -1,6 +1,7 @@ package com.contentgrid.thunx.pdp.opa; +import com.contentgrid.opa.rego.ast.Expression; import com.contentgrid.opa.rego.ast.Query; import com.contentgrid.opa.rego.ast.QuerySet; import com.contentgrid.opa.rego.ast.RegoVisitor; @@ -22,7 +23,9 @@ import com.contentgrid.thunx.predicates.model.SymbolicReference.StringPathElement; import com.contentgrid.thunx.predicates.model.ThunkExpression; import com.contentgrid.thunx.predicates.model.Variable; +import lombok.Getter; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; @@ -284,12 +287,90 @@ public ThunkExpression visit(Null nullValue) { @Override public ThunkExpression visit(ArrayTerm arrayTerm) { - return Scalar.of(arrayTerm.getValue()); + ScalarCollectingRegoVisitor collectingRegoVisitor = new ScalarCollectingRegoVisitor(); + arrayTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)); + return Scalar.of(collectingRegoVisitor.getScalars()); } @Override public ThunkExpression visit(SetTerm setTerm) { - return Scalar.of(setTerm.getValue()); + ScalarCollectingRegoVisitor collectingRegoVisitor = new ScalarCollectingRegoVisitor(); + setTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)); + return Scalar.of(collectingRegoVisitor.getScalars()); + } + } + + static class ScalarCollectingRegoVisitor implements RegoVisitor> { + + @Getter + List> scalars = new ArrayList<>(); + + @Override + public ThunkExpression visit(QuerySet querySet) { + return null; + } + + @Override + public ThunkExpression visit(Query query) { + return null; + } + + @Override + public ThunkExpression visit(Expression expression) { + return null; + } + + @Override + public ThunkExpression visit(Ref ref) { + return null; + } + + @Override + public ThunkExpression visit(Call call) { + return null; + } + + @Override + public ThunkExpression visit(Var var) { + return null; + } + + @Override + public ThunkExpression visit(Numeric numeric) { + Scalar scalar = Scalar.of(numeric.getValue()); + scalars.add(scalar); + return scalar; + } + + @Override + public ThunkExpression visit(Text text) { + Scalar scalar = Scalar.of(text.getValue()); + scalars.add(scalar); + return scalar; + } + + @Override + public ThunkExpression visit(Bool bool) { + return null; + } + + @Override + public ThunkExpression visit(Null aNull) { + Scalar scalar = Scalar.nullValue(); + scalars.add(scalar); + return scalar; + } + + @Override + public ThunkExpression visit(ArrayTerm arrayTerm) { + arrayTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(this)); + return null; + } + + @Override + public ThunkExpression visit(SetTerm setTerm) { + setTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(this)); + return null; } } diff --git a/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java b/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java index e47beef6..8adc1a7a 100644 --- a/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java +++ b/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java @@ -182,6 +182,6 @@ public Expression visit(Variable variable, QueryDslConversionContext context) @Override public Expression visit(CollectionValue collection, QueryDslConversionContext context) { - return Expressions.constant(collection.getValue().stream().map(Term.ScalarTerm::getValue).collect(Collectors.toList())); + return Expressions.constant(collection.getValue().stream().map(Scalar::getValue).collect(Collectors.toList())); } } From d724d4b5df8283af83a8fffb8649cc16163e93cb Mon Sep 17 00:00:00 2001 From: ystrict Date: Wed, 12 Jul 2023 17:01:12 +0300 Subject: [PATCH 5/8] added collection in collection support --- .../predicates/model/FunctionExpression.java | 2 +- .../QuerySetToThunkExpressionConverter.java | 58 ++++++++++--------- ...uerySetToThunkExpressionConverterTest.java | 23 +++++++- .../querydsl/QueryDslConvertingVisitor.java | 1 - 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java index 19480017..554597e9 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/FunctionExpression.java @@ -36,7 +36,7 @@ enum Operator { GREATER_THAN_OR_EQUAL_TO("gte", Boolean.class, (FunctionExpressionFactory) Comparison::greaterOrEquals), LESS_THAN("lt", Boolean.class, (FunctionExpressionFactory) Comparison::less), LESS_THEN_OR_EQUAL_TO("lte", Boolean.class, (FunctionExpressionFactory) Comparison::lessOrEquals), - IN("internal.member_2", Boolean.class, (FunctionExpressionFactory) Comparison::in), + IN("in", Boolean.class, (FunctionExpressionFactory) Comparison::in), // Logical operator AND("and", Boolean.class, (FunctionExpressionFactory) LogicalOperation::uncheckedConjunction), diff --git a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java index c3d491a3..d2d1bb41 100644 --- a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java +++ b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java @@ -23,6 +23,14 @@ import com.contentgrid.thunx.predicates.model.SymbolicReference.StringPathElement; import com.contentgrid.thunx.predicates.model.ThunkExpression; import com.contentgrid.thunx.predicates.model.Variable; + +import java.math.BigDecimal; +import java.util.Collection; + +import java.util.HashSet; + +import java.util.Set; + import lombok.Getter; import java.util.ArrayList; @@ -288,89 +296,85 @@ public ThunkExpression visit(Null nullValue) { @Override public ThunkExpression visit(ArrayTerm arrayTerm) { ScalarCollectingRegoVisitor collectingRegoVisitor = new ScalarCollectingRegoVisitor(); - arrayTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)); - return Scalar.of(collectingRegoVisitor.getScalars()); + return Scalar.of( + (List) arrayTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)).collect(Collectors.toList()) + ); } @Override public ThunkExpression visit(SetTerm setTerm) { ScalarCollectingRegoVisitor collectingRegoVisitor = new ScalarCollectingRegoVisitor(); - setTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)); - return Scalar.of(collectingRegoVisitor.getScalars()); + + return Scalar.of( + (Set) setTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)).collect(Collectors.toSet()) + ); } } static class ScalarCollectingRegoVisitor implements RegoVisitor> { - @Getter - List> scalars = new ArrayList<>(); - @Override public ThunkExpression visit(QuerySet querySet) { - return null; + throw new UnsupportedOperationException("QuerySet is not supported in collection"); } @Override public ThunkExpression visit(Query query) { - return null; + throw new UnsupportedOperationException("Query is not supported in collection"); } @Override public ThunkExpression visit(Expression expression) { - return null; + throw new UnsupportedOperationException("Expression is not supported in collection"); } @Override public ThunkExpression visit(Ref ref) { - return null; + throw new UnsupportedOperationException("Reference is not supported in collection"); } @Override public ThunkExpression visit(Call call) { - return null; + throw new UnsupportedOperationException("Call is not supported in collection"); } @Override public ThunkExpression visit(Var var) { - return null; + throw new UnsupportedOperationException("Variable is not supported in collection"); } @Override public ThunkExpression visit(Numeric numeric) { - Scalar scalar = Scalar.of(numeric.getValue()); - scalars.add(scalar); - return scalar; + return Scalar.of(numeric.getValue()); } @Override public ThunkExpression visit(Text text) { - Scalar scalar = Scalar.of(text.getValue()); - scalars.add(scalar); - return scalar; + return Scalar.of(text.getValue()); } @Override public ThunkExpression visit(Bool bool) { - return null; + return Scalar.of(bool.getValue()); } @Override public ThunkExpression visit(Null aNull) { - Scalar scalar = Scalar.nullValue(); - scalars.add(scalar); - return scalar; + return Scalar.nullValue(); } @Override public ThunkExpression visit(ArrayTerm arrayTerm) { - arrayTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(this)); - return null; + return Scalar.of( + (List) arrayTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(this)).collect(Collectors.toList()) + ); } @Override public ThunkExpression visit(SetTerm setTerm) { - setTerm.getValue().forEach(scalarTerm -> scalarTerm.accept(this)); - return null; + return Scalar.of( + (Set) setTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(this)).collect(Collectors.toSet()) + ); } } diff --git a/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java b/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java index d219036f..9f4fdd5c 100644 --- a/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java +++ b/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java @@ -152,7 +152,7 @@ void in() { assertThat(converter.convert(opaExpr)).isEqualTo(Comparison.in( SymbolicReference.of("entity", path -> path.string("security")), - Scalar.of(Set.of(new Term.Numeric(4), new Term.Numeric(5))) + Scalar.of(Set.of(Scalar.of(4), Scalar.of(5))) )); } @@ -197,6 +197,27 @@ void opaExpression_singleQuery_singleExpr() { SymbolicReference.of("data", p -> p.string("reports").var("$01").string("clearance_level")))); } + @Test + void opaExpression_collection_in_collection() { + // input.entity.security in {4, 5, {6, 7}} + var opaExpr = new Expression(0, List.of( + new Term.Ref(List.of(new Term.Var("internal"), new Term.Text("member_2"))), + new Term.Ref(List.of( + new Term.Var("input"), + new Term.Text("entity"), + new Term.Text("security") + )), + new Term.SetTerm(Set.of(new Term.Numeric(4), new Term.Numeric(5), + new Term.SetTerm(Set.of(new Term.Numeric(6), new Term.Numeric(7))) + )))); + + assertThat(converter.convert(opaExpr)).isEqualTo(Comparison.in( + SymbolicReference.of("entity", path -> path.string("security")), + Scalar.of(Set.of(Scalar.of(4), Scalar.of(5), + Scalar.of(Set.of(Scalar.of(6), Scalar.of(7)))) + ))); + } + /** * Scenario for partial evaluation of a policy, based on 'group' attribute on a set of documents/results * diff --git a/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java b/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java index 8adc1a7a..889d7d18 100644 --- a/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java +++ b/thunx-predicates-querydsl/src/main/java/com/contentgrid/thunx/predicates/querydsl/QueryDslConvertingVisitor.java @@ -1,6 +1,5 @@ package com.contentgrid.thunx.predicates.querydsl; -import com.contentgrid.opa.rego.ast.Term; import com.contentgrid.thunx.predicates.model.CollectionValue; import com.contentgrid.thunx.predicates.model.FunctionExpression; import com.contentgrid.thunx.predicates.model.Scalar; From e2eac8497cd7692542c0d4e66911a5b331246068 Mon Sep 17 00:00:00 2001 From: ystrict Date: Fri, 21 Jul 2023 15:18:29 +0300 Subject: [PATCH 6/8] Added json functionality for CollectionValue --- .../json/CollectionValueDeserializer.java | 67 +++++++++++++ .../json/ExpressionJsonConverter.java | 85 +++++++++++++--- .../encoding/json/JsonCollectionValueDto.java | 64 ++++++++++++ .../encoding/json/JsonExpressionDto.java | 4 +- .../json/ExpressionJsonConverterTest.java | 98 +++++++++++++++++++ .../json/JsonCollectionValueDtoTest.java | 46 +++++++++ .../predicates/model/CollectionValue.java | 9 +- .../ContextFreeThunkExpressionVisitor.java | 5 + .../thunx/predicates/model/Scalar.java | 2 +- .../predicates/model/SymbolicReference.java | 5 + .../visitor/reducer/ThunkReducerVisitor.java | 4 +- 11 files changed, 369 insertions(+), 20 deletions(-) create mode 100644 thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java create mode 100644 thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java create mode 100644 thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java diff --git a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java new file mode 100644 index 00000000..b422ab51 --- /dev/null +++ b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java @@ -0,0 +1,67 @@ +package com.contentgrid.thunx.encoding.json; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class CollectionValueDeserializer extends StdDeserializer { + + private final ObjectMapper objectMapper; + + public CollectionValueDeserializer() { + this(null); + } + + public CollectionValueDeserializer(Class vc) { + super(vc); + ObjectMapper mapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(JsonExpressionDto.class, this); + mapper.registerModule(module); + this.objectMapper = mapper; + } + + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.getCodec().readTree(p); + String type = node.get("type").asText(); + + if ("array".equals(type)) { + List> list = new ArrayList<>(); + Iterator values = node.get("value").elements(); + + while (values.hasNext()) { + JsonNode value = values.next(); + list.add(deserializeInnerValue(value)); + } + return new JsonCollectionValueDto("array", list); + } else if ("set".equals(type)) { + Set> set = new HashSet<>(); + + Iterator values = node.get("value").elements(); + while (values.hasNext()) { + JsonNode value = values.next(); + set.add(deserializeInnerValue(value)); + } + + return new JsonCollectionValueDto("set", set); + } + + throw new IllegalArgumentException("Unsupported type: " + type); + } + + private JsonScalarDto deserializeInnerValue(JsonNode data) { + return (JsonScalarDto) objectMapper.convertValue(data, JsonExpressionDto.class); + } +} diff --git a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java index 5ff4329e..1b5073d0 100644 --- a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java +++ b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java @@ -1,14 +1,19 @@ package com.contentgrid.thunx.encoding.json; +import com.contentgrid.thunx.predicates.model.CollectionValue; +import com.contentgrid.thunx.predicates.model.ContextFreeThunkExpressionVisitor; +import com.contentgrid.thunx.predicates.model.FunctionExpression; import com.contentgrid.thunx.predicates.model.Scalar; import com.contentgrid.thunx.predicates.model.SymbolicReference; import com.contentgrid.thunx.predicates.model.ThunkExpression; -import com.contentgrid.thunx.predicates.model.ContextFreeThunkExpressionVisitor; +import com.contentgrid.thunx.predicates.model.Variable; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.contentgrid.thunx.predicates.model.FunctionExpression; -import com.contentgrid.thunx.predicates.model.Variable; + +import com.fasterxml.jackson.databind.module.SimpleModule; + import java.io.UncheckedIOException; +import java.util.Collection; import java.util.stream.Collectors; public class ExpressionJsonConverter { @@ -17,7 +22,11 @@ public class ExpressionJsonConverter { private final JsonEncoderVisitor visitor = new JsonEncoderVisitor(); public ExpressionJsonConverter() { - this(new ObjectMapper()); + ObjectMapper mapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(JsonExpressionDto.class, new CollectionValueDeserializer(JsonCollectionValueDto.class)); + mapper.registerModule(module); + this.objectMapper = mapper; } public ExpressionJsonConverter(ObjectMapper objectMapper) { @@ -44,15 +53,7 @@ private static class JsonEncoderVisitor extends ContextFreeThunkExpressionVisito @Override public JsonExpressionDto visit(Scalar scalar) { - var resultType = scalar.getResultType(); - - var typeName = JsonScalarDto.SCALAR_TYPES.get(resultType); - if (typeName == null) { - String exMessage = String.format("Scalar of type <%s> is not supported", resultType.getName()); - throw new IllegalArgumentException(exMessage); - } - - return JsonScalarDto.of(typeName, scalar.getValue()); + return processScalar(scalar); } @Override @@ -75,8 +76,66 @@ public JsonExpressionDto visit(Variable variable) { return new JsonVariableDto(variable.getName()); } + @Override + protected JsonExpressionDto visit(CollectionValue collectionValue) { + return processCollectionDto(collectionValue); + } + } + + private static class JsonScalarEncoderVisitor extends ContextFreeThunkExpressionVisitor> { + + @Override + protected JsonScalarDto visit(Scalar scalar) { + return processScalar(scalar); + } + + @Override + protected JsonScalarDto visit(FunctionExpression functionExpression) { + throw new UnsupportedOperationException("Only Scalars are supported in Collection"); + } + + @Override + protected JsonScalarDto visit(SymbolicReference symbolicReference) { + throw new UnsupportedOperationException("Only Scalars are supported in Collection"); + } + + @Override + protected JsonScalarDto visit(Variable variable) { + throw new UnsupportedOperationException("Only Scalars are supported in Collection"); + } + + @Override + protected JsonScalarDto visit(CollectionValue collectionValue) { + return processCollectionDto(collectionValue); + } + } + private static JsonScalarDto processScalar(Scalar scalar) { + var resultType = scalar.getResultType(); + + var typeName = JsonScalarDto.SCALAR_TYPES.get(resultType); + if (typeName == null) { + String exMessage = String.format("Scalar of type <%s> is not supported", resultType.getName()); + throw new IllegalArgumentException(exMessage); + } + + return JsonScalarDto.of(typeName, scalar.getValue()); } + private static JsonScalarDto processCollectionDto(CollectionValue collectionValue) { + Collection result; + if (JsonCollectionValueDto.getTypeByClass(collectionValue.getResultType()).equals("set")) { + result = collectionValue.getValue().stream() + .map(scalar -> scalar.accept(new JsonScalarEncoderVisitor(), null)) + .collect(Collectors.toSet()); + } else if (JsonCollectionValueDto.getTypeByClass(collectionValue.getResultType()).equals("array")) { + result = collectionValue.getValue().stream() + .map(scalar -> scalar.accept(new JsonScalarEncoderVisitor(), null)).collect(Collectors.toList()); + } else { + throw new UnsupportedOperationException("Type is not supported: " + collectionValue.getResultType()); + } + + return JsonCollectionValueDto.of(JsonCollectionValueDto.getTypeByClass(collectionValue.getResultType()), result); + } } diff --git a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java new file mode 100644 index 00000000..10bf52d0 --- /dev/null +++ b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java @@ -0,0 +1,64 @@ +package com.contentgrid.thunx.encoding.json; + +import com.contentgrid.thunx.predicates.model.Scalar; +import com.contentgrid.thunx.predicates.model.ThunkExpression; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.util.stream.Collectors; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +@JsonDeserialize(using = CollectionValueDeserializer.class) +public class JsonCollectionValueDto extends JsonScalarDto>> { + + private static final Map, String> COLLECTION_TYPES = Map.of( + Set.class, "set", + List.class, "array"); + + public JsonCollectionValueDto(String type, Collection> value) { + super(type, value); + } + + @Override + public ThunkExpression toExpression() throws InvalidExpressionDataException { + switch (this.getType()) { + case "set": + Set> setValues = (Set) super.getValue(); + Set> setExpressions = setValues.stream() + .map(JsonScalarDto::toExpression).collect(Collectors.toSet()); + return Scalar.of((Collection>) setExpressions); + case "array": + List> listValues = (List) super.getValue(); + List> listExpressions = listValues.stream() + .map(JsonScalarDto::toExpression).collect(Collectors.toList()); + return Scalar.of((Collection>) listExpressions); + default: + String message = String.format("Collection of type: '%s' is not supported", this.getType()); + throw new UnsupportedOperationException(message); + } + } + + public static JsonCollectionValueDto of(String type, @NonNull Collection> value) { + return new JsonCollectionValueDto(type, value); + } + + public static String getTypeByClass(Class type) { + return COLLECTION_TYPES.entrySet() + .stream() + .filter(e -> e.getKey().isAssignableFrom(type)) + .map(Map.Entry::getValue) + .findFirst().orElseThrow(() -> new UnsupportedOperationException("Such type is not supported for collection: " + type)); + } + +} diff --git a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonExpressionDto.java b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonExpressionDto.java index 532886f3..1ba13ae1 100644 --- a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonExpressionDto.java +++ b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonExpressionDto.java @@ -19,7 +19,9 @@ @Type(value = JsonScalarDto.class, name = "bool"), @Type(value = JsonScalarDto.class, name = "null"), @Type(value = JsonVariableDto.class, name = "var"), - @Type(value = JsonSymbolicReferenceDto.class, name = "ref") + @Type(value = JsonSymbolicReferenceDto.class, name = "ref"), + @Type(value = JsonCollectionValueDto.class, name = "array"), + @Type(value = JsonCollectionValueDto.class, name = "set") }) public interface JsonExpressionDto { diff --git a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java index 2c4f9c46..03f72281 100644 --- a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java +++ b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java @@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import com.contentgrid.thunx.predicates.model.CollectionValue; import com.contentgrid.thunx.predicates.model.LogicalOperation; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -12,8 +13,11 @@ import com.contentgrid.thunx.predicates.model.Scalar; import com.contentgrid.thunx.predicates.model.SymbolicReference; import com.contentgrid.thunx.predicates.model.Variable; + import java.util.List; import java.util.Map; +import java.util.Set; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -67,6 +71,40 @@ void null_toJson() { } } + @Nested + class Collection { + + @Test + void collection_set_toJson() { + var expr = Scalar.of(Set.of(Scalar.of(1), + Scalar.of(2), Scalar.of(Set.of(Scalar.of(3), Scalar.of(4))))); + var result = converter.encode(expr); + + assertThatJson(result).isEqualTo("{type: 'set',value:" + + "[{type: 'number', value: 2}," + + "{type: 'number', value: 1}," + + "{type:'set', value:[" + + "{type: 'number', value: 4}," + + "{type : 'number', value: 3}]}" + + "]}"); + } + + @Test + void collection_array_toJson() { + var expr = Scalar.of(List.of(Scalar.of(1), + Scalar.of(2), Scalar.of(List.of(Scalar.of(3), Scalar.of(4))))); + var result = converter.encode(expr); + + assertThatJson(result).isEqualTo("{type: 'array',value:" + + "[{type: 'number', value: 1}," + + "{type: 'number', value: 2}," + + "{type:'array', value:[" + + "{type: 'number', value: 3}," + + "{type : 'number', value: 4}]}" + + "]}"); + } + } + @Nested class Operators { @@ -192,6 +230,27 @@ void logical_negation() throws InvalidExpressionDataException, JsonProcessingExc + "]}"); } + @Test + void in() { + // input.entity.security in {4, 5} + var expr = Comparison.in( + SymbolicReference.of("entity", path -> path.string("security")), + Scalar.of(Set.of(Scalar.of(4), Scalar.of(5))) + ); + + var result = converter.encode(expr); + + assertThatJson(result) + .isEqualTo("{type: 'function', " + + "operator: 'in', " + + "terms:[" + + "{type: 'ref', subject:{type: 'var', name: 'entity'}, " + + "path:[{type: 'string', value: 'security'}" + + "]}," + + "{type: 'set', value:[{type: 'number', value: 4},{type: 'number', value: 5}]}]}"); + + } + } @Nested @@ -332,6 +391,45 @@ void nullValue() throws JsonProcessingException, InvalidExpressionDataException } } + @Nested + class Collection { + + @Test + void collection_set_fromJson() throws JsonProcessingException { + var expr = "{\"type\": \"set\",\"value\":[\n" + + "{\"type\": \"number\", \"value\": 2},\n" + + "{\"type\": \"number\", \"value\": 1},\n" + + "{\"type\": \"set\", \"value\":[\n" + + "{\"type\": \"number\", \"value\": 4},\n" + + "{\"type\" : \"number\", \"value\": 3}]\n" + + "}]\n" + + "}"; + var result = converter.decode(expr); + + CollectionValue expected = Scalar.of(Set.of(Scalar.of(1), + Scalar.of(2), Scalar.of(Set.of(Scalar.of(3), Scalar.of(4))))); + assertThat(result).isEqualTo(expected); + } + + @Test + void collection_array_fromJson() throws JsonProcessingException { + var expr = "{\"type\": \"array\",\"value\":[\n" + + "{\"type\": \"number\", \"value\": 1},\n" + + "{\"type\": \"number\", \"value\": 2},\n" + + "{\"type\": \"array\", \"value\":[\n" + + "{\"type\": \"number\", \"value\": 3},\n" + + "{\"type\" : \"number\", \"value\": 4}]\n" + + "}]\n" + + "}"; + var result = converter.decode(expr); + + CollectionValue expected = Scalar.of(List.of(Scalar.of(1), + Scalar.of(2), Scalar.of(List.of(Scalar.of(3), Scalar.of(4))))); + + assertThat(result).isEqualTo(expected); + } + } + @Nested class Variables { diff --git a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java new file mode 100644 index 00000000..dcf9cef4 --- /dev/null +++ b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java @@ -0,0 +1,46 @@ +package com.contentgrid.thunx.encoding.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; +import static org.assertj.core.api.Assertions.assertThat; + +class JsonCollectionValueDtoTest { + + private final ObjectMapper mapper = new ObjectMapper(); + + @Test + void serialize() throws JsonProcessingException { + List> scalarDtos = List.of(JsonScalarDto.of(1)); + var dto = JsonCollectionValueDto.of(JsonCollectionValueDto.getTypeByClass(scalarDtos.getClass()), scalarDtos); + var json = mapper.writeValueAsString(dto); + + assertThatJson(json).isEqualTo("{type: 'array', value: [{type:'number',value: 1}]}"); + } + + @Test + void deserialize() throws JsonProcessingException { + var json = mapper.writeValueAsString(Map.of( + "type", "array", + "value", List.of(Map.of("type", "number", "value", 1)) + )); + + List> scalarDtos = List.of(JsonScalarDto.of(1)); + var dto = JsonCollectionValueDto.of(JsonCollectionValueDto.getTypeByClass(scalarDtos.getClass()), scalarDtos); + JsonExpressionDto scalar = mapper.readValue(json, JsonExpressionDto.class); + assertThat(scalar).isEqualTo(dto); + } + + @Test + void deserialize_nullValue() throws JsonProcessingException { + var json = "{ \"type\": \"array\", \"value\": null }"; + + JsonExpressionDto scalar = mapper.readValue(json, JsonExpressionDto.class); + assertThat(scalar).isEqualTo(new JsonCollectionValueDto("array", null)); + } +} diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java index fe970700..23971213 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java @@ -4,23 +4,26 @@ import java.util.Collection; -@EqualsAndHashCode +@EqualsAndHashCode(exclude = "type") public class CollectionValue implements Scalar>> { private final Collection> value; + private final Class>> type; + @Override public Collection> getValue() { return value; } - protected CollectionValue(Collection> value) { + protected CollectionValue(Collection> value, Class>> type) { this.value = value; + this.type = type; } @Override public Class>> getResultType() { - return null; + return type; } @Override diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ContextFreeThunkExpressionVisitor.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ContextFreeThunkExpressionVisitor.java index 75b24d82..24e5388f 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ContextFreeThunkExpressionVisitor.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/ContextFreeThunkExpressionVisitor.java @@ -22,10 +22,15 @@ public T visit(Variable variable, Void context) { return this.visit(variable); } + @Override + public T visit(CollectionValue collection, Void context) { + return this.visit(collection); + } protected abstract T visit(Scalar scalar); protected abstract T visit(FunctionExpression functionExpression); protected abstract T visit(SymbolicReference symbolicReference); protected abstract T visit(Variable variable); + protected abstract T visit(CollectionValue collectionValue); } diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java index 53e5afc6..41ec615b 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java @@ -32,7 +32,7 @@ static BooleanValue of(boolean value) { } static CollectionValue of(Collection> value) { - return new CollectionValue(value); + return new CollectionValue(value, (Class>>)value.getClass()); } static NullValue nullValue() { diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/SymbolicReference.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/SymbolicReference.java index 28d1b8f2..e28e31f2 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/SymbolicReference.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/SymbolicReference.java @@ -151,6 +151,11 @@ public final T visit(FunctionExpression functionExpression) { public final T visit(SymbolicReference symbolicReference) { return null; } + + @Override + protected T visit(CollectionValue collectionValue) { + return null; + } } @Data diff --git a/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java b/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java index ebeafc19..f613698d 100644 --- a/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java +++ b/thunx-visitor-reducer/src/main/java/com/contentgrid/thunx/visitor/reducer/ThunkReducerVisitor.java @@ -57,7 +57,7 @@ public ThunkExpression visit(Variable variable) { } @Override - public ThunkExpression visit(CollectionValue collection, Void context) { - return collection; + public ThunkExpression visit(CollectionValue collectionValue) { + return collectionValue; } } From 7343f855f1d2e9e56477f5a394998faa2841dd7f Mon Sep 17 00:00:00 2001 From: ystrict Date: Mon, 24 Jul 2023 12:33:18 +0300 Subject: [PATCH 7/8] test fix --- .../thunx/encoding/json/JsonCollectionValueDtoTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java index dcf9cef4..eae865f5 100644 --- a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java +++ b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java @@ -2,6 +2,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.ArrayList; + import org.junit.jupiter.api.Test; import java.util.List; @@ -41,6 +44,6 @@ void deserialize_nullValue() throws JsonProcessingException { var json = "{ \"type\": \"array\", \"value\": null }"; JsonExpressionDto scalar = mapper.readValue(json, JsonExpressionDto.class); - assertThat(scalar).isEqualTo(new JsonCollectionValueDto("array", null)); + assertThat(scalar).isEqualTo(new JsonCollectionValueDto("array", new ArrayList<>())); } } From ca1996cdc6e0428d8d9fb4c088bad6c472086a3a Mon Sep 17 00:00:00 2001 From: ystrict Date: Fri, 18 Aug 2023 11:27:44 +0300 Subject: [PATCH 8/8] fixed comments --- .../json/CollectionValueDeserializer.java | 67 ---------------- .../json/ExpressionJsonConverter.java | 8 +- .../encoding/json/JsonCollectionValueDto.java | 26 ++++--- .../json/ExpressionJsonConverterTest.java | 18 ++--- .../json/JsonCollectionValueDtoTest.java | 3 +- .../predicates/model/CollectionValue.java | 4 + .../thunx/predicates/model/Scalar.java | 5 -- .../QuerySetToThunkExpressionConverter.java | 76 +------------------ ...uerySetToThunkExpressionConverterTest.java | 7 +- 9 files changed, 39 insertions(+), 175 deletions(-) delete mode 100644 thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java diff --git a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java deleted file mode 100644 index b422ab51..00000000 --- a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/CollectionValueDeserializer.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.contentgrid.thunx.encoding.json; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.module.SimpleModule; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -public class CollectionValueDeserializer extends StdDeserializer { - - private final ObjectMapper objectMapper; - - public CollectionValueDeserializer() { - this(null); - } - - public CollectionValueDeserializer(Class vc) { - super(vc); - ObjectMapper mapper = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addDeserializer(JsonExpressionDto.class, this); - mapper.registerModule(module); - this.objectMapper = mapper; - } - - - @Override - public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { - JsonNode node = p.getCodec().readTree(p); - String type = node.get("type").asText(); - - if ("array".equals(type)) { - List> list = new ArrayList<>(); - Iterator values = node.get("value").elements(); - - while (values.hasNext()) { - JsonNode value = values.next(); - list.add(deserializeInnerValue(value)); - } - return new JsonCollectionValueDto("array", list); - } else if ("set".equals(type)) { - Set> set = new HashSet<>(); - - Iterator values = node.get("value").elements(); - while (values.hasNext()) { - JsonNode value = values.next(); - set.add(deserializeInnerValue(value)); - } - - return new JsonCollectionValueDto("set", set); - } - - throw new IllegalArgumentException("Unsupported type: " + type); - } - - private JsonScalarDto deserializeInnerValue(JsonNode data) { - return (JsonScalarDto) objectMapper.convertValue(data, JsonExpressionDto.class); - } -} diff --git a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java index 1b5073d0..532b01b4 100644 --- a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java +++ b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverter.java @@ -10,8 +10,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; - import java.io.UncheckedIOException; import java.util.Collection; import java.util.stream.Collectors; @@ -22,11 +20,7 @@ public class ExpressionJsonConverter { private final JsonEncoderVisitor visitor = new JsonEncoderVisitor(); public ExpressionJsonConverter() { - ObjectMapper mapper = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addDeserializer(JsonExpressionDto.class, new CollectionValueDeserializer(JsonCollectionValueDto.class)); - mapper.registerModule(module); - this.objectMapper = mapper; + this(new ObjectMapper()); } public ExpressionJsonConverter(ObjectMapper objectMapper) { diff --git a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java index 10bf52d0..78b97c8a 100644 --- a/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java +++ b/thunx-encoding-json/src/main/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDto.java @@ -1,25 +1,25 @@ package com.contentgrid.thunx.encoding.json; +import com.contentgrid.thunx.predicates.model.CollectionValue; import com.contentgrid.thunx.predicates.model.Scalar; import com.contentgrid.thunx.predicates.model.ThunkExpression; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import java.util.stream.Collectors; - +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; -import lombok.NonNull; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; @Data @EqualsAndHashCode(callSuper = true) @NoArgsConstructor -@JsonDeserialize(using = CollectionValueDeserializer.class) public class JsonCollectionValueDto extends JsonScalarDto>> { private static final Map, String> COLLECTION_TYPES = Map.of( @@ -37,20 +37,26 @@ public ThunkExpression toExpression() throws InvalidExpressionDataException { Set> setValues = (Set) super.getValue(); Set> setExpressions = setValues.stream() .map(JsonScalarDto::toExpression).collect(Collectors.toSet()); - return Scalar.of((Collection>) setExpressions); + return new CollectionValue((Collection>) setExpressions); case "array": List> listValues = (List) super.getValue(); List> listExpressions = listValues.stream() .map(JsonScalarDto::toExpression).collect(Collectors.toList()); - return Scalar.of((Collection>) listExpressions); + return new CollectionValue((Collection>) listExpressions); default: String message = String.format("Collection of type: '%s' is not supported", this.getType()); throw new UnsupportedOperationException(message); } } - public static JsonCollectionValueDto of(String type, @NonNull Collection> value) { - return new JsonCollectionValueDto(type, value); + @JsonCreator + public static JsonCollectionValueDto of(@JsonProperty("type") String type, @JsonProperty("value") Collection> value) { + if (type.equals("set")) { + return new JsonCollectionValueDto(type, value == null ? null : new HashSet<>(value)); + } else if (type.equals("array")) { + return new JsonCollectionValueDto(type, value == null ? null : new ArrayList<>(value)); + } + throw new UnsupportedOperationException("Type " + type + " is not supported"); } public static String getTypeByClass(Class type) { diff --git a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java index 03f72281..081c26af 100644 --- a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java +++ b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/ExpressionJsonConverterTest.java @@ -76,8 +76,8 @@ class Collection { @Test void collection_set_toJson() { - var expr = Scalar.of(Set.of(Scalar.of(1), - Scalar.of(2), Scalar.of(Set.of(Scalar.of(3), Scalar.of(4))))); + var expr = new CollectionValue(Set.of(Scalar.of(1), + Scalar.of(2), new CollectionValue(Set.of(Scalar.of(3), Scalar.of(4))))); var result = converter.encode(expr); assertThatJson(result).isEqualTo("{type: 'set',value:" + @@ -91,8 +91,8 @@ void collection_set_toJson() { @Test void collection_array_toJson() { - var expr = Scalar.of(List.of(Scalar.of(1), - Scalar.of(2), Scalar.of(List.of(Scalar.of(3), Scalar.of(4))))); + var expr = new CollectionValue(List.of(Scalar.of(1), + Scalar.of(2), new CollectionValue(List.of(Scalar.of(3), Scalar.of(4))))); var result = converter.encode(expr); assertThatJson(result).isEqualTo("{type: 'array',value:" + @@ -235,7 +235,7 @@ void in() { // input.entity.security in {4, 5} var expr = Comparison.in( SymbolicReference.of("entity", path -> path.string("security")), - Scalar.of(Set.of(Scalar.of(4), Scalar.of(5))) + new CollectionValue(Set.of(Scalar.of(4), Scalar.of(5))) ); var result = converter.encode(expr); @@ -406,8 +406,8 @@ void collection_set_fromJson() throws JsonProcessingException { "}"; var result = converter.decode(expr); - CollectionValue expected = Scalar.of(Set.of(Scalar.of(1), - Scalar.of(2), Scalar.of(Set.of(Scalar.of(3), Scalar.of(4))))); + CollectionValue expected = new CollectionValue(Set.of(Scalar.of(1), + Scalar.of(2), new CollectionValue(Set.of(Scalar.of(3), Scalar.of(4))))); assertThat(result).isEqualTo(expected); } @@ -423,8 +423,8 @@ void collection_array_fromJson() throws JsonProcessingException { "}"; var result = converter.decode(expr); - CollectionValue expected = Scalar.of(List.of(Scalar.of(1), - Scalar.of(2), Scalar.of(List.of(Scalar.of(3), Scalar.of(4))))); + CollectionValue expected = new CollectionValue(List.of(Scalar.of(1), + Scalar.of(2), new CollectionValue(List.of(Scalar.of(3), Scalar.of(4))))); assertThat(result).isEqualTo(expected); } diff --git a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java index eae865f5..e1118103 100644 --- a/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java +++ b/thunx-encoding-json/src/test/java/com/contentgrid/thunx/encoding/json/JsonCollectionValueDtoTest.java @@ -12,6 +12,7 @@ import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; class JsonCollectionValueDtoTest { @@ -44,6 +45,6 @@ void deserialize_nullValue() throws JsonProcessingException { var json = "{ \"type\": \"array\", \"value\": null }"; JsonExpressionDto scalar = mapper.readValue(json, JsonExpressionDto.class); - assertThat(scalar).isEqualTo(new JsonCollectionValueDto("array", new ArrayList<>())); + assertThat(scalar).isEqualTo(new JsonCollectionValueDto("array", null)); } } diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java index 23971213..7deb4e1e 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/CollectionValue.java @@ -21,6 +21,10 @@ protected CollectionValue(Collection> value, Class> value) { + this(value, (Class>>)value.getClass()); + } + @Override public Class>> getResultType() { return type; diff --git a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java index 41ec615b..fd044c7e 100644 --- a/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java +++ b/thunx-model/src/main/java/com/contentgrid/thunx/predicates/model/Scalar.java @@ -1,7 +1,6 @@ package com.contentgrid.thunx.predicates.model; import java.math.BigDecimal; -import java.util.Collection; public interface Scalar extends ThunkExpression { @@ -31,10 +30,6 @@ static BooleanValue of(boolean value) { return new BooleanValue(value); } - static CollectionValue of(Collection> value) { - return new CollectionValue(value, (Class>>)value.getClass()); - } - static NullValue nullValue() { return NullValue.INSTANCE; } diff --git a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java index d2d1bb41..abec76b5 100644 --- a/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java +++ b/thunx-pdp-opa/src/main/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverter.java @@ -15,6 +15,7 @@ import com.contentgrid.opa.rego.ast.Term.SetTerm; import com.contentgrid.opa.rego.ast.Term.Text; import com.contentgrid.opa.rego.ast.Term.Var; +import com.contentgrid.thunx.predicates.model.CollectionValue; import com.contentgrid.thunx.predicates.model.Comparison; import com.contentgrid.thunx.predicates.model.LogicalOperation; import com.contentgrid.thunx.predicates.model.NumericFunction; @@ -295,87 +296,16 @@ public ThunkExpression visit(Null nullValue) { @Override public ThunkExpression visit(ArrayTerm arrayTerm) { - ScalarCollectingRegoVisitor collectingRegoVisitor = new ScalarCollectingRegoVisitor(); - return Scalar.of( - (List) arrayTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)).collect(Collectors.toList()) - ); - } - - @Override - public ThunkExpression visit(SetTerm setTerm) { - ScalarCollectingRegoVisitor collectingRegoVisitor = new ScalarCollectingRegoVisitor(); - - return Scalar.of( - (Set) setTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(collectingRegoVisitor)).collect(Collectors.toSet()) - ); - } - } - - static class ScalarCollectingRegoVisitor implements RegoVisitor> { - - @Override - public ThunkExpression visit(QuerySet querySet) { - throw new UnsupportedOperationException("QuerySet is not supported in collection"); - } - - @Override - public ThunkExpression visit(Query query) { - throw new UnsupportedOperationException("Query is not supported in collection"); - } - - @Override - public ThunkExpression visit(Expression expression) { - throw new UnsupportedOperationException("Expression is not supported in collection"); - } - - @Override - public ThunkExpression visit(Ref ref) { - throw new UnsupportedOperationException("Reference is not supported in collection"); - } - - @Override - public ThunkExpression visit(Call call) { - throw new UnsupportedOperationException("Call is not supported in collection"); - } - - @Override - public ThunkExpression visit(Var var) { - throw new UnsupportedOperationException("Variable is not supported in collection"); - } - - @Override - public ThunkExpression visit(Numeric numeric) { - return Scalar.of(numeric.getValue()); - } - - @Override - public ThunkExpression visit(Text text) { - return Scalar.of(text.getValue()); - } - - @Override - public ThunkExpression visit(Bool bool) { - return Scalar.of(bool.getValue()); - } - - @Override - public ThunkExpression visit(Null aNull) { - return Scalar.nullValue(); - } - - @Override - public ThunkExpression visit(ArrayTerm arrayTerm) { - return Scalar.of( + return new CollectionValue( (List) arrayTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(this)).collect(Collectors.toList()) ); } @Override public ThunkExpression visit(SetTerm setTerm) { - return Scalar.of( + return new CollectionValue( (Set) setTerm.getValue().stream().map(scalarTerm -> scalarTerm.accept(this)).collect(Collectors.toSet()) ); } } - } diff --git a/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java b/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java index 9f4fdd5c..1da27131 100644 --- a/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java +++ b/thunx-pdp-opa/src/test/java/com/contentgrid/thunx/pdp/opa/QuerySetToThunkExpressionConverterTest.java @@ -6,6 +6,7 @@ import com.contentgrid.opa.rego.ast.Query; import com.contentgrid.opa.rego.ast.QuerySet; import com.contentgrid.opa.rego.ast.Term; +import com.contentgrid.thunx.predicates.model.CollectionValue; import com.contentgrid.thunx.predicates.model.Comparison; import com.contentgrid.thunx.predicates.model.LogicalOperation; import com.contentgrid.thunx.predicates.model.Scalar; @@ -152,7 +153,7 @@ void in() { assertThat(converter.convert(opaExpr)).isEqualTo(Comparison.in( SymbolicReference.of("entity", path -> path.string("security")), - Scalar.of(Set.of(Scalar.of(4), Scalar.of(5))) + new CollectionValue(Set.of(Scalar.of(4), Scalar.of(5))) )); } @@ -213,8 +214,8 @@ void opaExpression_collection_in_collection() { assertThat(converter.convert(opaExpr)).isEqualTo(Comparison.in( SymbolicReference.of("entity", path -> path.string("security")), - Scalar.of(Set.of(Scalar.of(4), Scalar.of(5), - Scalar.of(Set.of(Scalar.of(6), Scalar.of(7)))) + new CollectionValue(Set.of(Scalar.of(4), Scalar.of(5), + new CollectionValue(Set.of(Scalar.of(6), Scalar.of(7)))) ))); }