diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..bb00f19 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,5 @@ +## Roadmap +- Support custom operators and registry +- Make transformers more flexible +- Cleanup on code and optimize performance +- Move to Java 17 \ No newline at end of file diff --git a/src/main/java/com/intuit/graphql/filter/ast/Operator.java b/src/main/java/com/intuit/graphql/filter/ast/Operator.java index 47cb96b..14067b3 100644 --- a/src/main/java/com/intuit/graphql/filter/ast/Operator.java +++ b/src/main/java/com/intuit/graphql/filter/ast/Operator.java @@ -1,5 +1,6 @@ /* Copyright 2020 Intuit Inc. + Modifications Copyright 2024 Jean Luck Sossmeier Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,92 +17,43 @@ package com.intuit.graphql.filter.ast; /** - * Enum of operators for supporting relational - * and logical expressions. + * Class of operators for supporting relational and logical expressions. * * @author sjaiswal + * @author jeansossmeier */ -public enum Operator { - - /* Logical Operators */ - AND("and", "Logical", Kind.COMPOUND), - OR ("or", "Logical", Kind.COMPOUND), - NOT ("not", "Logical", Kind.UNARY), - - /* String Operators */ - EQUALS("equals", "String", Kind.BINARY), - CONTAINS("contains", "String", Kind.BINARY), - STARTS("starts", "String", Kind.BINARY), - ENDS("ends", "String", Kind.BINARY), - - /* Numeric Operators */ - EQ("eq", "Numeric", Kind.BINARY), - GT("gt", "Numeric", Kind.BINARY), - GTE("gte", "Numeric", Kind.BINARY), - LT("lt", "Numeric", Kind.BINARY), - LTE("lte", "Numeric", Kind.BINARY), - - /* Range Operators */ - IN("in", "String|Numeric", Kind.BINARY), - BETWEEN("between","DateTime|Numeric", Kind.BINARY); - - /** - * Enum of operator kind. - */ - enum Kind { - COMPOUND, - BINARY, - UNARY; - } - +public class Operator { private String name; private String type; private Kind kind; - Operator(String name, String type, Kind kind) { - this.type = type; + public Operator(String name, String type, Kind kind) { this.name = name; + this.type = type; this.kind = kind; } - /** - * Returns the Operator enum based - * on operator name. - * @param name - * @return - */ - public static Operator getOperator(String name) { - for (Operator operator : Operator.values()) { - if (operator.getName().equals(name)) { - return operator; - } - } - throw new IllegalArgumentException(String.valueOf(name)); - } - - /** - * Returns Operator kind based - * on operator name. - * @param name - * @return - */ - public static String getOperatorKind(String name) { - return getOperator(name).getKind().name(); - } + public String getName() { return name; } + public String getType() { return type; } + public Kind getKind() { return kind; } - /** - * Returns operator name. - * @return - */ - public String getName() { - return name; + public enum Kind { + COMPOUND, BINARY, UNARY } - /** - * Returns operator kind. - * @return - */ - public Kind getKind() { - return kind; - } -} + public static final Operator AND = new Operator("and", "Logical", Operator.Kind.COMPOUND); + + public static final Operator OR = new Operator("or", "Logical", Operator.Kind.COMPOUND); + public static final Operator NOT = new Operator("not", "Logical", Operator.Kind.UNARY); + public static final Operator EQUALS = new Operator("equals", "String", Operator.Kind.BINARY); + public static final Operator CONTAINS = new Operator("contains", "String", Operator.Kind.BINARY); + public static final Operator STARTS = new Operator("starts", "String", Operator.Kind.BINARY); + public static final Operator ENDS = new Operator("ends", "String", Operator.Kind.BINARY); + public static final Operator EQ = new Operator("eq", "Numeric", Operator.Kind.BINARY); + public static final Operator GT = new Operator("gt", "Numeric", Operator.Kind.BINARY); + public static final Operator GTE = new Operator("gte", "Numeric", Operator.Kind.BINARY); + public static final Operator LT = new Operator("lt", "Numeric", Operator.Kind.BINARY); + public static final Operator LTE = new Operator("lte", "Numeric", Operator.Kind.BINARY); + public static final Operator IN = new Operator("in", "String|Numeric", Operator.Kind.BINARY); + public static final Operator BETWEEN = new Operator("between", "DateTime|Numeric", Operator.Kind.BINARY); +} \ No newline at end of file diff --git a/src/main/java/com/intuit/graphql/filter/ast/OperatorRegistry.java b/src/main/java/com/intuit/graphql/filter/ast/OperatorRegistry.java new file mode 100644 index 0000000..63aab2e --- /dev/null +++ b/src/main/java/com/intuit/graphql/filter/ast/OperatorRegistry.java @@ -0,0 +1,90 @@ +/* + Copyright 2020 Intuit Inc. + Modifications Copyright 2024 Jean Luck Sossmeier + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +package com.intuit.graphql.filter.ast; + +import java.util.HashMap; +import java.util.Map; + +import static com.intuit.graphql.filter.ast.Operator.AND; +import static com.intuit.graphql.filter.ast.Operator.BETWEEN; +import static com.intuit.graphql.filter.ast.Operator.CONTAINS; +import static com.intuit.graphql.filter.ast.Operator.ENDS; +import static com.intuit.graphql.filter.ast.Operator.EQ; +import static com.intuit.graphql.filter.ast.Operator.EQUALS; +import static com.intuit.graphql.filter.ast.Operator.GT; +import static com.intuit.graphql.filter.ast.Operator.GTE; +import static com.intuit.graphql.filter.ast.Operator.IN; +import static com.intuit.graphql.filter.ast.Operator.LT; +import static com.intuit.graphql.filter.ast.Operator.LTE; +import static com.intuit.graphql.filter.ast.Operator.NOT; +import static com.intuit.graphql.filter.ast.Operator.OR; +import static com.intuit.graphql.filter.ast.Operator.STARTS; + +/** + * Class that represents a registry + * + * @author jeansossmeier + */ +public class OperatorRegistry { + private static final OperatorRegistry INSTANCE = new OperatorRegistry(); + + public static OperatorRegistry getInstance() { + return INSTANCE; + } + + private final Map operators = new HashMap<>(); + + public void registerOperator(Operator operator) { + operators.put(operator.getName(), operator); + } + + public Operator getOperator(String name) { + final Operator operator = operators.get(name); + if (operator == null) { + throw new IllegalArgumentException("No operator found with name: " + name); + } + return operator; + } + + public static OperatorRegistry withDefaultOperators() { + final OperatorRegistry registry = OperatorRegistry.getInstance(); + + // Logical Operators + registry.registerOperator(AND); + registry.registerOperator(OR); + registry.registerOperator(NOT); + + // String Operators + registry.registerOperator(EQUALS); + registry.registerOperator(CONTAINS); + registry.registerOperator(STARTS); + registry.registerOperator(ENDS); + + // Numeric Operators + registry.registerOperator(EQ); + registry.registerOperator(GT); + registry.registerOperator(GTE); + registry.registerOperator(LT); + registry.registerOperator(LTE); + + // Range Operators + registry.registerOperator(IN); + registry.registerOperator(BETWEEN); + + return registry; + } +} \ No newline at end of file diff --git a/src/main/java/com/intuit/graphql/filter/visitors/SQLExpressionVisitor.java b/src/main/java/com/intuit/graphql/filter/visitors/SQLExpressionVisitor.java index 2ac94a5..b5d67d2 100644 --- a/src/main/java/com/intuit/graphql/filter/visitors/SQLExpressionVisitor.java +++ b/src/main/java/com/intuit/graphql/filter/visitors/SQLExpressionVisitor.java @@ -30,6 +30,21 @@ import java.util.List; import java.util.Map; +import static com.intuit.graphql.filter.ast.Operator.AND; +import static com.intuit.graphql.filter.ast.Operator.BETWEEN; +import static com.intuit.graphql.filter.ast.Operator.CONTAINS; +import static com.intuit.graphql.filter.ast.Operator.ENDS; +import static com.intuit.graphql.filter.ast.Operator.EQ; +import static com.intuit.graphql.filter.ast.Operator.EQUALS; +import static com.intuit.graphql.filter.ast.Operator.GT; +import static com.intuit.graphql.filter.ast.Operator.GTE; +import static com.intuit.graphql.filter.ast.Operator.IN; +import static com.intuit.graphql.filter.ast.Operator.LT; +import static com.intuit.graphql.filter.ast.Operator.LTE; +import static com.intuit.graphql.filter.ast.Operator.NOT; +import static com.intuit.graphql.filter.ast.Operator.OR; +import static com.intuit.graphql.filter.ast.Operator.STARTS; + /** * This class is responsible for traversing * the expression tree and generating an @@ -207,48 +222,33 @@ public String visitExpressionValue(ExpressionValue value, private String resolveOperator(Operator operator) { String op = ""; - switch (operator) { - /* Logical operators */ - case AND: - case OR: - case NOT: - op = operator.getName().toUpperCase(); - break; + /* Logical operators */ + if (operator.equals(AND) || operator.equals(OR) || operator.equals(NOT)) { + op = operator.getName().toUpperCase(); /* Relational string operators*/ - case EQUALS: - op = "="; - break; - case CONTAINS: - case STARTS: - case ENDS: - op = "LIKE"; - break; + } else if (operator.equals(EQUALS)) { + op = "="; + } else if (operator.equals(CONTAINS) || operator.equals(STARTS) || operator.equals(ENDS)) { + op = "LIKE"; /* Relational numeric operators*/ - case LT: - op = "<"; - break; - case GT: - op = ">"; - break; - case EQ: - op = "="; - break; - case GTE: - op = ">="; - break; - case LTE: - op = "<="; - break; + } else if (operator.equals(LT)) { + op = "<"; + } else if (operator.equals(GT)) { + op = ">"; + } else if (operator.equals(EQ)) { + op = "="; + } else if (operator.equals(GTE)) { + op = ">="; + } else if (operator.equals(LTE)) { + op = "<="; /* Common operators */ - case IN: - op = "IN"; - break; - case BETWEEN: - op = "BETWEEN"; - break; + } else if (operator.equals(IN)) { + op = "IN"; + } else if (operator.equals(BETWEEN)) { + op = "BETWEEN"; } return op; }