From 0df4ec612d01d4adc1a3aa73d8ac03165b71fde0 Mon Sep 17 00:00:00 2001 From: qniwerss <73220736+werniq@users.noreply.github.com> Date: Wed, 30 Oct 2024 07:45:08 +0100 Subject: [PATCH] Feature: Implement Graceful shutdown (#21) * docs: Added more documentation to functions and objects in the project * fix: Repaired dockerfile and added commands in Makefile to build/push image with specified IMAGE_NAME variable * feat: Added Taskfile for the owner to compare it with Makefile * fix: Removed redundant clock package, changed all calls to time.Now() * Completely removed clock package * refactor: Adding more descriptive error messages, and renaming vars * Made visualizer and proxy server ports as flags, to allow user specify different ports * Handling errors during certificate loading. * Fix: removed redundant commans at the end of line. * Handlded error in proxy/main.go * Implemented graceful shutdown in collector and proxy * Implemented graceful shutdown in server/server.go * fix: Fixed CI/CD * fix: Repaired github action, and building docker image instead of project, since it can be later pushed * fix: Commented unused code * Running tests only in internal package instead of ./... * fix: Running tests in every folder in internal pkg * fix: Repairing Github Pipeline; Added pcap installation * fix: Repairing Github Pipeline; Added sudo to pcap install * Testing everything in waffle folder, since pcap error is resolved. * changed username in docker image to YOUR_USERNAME * Removed unused packages from project. * Repaired taskfile and added description to all tasks * fix: Building application binary instead of docker, since we should login in registry then * fixed task name from dockerBuild to build * fix: Changed output path in build task * Changed flag name to avoid duplication and added better logging. --- .github/workflows/go.yml | 15 +- Taskfile.yaml | 21 +- cmd/proxy/main.go | 5 +- internal/rule/compiler.go | 81 ----- internal/rule/compiler_test.go | 96 ------ internal/rule/expression_tree.go | 44 --- internal/rule/expression_tree_test.go | 118 ------- internal/rule/factory.go | 154 --------- internal/rule/factory_test.go | 158 ---------- internal/rule/mock_Builder.go | 92 ------ internal/rule/mock_ExpressionTreeBuilder.go | 91 ------ internal/rule/mock_ExpressionTreeFactory.go | 90 ------ internal/rule/mock_Tokenizer.go | 91 ------ internal/rule/mock_node.go | 82 ----- internal/rule/node.go | 55 ---- internal/rule/node_test.go | 328 -------------------- internal/rule/predicate.go | 44 --- internal/rule/predicate_test.go | 81 ----- internal/rule/tokenizer.go | 201 ------------ internal/rule/tokenizer_test.go | 144 --------- pkg/permission/windows.go | 30 -- 21 files changed, 32 insertions(+), 1989 deletions(-) delete mode 100644 internal/rule/compiler.go delete mode 100644 internal/rule/compiler_test.go delete mode 100644 internal/rule/expression_tree.go delete mode 100644 internal/rule/expression_tree_test.go delete mode 100644 internal/rule/factory.go delete mode 100644 internal/rule/factory_test.go delete mode 100644 internal/rule/mock_Builder.go delete mode 100644 internal/rule/mock_ExpressionTreeBuilder.go delete mode 100644 internal/rule/mock_ExpressionTreeFactory.go delete mode 100644 internal/rule/mock_Tokenizer.go delete mode 100644 internal/rule/mock_node.go delete mode 100644 internal/rule/node.go delete mode 100644 internal/rule/node_test.go delete mode 100644 internal/rule/predicate.go delete mode 100644 internal/rule/predicate_test.go delete mode 100644 internal/rule/tokenizer.go delete mode 100644 internal/rule/tokenizer_test.go delete mode 100644 pkg/permission/windows.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d36ceed..db01701 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -21,8 +21,17 @@ jobs: with: go-version: '1.22' - - name: Build - run: go build -v ./... + - name: Install pcap deps + run: sudo apt install -y libpcap-dev - - name: Test + - name: Download go.mod dependencies + run: go mod download + + - name: Test waffle WAF run: go test -v ./... + + - name: Install go-task to build docker image + run: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin + + - name: Build Waffle using Golang + run: task build \ No newline at end of file diff --git a/Taskfile.yaml b/Taskfile.yaml index a53b617..6bfe8bf 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,7 +1,11 @@ version: '3' +env: + IMAGE_NAME: '{{ .IMAGE_NAME | default "/:" }}' + tasks: certs_windows: + desc: "Generate SSL certificates for Windows environment using OpenSSL, including CA and server certificates." cmds: - mkdir -p "./cmd/proxy/.cert" - openssl ecparam -out ./cmd/proxy/.cert/ca.key -name prime256v1 -genkey @@ -13,6 +17,7 @@ tasks: - openssl x509 -in ./cmd/proxy/.cert/server.crt -text -noout certs: + desc: "Generate SSL certificates for non-Windows environments using OpenSSL, including CA and server certificates." cmds: - mkdir -p "./cmd/proxy/.cert" - openssl ecparam -out ./cmd/proxy/.cert/ca.key -name prime256v1 -genkey @@ -24,13 +29,21 @@ tasks: - openssl x509 -in ./cmd/proxy/.cert/server.crt -text -noout mocks: + desc: "Generate mock implementations for testing using Mockery." cmds: - mockery - docker-build: + build: + desc: "Build a project using Golang" + cmds: + - go build -o ./out/app ./cmd/proxy + + dockerBuild: + desc: "Build a Docker image using the specified Dockerfile and tag it with the provided image name." cmds: - - docker build -t ${IMAGE_NAME} -f .\build\Dockerfile . + - docker build -t {{ .IMAGE_NAME }} -f .\build\Dockerfile . - docker-push: + dockerPush: + desc: "Push the Docker image to the remote registry with the specified image name." cmds: - - docker push ${IMAGE_NAME} + - docker push {{ .IMAGE_NAME }} diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index d0d3e34..8009bc3 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -24,7 +24,7 @@ func main() { visualizeServerPort string proxyServerPort string ) - flag.StringVar(&visualizeServerPort, "p", "8081", "Port for server to listen on") + flag.StringVar(&visualizeServerPort, "vp", "8080", "Port for visualizer server to listen on") flag.StringVar(&proxyServerPort, "p", "8081", "Port for server to listen on") quit := make(chan os.Signal) @@ -37,7 +37,8 @@ func main() { } }() - log.Println("Server started on :8080") + log.Println("Visualizer server started on ", visualizeServerPort) + log.Println("Proxy server started on ", proxyServerPort) <-quit log.Println("Shutdown signal received, shutting down gracefully...") diff --git a/internal/rule/compiler.go b/internal/rule/compiler.go deleted file mode 100644 index 952a581..0000000 --- a/internal/rule/compiler.go +++ /dev/null @@ -1,81 +0,0 @@ -package rule - -import ( - "errors" - "fmt" - "strings" -) - -const ( - variableExpressionDivider = "=>" -) - -var ( - ErrEmptyPredicateName = errors.New("predicate name is empty") - ErrEmptyPredicateValue = errors.New("value to compile predicate is empty") - ErrMissingPredicateParts = errors.New("predicate parts are missing") - ErrInvalidPredicate = errors.New("predicate is invalid") -) - -type Compiler interface { - // Compile compiles rule to the functional predicate. - // Name - is a name of the predicate. - // Value - is a value that should be compiled into predicate. - Compile(name, value string) (*Predicate, error) -} - -type CustomCompiler struct { - builder Builder -} - -var _ Compiler = (*CustomCompiler)(nil) - -func NewCustomCompiler() *CustomCompiler { - return &CustomCompiler{ - builder: newPredicateBuilder(), - } -} - -func (c *CustomCompiler) Compile(name, value string) (*Predicate, error) { - if err := validateInput(name, value); err != nil { - return nil, fmt.Errorf("validate name and value: %w", err) - } - - variable, expression, err := getVariableAndLogicalExpression(value) - if err != nil { - return nil, fmt.Errorf("get variable and logical expression for predicate: %w", err) - } - - pred, err := c.builder.Build(name, variable, expression) - if err != nil { - return nil, fmt.Errorf("build predicate: %w", err) - } - - return pred, nil -} - -func validateInput(name string, value string) error { - if len(name) == 0 { - return ErrEmptyPredicateName - } - - if len(value) == 0 { - return ErrEmptyPredicateValue - } - - return nil -} - -func getVariableAndLogicalExpression(value string) (string, string, error) { - values := strings.Split(value, variableExpressionDivider) - - if len(values) < 2 { - return "", "", ErrMissingPredicateParts - } - - if len(values) > 2 { - return "", "", ErrInvalidPredicate - } - - return values[0], values[1], nil -} diff --git a/internal/rule/compiler_test.go b/internal/rule/compiler_test.go deleted file mode 100644 index 058c466..0000000 --- a/internal/rule/compiler_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package rule - -import ( - "errors" - "github.com/stretchr/testify/mock" - "reflect" - "testing" -) - -func TestCustomCompiler_Compile(t *testing.T) { - type fields struct { - builderFunc func() Builder - } - type args struct { - name string - value string - } - tests := []struct { - name string - fields fields - args args - want *Predicate - wantErr bool - }{ - { - name: "empty name, error returned", - args: args{ - name: "", - value: "p => p", - }, - wantErr: true, - }, - { - name: "empty value, error returned", - args: args{ - name: "name", - value: "", - }, - wantErr: true, - }, - { - name: "missing => sign, error returned", - args: args{ - name: "name", - value: "p p.length() > 0", - }, - wantErr: true, - }, - { - name: "to many => signs, error returned", - args: args{ - name: "name", - value: "p => p.format() => json", - }, - wantErr: true, - }, - { - name: "builder returns error, error returned", - fields: fields{ - builderFunc: func() Builder { - b := NewMockBuilder(t) - - b.EXPECT().Build(mock.Anything, mock.Anything, mock.Anything). - Return(nil, errors.New("err")) - - return b - }, - }, - args: args{ - name: "name", - value: "p => p.length() > 0", - }, - wantErr: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var builder Builder - if tt.fields.builderFunc != nil { - builder = tt.fields.builderFunc() - } - - c := &CustomCompiler{ - builder: builder, - } - got, err := c.Compile(tt.args.name, tt.args.value) - if (err != nil) != tt.wantErr { - t.Errorf("Compile() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Compile() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/rule/expression_tree.go b/internal/rule/expression_tree.go deleted file mode 100644 index e8837f4..0000000 --- a/internal/rule/expression_tree.go +++ /dev/null @@ -1,44 +0,0 @@ -package rule - -import "fmt" - -type expressionTree node - -type ExpressionTreeFactory interface { - CreateExpressionTree(tokens []Token) (expressionTree, error) -} - -// Tokenizer should be implemented by the structs that tokenizes input. -type Tokenizer interface { - // BuildTokens builds tokens based on the variable and expression. - // Returns error if tokenizer cannot be done with the input. - BuildTokens(variable string, expression string) ([]Token, error) -} - -type expressionTreeBuilder struct { - tokenizer Tokenizer - expressionTreeFactory ExpressionTreeFactory -} - -var _ ExpressionTreeBuilder = (*expressionTreeBuilder)(nil) - -func newExpressionTreeBuilder() *expressionTreeBuilder { - return &expressionTreeBuilder{ - tokenizer: &tokenizer{}, - expressionTreeFactory: &expressionTreeFactory{}, - } -} - -func (c *expressionTreeBuilder) BuildExpressionTree(variable, expression string) (expressionTree, error) { - tokens, err := c.tokenizer.BuildTokens(variable, expression) - if err != nil { - return nil, fmt.Errorf("build tokens: %w", err) - } - - tree, err := c.expressionTreeFactory.CreateExpressionTree(tokens) - if err != nil { - return nil, fmt.Errorf("create expression tree: %w", err) - } - - return tree, nil -} diff --git a/internal/rule/expression_tree_test.go b/internal/rule/expression_tree_test.go deleted file mode 100644 index d19e102..0000000 --- a/internal/rule/expression_tree_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package rule - -import ( - "errors" - "reflect" - "testing" - - "github.com/stretchr/testify/mock" -) - -func Test_expressionTreeBuilder_BuildExpressionTree(t *testing.T) { - type fields struct { - tokenizerFunc func() Tokenizer - expressionTreeFactoryFunc func() ExpressionTreeFactory - } - type args struct { - variable string - expression string - } - tests := []struct { - name string - fields fields - args args - want expressionTree - wantErr bool - }{ - { - name: "tokenizer returns error, error returned", - fields: fields{ - tokenizerFunc: func() Tokenizer { - tkn := NewMockTokenizer(t) - - tkn.EXPECT(). - BuildTokens(mock.Anything, mock.Anything). - Return(nil, errors.New("error")) - - return tkn - }, - }, - wantErr: true, - }, - { - name: "expression tree factory returns error, error returned", - fields: fields{ - tokenizerFunc: func() Tokenizer { - tkn := NewMockTokenizer(t) - - tkn.EXPECT(). - BuildTokens(mock.Anything, mock.Anything). - Return(testTokens, nil) - - return tkn - }, - expressionTreeFactoryFunc: func() ExpressionTreeFactory { - etf := NewMockExpressionTreeFactory(t) - - etf.EXPECT(). - CreateExpressionTree(mock.Anything). - Return(nil, errors.New("error")) - - return etf - }, - }, - wantErr: true, - }, - { - name: "expression tree factory returns expression tree, tree returned", - fields: fields{ - tokenizerFunc: func() Tokenizer { - tkn := NewMockTokenizer(t) - - tkn.EXPECT(). - BuildTokens(mock.Anything, mock.Anything). - Return(testTokens, nil) - - return tkn - }, - expressionTreeFactoryFunc: func() ExpressionTreeFactory { - etf := NewMockExpressionTreeFactory(t) - - etf.EXPECT(). - CreateExpressionTree(testTokens). - Return(and{}, nil) - - return etf - }, - }, - wantErr: false, - want: and{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var expressionTreeFactory ExpressionTreeFactory - if tt.fields.expressionTreeFactoryFunc != nil { - expressionTreeFactory = tt.fields.expressionTreeFactoryFunc() - } - - var tokenizer Tokenizer - if tt.fields.tokenizerFunc != nil { - tokenizer = tt.fields.tokenizerFunc() - } - - c := &expressionTreeBuilder{ - tokenizer: tokenizer, - expressionTreeFactory: expressionTreeFactory, - } - got, err := c.BuildExpressionTree(tt.args.variable, tt.args.expression) - if (err != nil) != tt.wantErr { - t.Errorf("BuildExpressionTree() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("BuildExpressionTree() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/rule/factory.go b/internal/rule/factory.go deleted file mode 100644 index aac6569..0000000 --- a/internal/rule/factory.go +++ /dev/null @@ -1,154 +0,0 @@ -package rule - -import "github.com/emirpasic/gods/stacks/arraystack" - -var ( - // operatorPrecedence is a slice of operators in the precedence order. - // The most important ones are on the top, the lest important ones are on the bottom. - // Also if there are two operators: A and B, and if those are in the same row, - // those are equal in terms of precedence. - operatorPrecedence = [][]string{ - // Postfix - { - tokenLParen, - tokenRParen, - tokenDot, - }, - // Relational - { - tokenMoreThan, - tokenLessThan, - }, - // Equality - { - tokenEqual, - tokenNotEqual, - }, - // AND - { - tokenDoubleAmpersand, - }, - // OR - { - tokenOr, - }, - // Dot - { - tokenDot, - }, - } -) - -type expressionTreeFactory struct { -} - -var _ ExpressionTreeFactory = (*expressionTreeFactory)(nil) - -func (e *expressionTreeFactory) CreateExpressionTree(tokens []Token) (expressionTree, error) { - return nil, nil -} - -// 1. While there are tokens to be read: -// 2. Read a token -// 3. If it's a number add it to queue -// 4. If it's an operator -// 5. While there's an operator on the top of the stack with greater precedence: -// 6. Pop operators from the stack onto the output queue -// 7. Push the current operator onto the stack -// 8. If it's a left bracket push it onto the stack -// 9. If it's a right bracket -// 10. While there's not a left bracket at the top of the stack: -// 11. Pop operators from the stack onto the output queue. -// 12. Pop the left bracket from the stack and discard it -// 13. While there are operators on the stack, pop them to the queue - -func reversePolishNotationSort(tokens []Token) []Token { - - var outputTokens []Token - operatorStack := arraystack.New() - - for _, token := range tokens { - if isOperator(token) { - if v, ok := operatorStack.Peek(); ok { - vt := v.(Token) - if isTokenWithGreaterPrecedenceFromThePrecedenceSlice(token, vt) { - for !operatorStack.Empty() { - v, ok := operatorStack.Pop() - if !ok { - continue - } - - vt := v.(Token) - - outputTokens = append(outputTokens, vt) - } - - operatorStack.Push(token) - } - } - - continue - } - - if token.Name == tokenLParen { - operatorStack.Push(token) - - continue - } - - if token.Name == tokenRParen { - var lParen *Token - for !operatorStack.Empty() || lParen == nil { - v, ok := operatorStack.Pop() - if !ok { - continue - } - - vt := v.(Token) - - if vt.Name == tokenLParen { - lParen = &vt - continue - } - - outputTokens = append(outputTokens, vt) - } - - continue - } - - tokens = append(tokens, token) - } - - return tokens -} - -func isOperator(tkn Token) bool { - for _, tknOperator := range tokensOperators { - if tkn.Name == tknOperator { - return true - } - } - - return false -} - -func isTokenWithGreaterPrecedenceFromThePrecedenceSlice(tkn Token, lastTknFromStack Token) bool { - var ( - tknIdx int - lastTknFromStackIdx int - ) - - for i, op := range operatorPrecedence { - for _, opp := range op { - if opp == tkn.Name { - tknIdx = i - } - if opp == lastTknFromStack.Name { - lastTknFromStackIdx = i - } - } - } - - return lastTknFromStackIdx < tknIdx -} diff --git a/internal/rule/factory_test.go b/internal/rule/factory_test.go deleted file mode 100644 index df281e3..0000000 --- a/internal/rule/factory_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package rule - -import ( - "reflect" - "testing" -) - -func Test_isOperator(t *testing.T) { - type args struct { - tkn Token - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "is an operator, true returned", - args: args{ - tkn: Token{ - Name: tokenMoreThan, - }, - }, - want: true, - }, - { - name: "isn't an operator, false returned", - args: args{ - tkn: Token{ - Name: tokenLParen, - }, - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := isOperator(tt.args.tkn); got != tt.want { - t.Errorf("isOperator() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_isTokenWithGreaterPrecedenceFromThePrecedenceSlices(t *testing.T) { - type args struct { - tkn Token - lastTknFromStack Token - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "token with greater precedence, true returned", - args: args{ - tkn: Token{ - Name: tokenOr, - Value: tokenOr, - }, - lastTknFromStack: Token{ - Name: tokenLParen, - Value: tokenLParen, - }, - }, - want: true, - }, - { - name: "token with less precedence, false returned", - args: args{ - tkn: Token{ - Name: tokenLParen, - Value: tokenLParen, - }, - lastTknFromStack: Token{ - Name: tokenOr, - Value: tokenOr, - }, - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := isTokenWithGreaterPrecedenceFromThePrecedenceSlice(tt.args.tkn, tt.args.lastTknFromStack); got != tt.want { - t.Errorf("isTokenWithGreaterPrecedenceFromThePrecedenceSlice() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_reversePolishNotationSort(t *testing.T) { - type args struct { - tokens []Token - } - tests := []struct { - name string - args args - want []Token - }{ - { - name: "tokens should be ordered", - args: args{ - tokens: []Token{ - { - Name: tokenFunction, - Value: "LEN", - }, - { - Name: tokenLParen, - Value: tokenLParen, - }, - { - Name: tokenVariable, - Value: "p", - }, - { - Name: tokenDot, - Value: ".", - }, - { - Name: tokenField, - Value: "headers", - }, - { - Name: tokenRParen, - Value: tokenRParen, - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenMoreThan, - Value: tokenMoreThan, - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenNumber, - Value: "5", - }, - }, - }, - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := reversePolishNotationSort(tt.args.tokens); !reflect.DeepEqual(got, tt.want) { - t.Errorf("reversePolishNotationSort() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/rule/mock_Builder.go b/internal/rule/mock_Builder.go deleted file mode 100644 index d69d712..0000000 --- a/internal/rule/mock_Builder.go +++ /dev/null @@ -1,92 +0,0 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. - -package rule - -import mock "github.com/stretchr/testify/mock" - -// MockBuilder is an autogenerated mock type for the Builder type -type MockBuilder struct { - mock.Mock -} - -type MockBuilder_Expecter struct { - mock *mock.Mock -} - -func (_m *MockBuilder) EXPECT() *MockBuilder_Expecter { - return &MockBuilder_Expecter{mock: &_m.Mock} -} - -// Build provides a mock function with given fields: name, variable, expression -func (_m *MockBuilder) Build(name string, variable string, expression string) (*Predicate, error) { - ret := _m.Called(name, variable, expression) - - if len(ret) == 0 { - panic("no return value specified for Build") - } - - var r0 *Predicate - var r1 error - if rf, ok := ret.Get(0).(func(string, string, string) (*Predicate, error)); ok { - return rf(name, variable, expression) - } - if rf, ok := ret.Get(0).(func(string, string, string) *Predicate); ok { - r0 = rf(name, variable, expression) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*Predicate) - } - } - - if rf, ok := ret.Get(1).(func(string, string, string) error); ok { - r1 = rf(name, variable, expression) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockBuilder_Build_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Build' -type MockBuilder_Build_Call struct { - *mock.Call -} - -// Build is a helper method to define mock.On call -// - name string -// - variable string -// - expression string -func (_e *MockBuilder_Expecter) Build(name interface{}, variable interface{}, expression interface{}) *MockBuilder_Build_Call { - return &MockBuilder_Build_Call{Call: _e.mock.On("Build", name, variable, expression)} -} - -func (_c *MockBuilder_Build_Call) Run(run func(name string, variable string, expression string)) *MockBuilder_Build_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string), args[2].(string)) - }) - return _c -} - -func (_c *MockBuilder_Build_Call) Return(_a0 *Predicate, _a1 error) *MockBuilder_Build_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockBuilder_Build_Call) RunAndReturn(run func(string, string, string) (*Predicate, error)) *MockBuilder_Build_Call { - _c.Call.Return(run) - return _c -} - -// NewMockBuilder creates a new instance of MockBuilder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockBuilder(t interface { - mock.TestingT - Cleanup(func()) -}) *MockBuilder { - mock := &MockBuilder{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/rule/mock_ExpressionTreeBuilder.go b/internal/rule/mock_ExpressionTreeBuilder.go deleted file mode 100644 index 64b31f7..0000000 --- a/internal/rule/mock_ExpressionTreeBuilder.go +++ /dev/null @@ -1,91 +0,0 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. - -package rule - -import mock "github.com/stretchr/testify/mock" - -// MockExpressionTreeBuilder is an autogenerated mock type for the ExpressionTreeBuilder type -type MockExpressionTreeBuilder struct { - mock.Mock -} - -type MockExpressionTreeBuilder_Expecter struct { - mock *mock.Mock -} - -func (_m *MockExpressionTreeBuilder) EXPECT() *MockExpressionTreeBuilder_Expecter { - return &MockExpressionTreeBuilder_Expecter{mock: &_m.Mock} -} - -// BuildExpressionTree provides a mock function with given fields: variable, expression -func (_m *MockExpressionTreeBuilder) BuildExpressionTree(variable string, expression string) (expressionTree, error) { - ret := _m.Called(variable, expression) - - if len(ret) == 0 { - panic("no return value specified for BuildExpressionTree") - } - - var r0 expressionTree - var r1 error - if rf, ok := ret.Get(0).(func(string, string) (expressionTree, error)); ok { - return rf(variable, expression) - } - if rf, ok := ret.Get(0).(func(string, string) expressionTree); ok { - r0 = rf(variable, expression) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(expressionTree) - } - } - - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(variable, expression) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockExpressionTreeBuilder_BuildExpressionTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildExpressionTree' -type MockExpressionTreeBuilder_BuildExpressionTree_Call struct { - *mock.Call -} - -// BuildExpressionTree is a helper method to define mock.On call -// - variable string -// - expression string -func (_e *MockExpressionTreeBuilder_Expecter) BuildExpressionTree(variable interface{}, expression interface{}) *MockExpressionTreeBuilder_BuildExpressionTree_Call { - return &MockExpressionTreeBuilder_BuildExpressionTree_Call{Call: _e.mock.On("BuildExpressionTree", variable, expression)} -} - -func (_c *MockExpressionTreeBuilder_BuildExpressionTree_Call) Run(run func(variable string, expression string)) *MockExpressionTreeBuilder_BuildExpressionTree_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockExpressionTreeBuilder_BuildExpressionTree_Call) Return(_a0 expressionTree, _a1 error) *MockExpressionTreeBuilder_BuildExpressionTree_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockExpressionTreeBuilder_BuildExpressionTree_Call) RunAndReturn(run func(string, string) (expressionTree, error)) *MockExpressionTreeBuilder_BuildExpressionTree_Call { - _c.Call.Return(run) - return _c -} - -// NewMockExpressionTreeBuilder creates a new instance of MockExpressionTreeBuilder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockExpressionTreeBuilder(t interface { - mock.TestingT - Cleanup(func()) -}) *MockExpressionTreeBuilder { - mock := &MockExpressionTreeBuilder{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/rule/mock_ExpressionTreeFactory.go b/internal/rule/mock_ExpressionTreeFactory.go deleted file mode 100644 index 667119f..0000000 --- a/internal/rule/mock_ExpressionTreeFactory.go +++ /dev/null @@ -1,90 +0,0 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. - -package rule - -import mock "github.com/stretchr/testify/mock" - -// MockExpressionTreeFactory is an autogenerated mock type for the ExpressionTreeFactory type -type MockExpressionTreeFactory struct { - mock.Mock -} - -type MockExpressionTreeFactory_Expecter struct { - mock *mock.Mock -} - -func (_m *MockExpressionTreeFactory) EXPECT() *MockExpressionTreeFactory_Expecter { - return &MockExpressionTreeFactory_Expecter{mock: &_m.Mock} -} - -// CreateExpressionTree provides a mock function with given fields: tokens -func (_m *MockExpressionTreeFactory) CreateExpressionTree(tokens []Token) (expressionTree, error) { - ret := _m.Called(tokens) - - if len(ret) == 0 { - panic("no return value specified for CreateExpressionTree") - } - - var r0 expressionTree - var r1 error - if rf, ok := ret.Get(0).(func([]Token) (expressionTree, error)); ok { - return rf(tokens) - } - if rf, ok := ret.Get(0).(func([]Token) expressionTree); ok { - r0 = rf(tokens) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(expressionTree) - } - } - - if rf, ok := ret.Get(1).(func([]Token) error); ok { - r1 = rf(tokens) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockExpressionTreeFactory_CreateExpressionTree_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateExpressionTree' -type MockExpressionTreeFactory_CreateExpressionTree_Call struct { - *mock.Call -} - -// CreateExpressionTree is a helper method to define mock.On call -// - tokens []Token -func (_e *MockExpressionTreeFactory_Expecter) CreateExpressionTree(tokens interface{}) *MockExpressionTreeFactory_CreateExpressionTree_Call { - return &MockExpressionTreeFactory_CreateExpressionTree_Call{Call: _e.mock.On("CreateExpressionTree", tokens)} -} - -func (_c *MockExpressionTreeFactory_CreateExpressionTree_Call) Run(run func(tokens []Token)) *MockExpressionTreeFactory_CreateExpressionTree_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].([]Token)) - }) - return _c -} - -func (_c *MockExpressionTreeFactory_CreateExpressionTree_Call) Return(_a0 expressionTree, _a1 error) *MockExpressionTreeFactory_CreateExpressionTree_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockExpressionTreeFactory_CreateExpressionTree_Call) RunAndReturn(run func([]Token) (expressionTree, error)) *MockExpressionTreeFactory_CreateExpressionTree_Call { - _c.Call.Return(run) - return _c -} - -// NewMockExpressionTreeFactory creates a new instance of MockExpressionTreeFactory. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockExpressionTreeFactory(t interface { - mock.TestingT - Cleanup(func()) -}) *MockExpressionTreeFactory { - mock := &MockExpressionTreeFactory{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/rule/mock_Tokenizer.go b/internal/rule/mock_Tokenizer.go deleted file mode 100644 index ada4866..0000000 --- a/internal/rule/mock_Tokenizer.go +++ /dev/null @@ -1,91 +0,0 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. - -package rule - -import mock "github.com/stretchr/testify/mock" - -// MockTokenizer is an autogenerated mock type for the Tokenizer type -type MockTokenizer struct { - mock.Mock -} - -type MockTokenizer_Expecter struct { - mock *mock.Mock -} - -func (_m *MockTokenizer) EXPECT() *MockTokenizer_Expecter { - return &MockTokenizer_Expecter{mock: &_m.Mock} -} - -// BuildTokens provides a mock function with given fields: variable, expression -func (_m *MockTokenizer) BuildTokens(variable string, expression string) ([]Token, error) { - ret := _m.Called(variable, expression) - - if len(ret) == 0 { - panic("no return value specified for BuildTokens") - } - - var r0 []Token - var r1 error - if rf, ok := ret.Get(0).(func(string, string) ([]Token, error)); ok { - return rf(variable, expression) - } - if rf, ok := ret.Get(0).(func(string, string) []Token); ok { - r0 = rf(variable, expression) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]Token) - } - } - - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(variable, expression) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// MockTokenizer_BuildTokens_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BuildTokens' -type MockTokenizer_BuildTokens_Call struct { - *mock.Call -} - -// BuildTokens is a helper method to define mock.On call -// - variable string -// - expression string -func (_e *MockTokenizer_Expecter) BuildTokens(variable interface{}, expression interface{}) *MockTokenizer_BuildTokens_Call { - return &MockTokenizer_BuildTokens_Call{Call: _e.mock.On("BuildTokens", variable, expression)} -} - -func (_c *MockTokenizer_BuildTokens_Call) Run(run func(variable string, expression string)) *MockTokenizer_BuildTokens_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(string)) - }) - return _c -} - -func (_c *MockTokenizer_BuildTokens_Call) Return(_a0 []Token, _a1 error) *MockTokenizer_BuildTokens_Call { - _c.Call.Return(_a0, _a1) - return _c -} - -func (_c *MockTokenizer_BuildTokens_Call) RunAndReturn(run func(string, string) ([]Token, error)) *MockTokenizer_BuildTokens_Call { - _c.Call.Return(run) - return _c -} - -// NewMockTokenizer creates a new instance of MockTokenizer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockTokenizer(t interface { - mock.TestingT - Cleanup(func()) -}) *MockTokenizer { - mock := &MockTokenizer{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/rule/mock_node.go b/internal/rule/mock_node.go deleted file mode 100644 index d08b928..0000000 --- a/internal/rule/mock_node.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by mockery v2.42.1. DO NOT EDIT. - -package rule - -import ( - request "waffle/internal/request" - - mock "github.com/stretchr/testify/mock" -) - -// Mocknode is an autogenerated mock type for the node type -type Mocknode struct { - mock.Mock -} - -type Mocknode_Expecter struct { - mock *mock.Mock -} - -func (_m *Mocknode) EXPECT() *Mocknode_Expecter { - return &Mocknode_Expecter{mock: &_m.Mock} -} - -// Eval provides a mock function with given fields: r -func (_m *Mocknode) Eval(r request.Wrapper) bool { - ret := _m.Called(r) - - if len(ret) == 0 { - panic("no return value specified for Eval") - } - - var r0 bool - if rf, ok := ret.Get(0).(func(request.Wrapper) bool); ok { - r0 = rf(r) - } else { - r0 = ret.Get(0).(bool) - } - - return r0 -} - -// Mocknode_Eval_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Eval' -type Mocknode_Eval_Call struct { - *mock.Call -} - -// Eval is a helper method to define mock.On call -// - r request.Wrapper -func (_e *Mocknode_Expecter) Eval(r interface{}) *Mocknode_Eval_Call { - return &Mocknode_Eval_Call{Call: _e.mock.On("Eval", r)} -} - -func (_c *Mocknode_Eval_Call) Run(run func(r request.Wrapper)) *Mocknode_Eval_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(request.Wrapper)) - }) - return _c -} - -func (_c *Mocknode_Eval_Call) Return(_a0 bool) *Mocknode_Eval_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *Mocknode_Eval_Call) RunAndReturn(run func(request.Wrapper) bool) *Mocknode_Eval_Call { - _c.Call.Return(run) - return _c -} - -// NewMocknode creates a new instance of Mocknode. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMocknode(t interface { - mock.TestingT - Cleanup(func()) -}) *Mocknode { - mock := &Mocknode{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/internal/rule/node.go b/internal/rule/node.go deleted file mode 100644 index c859819..0000000 --- a/internal/rule/node.go +++ /dev/null @@ -1,55 +0,0 @@ -package rule - -import "waffle/internal/request" - -type node interface { - Eval(r request.Wrapper) bool -} - -type and struct { - left, right node -} - -func (a and) Eval(r request.Wrapper) bool { - return a.left.Eval(r) && a.right.Eval(r) -} - -func (a and) SetChild(left, right node) { - a.left = left - a.right = right -} - -type or struct { - left, right node -} - -func (o or) Eval(r request.Wrapper) bool { - return o.left.Eval(r) || o.right.Eval(r) -} - -func (o or) SetChild(left, right node) { - o.left = left - o.right = right -} - -type eq struct { - left, right node -} - -func (e eq) Eval(r request.Wrapper) bool { - return e.left.Eval(r) == e.right.Eval(r) -} - -type gt struct { - valueFunc func(r request.Wrapper) (int, error) - check int -} - -func (g gt) Eval(r request.Wrapper) bool { - value, err := g.valueFunc(r) - if err != nil { - return false - } - - return value > g.check -} diff --git a/internal/rule/node_test.go b/internal/rule/node_test.go deleted file mode 100644 index f1c2320..0000000 --- a/internal/rule/node_test.go +++ /dev/null @@ -1,328 +0,0 @@ -package rule - -import ( - "testing" - - "github.com/stretchr/testify/mock" - - "waffle/internal/request" -) - -func Test_and_Eval(t *testing.T) { - type fields struct { - leftFunc func() node - rightFunc func() node - } - type args struct { - r request.Wrapper - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - name: "both nodes true, true returned", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - rightFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - }, - want: true, - }, - { - name: "one of nodes false, false returned", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - rightFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(false) - - return n - }, - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var left, right node - if tt.fields.leftFunc != nil { - left = tt.fields.leftFunc() - } - if tt.fields.rightFunc != nil { - right = tt.fields.rightFunc() - } - - a := and{ - left: left, - right: right, - } - if got := a.Eval(tt.args.r); got != tt.want { - t.Errorf("Eval() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_or_Eval(t *testing.T) { - type fields struct { - leftFunc func() node - rightFunc func() node - } - type args struct { - r request.Wrapper - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - name: "first node true, returns true", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - }, - want: true, - }, - { - name: "both nodes false, returns false", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(false) - - return n - }, - rightFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(false) - - return n - }, - }, - want: false, - }, - { - name: "first node false, second true, returns true", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(false) - - return n - }, - rightFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - }, - want: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var left, right node - if tt.fields.leftFunc != nil { - left = tt.fields.leftFunc() - } - if tt.fields.rightFunc != nil { - right = tt.fields.rightFunc() - } - - o := or{ - left: left, - right: right, - } - if got := o.Eval(tt.args.r); got != tt.want { - t.Errorf("Eval() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_eq_Eval(t *testing.T) { - type fields struct { - leftFunc func() node - rightFunc func() node - } - type args struct { - r request.Wrapper - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - name: "both nodes true, true returned", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - rightFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - }, - want: true, - }, - { - name: "both nodes false, returns true", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(false) - - return n - }, - rightFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(false) - - return n - }, - }, - want: true, - }, - { - name: "different nodes, false returned", - fields: fields{ - leftFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(true) - - return n - }, - rightFunc: func() node { - n := NewMocknode(t) - - n.EXPECT().Eval(mock.Anything).Return(false) - - return n - }, - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var left, right node - if tt.fields.leftFunc != nil { - left = tt.fields.leftFunc() - } - if tt.fields.rightFunc != nil { - right = tt.fields.rightFunc() - } - - e := eq{ - left: left, - right: right, - } - if got := e.Eval(tt.args.r); got != tt.want { - t.Errorf("Eval() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_gt_Eval(t *testing.T) { - type fields struct { - valueFunc func(r request.Wrapper) (int, error) - check int - } - type args struct { - r request.Wrapper - } - tests := []struct { - name string - fields fields - args args - want bool - }{ - { - name: "value and check eq, false returned", - fields: fields{ - valueFunc: func(_ request.Wrapper) (int, error) { - return 69, nil - }, - check: 69, - }, - want: false, - }, - { - name: "value is greater than check, true returned", - fields: fields{ - valueFunc: func(_ request.Wrapper) (int, error) { - return 69, nil - }, - check: 1, - }, - want: true, - }, - { - name: "value is less than check, false returned", - fields: fields{ - valueFunc: func(_ request.Wrapper) (int, error) { - return 1, nil - }, - check: 69, - }, - want: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := gt{ - valueFunc: tt.fields.valueFunc, - check: tt.fields.check, - } - if got := g.Eval(tt.args.r); got != tt.want { - t.Errorf("Eval() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/internal/rule/predicate.go b/internal/rule/predicate.go deleted file mode 100644 index fdf1389..0000000 --- a/internal/rule/predicate.go +++ /dev/null @@ -1,44 +0,0 @@ -package rule - -import ( - "fmt" - - "waffle/internal/request" -) - -type ExpressionTreeBuilder interface { - BuildExpressionTree(variable, expression string) (expressionTree, error) -} - -type Builder interface { - Build(name, variable, expression string) (*Predicate, error) -} - -type Predicate struct { - Name string - Eval func(r request.Wrapper) bool -} - -type predicateBuilder struct { - treeBuilder ExpressionTreeBuilder -} - -func newPredicateBuilder() *predicateBuilder { - return &predicateBuilder{ - treeBuilder: newExpressionTreeBuilder(), - } -} - -var _ Builder = (*predicateBuilder)(nil) - -func (p *predicateBuilder) Build(name, variable, expression string) (*Predicate, error) { - tree, err := p.treeBuilder.BuildExpressionTree(variable, expression) - if err != nil { - return nil, fmt.Errorf("build expression tree: %w", err) - } - - return &Predicate{ - Name: name, - Eval: tree.Eval, - }, nil -} diff --git a/internal/rule/predicate_test.go b/internal/rule/predicate_test.go deleted file mode 100644 index e9e0af3..0000000 --- a/internal/rule/predicate_test.go +++ /dev/null @@ -1,81 +0,0 @@ -package rule - -import ( - "errors" - "github.com/stretchr/testify/mock" - "testing" -) - -func Test_predicateBuilder_Build(t *testing.T) { - andNode := &and{} - - type fields struct { - treeBuilderFunc func() ExpressionTreeBuilder - } - type args struct { - name string - variable string - expression string - } - tests := []struct { - name string - fields fields - args args - wantPredicate bool - wantErr bool - }{ - { - name: "builder returns error, error returned", - fields: fields{ - treeBuilderFunc: func() ExpressionTreeBuilder { - etb := NewMockExpressionTreeBuilder(t) - - etb.EXPECT().BuildExpressionTree(mock.Anything, mock.Anything).Return(nil, errors.New("error")) - - return etb - }, - }, - wantErr: true, - }, - { - name: "builder returns tree node, predicate returned", - fields: fields{ - treeBuilderFunc: func() ExpressionTreeBuilder { - etb := NewMockExpressionTreeBuilder(t) - - etb.EXPECT(). - BuildExpressionTree(mock.Anything, mock.Anything). - Return(andNode, nil) - - return etb - }, - }, - args: args{ - name: "test is test", - variable: "test", - expression: "test => LEN(test.payload) > 0", - }, - wantPredicate: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var treeBuilder ExpressionTreeBuilder - if tt.fields.treeBuilderFunc != nil { - treeBuilder = tt.fields.treeBuilderFunc() - } - - p := &predicateBuilder{ - treeBuilder: treeBuilder, - } - got, err := p.Build(tt.args.name, tt.args.variable, tt.args.expression) - if (err != nil) != tt.wantErr { - t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr) - return - } - if (got != nil) != tt.wantPredicate { - t.Errorf("Build() got = %v, want %v", got, tt.wantPredicate) - } - }) - } -} diff --git a/internal/rule/tokenizer.go b/internal/rule/tokenizer.go deleted file mode 100644 index f64fb4e..0000000 --- a/internal/rule/tokenizer.go +++ /dev/null @@ -1,201 +0,0 @@ -package rule - -import ( - "fmt" - "unicode" -) - -var ( - tokenVariable = "var" - tokenFunction = "func" - tokenNumber = "token_number" - tokenField = "field" - tokenSpace = "space" - - tokenLParen = "(" - tokenRParen = ")" - tokenDot = "." - tokenDoubleAmpersand = "&&" - tokenMoreThan = ">" - tokenLessThan = "<" - tokenSingleApostrophe = "'" - tokenOr = "||" - tokenEqual = "==" - tokenNotEqual = "!=" - tokensOperators = []string{ - tokenDot, - tokenDoubleAmpersand, - tokenMoreThan, - tokenLessThan, - tokenSingleApostrophe, - tokenOr, - tokenEqual, - tokenNotEqual, - } - - methodLen = "LEN" - methodFormat = "FORMAT" - methods = []string{ - methodLen, - methodFormat, - } - - fieldPayload = "payload" - fieldHeaders = "headers" - fields = []string{ - fieldPayload, - fieldHeaders, - } -) - -type Token struct { - Name string - Value string -} - -type tokenizer struct { -} - -var _ Tokenizer = (*tokenizer)(nil) - -func (t *tokenizer) BuildTokens(variable string, expression string) ([]Token, error) { - var ( - tokens []Token - match []rune - ) - - runeExpression := []rune(expression) - - for i, r := range runeExpression { - match = append(match, r) - - v, ok := getFunction(match) - if ok { - tokens = append(tokens, Token{ - Name: tokenFunction, - Value: v, - }) - - match = []rune{} - continue - } - - if isVariable(match, variable) && len(expression) >= i+1 && runeExpression[i+1] == '.' { - tokens = append(tokens, Token{ - Name: tokenVariable, - Value: string(match), - }) - - match = []rune{} - continue - } - - if v, ok := getSpecialCharacter(match); ok { - tokens = append(tokens, Token{ - Name: v, - Value: v, - }) - - match = []rune{} - continue - } - - if v, ok := getField(match); ok { - tokens = append(tokens, Token{ - Name: tokenField, - Value: v, - }) - - match = []rune{} - continue - } - - if unicode.IsSpace(match[0]) { - tokens = append(tokens, Token{ - Name: tokenSpace, - Value: tokenSpace, - }) - - match = []rune{} - continue - } - - if unicode.IsNumber(match[0]) { - tokens = append(tokens, Token{ - Name: tokenNumber, - Value: string(match[0]), - }) - - match = []rune{} - continue - } - - if string(match[0]) == tokenLParen { - tokens = append(tokens, Token{ - Name: tokenLParen, - Value: tokenLParen, - }) - - match = []rune{} - continue - } - - if string(match[0]) == tokenRParen { - tokens = append(tokens, Token{ - Name: tokenRParen, - Value: tokenRParen, - }) - - match = []rune{} - continue - } - } - - if len(match) > 0 { - return nil, fmt.Errorf("there are still unmatched characters") - } - - return tokens, nil -} - -func getFunction(match []rune) (string, bool) { - strMatch := string(match) - - for _, method := range methods { - if method == strMatch { - return method, true - } - } - - return "", false -} - -func isVariable(match []rune, variable string) bool { - strMatch := string(match) - - return strMatch == variable -} - -func getSpecialCharacter(match []rune) (string, bool) { - strMatch := string(match) - - for _, sch := range tokensOperators { - if sch == strMatch { - return sch, true - } - } - - return "", false -} - -func getField(match []rune) (string, bool) { - strMatch := string(match) - - for _, field := range fields { - if field == strMatch { - return field, true - } - } - - return "", false -} diff --git a/internal/rule/tokenizer_test.go b/internal/rule/tokenizer_test.go deleted file mode 100644 index cf823df..0000000 --- a/internal/rule/tokenizer_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package rule - -import ( - "reflect" - "testing" -) - -var testTokens = []Token{ - { - Name: tokenFunction, - Value: "LEN", - }, - { - Name: tokenLParen, - Value: tokenLParen, - }, - { - Name: tokenVariable, - Value: "p", - }, - { - Name: tokenDot, - Value: tokenDot, - }, - { - Name: tokenField, - Value: "payload", - }, - { - Name: tokenRParen, - Value: tokenRParen, - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenMoreThan, - Value: ">", - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenNumber, - Value: "0", - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenDoubleAmpersand, - Value: "&&", - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenFunction, - Value: "LEN", - }, - { - Name: tokenLParen, - Value: tokenLParen, - }, - { - Name: tokenVariable, - Value: "p", - }, - { - Name: tokenDot, - Value: tokenDot, - }, - { - Name: tokenField, - Value: "headers", - }, - { - Name: tokenRParen, - Value: tokenRParen, - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenMoreThan, - Value: ">", - }, - { - Name: tokenSpace, - Value: tokenSpace, - }, - { - Name: tokenNumber, - Value: "0", - }, -} - -func Test_tokenizer_BuildTokens(t1 *testing.T) { - type args struct { - variable string - expression string - } - tests := []struct { - name string - args args - want []Token - wantErr bool - }{ - { - name: "wrong predicate, error returned", - args: args{ - variable: "add", - expression: "LANER(add.format) == 'json'", - }, - wantErr: true, - }, - { - name: "slice of tokens returned", - args: args{ - variable: "p", - expression: "LEN(p.payload) > 0 && LEN(p.headers) > 0", - }, - want: testTokens, - }, - } - for _, tt := range tests { - t1.Run(tt.name, func(t1 *testing.T) { - t := &tokenizer{} - got, err := t.BuildTokens(tt.args.variable, tt.args.expression) - if (err != nil) != tt.wantErr { - t1.Errorf("BuildTokens() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t1.Errorf("BuildTokens() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/pkg/permission/windows.go b/pkg/permission/windows.go deleted file mode 100644 index 2169951..0000000 --- a/pkg/permission/windows.go +++ /dev/null @@ -1,30 +0,0 @@ -package permission - -import ( - "fmt" - "golang.org/x/sys/windows" - "os" - "strings" - "syscall" -) - -func RunMeElevated() error { - verb := "runas" - exe, _ := os.Executable() - cwd, _ := os.Getwd() - args := strings.Join(os.Args[1:], " ") - - verbPtr, _ := syscall.UTF16PtrFromString(verb) - exePtr, _ := syscall.UTF16PtrFromString(exe) - cwdPtr, _ := syscall.UTF16PtrFromString(cwd) - argPtr, _ := syscall.UTF16PtrFromString(args) - - var showCmd int32 = 1 //SW_NORMAL - - err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) - if err != nil { - return fmt.Errorf("windows shell execute: %w", err) - } - - return nil -}