Improve error message

This commit is contained in:
sunboyy 2021-02-14 11:48:09 +07:00
parent 18f37a3328
commit fa109a09a8
12 changed files with 269 additions and 66 deletions

View file

@ -2,8 +2,8 @@ coverage:
status: status:
project: project:
default: default:
target: 80% target: 85%
threshold: 4% threshold: 3%
patch: patch:
default: default:
target: 75% target: 75%

View file

@ -1,20 +1,31 @@
package mongo package mongo
// GenerationError is an error from generating MongoDB repository import (
type GenerationError string "fmt"
)
func (err GenerationError) Error() string { // NewOperationNotSupportedError creates operationNotSupportedError
switch err { func NewOperationNotSupportedError(operationName string) error {
case OperationNotSupportedError: return operationNotSupportedError{OperationName: operationName}
return "operation not supported"
case BsonTagNotFoundError:
return "bson tag not found"
}
return string(err)
} }
// generation error constants type operationNotSupportedError struct {
const ( OperationName string
OperationNotSupportedError GenerationError = "ERROR_OPERATION_NOT_SUPPORTED" }
BsonTagNotFoundError GenerationError = "ERROR_BSON_TAG_NOT_FOUND"
) func (err operationNotSupportedError) Error() string {
return fmt.Sprintf("operation '%s' not supported", err.OperationName)
}
// NewBsonTagNotFoundError creates bsonTagNotFoundError
func NewBsonTagNotFoundError(fieldName string) error {
return bsonTagNotFoundError{FieldName: fieldName}
}
type bsonTagNotFoundError struct {
FieldName string
}
func (err bsonTagNotFoundError) Error() string {
return fmt.Sprintf("bson tag of field '%s' not found", err.FieldName)
}

View file

@ -0,0 +1,36 @@
package mongo_test
import (
"testing"
"github.com/sunboyy/repogen/internal/mongo"
)
type ErrorTestCase struct {
Name string
Error error
ExpectedString string
}
func TestError(t *testing.T) {
testTable := []ErrorTestCase{
{
Name: "OperationNotSupportedError",
Error: mongo.NewOperationNotSupportedError("Stub"),
ExpectedString: "operation 'Stub' not supported",
},
{
Name: "BsonTagNotFoundError",
Error: mongo.NewBsonTagNotFoundError("AccessToken"),
ExpectedString: "bson tag of field 'AccessToken' not found",
},
}
for _, testCase := range testTable {
t.Run(testCase.Name, func(t *testing.T) {
if testCase.Error.Error() != testCase.ExpectedString {
t.Errorf("Expected = %v\nReceived = %v", testCase.ExpectedString, testCase.Error.Error())
}
})
}
}

View file

@ -87,9 +87,9 @@ func (g RepositoryGenerator) generateMethodImplementation(methodSpec spec.Method
return g.generateDeleteImplementation(operation) return g.generateDeleteImplementation(operation)
case spec.CountOperation: case spec.CountOperation:
return g.generateCountImplementation(operation) return g.generateCountImplementation(operation)
default:
return "", NewOperationNotSupportedError(operation.Name())
} }
return "", OperationNotSupportedError
} }
func (g RepositoryGenerator) generateInsertImplementation(operation spec.InsertOperation) (string, error) { func (g RepositoryGenerator) generateInsertImplementation(operation spec.InsertOperation) (string, error) {
@ -201,7 +201,7 @@ func (g RepositoryGenerator) bsonTagFromFieldName(fieldName string) (string, err
bsonTag, ok := structField.Tags["bson"] bsonTag, ok := structField.Tags["bson"]
if !ok { if !ok {
return "", BsonTagNotFoundError return "", NewBsonTagNotFoundError(fieldName)
} }
return bsonTag[0], nil return bsonTag[0], nil

View file

@ -1453,6 +1453,13 @@ type GenerateMethodInvalidTestCase struct {
ExpectedError error ExpectedError error
} }
type StubOperation struct {
}
func (o StubOperation) Name() string {
return "Stub"
}
func TestGenerateMethod_Invalid(t *testing.T) { func TestGenerateMethod_Invalid(t *testing.T) {
testTable := []GenerateMethodInvalidTestCase{ testTable := []GenerateMethodInvalidTestCase{
{ {
@ -1467,9 +1474,9 @@ func TestGenerateMethod_Invalid(t *testing.T) {
code.ArrayType{ContainedType: code.PointerType{ContainedType: code.SimpleType("UserModel")}}, code.ArrayType{ContainedType: code.PointerType{ContainedType: code.SimpleType("UserModel")}},
code.SimpleType("error"), code.SimpleType("error"),
}, },
Operation: "search", Operation: StubOperation{},
}, },
ExpectedError: mongo.OperationNotSupportedError, ExpectedError: mongo.NewOperationNotSupportedError("Stub"),
}, },
{ {
Name: "bson tag not found in query", Name: "bson tag not found in query",
@ -1492,7 +1499,7 @@ func TestGenerateMethod_Invalid(t *testing.T) {
}, },
}, },
}, },
ExpectedError: mongo.BsonTagNotFoundError, ExpectedError: mongo.NewBsonTagNotFoundError("AccessToken"),
}, },
{ {
Name: "bson tag not found in update field", Name: "bson tag not found in update field",
@ -1519,7 +1526,7 @@ func TestGenerateMethod_Invalid(t *testing.T) {
}, },
}, },
}, },
ExpectedError: mongo.BsonTagNotFoundError, ExpectedError: mongo.NewBsonTagNotFoundError("AccessToken"),
}, },
} }

