Skip to content

Commit d9535b1

Browse files
lexer parser added
1 parent 2f9d9ce commit d9535b1

File tree

7 files changed

+178
-49
lines changed

7 files changed

+178
-49
lines changed

config.yaml

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
inputconfig:
2-
csvsourcefilename: sample.csv
3-
inputmethod: CSV
2+
collection: sampledata
3+
credentialfileaddr: firebaseConfig.json
4+
document: "1"
5+
inputmethod: Firebase
46
outputconfig:
5-
csvdestinationfilename: samplenew.csv
6-
outputmethod: CSV
7-
cronjob:
8-
cronexpression: "@every 5s" # Every 5 seconds
9-
jobname: "Data Fetch and Process"
10-
task: "process_csv"
7+
collection: abcd
8+
connstring: mongodb://localhost:27017/
9+
database: test1
10+
outputmethod: MongoDB
11+
transformations: |
12+
ADD_FIELD("processed_at", '2004-10-22')
13+
validations: |
14+
FIELD("age") RANGE(30, 35)

go.mod

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ toolchain go1.22.9
66

77
require (
88
firebase.google.com/go v3.13.0+incompatible
9-
github.com/golang/mock v1.1.1
109
github.com/jlaffaye/ftp v0.2.0
1110
github.com/manifoldco/promptui v0.9.0
1211
github.com/pkg/sftp v1.13.7

go.sum

-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
9292
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
9393
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
9494
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
95-
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
9695
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
9796
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
9897
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=

integrations/mongodb.go

+25-39
Original file line numberDiff line numberDiff line change
@@ -109,60 +109,46 @@ func (m MongoDBDestination) SendData(data interface{}, req interfaces.Request) e
109109
}
110110
logger.Infof("Connecting to MongoDB destination...")
111111

112+
// Initialize MongoDB client
112113
clientOptions := options.Client().ApplyURI(req.TargetMongoDBConnString)
113114
client, err := mongo.Connect(context.TODO(), clientOptions)
114115
if err != nil {
115-
return err
116+
return fmt.Errorf("failed to connect to MongoDB: %w", err)
116117
}
117118
defer func() {
118119
if err = client.Disconnect(context.TODO()); err != nil {
119120
logger.Errorf("Error disconnecting MongoDB client: %v", err)
120121
}
121122
}()
122123

