Fix Update for db service (#140)

This commit is contained in:
Janos Dobronszki
2021-06-04 11:18:50 +01:00
committed by GitHub
parent 9612f1e7a8
commit f828bd2438
4 changed files with 76 additions and 58 deletions

View File

@@ -4,17 +4,31 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"regexp"
"strings"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/micro/micro/v3/service/errors" "github.com/micro/micro/v3/service/errors"
db "github.com/micro/services/db/proto" db "github.com/micro/services/db/proto"
gorm2 "github.com/micro/services/pkg/gorm" gorm2 "github.com/micro/services/pkg/gorm"
"github.com/micro/services/pkg/tenant"
"github.com/patrickmn/go-cache"
"gorm.io/datatypes" "gorm.io/datatypes"
"gorm.io/gorm"
) )
const idKey = "id"
const stmt = "create table %v(id text not null, data jsonb, primary key(id));"
var re = regexp.MustCompile("^[a-zA-Z0-9_]*$")
var c = cache.New(5*time.Minute, 10*time.Minute)
type Record struct { type Record struct {
ID string ID string
Data datatypes.JSON `json:"data"` Data datatypes.JSON `json:"data"`
// private field, ignored from gorm
table string `gorm:"-"`
} }
type Db struct { type Db struct {
@@ -26,23 +40,38 @@ func (e *Db) Create(ctx context.Context, req *db.CreateRequest, rsp *db.CreateRe
if len(req.Record) == 0 { if len(req.Record) == 0 {
return errors.BadRequest("db.create", "missing record") return errors.BadRequest("db.create", "missing record")
} }
tenantId, ok := tenant.FromContext(ctx)
if !ok {
tenantId = "micro"
}
tenantId = strings.Replace(tenantId, "/", "_", -1)
tableName := tenantId + "_" + req.Table
if !re.Match([]byte(tableName)) {
return errors.BadRequest("db.create", "table name is invalid")
}
db, err := e.GetDBConn(ctx) db, err := e.GetDBConn(ctx)
if err != nil { if err != nil {
return err return err
} }
_, ok = c.Get(req.Table)
if !ok {
db.Exec(fmt.Sprintf(stmt, tableName))
c.Set(req.Table, true, 0)
}
m := map[string]interface{}{} m := map[string]interface{}{}
err = json.Unmarshal([]byte(req.Record), &m) err = json.Unmarshal([]byte(req.Record), &m)
if err != nil { if err != nil {
return err return err
} }
if _, ok := m["ID"].(string); !ok { if _, ok := m[idKey].(string); !ok {
m["ID"] = uuid.New().String() m[idKey] = uuid.New().String()
} }
bs, _ := json.Marshal(m) bs, _ := json.Marshal(m)
err = db.Table(req.Table).Create(Record{ err = db.Table(tableName).Create(Record{
ID: m["ID"].(string), ID: m[idKey].(string),
Data: bs, Data: bs,
}).Error }).Error
if err != nil { if err != nil {
@@ -50,7 +79,7 @@ func (e *Db) Create(ctx context.Context, req *db.CreateRequest, rsp *db.CreateRe
} }
// set the response id // set the response id
rsp.Id = m["ID"].(string) rsp.Id = m[idKey].(string)
return nil return nil
} }
@@ -59,7 +88,11 @@ func (e *Db) Update(ctx context.Context, req *db.UpdateRequest, rsp *db.UpdateRe
if len(req.Record) == 0 { if len(req.Record) == 0 {
return errors.BadRequest("db.update", "missing record") return errors.BadRequest("db.update", "missing record")
} }
tenantId, ok := tenant.FromContext(ctx)
if !ok {
tenantId = "micro"
}
tenantId = strings.Replace(tenantId, "/", "_", -1)
db, err := e.GetDBConn(ctx) db, err := e.GetDBConn(ctx)
if err != nil { if err != nil {
return err return err
@@ -71,59 +104,37 @@ func (e *Db) Update(ctx context.Context, req *db.UpdateRequest, rsp *db.UpdateRe
return err return err
} }
// do we really need to remarshal this?
data, _ := json.Marshal(m)
// where ID is specified do a single update record update // where ID is specified do a single update record update
if id, ok := m["ID"].(string); ok { id, ok := m[idKey].(string)
// apply the update to a single record if !ok {
return db.Table(req.Table).First(&Record{ID: id}).Updates(Record{Data: data}).Error return fmt.Errorf("update failed: missing id")
} }
// define the db db.Transaction(func(tx *gorm.DB) error {
db = db.Table(req.Table) rec := []Record{}
err = tx.Table(tenantId+"_"+req.Table).Where("ID = ?", id).Find(&rec).Error
// no ID param so we're expecting a query if err != nil {
if len(req.Query) == 0 { return err
// apply the updates to all records
return db.Find(&Record{}).Updates(Record{Data: data}).Error
}
// parse the query
queries, err := Parse(req.Query)
if err != nil {
return err
}
// get the filters
for _, query := range queries {
typ := "text"
switch query.Value.(type) {
case int64:
typ = "int"
case bool:
typ = "boolean"
} }
op := "" if len(rec) == 0 {
switch query.Op { return fmt.Errorf("update failed: not found")
case itemEquals:
op = "="
case itemGreaterThan:
op = ">"
case itemGreaterThanEquals:
op = ">="
case itemLessThan:
op = "<"
case itemLessThanEquals:
op = "<="
case itemNotEquals:
op = "!="
} }
db = db.Where(fmt.Sprintf("(data ->> '%v')::%v %v ?", query.Field, typ, op), query.Value) old := map[string]interface{}{}
} err = json.Unmarshal(rec[0].Data, &old)
if err != nil {
return err
}
for k, v := range old {
m[k] = v
}
bs, _ := json.Marshal(m)
// apply updates to the filtered records return tx.Table(tenantId + "_" + req.Table).Save(Record{
return db.Updates(Record{Data: data}).Error ID: m[idKey].(string),
Data: bs,
}).Error
})
return nil
} }
func (e *Db) Read(ctx context.Context, req *db.ReadRequest, rsp *db.ReadResponse) error { func (e *Db) Read(ctx context.Context, req *db.ReadRequest, rsp *db.ReadResponse) error {
@@ -132,11 +143,16 @@ func (e *Db) Read(ctx context.Context, req *db.ReadRequest, rsp *db.ReadResponse
if err != nil { if err != nil {
return err return err
} }
tenantId, ok := tenant.FromContext(ctx)
if !ok {
tenantId = "micro"
}
tenantId = strings.Replace(tenantId, "/", "_", -1)
db, err := e.GetDBConn(ctx) db, err := e.GetDBConn(ctx)
if err != nil { if err != nil {
return err return err
} }
db = db.Table(req.Table) db = db.Table(tenantId + "_" + req.Table)
for _, query := range queries { for _, query := range queries {
typ := "text" typ := "text"
switch query.Value.(type) { switch query.Value.(type) {
@@ -174,7 +190,7 @@ func (e *Db) Read(ctx context.Context, req *db.ReadRequest, rsp *db.ReadResponse
} }
ma := map[string]interface{}{} ma := map[string]interface{}{}
json.Unmarshal(m, &ma) json.Unmarshal(m, &ma)
ma["ID"] = rec.ID ma[idKey] = rec.ID
ret = append(ret, ma) ret = append(ret, ma)
} }
bs, _ := json.Marshal(ret) bs, _ := json.Marshal(ret)

View File

@@ -35,13 +35,13 @@ func main() {
logger.Fatalf("Failed to open connection to DB %s", err) logger.Fatalf("Failed to open connection to DB %s", err)
} }
h := &handler.Db{} h := &handler.Db{}
h.DBConn(sqlDB).Migrations(&handler.Record{}) h.DBConn(sqlDB)
// Register handler // Register handler
pb.RegisterDbHandler(srv.Server(), h) pb.RegisterDbHandler(srv.Server(), h)
// Register handler // Register handler
pb.RegisterDbHandler(srv.Server(), new(handler.Db)) pb.RegisterDbHandler(srv.Server(), &handler.Db{})
// Run service // Run service
if err := srv.Run(); err != nil { if err := srv.Run(); err != nil {

2
go.mod
View File

@@ -41,7 +41,7 @@ require (
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
gorm.io/datatypes v1.0.1 gorm.io/datatypes v1.0.1
gorm.io/driver/postgres v1.0.8 gorm.io/driver/postgres v1.0.8
gorm.io/gorm v1.21.6 gorm.io/gorm v1.21.10
) )
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0 replace google.golang.org/grpc => google.golang.org/grpc v1.26.0

2
go.sum
View File

@@ -780,6 +780,8 @@ gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.21.6 h1:xEFbH7WShsnAM+HeRNv7lOeyqmDAK+dDnf1AMf/cVPQ= gorm.io/gorm v1.21.6 h1:xEFbH7WShsnAM+HeRNv7lOeyqmDAK+dDnf1AMf/cVPQ=
gorm.io/gorm v1.21.6/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.21.6/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.21.10 h1:kBGiBsaqOQ+8f6S2U6mvGFz6aWWyCeIiuaFcaBozp4M=
gorm.io/gorm v1.21.10/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=