mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-01-02 18:04:25 +00:00
Massive refactor and introduction of restheadspec
This commit is contained in:
343
pkg/common/adapters/database/bun.go
Normal file
343
pkg/common/adapters/database/bun.go
Normal file
@@ -0,0 +1,343 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
// BunAdapter adapts Bun to work with our Database interface
|
||||
// This demonstrates how the abstraction works with different ORMs
|
||||
type BunAdapter struct {
|
||||
db *bun.DB
|
||||
}
|
||||
|
||||
// NewBunAdapter creates a new Bun adapter
|
||||
func NewBunAdapter(db *bun.DB) *BunAdapter {
|
||||
return &BunAdapter{db: db}
|
||||
}
|
||||
|
||||
func (b *BunAdapter) NewSelect() common.SelectQuery {
|
||||
return &BunSelectQuery{query: b.db.NewSelect()}
|
||||
}
|
||||
|
||||
func (b *BunAdapter) NewInsert() common.InsertQuery {
|
||||
return &BunInsertQuery{query: b.db.NewInsert()}
|
||||
}
|
||||
|
||||
func (b *BunAdapter) NewUpdate() common.UpdateQuery {
|
||||
return &BunUpdateQuery{query: b.db.NewUpdate()}
|
||||
}
|
||||
|
||||
func (b *BunAdapter) NewDelete() common.DeleteQuery {
|
||||
return &BunDeleteQuery{query: b.db.NewDelete()}
|
||||
}
|
||||
|
||||
func (b *BunAdapter) Exec(ctx context.Context, query string, args ...interface{}) (common.Result, error) {
|
||||
result, err := b.db.ExecContext(ctx, query, args...)
|
||||
return &BunResult{result: result}, err
|
||||
}
|
||||
|
||||
func (b *BunAdapter) Query(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
|
||||
return b.db.NewRaw(query, args...).Scan(ctx, dest)
|
||||
}
|
||||
|
||||
func (b *BunAdapter) BeginTx(ctx context.Context) (common.Database, error) {
|
||||
tx, err := b.db.BeginTx(ctx, &sql.TxOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// For Bun, we'll return a special wrapper that holds the transaction
|
||||
return &BunTxAdapter{tx: tx}, nil
|
||||
}
|
||||
|
||||
func (b *BunAdapter) CommitTx(ctx context.Context) error {
|
||||
// For Bun, we need to handle this differently
|
||||
// This is a simplified implementation
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BunAdapter) RollbackTx(ctx context.Context) error {
|
||||
// For Bun, we need to handle this differently
|
||||
// This is a simplified implementation
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BunAdapter) RunInTransaction(ctx context.Context, fn func(common.Database) error) error {
|
||||
return b.db.RunInTx(ctx, &sql.TxOptions{}, func(ctx context.Context, tx bun.Tx) error {
|
||||
// Create adapter with transaction
|
||||
adapter := &BunTxAdapter{tx: tx}
|
||||
return fn(adapter)
|
||||
})
|
||||
}
|
||||
|
||||
// BunSelectQuery implements SelectQuery for Bun
|
||||
type BunSelectQuery struct {
|
||||
query *bun.SelectQuery
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Model(model interface{}) common.SelectQuery {
|
||||
b.query = b.query.Model(model)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Table(table string) common.SelectQuery {
|
||||
b.query = b.query.Table(table)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Column(columns ...string) common.SelectQuery {
|
||||
b.query = b.query.Column(columns...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Where(query string, args ...interface{}) common.SelectQuery {
|
||||
b.query = b.query.Where(query, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) WhereOr(query string, args ...interface{}) common.SelectQuery {
|
||||
b.query = b.query.WhereOr(query, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Join(query string, args ...interface{}) common.SelectQuery {
|
||||
b.query = b.query.Join(query, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) LeftJoin(query string, args ...interface{}) common.SelectQuery {
|
||||
b.query = b.query.Join("LEFT JOIN " + query, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Preload(relation string, conditions ...interface{}) common.SelectQuery {
|
||||
// Bun uses Relation() method for preloading
|
||||
// For now, we'll just pass the relation name without conditions
|
||||
// TODO: Implement proper condition handling for Bun
|
||||
b.query = b.query.Relation(relation)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Order(order string) common.SelectQuery {
|
||||
b.query = b.query.Order(order)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Limit(n int) common.SelectQuery {
|
||||
b.query = b.query.Limit(n)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Offset(n int) common.SelectQuery {
|
||||
b.query = b.query.Offset(n)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Group(group string) common.SelectQuery {
|
||||
b.query = b.query.Group(group)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Having(having string, args ...interface{}) common.SelectQuery {
|
||||
b.query = b.query.Having(having, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Scan(ctx context.Context, dest interface{}) error {
|
||||
return b.query.Scan(ctx, dest)
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Count(ctx context.Context) (int, error) {
|
||||
count, err := b.query.Count(ctx)
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (b *BunSelectQuery) Exists(ctx context.Context) (bool, error) {
|
||||
return b.query.Exists(ctx)
|
||||
}
|
||||
|
||||
// BunInsertQuery implements InsertQuery for Bun
|
||||
type BunInsertQuery struct {
|
||||
query *bun.InsertQuery
|
||||
values map[string]interface{}
|
||||
}
|
||||
|
||||
func (b *BunInsertQuery) Model(model interface{}) common.InsertQuery {
|
||||
b.query = b.query.Model(model)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunInsertQuery) Table(table string) common.InsertQuery {
|
||||
b.query = b.query.Table(table)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunInsertQuery) Value(column string, value interface{}) common.InsertQuery {
|
||||
if b.values == nil {
|
||||
b.values = make(map[string]interface{})
|
||||
}
|
||||
b.values[column] = value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunInsertQuery) OnConflict(action string) common.InsertQuery {
|
||||
b.query = b.query.On(action)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunInsertQuery) Returning(columns ...string) common.InsertQuery {
|
||||
if len(columns) > 0 {
|
||||
b.query = b.query.Returning(columns[0])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunInsertQuery) Exec(ctx context.Context) (common.Result, error) {
|
||||
if b.values != nil {
|
||||
// For Bun, we need to handle this differently
|
||||
for k, v := range b.values {
|
||||
b.query = b.query.Set("? = ?", bun.Ident(k), v)
|
||||
}
|
||||
}
|
||||
result, err := b.query.Exec(ctx)
|
||||
return &BunResult{result: result}, err
|
||||
}
|
||||
|
||||
// BunUpdateQuery implements UpdateQuery for Bun
|
||||
type BunUpdateQuery struct {
|
||||
query *bun.UpdateQuery
|
||||
}
|
||||
|
||||
func (b *BunUpdateQuery) Model(model interface{}) common.UpdateQuery {
|
||||
b.query = b.query.Model(model)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunUpdateQuery) Table(table string) common.UpdateQuery {
|
||||
b.query = b.query.Table(table)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunUpdateQuery) Set(column string, value interface{}) common.UpdateQuery {
|
||||
b.query = b.query.Set(column+" = ?", value)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunUpdateQuery) SetMap(values map[string]interface{}) common.UpdateQuery {
|
||||
for column, value := range values {
|
||||
b.query = b.query.Set(column+" = ?", value)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunUpdateQuery) Where(query string, args ...interface{}) common.UpdateQuery {
|
||||
b.query = b.query.Where(query, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunUpdateQuery) Returning(columns ...string) common.UpdateQuery {
|
||||
if len(columns) > 0 {
|
||||
b.query = b.query.Returning(columns[0])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunUpdateQuery) Exec(ctx context.Context) (common.Result, error) {
|
||||
result, err := b.query.Exec(ctx)
|
||||
return &BunResult{result: result}, err
|
||||
}
|
||||
|
||||
// BunDeleteQuery implements DeleteQuery for Bun
|
||||
type BunDeleteQuery struct {
|
||||
query *bun.DeleteQuery
|
||||
}
|
||||
|
||||
func (b *BunDeleteQuery) Model(model interface{}) common.DeleteQuery {
|
||||
b.query = b.query.Model(model)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunDeleteQuery) Table(table string) common.DeleteQuery {
|
||||
b.query = b.query.Table(table)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunDeleteQuery) Where(query string, args ...interface{}) common.DeleteQuery {
|
||||
b.query = b.query.Where(query, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunDeleteQuery) Exec(ctx context.Context) (common.Result, error) {
|
||||
result, err := b.query.Exec(ctx)
|
||||
return &BunResult{result: result}, err
|
||||
}
|
||||
|
||||
// BunResult implements Result for Bun
|
||||
type BunResult struct {
|
||||
result sql.Result
|
||||
}
|
||||
|
||||
func (b *BunResult) RowsAffected() int64 {
|
||||
if b.result == nil {
|
||||
return 0
|
||||
}
|
||||
rows, _ := b.result.RowsAffected()
|
||||
return rows
|
||||
}
|
||||
|
||||
func (b *BunResult) LastInsertId() (int64, error) {
|
||||
if b.result == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return b.result.LastInsertId()
|
||||
}
|
||||
|
||||
// BunTxAdapter wraps a Bun transaction to implement the Database interface
|
||||
type BunTxAdapter struct {
|
||||
tx bun.Tx
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) NewSelect() common.SelectQuery {
|
||||
return &BunSelectQuery{query: b.tx.NewSelect()}
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) NewInsert() common.InsertQuery {
|
||||
return &BunInsertQuery{query: b.tx.NewInsert()}
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) NewUpdate() common.UpdateQuery {
|
||||
return &BunUpdateQuery{query: b.tx.NewUpdate()}
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) NewDelete() common.DeleteQuery {
|
||||
return &BunDeleteQuery{query: b.tx.NewDelete()}
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) Exec(ctx context.Context, query string, args ...interface{}) (common.Result, error) {
|
||||
result, err := b.tx.ExecContext(ctx, query, args...)
|
||||
return &BunResult{result: result}, err
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) Query(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
|
||||
return b.tx.NewRaw(query, args...).Scan(ctx, dest)
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) BeginTx(ctx context.Context) (common.Database, error) {
|
||||
return nil, fmt.Errorf("nested transactions not supported")
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) CommitTx(ctx context.Context) error {
|
||||
return b.tx.Commit()
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) RollbackTx(ctx context.Context) error {
|
||||
return b.tx.Rollback()
|
||||
}
|
||||
|
||||
func (b *BunTxAdapter) RunInTransaction(ctx context.Context, fn func(common.Database) error) error {
|
||||
return fn(b) // Already in transaction
|
||||
}
|
||||
289
pkg/common/adapters/database/gorm.go
Normal file
289
pkg/common/adapters/database/gorm.go
Normal file
@@ -0,0 +1,289 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// GormAdapter adapts GORM to work with our Database interface
|
||||
type GormAdapter struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewGormAdapter creates a new GORM adapter
|
||||
func NewGormAdapter(db *gorm.DB) *GormAdapter {
|
||||
return &GormAdapter{db: db}
|
||||
}
|
||||
|
||||
func (g *GormAdapter) NewSelect() common.SelectQuery {
|
||||
return &GormSelectQuery{db: g.db}
|
||||
}
|
||||
|
||||
func (g *GormAdapter) NewInsert() common.InsertQuery {
|
||||
return &GormInsertQuery{db: g.db}
|
||||
}
|
||||
|
||||
func (g *GormAdapter) NewUpdate() common.UpdateQuery {
|
||||
return &GormUpdateQuery{db: g.db}
|
||||
}
|
||||
|
||||
func (g *GormAdapter) NewDelete() common.DeleteQuery {
|
||||
return &GormDeleteQuery{db: g.db}
|
||||
}
|
||||
|
||||
func (g *GormAdapter) Exec(ctx context.Context, query string, args ...interface{}) (common.Result, error) {
|
||||
result := g.db.WithContext(ctx).Exec(query, args...)
|
||||
return &GormResult{result: result}, result.Error
|
||||
}
|
||||
|
||||
func (g *GormAdapter) Query(ctx context.Context, dest interface{}, query string, args ...interface{}) error {
|
||||
return g.db.WithContext(ctx).Raw(query, args...).Find(dest).Error
|
||||
}
|
||||
|
||||
func (g *GormAdapter) BeginTx(ctx context.Context) (common.Database, error) {
|
||||
tx := g.db.WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
return &GormAdapter{db: tx}, nil
|
||||
}
|
||||
|
||||
func (g *GormAdapter) CommitTx(ctx context.Context) error {
|
||||
return g.db.WithContext(ctx).Commit().Error
|
||||
}
|
||||
|
||||
func (g *GormAdapter) RollbackTx(ctx context.Context) error {
|
||||
return g.db.WithContext(ctx).Rollback().Error
|
||||
}
|
||||
|
||||
func (g *GormAdapter) RunInTransaction(ctx context.Context, fn func(common.Database) error) error {
|
||||
return g.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
adapter := &GormAdapter{db: tx}
|
||||
return fn(adapter)
|
||||
})
|
||||
}
|
||||
|
||||
// GormSelectQuery implements SelectQuery for GORM
|
||||
type GormSelectQuery struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Model(model interface{}) common.SelectQuery {
|
||||
g.db = g.db.Model(model)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Table(table string) common.SelectQuery {
|
||||
g.db = g.db.Table(table)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Column(columns ...string) common.SelectQuery {
|
||||
g.db = g.db.Select(columns)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Where(query string, args ...interface{}) common.SelectQuery {
|
||||
g.db = g.db.Where(query, args...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) WhereOr(query string, args ...interface{}) common.SelectQuery {
|
||||
g.db = g.db.Or(query, args...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Join(query string, args ...interface{}) common.SelectQuery {
|
||||
g.db = g.db.Joins(query, args...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) LeftJoin(query string, args ...interface{}) common.SelectQuery {
|
||||
g.db = g.db.Joins("LEFT JOIN "+query, args...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Preload(relation string, conditions ...interface{}) common.SelectQuery {
|
||||
g.db = g.db.Preload(relation, conditions...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Order(order string) common.SelectQuery {
|
||||
g.db = g.db.Order(order)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Limit(n int) common.SelectQuery {
|
||||
g.db = g.db.Limit(n)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Offset(n int) common.SelectQuery {
|
||||
g.db = g.db.Offset(n)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Group(group string) common.SelectQuery {
|
||||
g.db = g.db.Group(group)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Having(having string, args ...interface{}) common.SelectQuery {
|
||||
g.db = g.db.Having(having, args...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Scan(ctx context.Context, dest interface{}) error {
|
||||
return g.db.WithContext(ctx).Find(dest).Error
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Count(ctx context.Context) (int, error) {
|
||||
var count int64
|
||||
err := g.db.WithContext(ctx).Count(&count).Error
|
||||
return int(count), err
|
||||
}
|
||||
|
||||
func (g *GormSelectQuery) Exists(ctx context.Context) (bool, error) {
|
||||
var count int64
|
||||
err := g.db.WithContext(ctx).Limit(1).Count(&count).Error
|
||||
return count > 0, err
|
||||
}
|
||||
|
||||
// GormInsertQuery implements InsertQuery for GORM
|
||||
type GormInsertQuery struct {
|
||||
db *gorm.DB
|
||||
model interface{}
|
||||
values map[string]interface{}
|
||||
}
|
||||
|
||||
func (g *GormInsertQuery) Model(model interface{}) common.InsertQuery {
|
||||
g.model = model
|
||||
g.db = g.db.Model(model)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormInsertQuery) Table(table string) common.InsertQuery {
|
||||
g.db = g.db.Table(table)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormInsertQuery) Value(column string, value interface{}) common.InsertQuery {
|
||||
if g.values == nil {
|
||||
g.values = make(map[string]interface{})
|
||||
}
|
||||
g.values[column] = value
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormInsertQuery) OnConflict(action string) common.InsertQuery {
|
||||
// GORM handles conflicts differently, this would need specific implementation
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormInsertQuery) Returning(columns ...string) common.InsertQuery {
|
||||
// GORM doesn't have explicit RETURNING, but updates the model
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormInsertQuery) Exec(ctx context.Context) (common.Result, error) {
|
||||
var result *gorm.DB
|
||||
if g.model != nil {
|
||||
result = g.db.WithContext(ctx).Create(g.model)
|
||||
} else if g.values != nil {
|
||||
result = g.db.WithContext(ctx).Create(g.values)
|
||||
} else {
|
||||
result = g.db.WithContext(ctx).Create(map[string]interface{}{})
|
||||
}
|
||||
return &GormResult{result: result}, result.Error
|
||||
}
|
||||
|
||||
// GormUpdateQuery implements UpdateQuery for GORM
|
||||
type GormUpdateQuery struct {
|
||||
db *gorm.DB
|
||||
model interface{}
|
||||
updates interface{}
|
||||
}
|
||||
|
||||
func (g *GormUpdateQuery) Model(model interface{}) common.UpdateQuery {
|
||||
g.model = model
|
||||
g.db = g.db.Model(model)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormUpdateQuery) Table(table string) common.UpdateQuery {
|
||||
g.db = g.db.Table(table)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormUpdateQuery) Set(column string, value interface{}) common.UpdateQuery {
|
||||
if g.updates == nil {
|
||||
g.updates = make(map[string]interface{})
|
||||
}
|
||||
if updates, ok := g.updates.(map[string]interface{}); ok {
|
||||
updates[column] = value
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormUpdateQuery) SetMap(values map[string]interface{}) common.UpdateQuery {
|
||||
g.updates = values
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormUpdateQuery) Where(query string, args ...interface{}) common.UpdateQuery {
|
||||
g.db = g.db.Where(query, args...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormUpdateQuery) Returning(columns ...string) common.UpdateQuery {
|
||||
// GORM doesn't have explicit RETURNING
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormUpdateQuery) Exec(ctx context.Context) (common.Result, error) {
|
||||
result := g.db.WithContext(ctx).Updates(g.updates)
|
||||
return &GormResult{result: result}, result.Error
|
||||
}
|
||||
|
||||
// GormDeleteQuery implements DeleteQuery for GORM
|
||||
type GormDeleteQuery struct {
|
||||
db *gorm.DB
|
||||
model interface{}
|
||||
}
|
||||
|
||||
func (g *GormDeleteQuery) Model(model interface{}) common.DeleteQuery {
|
||||
g.model = model
|
||||
g.db = g.db.Model(model)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormDeleteQuery) Table(table string) common.DeleteQuery {
|
||||
g.db = g.db.Table(table)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormDeleteQuery) Where(query string, args ...interface{}) common.DeleteQuery {
|
||||
g.db = g.db.Where(query, args...)
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *GormDeleteQuery) Exec(ctx context.Context) (common.Result, error) {
|
||||
result := g.db.WithContext(ctx).Delete(g.model)
|
||||
return &GormResult{result: result}, result.Error
|
||||
}
|
||||
|
||||
// GormResult implements Result for GORM
|
||||
type GormResult struct {
|
||||
result *gorm.DB
|
||||
}
|
||||
|
||||
func (g *GormResult) RowsAffected() int64 {
|
||||
return g.result.RowsAffected
|
||||
}
|
||||
|
||||
func (g *GormResult) LastInsertId() (int64, error) {
|
||||
// GORM doesn't directly provide last insert ID, would need specific implementation
|
||||
return 0, nil
|
||||
}
|
||||
193
pkg/common/adapters/router/bunrouter.go
Normal file
193
pkg/common/adapters/router/bunrouter.go
Normal file
@@ -0,0 +1,193 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
||||
"github.com/uptrace/bunrouter"
|
||||
)
|
||||
|
||||
// BunRouterAdapter adapts uptrace/bunrouter to work with our Router interface
|
||||
type BunRouterAdapter struct {
|
||||
router *bunrouter.Router
|
||||
}
|
||||
|
||||
// NewBunRouterAdapter creates a new bunrouter adapter
|
||||
func NewBunRouterAdapter(router *bunrouter.Router) *BunRouterAdapter {
|
||||
return &BunRouterAdapter{router: router}
|
||||
}
|
||||
|
||||
// NewBunRouterAdapterDefault creates a new bunrouter adapter with default router
|
||||
func NewBunRouterAdapterDefault() *BunRouterAdapter {
|
||||
return &BunRouterAdapter{router: bunrouter.New()}
|
||||
}
|
||||
|
||||
func (b *BunRouterAdapter) HandleFunc(pattern string, handler common.HTTPHandlerFunc) common.RouteRegistration {
|
||||
route := &BunRouterRegistration{
|
||||
router: b.router,
|
||||
pattern: pattern,
|
||||
handler: handler,
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
func (b *BunRouterAdapter) ServeHTTP(w common.ResponseWriter, r common.Request) {
|
||||
// This method would be used when we need to serve through our interface
|
||||
// For now, we'll work directly with the underlying router
|
||||
panic("ServeHTTP not implemented - use GetBunRouter() for direct access")
|
||||
}
|
||||
|
||||
// GetBunRouter returns the underlying bunrouter for direct access
|
||||
func (b *BunRouterAdapter) GetBunRouter() *bunrouter.Router {
|
||||
return b.router
|
||||
}
|
||||
|
||||
// BunRouterRegistration implements RouteRegistration for bunrouter
|
||||
type BunRouterRegistration struct {
|
||||
router *bunrouter.Router
|
||||
pattern string
|
||||
handler common.HTTPHandlerFunc
|
||||
}
|
||||
|
||||
func (b *BunRouterRegistration) Methods(methods ...string) common.RouteRegistration {
|
||||
// bunrouter handles methods differently - we'll register for each method
|
||||
for _, method := range methods {
|
||||
b.router.Handle(method, b.pattern, func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
// Convert bunrouter.Request to our BunRouterRequest
|
||||
reqAdapter := &BunRouterRequest{req: req}
|
||||
respAdapter := &HTTPResponseWriter{resp: w}
|
||||
b.handler(respAdapter, reqAdapter)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BunRouterRegistration) PathPrefix(prefix string) common.RouteRegistration {
|
||||
// bunrouter doesn't have PathPrefix like mux, but we can modify the pattern
|
||||
newPattern := prefix + b.pattern
|
||||
b.pattern = newPattern
|
||||
return b
|
||||
}
|
||||
|
||||
// BunRouterRequest adapts bunrouter.Request to our Request interface
|
||||
type BunRouterRequest struct {
|
||||
req bunrouter.Request
|
||||
body []byte
|
||||
}
|
||||
|
||||
// NewBunRouterRequest creates a new BunRouterRequest adapter
|
||||
func NewBunRouterRequest(req bunrouter.Request) *BunRouterRequest {
|
||||
return &BunRouterRequest{req: req}
|
||||
}
|
||||
|
||||
func (b *BunRouterRequest) Method() string {
|
||||
return b.req.Method
|
||||
}
|
||||
|
||||
func (b *BunRouterRequest) URL() string {
|
||||
return b.req.URL.String()
|
||||
}
|
||||
|
||||
func (b *BunRouterRequest) Header(key string) string {
|
||||
return b.req.Header.Get(key)
|
||||
}
|
||||
|
||||
func (b *BunRouterRequest) Body() ([]byte, error) {
|
||||
if b.body != nil {
|
||||
return b.body, nil
|
||||
}
|
||||
|
||||
if b.req.Body == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create HTTPRequest adapter and use its Body() method
|
||||
httpAdapter := NewHTTPRequest(b.req.Request)
|
||||
body, err := httpAdapter.Body()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.body = body
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (b *BunRouterRequest) PathParam(key string) string {
|
||||
return b.req.Param(key)
|
||||
}
|
||||
|
||||
func (b *BunRouterRequest) QueryParam(key string) string {
|
||||
return b.req.URL.Query().Get(key)
|
||||
}
|
||||
|
||||
func (b *BunRouterRequest) AllHeaders() map[string]string {
|
||||
headers := make(map[string]string)
|
||||
for key, values := range b.req.Header {
|
||||
if len(values) > 0 {
|
||||
headers[key] = values[0]
|
||||
}
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
// StandardBunRouterAdapter creates routes compatible with standard bunrouter handlers
|
||||
type StandardBunRouterAdapter struct {
|
||||
*BunRouterAdapter
|
||||
}
|
||||
|
||||
func NewStandardBunRouterAdapter() *StandardBunRouterAdapter {
|
||||
return &StandardBunRouterAdapter{
|
||||
BunRouterAdapter: NewBunRouterAdapterDefault(),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoute registers a route that works with the existing Handler
|
||||
func (s *StandardBunRouterAdapter) RegisterRoute(method, pattern string, handler func(http.ResponseWriter, *http.Request, map[string]string)) {
|
||||
s.router.Handle(method, pattern, func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
// Extract path parameters
|
||||
params := make(map[string]string)
|
||||
|
||||
// bunrouter doesn't provide a direct way to get all params
|
||||
// You would typically access them individually with req.Param("name")
|
||||
// For this example, we'll create the map based on the request context
|
||||
|
||||
handler(w, req.Request, params)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterRouteWithParams registers a route with explicit parameter extraction
|
||||
func (s *StandardBunRouterAdapter) RegisterRouteWithParams(method, pattern string, paramNames []string, handler func(http.ResponseWriter, *http.Request, map[string]string)) {
|
||||
s.router.Handle(method, pattern, func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||
// Extract specified path parameters
|
||||
params := make(map[string]string)
|
||||
for _, paramName := range paramNames {
|
||||
params[paramName] = req.Param(paramName)
|
||||
}
|
||||
|
||||
handler(w, req.Request, params)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// BunRouterConfig holds bunrouter-specific configuration
|
||||
type BunRouterConfig struct {
|
||||
UseStrictSlash bool
|
||||
RedirectTrailingSlash bool
|
||||
HandleMethodNotAllowed bool
|
||||
HandleOPTIONS bool
|
||||
GlobalOPTIONS http.Handler
|
||||
GlobalMethodNotAllowed http.Handler
|
||||
PanicHandler func(http.ResponseWriter, *http.Request, interface{})
|
||||
}
|
||||
|
||||
// DefaultBunRouterConfig returns default bunrouter configuration
|
||||
func DefaultBunRouterConfig() *BunRouterConfig {
|
||||
return &BunRouterConfig{
|
||||
UseStrictSlash: false,
|
||||
RedirectTrailingSlash: true,
|
||||
HandleMethodNotAllowed: true,
|
||||
HandleOPTIONS: true,
|
||||
}
|
||||
}
|
||||
|
||||
208
pkg/common/adapters/router/mux.go
Normal file
208
pkg/common/adapters/router/mux.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// MuxAdapter adapts Gorilla Mux to work with our Router interface
|
||||
type MuxAdapter struct {
|
||||
router *mux.Router
|
||||
}
|
||||
|
||||
// NewMuxAdapter creates a new Mux adapter
|
||||
func NewMuxAdapter(router *mux.Router) *MuxAdapter {
|
||||
return &MuxAdapter{router: router}
|
||||
}
|
||||
|
||||
func (m *MuxAdapter) HandleFunc(pattern string, handler common.HTTPHandlerFunc) common.RouteRegistration {
|
||||
route := &MuxRouteRegistration{
|
||||
router: m.router,
|
||||
pattern: pattern,
|
||||
handler: handler,
|
||||
}
|
||||
return route
|
||||
}
|
||||
|
||||
func (m *MuxAdapter) ServeHTTP(w common.ResponseWriter, r common.Request) {
|
||||
// This method would be used when we need to serve through our interface
|
||||
// For now, we'll work directly with the underlying router
|
||||
panic("ServeHTTP not implemented - use GetMuxRouter() for direct access")
|
||||
}
|
||||
|
||||
// MuxRouteRegistration implements RouteRegistration for Mux
|
||||
type MuxRouteRegistration struct {
|
||||
router *mux.Router
|
||||
pattern string
|
||||
handler common.HTTPHandlerFunc
|
||||
route *mux.Route
|
||||
}
|
||||
|
||||
func (m *MuxRouteRegistration) Methods(methods ...string) common.RouteRegistration {
|
||||
if m.route == nil {
|
||||
m.route = m.router.HandleFunc(m.pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
reqAdapter := &HTTPRequest{req: r, vars: mux.Vars(r)}
|
||||
respAdapter := &HTTPResponseWriter{resp: w}
|
||||
m.handler(respAdapter, reqAdapter)
|
||||
})
|
||||
}
|
||||
m.route.Methods(methods...)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MuxRouteRegistration) PathPrefix(prefix string) common.RouteRegistration {
|
||||
if m.route == nil {
|
||||
m.route = m.router.HandleFunc(m.pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
reqAdapter := &HTTPRequest{req: r, vars: mux.Vars(r)}
|
||||
respAdapter := &HTTPResponseWriter{resp: w}
|
||||
m.handler(respAdapter, reqAdapter)
|
||||
})
|
||||
}
|
||||
m.route.PathPrefix(prefix)
|
||||
return m
|
||||
}
|
||||
|
||||
// HTTPRequest adapts standard http.Request to our Request interface
|
||||
type HTTPRequest struct {
|
||||
req *http.Request
|
||||
vars map[string]string
|
||||
body []byte
|
||||
}
|
||||
|
||||
func NewHTTPRequest(r *http.Request) *HTTPRequest {
|
||||
return &HTTPRequest{
|
||||
req: r,
|
||||
vars: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *HTTPRequest) Method() string {
|
||||
return h.req.Method
|
||||
}
|
||||
|
||||
func (h *HTTPRequest) URL() string {
|
||||
return h.req.URL.String()
|
||||
}
|
||||
|
||||
func (h *HTTPRequest) Header(key string) string {
|
||||
return h.req.Header.Get(key)
|
||||
}
|
||||
|
||||
func (h *HTTPRequest) Body() ([]byte, error) {
|
||||
if h.body != nil {
|
||||
return h.body, nil
|
||||
}
|
||||
if h.req.Body == nil {
|
||||
return nil, nil
|
||||
}
|
||||
defer h.req.Body.Close()
|
||||
body, err := io.ReadAll(h.req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h.body = body
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func (h *HTTPRequest) PathParam(key string) string {
|
||||
return h.vars[key]
|
||||
}
|
||||
|
||||
func (h *HTTPRequest) QueryParam(key string) string {
|
||||
return h.req.URL.Query().Get(key)
|
||||
}
|
||||
|
||||
func (h *HTTPRequest) AllHeaders() map[string]string {
|
||||
headers := make(map[string]string)
|
||||
for key, values := range h.req.Header {
|
||||
if len(values) > 0 {
|
||||
headers[key] = values[0]
|
||||
}
|
||||
}
|
||||
return headers
|
||||
}
|
||||
|
||||
// HTTPResponseWriter adapts our ResponseWriter interface to standard http.ResponseWriter
|
||||
type HTTPResponseWriter struct {
|
||||
resp http.ResponseWriter
|
||||
w common.ResponseWriter
|
||||
status int
|
||||
}
|
||||
|
||||
func NewHTTPResponseWriter(w http.ResponseWriter) *HTTPResponseWriter {
|
||||
return &HTTPResponseWriter{resp: w}
|
||||
}
|
||||
|
||||
func (h *HTTPResponseWriter) SetHeader(key, value string) {
|
||||
h.resp.Header().Set(key, value)
|
||||
}
|
||||
|
||||
func (h *HTTPResponseWriter) WriteHeader(statusCode int) {
|
||||
h.status = statusCode
|
||||
h.resp.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (h *HTTPResponseWriter) Write(data []byte) (int, error) {
|
||||
return h.resp.Write(data)
|
||||
}
|
||||
|
||||
func (h *HTTPResponseWriter) WriteJSON(data interface{}) error {
|
||||
h.SetHeader("Content-Type", "application/json")
|
||||
return json.NewEncoder(h.resp).Encode(data)
|
||||
}
|
||||
|
||||
// StandardMuxAdapter creates routes compatible with standard http.HandlerFunc
|
||||
type StandardMuxAdapter struct {
|
||||
*MuxAdapter
|
||||
}
|
||||
|
||||
func NewStandardMuxAdapter() *StandardMuxAdapter {
|
||||
return &StandardMuxAdapter{
|
||||
MuxAdapter: NewMuxAdapter(mux.NewRouter()),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterRoute registers a route that works with the existing Handler
|
||||
func (s *StandardMuxAdapter) RegisterRoute(pattern string, handler func(http.ResponseWriter, *http.Request, map[string]string)) *mux.Route {
|
||||
return s.router.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
handler(w, r, vars)
|
||||
})
|
||||
}
|
||||
|
||||
// GetMuxRouter returns the underlying mux router for direct access
|
||||
func (s *StandardMuxAdapter) GetMuxRouter() *mux.Router {
|
||||
return s.router
|
||||
}
|
||||
|
||||
// PathParamExtractor extracts path parameters from different router types
|
||||
type PathParamExtractor interface {
|
||||
ExtractParams(*http.Request) map[string]string
|
||||
}
|
||||
|
||||
// MuxParamExtractor extracts parameters from Gorilla Mux
|
||||
type MuxParamExtractor struct{}
|
||||
|
||||
func (m MuxParamExtractor) ExtractParams(r *http.Request) map[string]string {
|
||||
return mux.Vars(r)
|
||||
}
|
||||
|
||||
// RouterConfig holds router configuration
|
||||
type RouterConfig struct {
|
||||
PathPrefix string
|
||||
Middleware []func(http.Handler) http.Handler
|
||||
ParamExtractor PathParamExtractor
|
||||
}
|
||||
|
||||
// DefaultRouterConfig returns default router configuration
|
||||
func DefaultRouterConfig() *RouterConfig {
|
||||
return &RouterConfig{
|
||||
PathPrefix: "",
|
||||
Middleware: make([]func(http.Handler) http.Handler, 0),
|
||||
ParamExtractor: MuxParamExtractor{},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user