diff --git a/cmd/.gitkeep b/cmd/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..f165de3 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "context" + + "git.dragse.it/anthrove/plug-template/config" + "git.dragse.it/anthrove/plug-template/internal/database/graph" + "git.dragse.it/anthrove/plug-template/internal/utils" + log "github.com/sirupsen/logrus" +) + +// cfg holds the orchestrator configuration +var cfg config.PlugConfig + +// init is used to load the orchestrator configuration +func init() { + // Load the orchestrator configuration + localCfg, err := config.LoadConfig(cfg) + if err != nil { + log.Panic(err) + } + cfg = localCfg +} + +func main() { + // ctx is the root context for the main function + var ctx = context.Background() + + // Set up the logger based on the configuration + utils.SetupLogger(cfg.LogLevel, cfg.LogFormat) + + // Connect to the graph database + _, err := graph.ConnectToGraphDatabase(ctx) + if err != nil { + log.Panicf("main: %v", err) + } +} diff --git a/config/.gitkeep b/config/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..7bcdcc7 --- /dev/null +++ b/config/config.go @@ -0,0 +1,36 @@ +package config + +import ( + "fmt" + + "github.com/caarlos0/env/v10" + "github.com/go-playground/validator/v10" +) + +type PlugConfig struct { + LogLevel string `validate:"required,eq=FATAL|eq=ERROR|eq=WARN|eq=INFO|eq=DEBUG|eq=TRACE" env:"LOG_LEVEL" envDefault:"INFO" json:"log_level"` + LogFormat string `validate:"required,eq=PLAIN|eq=JSON" env:"LOG_FORMAT" envDefault:"PLAIN" json:"log_format"` +} + +type OtterSpaceConfig struct { + Type string `env:"DB_TYPE,required"` + Endpoint string `env:"DB_URL,required"` + Username string `env:"DB_USERNAME,required"` + Password string `env:"DB_PASSWORD,required,unset"` + Debug bool `env:"DB_DEBUG" envDefault:"FALSE"` +} + +// LoadConfig loads the configuration from environment variables and validates it. +func LoadConfig[T any](cfg T) (T, error) { + + if err := env.Parse(&cfg); err != nil { + return cfg, fmt.Errorf("config: error parsing configuration: %w", err) + } + + validate := validator.New() + if err := validate.Struct(cfg); err != nil { + return cfg, fmt.Errorf("config: validation error: %w", err) + } + + return cfg, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0a7048d --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module git.dragse.it/anthrove/plug-template + +go 1.22.0 + +require ( + git.dragse.it/anthrove/otter-space-sdk v1.0.0 + github.com/caarlos0/env/v10 v10.0.0 + github.com/go-playground/validator/v10 v10.18.0 + github.com/sirupsen/logrus v1.9.3 + google.golang.org/grpc v1.61.1 + google.golang.org/protobuf v1.32.0 +) + +require ( + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/neo4j/neo4j-go-driver/v5 v5.17.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..63093bc --- /dev/null +++ b/go.sum @@ -0,0 +1,57 @@ +git.dragse.it/anthrove/otter-space-sdk v1.0.0 h1:sJ5d8yMFAosP9+315PxZc5yzYUdzAJEEyxoKDmc8YoE= +git.dragse.it/anthrove/otter-space-sdk v1.0.0/go.mod h1:QI2W7gm1GflcAeipEoyLZ3hj86DlIgwT9Lisc4c99Vs= +github.com/caarlos0/env/v10 v10.0.0 h1:yIHUBZGsyqCnpTkbjk8asUlx6RFhhEs+h7TOBdgdzXA= +github.com/caarlos0/env/v10 v10.0.0/go.mod h1:ZfulV76NvVPw3tm591U4SwL3Xx9ldzBP9aGxzeN7G18= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/neo4j/neo4j-go-driver/v5 v5.17.0 h1:Bdqg1Y8Hd3uLYToXtBjysDYXTdMiP7zeUNUEwfbJkSo= +github.com/neo4j/neo4j-go-driver/v5 v5.17.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= +google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/.gitkeep b/internal/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/internal/database/graph/connect.go b/internal/database/graph/connect.go new file mode 100644 index 0000000..feffa73 --- /dev/null +++ b/internal/database/graph/connect.go @@ -0,0 +1,47 @@ +package graph + +import ( + "context" + "fmt" + "strings" + + "git.dragse.it/anthrove/otter-space-sdk/pkg/graph" + "git.dragse.it/anthrove/plug-template/config" + + log "github.com/sirupsen/logrus" +) + +var cfg config.OtterSpaceConfig + +func init() { + localCfg, err := config.LoadConfig[config.OtterSpaceConfig](cfg) + if err != nil { + log.Panic(err) + } + cfg = localCfg +} + +// ConnectToGraphDatabase establishes a connection to a graph database specified in the config. +// Currently, it only supports Neo4j. If the database type is not supported, it returns an error. +func ConnectToGraphDatabase(ctx context.Context) (graph.OtterSpace, error) { + var connection graph.OtterSpace + var err error + + switch strings.ToLower(cfg.Type) { + case "neo4j": + connection = graph.NewGraphConnection(cfg.Debug) + err = connection.Connect(ctx, cfg.Endpoint, cfg.Username, cfg.Password) + if err != nil { + return nil, fmt.Errorf("graph: failed to connect to Neo4j: %w", err) + } + default: + return nil, fmt.Errorf("graph: unsupported database type: %s", cfg.Type) + } + + log.WithFields(log.Fields{ + "graph_type": cfg.Type, + "graph_endpoint": cfg.Endpoint, + }, + ).Info("graph: successfully connected") + return connection, nil +} diff --git a/internal/utils/logging.go b/internal/utils/logging.go new file mode 100644 index 0000000..e237f8c --- /dev/null +++ b/internal/utils/logging.go @@ -0,0 +1,60 @@ +package utils + +import ( + "strings" + + log "github.com/sirupsen/logrus" +) + +// SetupLogger configures the logging level and format based on the provided input. +// It supports various log levels and two formats: plain text and JSON. +func SetupLogger(logLevel string, logFormat string) { + setLogLevel(logLevel) + setLogFormat(logFormat) + + log.WithFields(log.Fields{ + "log_level": log.GetLevel(), + "formatter": logFormat, + }).Info("Logger configuration set") +} + +// setLogLevel configures the logging level based on the provided input. +func setLogLevel(logLevel string) { + switch strings.ToUpper(logLevel) { + case "FATAL": + log.SetLevel(log.FatalLevel) + case "ERROR": + log.SetLevel(log.ErrorLevel) + case "WARN": + log.SetLevel(log.WarnLevel) + case "INFO": + log.SetLevel(log.InfoLevel) + case "DEBUG": + log.SetLevel(log.DebugLevel) + case "TRACE": + log.SetLevel(log.TraceLevel) + default: + log.Panic("No valid log level was set") + } +} + +// setLogFormat configures the logging format based on the provided input. +func setLogFormat(logFormat string) { + switch strings.ToUpper(logFormat) { + case "PLAIN": + log.SetFormatter(&log.TextFormatter{ + ForceColors: true, + ForceQuote: true, + DisableLevelTruncation: true, + PadLevelText: true, + FullTimestamp: true, + TimestampFormat: "2006-01-02 15:04:05", + }) + case "JSON": + log.SetFormatter(&log.JSONFormatter{ + TimestampFormat: "2006-01-02 15:04:05", + }) + default: + log.Panic("No valid formatter was set") + } +}