View file

@ -1,38 +1,73 @@
package spec package spec
import (
"fmt"
"strings"
)
// ParsingError is an error from parsing interface methods // ParsingError is an error from parsing interface methods
type ParsingError string type ParsingError string
func (err ParsingError) Error() string { func (err ParsingError) Error() string {
switch err { switch err {
case UnknownOperationError:
return "unknown operation"
case UnsupportedNameError:
return "method name is not supported"
case UnsupportedReturnError: case UnsupportedReturnError:
return "this type of return is not supported" return "this type of return is not supported"
case InvalidQueryError: case QueryRequiredError:
return "invalid query" return "query is required"
case InvalidParamError: case InvalidParamError:
return "parameters do not match the query" return "parameters do not match the query"
case InvalidUpdateFieldsError: case InvalidUpdateFieldsError:
return "update fields is invalid" return "update fields is invalid"
case ContextParamRequiredError: case ContextParamRequiredError:
return "context parameter is required" return "context parameter is required"
case StructFieldNotFoundError:
return "struct field not found"
} }
return string(err) return string(err)
} }
// parsing error constants // parsing error constants
const ( const (
UnknownOperationError ParsingError = "ERROR_UNKNOWN_OPERATION"
UnsupportedNameError ParsingError = "ERROR_UNSUPPORTED"
UnsupportedReturnError ParsingError = "ERROR_UNSUPPORTED_RETURN" UnsupportedReturnError ParsingError = "ERROR_UNSUPPORTED_RETURN"
InvalidQueryError ParsingError = "ERROR_INVALID_QUERY" QueryRequiredError ParsingError = "ERROR_QUERY_REQUIRED"
InvalidParamError ParsingError = "ERROR_INVALID_PARAM" InvalidParamError ParsingError = "ERROR_INVALID_PARAM"
InvalidUpdateFieldsError ParsingError = "ERROR_INVALID_UPDATE_FIELDS" InvalidUpdateFieldsError ParsingError = "ERROR_INVALID_UPDATE_FIELDS"
ContextParamRequiredError ParsingError = "ERROR_CONTEXT_PARAM_REQUIRED" ContextParamRequiredError ParsingError = "ERROR_CONTEXT_PARAM_REQUIRED"
StructFieldNotFoundError ParsingError = "ERROR_STRUCT_FIELD_NOT_FOUND"
) )
// NewInvalidQueryError creates invalidQueryError
func NewInvalidQueryError(queryTokens []string) error {
return invalidQueryError{QueryString: strings.Join(queryTokens, "")}
}
type invalidQueryError struct {
QueryString string
}
func (err invalidQueryError) Error() string {
return fmt.Sprintf("invalid query '%s'", err.QueryString)
}
// NewUnknownOperationError creates unknownOperationError
func NewUnknownOperationError(operationName string) error {
return unknownOperationError{OperationName: operationName}
}
type unknownOperationError struct {
OperationName string
}
func (err unknownOperationError) Error() string {
return fmt.Sprintf("unknown operation '%s'", err.OperationName)
}
// NewStructFieldNotFoundError creates structFieldNotFoundError
func NewStructFieldNotFoundError(fieldName string) error {
return structFieldNotFoundError{FieldName: fieldName}
}
type structFieldNotFoundError struct {
FieldName string
}
func (err structFieldNotFoundError) Error() string {
return fmt.Sprintf("struct field '%s' not found", err.FieldName)
}

