Skip to content

Commit

Permalink
WIP: Added interfaces for databases and implemented logger using slog
Browse files Browse the repository at this point in the history
  • Loading branch information
shashank-priyadarshi committed Feb 15, 2024
1 parent f83c124 commit 5f4fffc
Show file tree
Hide file tree
Showing 40 changed files with 1,236 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,59 @@
# utilities

This repository contains common libraries for my projects.

This repository provides the following packages:

- [Logger](#logger)
- [Database](#database)
- [Network](#network)
- [Security](#security)

## Logger

```
type Logger interface {
Info(string, ...interface{})
Warn(string, ...interface{})
Error(error, ...interface{})
Fatal(error, ...interface{})
Debug(string, ...interface{})
With(key string, args ...interface{})
}
```

## Database

```
type Database interface {
Operations
Transactions
Closer
Configure
}
type Operations interface {
Create(context.Context, ...interface{}) (models.Response, error)
Query(context.Context, ...interface{}) (models.Response, error)
Update(context.Context, ...interface{}) (models.Response, error)
Delete(context.Context, ...interface{}) (models.Response, error)
}
type Transactions interface {
Begin(context.Context, ...interface{}) (models.Response, error)
Execute(context.Context, ...interface{}) (models.Response, error)
Rollback(context.Context, ...interface{}) (models.Response, error)
}
type Closer interface {
Close() error
}
type Configure interface {
Configure(context.Context, ...interface{}) error
}
```

## Network

## Security
25 changes: 25 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# TODO

- Database

- Replace placeholder code of Database interface implementation with actual implementation
- Integration tests

- Logger

- File logging
- Log rotation
- Log forwarding
- Tinted logs
- Integration tests

- Network

- Add correct segregation of Protocols, REST and RPCs
- Add interfaces

- Security

- Add correct segregation of Basic and Bearer authentication, JWT, SAML, Cookie and Session based authentication, API keys
and OAuth
- Add interfaces
29 changes: 29 additions & 0 deletions database/constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package constants

type Type string

type Database Type

const (
MONGODB Database = "mongodb"
REDIS Database = "redis"
MYSQLDB Database = "mysql"
)

type RDBMS Database

type Driver RDBMS

const (
MYSQL Driver = "mysql"
POSTGRES Driver = "postgres"
SQLITE Driver = "sqlite"
VITESS Driver = "vitess"
COCKROACHDB Driver = "cockroachdb"
)

type ORM RDBMS

const (
GORM ORM = "gorm"
)
22 changes: 22 additions & 0 deletions database/constants/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package constants

import "errors"

type Error Type

func (t Error) Error() *error {
err := errors.New(*t.String())
return &err
}

func (t Error) String() *string {
str := string(t)
return &str
}

type ErrorFormat Error

var (
CANNOTBEEMTPY ErrorFormat = "%v cannot be empty"
UNSUPPORTED ErrorFormat = "unsupported %v type"
)
49 changes: 49 additions & 0 deletions database/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package database

import (
"context"
"fmt"
"github.com/shashank-priyadarshi/utilities/database/constants"
adapters "github.com/shashank-priyadarshi/utilities/database/internal"
"github.com/shashank-priyadarshi/utilities/database/models"
"github.com/shashank-priyadarshi/utilities/database/ports"
"github.com/shashank-priyadarshi/utilities/logger"
loggerPort "github.com/shashank-priyadarshi/utilities/logger/ports"
)

func NewDatabase(ctx context.Context, log loggerPort.Logger, config models.Config) (database ports.Database, err error) {

if !isSupported(config.Type) {
err = fmt.Errorf("unsupported database type: %s", config.Type)
return
}

if log == nil {
err = fmt.Errorf("uninitialized logger, initializing module logger")
log.Warn("%v", err)

log, err = logger.NewLogger()
log.With("module", "database")

if err != nil {
err = fmt.Errorf("error initializing new logger: %v", err)
return
}
}

return adapters.NewDatabaseAdapter(ctx, log, &config)
}

func isSupported(db constants.Database) bool {

var supported = make(map[constants.Database]any)
supported[constants.MYSQLDB] = nil
supported[constants.MONGODB] = nil
supported[constants.REDIS] = nil

if _, ok := supported[db]; !ok {
return false
}

return true
}
52 changes: 52 additions & 0 deletions database/internal/adapters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package adapters

import (
"context"
"fmt"
"github.com/shashank-priyadarshi/utilities/database/constants"
"github.com/shashank-priyadarshi/utilities/database/internal/connections"
"github.com/shashank-priyadarshi/utilities/database/internal/mongodb"
"github.com/shashank-priyadarshi/utilities/database/internal/rdbms"
"github.com/shashank-priyadarshi/utilities/database/internal/redis"
"github.com/shashank-priyadarshi/utilities/database/models"
"github.com/shashank-priyadarshi/utilities/database/ports"
ports2 "github.com/shashank-priyadarshi/utilities/logger/ports"
)

func NewDatabaseAdapter(ctx context.Context, log ports2.Logger, config *models.Config) (handle ports.Database, err error) {

switch config.Type {
case constants.MONGODB:
client, err := connections.NewMongoDBClient(ctx, log, config)
if err != nil {
return nil, fmt.Errorf("error connecting to mongo db: %w", err)
}

handle = mongodb.NewMongoDBHandle(log, client)

case constants.MYSQLDB:
client, err := connections.NewRDBMSClient(ctx, log, config)
if err != nil {
return nil, fmt.Errorf("error connecting to rdbms: %w", err)
}

handle, err = rdbms.NewRelationalDBHandle(log, config.Options.WithORM, config.Options.ORM, client)
if err != nil {
return nil, fmt.Errorf("error creating relational db handle: %w", err)
}

case constants.REDIS:
client, err := connections.NewRedisClient(ctx, log, config)
if err != nil {
return nil, fmt.Errorf("error connecting to redis: %w", err)
}

handle = redis.NewRedisHandle(log, client)

default:
return nil, fmt.Errorf("database type %s is not supported", config.Type)

}

return
}
41 changes: 41 additions & 0 deletions database/internal/connections/mongodb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package connections

import (
"context"
"fmt"
"github.com/shashank-priyadarshi/utilities/database/models"
"github.com/shashank-priyadarshi/utilities/logger/ports"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
_ "go.mongodb.org/mongo-driver/mongo/options"
)

var opts *options.ClientOptions

func NewMongoDBClient(ctx context.Context, log ports.Logger, config *models.Config) (client *mongo.Client, err error) {

if len(config.Options.URI) == 0 {
err = fmt.Errorf("mongo db uri cannot be empty")
log.Error(err)
return
}

opts.ApplyURI(config.Options.URI)

// Backoff connection logic
if client, err = mongo.Connect(ctx, opts); err != nil {
err = fmt.Errorf("error connecting to database: %v", err)
log.Error(err)
return
}

if err = client.Ping(ctx, nil); err != nil {
err = fmt.Errorf("error pinging mongo db on established connection: %v", err)
log.Error(err)
// Backoff disconnection logic
client.Disconnect(ctx)
return
}

return
}
98 changes: 98 additions & 0 deletions database/internal/connections/rdbms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package connections

import (
"context"
"database/sql"
"fmt"
"github.com/glebarez/sqlite"
"github.com/shashank-priyadarshi/utilities/database/constants"
"github.com/shashank-priyadarshi/utilities/database/models"
"github.com/shashank-priyadarshi/utilities/logger/ports"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)

func NewRDBMSClient(ctx context.Context, log ports.Logger, config *models.Config) (client interface{}, err error) {

if !isSupported(config.Options.Driver) {
err = fmt.Errorf("unsupported sql driver")
log.Error(err)
return
}

conn, err := sql.Open(string(config.Options.Driver), "")
if err != nil {
err = fmt.Errorf("error connecting to database: %v", err)
log.Error(err)
return
}

client = conn

switch config.Options.WithORM {
case true:
switch config.Options.ORM {
case constants.GORM:
if gormDB, err := createGORMConnection(config.Options.Driver, conn); err != nil {
err = fmt.Errorf("error creating gorm connection: %v", err)
return
} else {
client = gormDB
}
return

default:
err = fmt.Errorf("unsupported orm type")
log.Error(err)
return
}

default:
log.Info("no orm configured")
return
}
}

func isSupported(driver constants.Driver) bool {

var supported = make(map[constants.Driver]any)

supported[constants.VITESS] = nil
supported[constants.COCKROACHDB] = nil

// GORM supported
supported[constants.MYSQL] = nil
supported[constants.POSTGRES] = nil
supported[constants.SQLITE] = nil

if _, ok := supported[driver]; !ok {
return false
}

return true
}

func createGORMConnection(driver constants.Driver, conn *sql.DB) (db *gorm.DB, err error) {

switch driver {
case constants.MYSQL:
db, err = gorm.Open(mysql.New(mysql.Config{Conn: conn}), &gorm.Config{})
return

case constants.POSTGRES:
db, err = gorm.Open(postgres.New(postgres.Config{
Conn: conn,
}), &gorm.Config{})
return

case constants.SQLITE:
db, err = gorm.Open(sqlite.Open("sqlite.db"), &gorm.Config{})
return

default:
err = fmt.Errorf("unsupported gorm connection for the driver type %s", driver)
}

return
}
Loading

0 comments on commit 5f4fffc

Please sign in to comment.