refactor: Update ARR body parsing logic
The code changes in `pattern_test.go` update the ARR body parsing logic. The message body parsing now correctly handles ARR bodies with a different pattern format. This ensures accurate extraction of data based on patterns and improves the overall functionality of the code.
This commit is contained in:
@@ -3,12 +3,14 @@ package main
|
||||
import (
|
||||
"caatsm/internal/config"
|
||||
"caatsm/internal/nats"
|
||||
"caatsm/pkg/utils"
|
||||
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg, err := config.LoadConfig()
|
||||
log := utils.GetLogger()
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading configuration: %v\n", err)
|
||||
return
|
||||
@@ -21,6 +23,7 @@ func main() {
|
||||
|
||||
fmt.Println("Loaded configuration successfully")
|
||||
|
||||
log.Info("Starting nats subscriber")
|
||||
subscribe := nats.NewNatsHandler(cfg)
|
||||
subscribe.Subscribe()
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
[nats]
|
||||
url = "nats://localhost:4222"
|
||||
cluster_id = "test-cluster"
|
||||
client_id = "test-client"
|
||||
|
||||
[subscription]
|
||||
topic = "example-topic"
|
||||
queue_group = "example-group"
|
||||
|
||||
[timeouts]
|
||||
server_timeout = "30s"
|
||||
reconnect_wait = "10s"
|
||||
close_timeout = "10s"
|
||||
ack_wait_timeout = "5s"
|
||||
|
||||
[[body]]
|
||||
name = "FPL"
|
||||
[[body.patterns]]
|
||||
pattern = "^\\((?P<type>[A-Z]{3})\\-(?P<number>[A-Z]+\\d+)\\-(?P<indicator>[A-Z]{2})(?:.*\\s*)?\\-(?P<aircraft>[A-Z]+\\d+/?[A-Z]?)\\s*\\-(?P<surve>.*)\\s*\\-(?P<departure>[A-Z]{4})(?P<departure_time>\\d{4})\\s*\\-(?P<speed>[A-Z]+\\d+)(?P<level>[A-Z0-9]+)\\s(?P<route>.*)\\s*\\-(?P<destination>[A-Z]{4})(?P<estt>\\d{4})\\s(?P<alter>[A-Z]{4})\\s*\\-(?P<pbn>PBN\\/[A-Z0-9]+)\\s(?P<nav>NAV\\/\\w+)\\sREG\\/(?P<reg>[A-Z0-9]+)\\sEET\\/(?P<eet>\\w{4}\\d{4})\\sSEL\\/(?P<sel>\\w+)\\sPER\\/(?P<performance>\\w)\\sRIF\\/(?P<rif>\\w+\\s[A-Z0-9]+\\s[A-Z]+)\\s*RMK\\/(?P<remark>.*)\\)$"
|
||||
comments = "FPL multi line expression"
|
||||
|
||||
|
||||
|
||||
# [[body]]
|
||||
# name = "ARR"
|
||||
# patterns = [
|
||||
# {
|
||||
# pattern = '^\((?P<type>[A-Z]{3})\-(?P<number>[A-Z]+\d+)\-(?P<indicator>[A-Z]{2})(?:.*\s*)?\-(?P<aircraft>[A-Z]+\d+/?[A-Z]?)\s*\-(?P<surve>.*)\s*\-(?P<departure>[A-Z]{4})(?P<departure_time>\d{4})\s*\-(?P<speed>[A-Z]+\d+)(?P<level>[A-Z0-9]+)\s(?P<route>.*)\s*\-(?P<destination>[A-Z]{4})(?P<estt>\d{4})\s(?P<alter>[A-Z]{4})\s*\-(?P<pbn>PBN\/[A-Z0-9]+)\s(?P<nav>NAV\/\w+)\sREG\/(?P<reg>[A-Z0-9]+)\sEET\/(?P<eet>\w{4}\d{4})\sSEL\/(?P<sel>\w+)\sPER\/(?P<performance>\w)\sRIF\/(?P<rif>\w+\s[A-Z0-9]+\s[A-Z]+)\s*RMK\/(?P<remark>.*)\)$',
|
||||
# comments = "one line arrival expression"
|
||||
# }
|
||||
# ]
|
||||
|
||||
# [[body]]
|
||||
# name = "DEP"
|
||||
# patterns = [
|
||||
# {
|
||||
# pattern = '^\((?P<type>[A-Z]{3})\-(?P<number>([A-Z]+\d+))\/?(?P<ssr>[A-Z]+\d+)\-(?P<departure>[A-Z]{4})(?P<departure_time>\d{4})\-(?P<destination>[A-Z]{4})(?P<destination_time>\d{4})?\-?(?P<alter>[A-Z]{4})?\-?(?P<other>.*)?\)$',
|
||||
# comments = "one line departure expression"
|
||||
# }
|
||||
# ]
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"caatsm/pkg/utils"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -9,9 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
var MyConfig *Config
|
||||
@@ -56,21 +52,6 @@ type HasuraConfig struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
// LoggerConfig represents the configuration for the logger.
|
||||
type LoggerConfig struct {
|
||||
ZapConfig zap.Config `json:"zapConfig"`
|
||||
LumberjackConfig LumberjackConfig `json:"lumberjackConfig"`
|
||||
}
|
||||
|
||||
// LumberjackConfig represents the configuration for lumberjack logging.
|
||||
type LumberjackConfig struct {
|
||||
Filename string `json:"filename"`
|
||||
MaxSize int `json:"maxSize"`
|
||||
MaxBackups int `json:"maxBackups"`
|
||||
MaxAge int `json:"maxAge"`
|
||||
Compress bool `json:"compress"`
|
||||
}
|
||||
|
||||
const (
|
||||
EnvProd = "prod"
|
||||
// logConfigFile = "configs/log_config.json"
|
||||
@@ -212,6 +193,7 @@ func LoadConfig() (*Config, error) {
|
||||
// loadLoggerConfig()
|
||||
|
||||
// log.Debugf("Config loaded: %+v", config)
|
||||
// loadLoggerConfig()
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
@@ -231,83 +213,3 @@ func ValidateConfig(cfg *Config) error {
|
||||
// fmt.Println("config validation passed")
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadLoggerConfig() {
|
||||
// log := utils.Logger
|
||||
env := os.Getenv("GO_ENV")
|
||||
if env == "" {
|
||||
env = "dev"
|
||||
}
|
||||
// log.Infof("Environment: %s", env)
|
||||
|
||||
viper.SetConfigType("json")
|
||||
viper.SetConfigName("logger." + env)
|
||||
viper.AddConfigPath("configs")
|
||||
// viper.SetEnvPrefix("tele")
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
errMsg := fmt.Sprintf("error reading logger config file for environment '%s': %v", env, err)
|
||||
// log.Error(errMsg)
|
||||
panic(errMsg)
|
||||
}
|
||||
|
||||
// log.Debug("Logger config file read successfully")
|
||||
// log.Debugf("Logger config keys: %v", viper.AllKeys())
|
||||
|
||||
var config LoggerConfig
|
||||
if err := viper.Unmarshal(&config); err != nil {
|
||||
errMsg := fmt.Sprintf("unable to decode logger config into struct for environment '%s': %v", env, err)
|
||||
// log.Error(errMsg)
|
||||
// return nil, fmt.Errorf(errMsg)
|
||||
panic(errMsg)
|
||||
}
|
||||
var logWriter zapcore.WriteSyncer
|
||||
if env == EnvProd {
|
||||
logWriter = zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: config.LumberjackConfig.Filename,
|
||||
MaxSize: config.LumberjackConfig.MaxSize,
|
||||
MaxBackups: config.LumberjackConfig.MaxBackups,
|
||||
MaxAge: config.LumberjackConfig.MaxAge,
|
||||
Compress: config.LumberjackConfig.Compress,
|
||||
})
|
||||
} else {
|
||||
logWriter = zapcore.AddSync(os.Stdout)
|
||||
}
|
||||
|
||||
encoder := zapcore.NewJSONEncoder(config.ZapConfig.EncoderConfig)
|
||||
level := parseLogLevel(config.ZapConfig.Level.String())
|
||||
|
||||
core := zapcore.NewCore(
|
||||
encoder,
|
||||
logWriter,
|
||||
level,
|
||||
)
|
||||
|
||||
log := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
utils.Logger = log.Sugar()
|
||||
// log.Debugf("Logger config loaded: %+v", config)
|
||||
|
||||
}
|
||||
|
||||
// parseLogLevel converts the log level string to zapcore.Level.
|
||||
func parseLogLevel(level string) zapcore.Level {
|
||||
switch level {
|
||||
case "debug":
|
||||
return zapcore.DebugLevel
|
||||
case "info":
|
||||
return zapcore.InfoLevel
|
||||
case "warn":
|
||||
return zapcore.WarnLevel
|
||||
case "error":
|
||||
return zapcore.ErrorLevel
|
||||
case "dpanic":
|
||||
return zapcore.DPanicLevel
|
||||
case "panic":
|
||||
return zapcore.PanicLevel
|
||||
case "fatal":
|
||||
return zapcore.FatalLevel
|
||||
default:
|
||||
return zapcore.InfoLevel
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,12 @@ type TimeReceiver struct {
|
||||
}
|
||||
|
||||
func (h *SITAHeader) Validate() error {
|
||||
log := utils.GetLogger()
|
||||
// Validate SendTime format (e.g., DDHHMM)
|
||||
if len(h.SendTime) != 6 {
|
||||
err := "invalid send_time format"
|
||||
|
||||
utils.Logger.Error(err)
|
||||
log.Errorf("error validating send_time: %v", err)
|
||||
return fmt.Errorf(err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"caatsm/internal/domain"
|
||||
"caatsm/internal/parsers"
|
||||
"caatsm/internal/repository"
|
||||
"caatsm/pkg/utils"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -62,9 +63,10 @@ func (n *NatsHandler) Subscribe() {
|
||||
}
|
||||
}
|
||||
func (n *NatsHandler) handleMessage(msg *message.Message) error {
|
||||
// log := utils.Logger
|
||||
log := utils.GetSugaredLogger()
|
||||
if msg.Payload == nil {
|
||||
fmt.Println("empty message")
|
||||
// fmt.Println("empty message")
|
||||
log.Error("empty message")
|
||||
return fmt.Errorf("empty message")
|
||||
}
|
||||
payload := string(msg.Payload)
|
||||
@@ -72,11 +74,13 @@ func (n *NatsHandler) handleMessage(msg *message.Message) error {
|
||||
var parsed *domain.ParsedMessage
|
||||
if parsed, err = parsers.Parse(payload); err != nil {
|
||||
|
||||
fmt.Printf("not parsed: [%s] : {%s} - %v\n", msg.UUID, msg.Payload, err)
|
||||
// fmt.Printf("not parsed: [%s] : {%s} - %v\n", msg.UUID, msg.Payload, err)
|
||||
log.Infof("not parsed: [%s] : {%s} - %v\n", msg.UUID, msg.Payload, err)
|
||||
// return err
|
||||
} else {
|
||||
|
||||
fmt.Printf("parsed [%s]: %v\n", msg.UUID, parsed)
|
||||
// fmt.Printf("parsed [%s]: %v\n", msg.UUID, parsed)
|
||||
log.Infof("parsed [%s]: %v\n", msg.UUID, parsed)
|
||||
|
||||
}
|
||||
parsed.Uuid = msg.UUID
|
||||
|
||||
@@ -3,10 +3,10 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"caatsm/internal/domain"
|
||||
"caatsm/pkg/utils"
|
||||
|
||||
"github.com/Khan/genqlient/graphql"
|
||||
"github.com/google/uuid"
|
||||
@@ -30,6 +30,7 @@ func NewHasuraRepo(endpoint, secret string) *HasuraRepository {
|
||||
|
||||
// InsertParsedMessage inserts a new ParsedMessage into the Hasura GraphQL API
|
||||
func (hr *HasuraRepository) CreateNew(pm *domain.ParsedMessage) error {
|
||||
log := utils.GetSugaredLogger()
|
||||
bodyString, _ := json.Marshal(pm.BodyData)
|
||||
secondAddress, _ := json.Marshal(pm.SecondaryAddresses)
|
||||
variables := Aviation_telegrams_insert_input{
|
||||
@@ -51,6 +52,7 @@ func (hr *HasuraRepository) CreateNew(pm *domain.ParsedMessage) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Inserted new message: %v\n", resp)
|
||||
// fmt.Printf("Inserted new message: %v\n", resp)
|
||||
log.Infof("Saved : %v\n", resp)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
CREATE SCHEMA IF NOT EXISTS aviation;
|
||||
|
||||
CREATE TABLE aviation.telegrams (
|
||||
uuid UUID PRIMARY KEY,
|
||||
message_id VARCHAR(255) ,
|
||||
date_time VARCHAR(255) ,
|
||||
priority_indicator VARCHAR(255) ,
|
||||
primary_address VARCHAR(255) ,
|
||||
secondary_addresses TEXT[], -- Array of strings
|
||||
originator VARCHAR(255),
|
||||
originator_date_time VARCHAR(255),
|
||||
category VARCHAR(255),
|
||||
body_and_footer TEXT,
|
||||
body_data JSONB, -- JSONB to store parsed body data
|
||||
received_at TIMESTAMP NOT NULL,
|
||||
parsed_at TIMESTAMP,
|
||||
dispatched_at TIMESTAMP,
|
||||
need_dispatch BOOLEAN
|
||||
);
|
||||
|
||||
-- Indexes for better query performance
|
||||
CREATE INDEX idx_telegrams_message_id ON aviation.telegrams (message_id);
|
||||
CREATE INDEX idx_telegrams_date_time ON aviation.telegrams (date_time);
|
||||
CREATE INDEX idx_telegrams_priority_indicator ON aviation.telegrams (priority_indicator);
|
||||
CREATE INDEX idx_telegrams_primary_address ON aviation.telegrams (primary_address);
|
||||
CREATE INDEX idx_telegrams_received_at ON aviation.telegrams (received_at);
|
||||
+152
-1
@@ -1,7 +1,158 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
var Logger *zap.SugaredLogger
|
||||
const (
|
||||
TestConfigFileName = "./configs/logger.test.json"
|
||||
ProdConfigFileName = ".configs/logger.json"
|
||||
DevelopmentConfigFileName = "./configs/logger.dev.json"
|
||||
EnvTest = "test"
|
||||
EnvProd = "prod"
|
||||
EnvDev = "dev"
|
||||
)
|
||||
|
||||
// LoggerConfig represents the configuration for the logger.
|
||||
type LoggerConfig struct {
|
||||
ZapConfig zap.Config `json:"zapConfig"`
|
||||
LumberjackConfig LumberjackConfig `json:"lumberjackConfig"`
|
||||
}
|
||||
|
||||
// LumberjackConfig represents the configuration for lumberjack logging.
|
||||
type LumberjackConfig struct {
|
||||
Filename string `json:"filename"`
|
||||
MaxSize int `json:"maxSize"`
|
||||
MaxBackups int `json:"maxBackups"`
|
||||
MaxAge int `json:"maxAge"`
|
||||
Compress bool `json:"compress"`
|
||||
}
|
||||
|
||||
var (
|
||||
sugar *zap.SugaredLogger
|
||||
log *zap.Logger
|
||||
// once sync.Once
|
||||
)
|
||||
|
||||
// load initializes the logger. It ensures that the logger is initialized only once.
|
||||
func load() {
|
||||
if log == nil {
|
||||
env := getEnv()
|
||||
fmt.Printf("Enviroment : %s\n", env)
|
||||
configFile := getConfigFile(env)
|
||||
// fmt.Printf("Config File : %s\n", configFile)
|
||||
// if err != nil {
|
||||
// fmt.Printf("Error finding config file: %v\n", err)
|
||||
// return
|
||||
// }
|
||||
// fmt.Printf("Loading config from file: %s\n", configFile)
|
||||
config, err := loadConfig(configFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Error loading config: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var logWriter zapcore.WriteSyncer
|
||||
if env == EnvProd {
|
||||
logWriter = zapcore.AddSync(&lumberjack.Logger{
|
||||
Filename: config.LumberjackConfig.Filename,
|
||||
MaxSize: config.LumberjackConfig.MaxSize,
|
||||
MaxBackups: config.LumberjackConfig.MaxBackups,
|
||||
MaxAge: config.LumberjackConfig.MaxAge,
|
||||
Compress: config.LumberjackConfig.Compress,
|
||||
})
|
||||
} else {
|
||||
logWriter = zapcore.AddSync(os.Stdout)
|
||||
}
|
||||
|
||||
encoder := zapcore.NewJSONEncoder(config.ZapConfig.EncoderConfig)
|
||||
level := parseLogLevel(config.ZapConfig.Level.String())
|
||||
|
||||
core := zapcore.NewCore(
|
||||
encoder,
|
||||
logWriter,
|
||||
level,
|
||||
)
|
||||
|
||||
log = zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
|
||||
sugar = log.Sugar()
|
||||
// fmt.Println("Logger initialized")
|
||||
}
|
||||
}
|
||||
|
||||
func GetSugaredLogger() *zap.SugaredLogger {
|
||||
load()
|
||||
return log.Sugar()
|
||||
}
|
||||
|
||||
// getEnv retrieves the logging environment from the TELE_MODE environment variable.
|
||||
func getEnv() string {
|
||||
env := os.Getenv("TELE_MODE")
|
||||
if env == "" {
|
||||
env = EnvDev
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// getConfigFile determines the configuration file path based on the environment.
|
||||
func getConfigFile(env string) string {
|
||||
switch env {
|
||||
case EnvTest:
|
||||
return TestConfigFileName
|
||||
case EnvProd:
|
||||
return ProdConfigFileName
|
||||
default:
|
||||
return DevelopmentConfigFileName
|
||||
}
|
||||
}
|
||||
|
||||
// loadConfig loads the logger configuration from the given file.
|
||||
func loadConfig(configFile string) (LoggerConfig, error) {
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return LoggerConfig{}, fmt.Errorf("error opening file: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var config LoggerConfig
|
||||
if err := json.NewDecoder(file).Decode(&config); err != nil {
|
||||
return LoggerConfig{}, fmt.Errorf("error decoding config: %v", err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// GetLogger returns the initialized SugaredLogger instance.
|
||||
func GetLogger() *zap.SugaredLogger {
|
||||
if sugar == nil {
|
||||
load()
|
||||
}
|
||||
return sugar
|
||||
}
|
||||
|
||||
// parseLogLevel converts the log level string to zapcore.Level.
|
||||
func parseLogLevel(level string) zapcore.Level {
|
||||
switch level {
|
||||
case "debug":
|
||||
return zapcore.DebugLevel
|
||||
case "info":
|
||||
return zapcore.InfoLevel
|
||||
case "warn":
|
||||
return zapcore.WarnLevel
|
||||
case "error":
|
||||
return zapcore.ErrorLevel
|
||||
case "dpanic":
|
||||
return zapcore.DPanicLevel
|
||||
case "panic":
|
||||
return zapcore.PanicLevel
|
||||
case "fatal":
|
||||
return zapcore.FatalLevel
|
||||
default:
|
||||
return zapcore.InfoLevel
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user