View file

@ -0,0 +1,41 @@
package spec_test
import (
"testing"
"github.com/sunboyy/repogen/internal/spec"
)
type ErrorTestCase struct {
Name string
Error error
ExpectedString string
}
func TestError(t *testing.T) {
testTable := []ErrorTestCase{
{
Name: "UnknownOperationError",
Error: spec.NewUnknownOperationError("Search"),
ExpectedString: "unknown operation 'Search'",
},
{
Name: "StructFieldNotFoundError",
Error: spec.NewStructFieldNotFoundError("Country"),
ExpectedString: "struct field 'Country' not found",
},
{
Name: "InvalidQueryError",
Error: spec.NewInvalidQueryError([]string{"By", "And"}),
ExpectedString: "invalid query 'ByAnd'",
},
}
for _, testCase := range testTable {
t.Run(testCase.Name, func(t *testing.T) {
if testCase.Error.Error() != testCase.ExpectedString {
t.Errorf("Expected = %v\nReceived = %v", testCase.ExpectedString, testCase.Error.Error())
}
})
}
}

View file

@ -23,6 +23,7 @@ type MethodSpec struct {
// Operation is an interface for any kind of operation // Operation is an interface for any kind of operation
type Operation interface { type Operation interface {
Name() string
} }
// InsertOperation is a method specification for insert operations // InsertOperation is a method specification for insert operations
@ -30,12 +31,22 @@ type InsertOperation struct {
Mode QueryMode Mode QueryMode
} }
// Name returns "Insert" operation name
func (o InsertOperation) Name() string {
return "Insert"
}
// FindOperation is a method specification for find operations // FindOperation is a method specification for find operations
type FindOperation struct { type FindOperation struct {
Mode QueryMode Mode QueryMode
Query QuerySpec Query QuerySpec
} }
// Name returns "Find" operation name
func (o FindOperation) Name() string {
return "Find"
}
// UpdateOperation is a method specification for update operations // UpdateOperation is a method specification for update operations
type UpdateOperation struct { type UpdateOperation struct {
Fields []UpdateField Fields []UpdateField
@ -43,6 +54,11 @@ type UpdateOperation struct {
Query QuerySpec Query QuerySpec
} }
// Name returns "Update" operation name
func (o UpdateOperation) Name() string {
return "Update"
}
// UpdateField stores mapping between field name in the model and the parameter index // UpdateField stores mapping between field name in the model and the parameter index
type UpdateField struct { type UpdateField struct {
Name string Name string
@ -55,7 +71,17 @@ type DeleteOperation struct {
Query QuerySpec Query QuerySpec
} }
// Name returns "Delete" operation name
func (o DeleteOperation) Name() string {
return "Delete"
}
// CountOperation is a method specification for count operations // CountOperation is a method specification for count operations
type CountOperation struct { type CountOperation struct {
Query QuerySpec Query QuerySpec
} }
// Name returns "Count" operation name
func (o CountOperation) Name() string {
return "Count"
}

View file

@ -0,0 +1,45 @@
package spec_test
import (
"testing"
"github.com/sunboyy/repogen/internal/spec"
)
type OperationTestCase struct {
Operation spec.Operation
ExpectedName string
}
func TestOperationName(t *testing.T) {
testTable := []OperationTestCase{
{
Operation: spec.InsertOperation{},
ExpectedName: "Insert",
},
{
Operation: spec.FindOperation{},
ExpectedName: "Find",
},
{
Operation: spec.UpdateOperation{},
ExpectedName: "Update",
},
{
Operation: spec.DeleteOperation{},
ExpectedName: "Delete",
},
{
Operation: spec.CountOperation{},
ExpectedName: "Count",
},
}
for _, testCase := range testTable {
t.Run(testCase.ExpectedName, func(t *testing.T) {
if testCase.Operation.Name() != testCase.ExpectedName {
t.Errorf("Expected = %v\nReceived = %v", testCase.ExpectedName, testCase.Operation.Name())
}
})
}
}

View file

@ -48,7 +48,7 @@ func (p interfaceMethodParser) parseMethod() (Operation, error) {
case "Count": case "Count":
return p.parseCountOperation(methodNameTokens[1:]) return p.parseCountOperation(methodNameTokens[1:])
} }
return nil, UnknownOperationError return nil, NewUnknownOperationError(methodNameTokens[0])
} }
func (p interfaceMethodParser) parseInsertOperation(tokens []string) (Operation, error) { func (p interfaceMethodParser) parseInsertOperation(tokens []string) (Operation, error) {
@ -202,7 +202,7 @@ func (p interfaceMethodParser) parseUpdateOperation(tokens []string) (Operation,
for _, field := range fields { for _, field := range fields {
structField, ok := p.StructModel.Fields.ByName(field.Name) structField, ok := p.StructModel.Fields.ByName(field.Name)
if !ok { if !ok {
return nil, StructFieldNotFoundError return nil, NewStructFieldNotFoundError(field.Name)
} }
if structField.Type != p.Method.Params[field.ParamIndex].Type { if structField.Type != p.Method.Params[field.ParamIndex].Type {
@ -340,7 +340,7 @@ func (p interfaceMethodParser) validateQueryFromParams(params []code.Param, quer
for _, predicate := range querySpec.Predicates { for _, predicate := range querySpec.Predicates {
structField, ok := p.StructModel.Fields.ByName(predicate.Field) structField, ok := p.StructModel.Fields.ByName(predicate.Field)
if !ok { if !ok {
return StructFieldNotFoundError return NewStructFieldNotFoundError(predicate.Field)
} }
for i := 0; i < predicate.Comparator.NumberOfArguments(); i++ { for i := 0; i < predicate.Comparator.NumberOfArguments(); i++ {

View file

@ -850,8 +850,9 @@ func TestParseInterfaceMethod_Invalid(t *testing.T) {
Name: "SearchByID", Name: "SearchByID",
}) })
if err != spec.UnknownOperationError { expectedError := spec.NewUnknownOperationError("Search")
t.Errorf("\nExpected = %v\nReceived = %v", spec.UnknownOperationError, err) if err != expectedError {
t.Errorf("\nExpected = %v\nReceived = %v", expectedError, err)
} }
} }
@ -1008,7 +1009,7 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.QueryRequiredError,
}, },
{ {
Name: "misplaced operator token (leftmost)", Name: "misplaced operator token (leftmost)",
@ -1019,7 +1020,7 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "And", "Gender"}),
}, },
{ {
Name: "misplaced operator token (rightmost)", Name: "misplaced operator token (rightmost)",
@ -1030,7 +1031,7 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "Gender", "And"}),
}, },
{ {
Name: "misplaced operator token (double operator)", Name: "misplaced operator token (double operator)",
@ -1041,7 +1042,7 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "Gender", "And", "And", "City"}),
}, },
{ {
Name: "ambiguous query", Name: "ambiguous query",
@ -1052,7 +1053,7 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "Gender", "And", "City", "Or", "Age"}),
}, },
{ {
Name: "no context parameter", Name: "no context parameter",
@ -1097,7 +1098,7 @@ func TestParseInterfaceMethod_Find_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.StructFieldNotFoundError, ExpectedError: spec.NewStructFieldNotFoundError("Country"),
}, },
{ {
Name: "mismatched method parameter type", Name: "mismatched method parameter type",
@ -1209,7 +1210,7 @@ func TestParseInterfaceMethod_Update_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.QueryRequiredError,
}, },
{ {
Name: "ambiguous query", Name: "ambiguous query",
@ -1220,7 +1221,7 @@ func TestParseInterfaceMethod_Update_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "ID", "And", "Username", "Or", "Gender"}),
}, },
{ {
Name: "no context parameter", Name: "no context parameter",
@ -1251,7 +1252,7 @@ func TestParseInterfaceMethod_Update_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.StructFieldNotFoundError, ExpectedError: spec.NewStructFieldNotFoundError("Country"),
}, },
{ {
Name: "struct field does not match parameter in update fields", Name: "struct field does not match parameter in update fields",
@ -1343,7 +1344,7 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.QueryRequiredError,
}, },
{ {
Name: "misplaced operator token (leftmost)", Name: "misplaced operator token (leftmost)",
@ -1354,7 +1355,7 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "And", "Gender"}),
}, },
{ {
Name: "misplaced operator token (rightmost)", Name: "misplaced operator token (rightmost)",
@ -1365,7 +1366,7 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "Gender", "And"}),
}, },
{ {
Name: "misplaced operator token (double operator)", Name: "misplaced operator token (double operator)",
@ -1376,7 +1377,7 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "Gender", "And", "And", "City"}),
}, },
{ {
Name: "ambiguous query", Name: "ambiguous query",
@ -1387,7 +1388,7 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By", "Gender", "And", "City", "Or", "Age"}),
}, },
{ {
Name: "no context parameter", Name: "no context parameter",
@ -1432,7 +1433,7 @@ func TestParseInterfaceMethod_Delete_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.StructFieldNotFoundError, ExpectedError: spec.NewStructFieldNotFoundError("Country"),
}, },
{ {
Name: "mismatched method parameter type", Name: "mismatched method parameter type",
@ -1522,7 +1523,7 @@ func TestParseInterfaceMethod_Count_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.QueryRequiredError,
}, },
{ {
Name: "invalid query", Name: "invalid query",
@ -1536,7 +1537,7 @@ func TestParseInterfaceMethod_Count_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.InvalidQueryError, ExpectedError: spec.NewInvalidQueryError([]string{"By"}),
}, },
{ {
Name: "context parameter not provided", Name: "context parameter not provided",
@ -1593,7 +1594,7 @@ func TestParseInterfaceMethod_Count_Invalid(t *testing.T) {
code.SimpleType("error"), code.SimpleType("error"),
}, },
}, },
ExpectedError: spec.StructFieldNotFoundError, ExpectedError: spec.NewStructFieldNotFoundError("Country"),
}, },
} }