123-
collection := client.Database(req.TargetMongoDBDatabase).Collection(req.TargetMongoDBCollection)
124-
125-
// Assert that data is a slice of bson.M
126-
dataSlice, ok := data.([]bson.M)
127-
if !ok {
128-
dataSlice, _ = TransformDataToBSON(data)
124+
// Transform data to BSON
125+
bsonData, err := TransformDataToBSON(data)
126+
if err != nil {
127+
return fmt.Errorf("data transformation failed: %w", err)
129128
}
130129

131-
// Buffered channel for sending documents
132-
dataChannel := make(chan bson.M, bufferSize)
133-
errorChannel := make(chan error, bufferSize)
134-
var wg sync.WaitGroup
135-
136-
// Goroutines for worker pool
137-
for i := 0; i < bufferSize; i++ { // Worker pool
138-
wg.Add(1)
139-
go func() {
140-
defer wg.Done()
141-
for doc := range dataChannel {
142-
if _, err := collection.InsertOne(context.TODO(), doc); err != nil {
143-
logger.Errorf("Error inserting into collection %s: %v", req.TargetMongoDBCollection, err)
144-
errorChannel <- err
145-
} else {
146-
logger.Infof("Data sent to MongoDB target collection %s: %v", req.TargetMongoDBCollection, doc)
147-
}
148-
}
149-
}()
150-
}
130+
// Access database and collection
131+
collection := client.Database(req.TargetMongoDBDatabase).Collection(req.TargetMongoDBCollection)
151132

152-
// Feed data into the channel
153-
go func() {
154-
for _, doc := range dataSlice {
155-
dataChannel <- doc
133+
// Insert data into MongoDB
134+
if len(bsonData) == 1 {
135+
// Insert a single document
136+
_, err = collection.InsertOne(context.TODO(), bsonData[0])
137+
if err != nil {
138+
return fmt.Errorf("failed to insert document: %w", err)
156139
}
157-
close(dataChannel)
158-
}()
159140

160-
wg.Wait()
161-
close(errorChannel)
162-
163-
// Check for errors in the error channel
164-
if len(errorChannel) > 0 {
165-
return errors.New("one or more errors occurred while inserting data")
141+
} else {
142+
// Insert multiple documents
143+
docs := make([]interface{}, len(bsonData))
144+
for i, doc := range bsonData {
145+
docs[i] = doc
146+
}
147+
_, err = collection.InsertMany(context.TODO(), docs)
148+
if err != nil {
149+
return fmt.Errorf("failed to insert documents: %w", err)
150+
}
151+
logger.Infof("Successfully inserted %d documents into MongoDB collection %s", len(bsonData), req.TargetMongoDBCollection)
166152
}
167153

168154
return nil

language/lexer.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package language
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strings"
7+
)
8+
9+
// TokenType represents the type of a token
10+
type TokenType string
11+
12+
const (
13+
TokenField TokenType = "FIELD"
14+
TokenCondition TokenType = "CONDITION"
15+
TokenOperator TokenType = "OPERATOR"
16+
TokenValue TokenType = "VALUE"
17+
TokenLogical TokenType = "LOGICAL"
18+
TokenSeparator TokenType = "SEPARATOR"
19+
TokenTransform TokenType = "TRANSFORM"
20+
TokenInvalid TokenType = "INVALID"
21+
)
22+
23+
// Token represents a single token
24+
type Token struct {
25+
Type TokenType
26+
Value string
27+
}
28+
29+
// Lexer for parsing rules
30+
type Lexer struct {
31+
input string
32+
pos int
33+
}
34+
35+
// NewLexer initializes a lexer with the input string
36+
func NewLexer(input string) *Lexer {
37+
return &Lexer{
38+
input: strings.TrimSpace(input),
39+
pos: 0,
40+
}
41+
}
42+
43+
// Tokenize splits the input into tokens
44+
func (l *Lexer) Tokenize(input string) ([]Token, error) {
45+
var tokens []Token
46+
pos := 0
47+
patterns := map[TokenType]*regexp.Regexp{
48+
TokenField: regexp.MustCompile(`^FIELD\("([^"]+)"\)`), // Match FIELD("field_name")
49+
TokenCondition: regexp.MustCompile(`^(TYPE|RANGE|MATCHES|IN|REQUIRED)`), // Custom conditions
50+
TokenValue: regexp.MustCompile(`^"([^"]*)"|'([^']*)'|[\d\.]+|\([^)]*\)`), // Match strings, numbers, lists
51+
TokenLogical: regexp.MustCompile(`^(AND|OR|NOT)`), // Logical operators
52+
TokenSeparator: regexp.MustCompile(`^,`), // Separators
53+
}
54+
55+
for pos < len(input) {
56+
input = strings.TrimSpace(input[pos:])
57+
pos = 0
58+
59+
matched := false
60+
for tokenType, pattern := range patterns {
61+
if loc := pattern.FindStringIndex(input); loc != nil && loc[0] == 0 {
62+
value := input[loc[0]:loc[1]]
63+
tokens = append(tokens, Token{Type: tokenType, Value: value})
64+
pos += len(value)
65+
matched = true
66+
break
67+
}
68+
}
69+
70+
if !matched {
71+
return nil, fmt.Errorf("unexpected token at: %s", input)
72+
}
73+
}
74+
75+
return tokens, nil
76+
}

language/parser.go

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package language
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
// Node represents a node in the Abstract Syntax Tree (AST)
9+
type Node struct {
10+
Type TokenType
11+
Value string
12+
Children []*Node
13+
}
14+
15+
// Parser for validation and transformation rules
16+
type Parser struct{}
17+
18+
// NewParser initializes a parser
19+
func NewParser() *Parser {
20+
return &Parser{}
21+
}
22+
23+
func (p *Parser) ParseRules(tokens []Token) (*Node, error) {
24+
if len(tokens) < 3 {
25+
return nil, errors.New("insufficient parameters")
26+
}
27+
28+
root := &Node{Type: "ROOT", Children: []*Node{}}
29+
var currentField string
30+
31+
for i := 0; i < len(tokens); i++ {
32+
token := tokens[i]
33+
34+
if token.Type == "FIELD" {
35+
// Set the current field and continue to the next token
36+
currentField = token.Value
37+
} else if token.Type == "CONDITION" {
38+
// Ensure there is a following value
39+
if i+1 >= len(tokens) {
40+
return nil, errors.New("expected value after condition")
41+
}
42+
43+
condition := token
44+
value := tokens[i+1] // Next token is the value
45+
46+
node := &Node{Type: "EXPRESSION", Children: []*Node{
47+
{Type: "FIELD", Value: currentField},
48+
{Type: "CONDITION", Value: condition.Value},
49+
{Type: "VALUE", Value: value.Value},
50+
}}
51+
52+
root.Children = append(root.Children, node)
53+
54+
// Move past the value token
55+
i++
56+
} else {
57+
return nil, fmt.Errorf("unexpected token: %s", token.Value)
58+
}
59+
}
60+
61+
return root, nil
62+
}

main.go

+3
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,8 @@ func mapConfigToRequest(config map[string]interface{}) interfaces.Request {
229229
SFTPPassword: getStringField(config, "password", ""),
230230
WebSocketSourceURL: getStringField(config, "url", ""),
231231
WebSocketDestURL: getStringField(config, "url", ""),
232+
CredentialFileAddr: getStringField(config, "credentialfileaddr", ""),
233+
Collection: getStringField(config, "collection", ""),
234+
Document: getStringField(config, "document", ""),
232235
}
233236
}

0 commit comments

Comments
 (0)