View file

@ -95,11 +95,12 @@ func (t predicateToken) ToPredicate(paramIndex int) Predicate {
return Predicate{Field: strings.Join(t, ""), Comparator: ComparatorEqual, ParamIndex: paramIndex} return Predicate{Field: strings.Join(t, ""), Comparator: ComparatorEqual, ParamIndex: paramIndex}
} }
func parseQuery(tokens []string, paramIndex int) (QuerySpec, error) { func parseQuery(rawTokens []string, paramIndex int) (QuerySpec, error) {
if len(tokens) == 0 { if len(rawTokens) == 0 {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, QueryRequiredError
} }
tokens := rawTokens
if len(tokens) == 1 && tokens[0] == "All" { if len(tokens) == 1 && tokens[0] == "All" {
return QuerySpec{}, nil return QuerySpec{}, nil
} }
@ -112,7 +113,7 @@ func parseQuery(tokens []string, paramIndex int) (QuerySpec, error) {
} }
if len(tokens) == 0 || tokens[0] == "And" || tokens[0] == "Or" { if len(tokens) == 0 || tokens[0] == "And" || tokens[0] == "Or" {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, NewInvalidQueryError(rawTokens)
} }
var operator Operator var operator Operator
@ -122,7 +123,7 @@ func parseQuery(tokens []string, paramIndex int) (QuerySpec, error) {
if token != "And" && token != "Or" { if token != "And" && token != "Or" {
aggregatedToken = append(aggregatedToken, token) aggregatedToken = append(aggregatedToken, token)
} else if len(aggregatedToken) == 0 { } else if len(aggregatedToken) == 0 {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, NewInvalidQueryError(rawTokens)
} else if token == "And" && operator != OperatorOr { } else if token == "And" && operator != OperatorOr {
operator = OperatorAnd operator = OperatorAnd
predicate := aggregatedToken.ToPredicate(paramIndex) predicate := aggregatedToken.ToPredicate(paramIndex)
@ -136,11 +137,11 @@ func parseQuery(tokens []string, paramIndex int) (QuerySpec, error) {
paramIndex += predicate.Comparator.NumberOfArguments() paramIndex += predicate.Comparator.NumberOfArguments()
aggregatedToken = predicateToken{} aggregatedToken = predicateToken{}
} else { } else {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, NewInvalidQueryError(rawTokens)
} }
} }
if len(aggregatedToken) == 0 { if len(aggregatedToken) == 0 {
return QuerySpec{}, InvalidQueryError return QuerySpec{}, NewInvalidQueryError(rawTokens)
} }
predicates = append(predicates, aggregatedToken.ToPredicate(paramIndex)) predicates = append(predicates, aggregatedToken.ToPredicate(paramIndex))