diff --git a/db/.gitignore b/db/.gitignore new file mode 100644 index 0000000..f6fa3bc --- /dev/null +++ b/db/.gitignore @@ -0,0 +1,2 @@ + +db diff --git a/db/Dockerfile b/db/Dockerfile new file mode 100644 index 0000000..c13c42c --- /dev/null +++ b/db/Dockerfile @@ -0,0 +1,3 @@ +FROM alpine +ADD db /db +ENTRYPOINT [ "/db" ] diff --git a/db/Makefile b/db/Makefile new file mode 100644 index 0000000..b9cdf10 --- /dev/null +++ b/db/Makefile @@ -0,0 +1,22 @@ + +GOPATH:=$(shell go env GOPATH) +.PHONY: init +init: + go get -u github.com/golang/protobuf/proto + go get -u github.com/golang/protobuf/protoc-gen-go + go get github.com/micro/micro/v3/cmd/protoc-gen-micro +.PHONY: proto +proto: + protoc --proto_path=. --micro_out=. --go_out=:. proto/db.proto + +.PHONY: build +build: + go build -o db *.go + +.PHONY: test +test: + go test -v ./... -cover + +.PHONY: docker +docker: + docker build . -t db:latest diff --git a/db/README.md b/db/README.md new file mode 100644 index 0000000..23564ee --- /dev/null +++ b/db/README.md @@ -0,0 +1,23 @@ +# Db Service + +This is the Db service + +Generated with + +``` +micro new db +``` + +## Usage + +Generate the proto code + +``` +make proto +``` + +Run the service + +``` +micro run . +``` \ No newline at end of file diff --git a/db/generate.go b/db/generate.go new file mode 100644 index 0000000..96f431a --- /dev/null +++ b/db/generate.go @@ -0,0 +1,2 @@ +package main +//go:generate make proto diff --git a/db/handler/db.go b/db/handler/db.go new file mode 100644 index 0000000..00ab2aa --- /dev/null +++ b/db/handler/db.go @@ -0,0 +1,108 @@ +package handler + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/google/uuid" + db "github.com/micro/services/db/proto" + gorm2 "github.com/micro/services/pkg/gorm" + "gorm.io/datatypes" +) + +type Record struct { + ID string + Data datatypes.JSON `json:"data"` +} + +type Db struct { + gorm2.Helper +} + +// Call is a single request handler called via client.Call or the generated client code +func (e *Db) Create(ctx context.Context, req *db.CreateRequest, rsp *db.CreateResponse) error { + db, err := e.GetDBConn(ctx) + if err != nil { + return err + } + m := map[string]interface{}{} + err = json.Unmarshal([]byte(req.Record), &m) + if err != nil { + return err + } + if _, ok := m["ID"].(string); !ok { + m["ID"] = uuid.New().String() + } + bs, _ := json.Marshal(m) + return db.Table(req.Table).Create(Record{ + ID: m["ID"].(string), + Data: bs, + }).Error +} + +func (e *Db) Update(ctx context.Context, req *db.UpdateRequest, rsp *db.UpdateResponse) error { + + return nil +} + +func (e *Db) Read(ctx context.Context, req *db.ReadRequest, rsp *db.ReadResponse) error { + recs := []Record{} + queries, err := Parse(req.Query) + if err != nil { + return err + } + db, err := e.GetDBConn(ctx) + if err != nil { + return err + } + db = db.Table(req.Table) + for _, query := range queries { + typ := "text" + switch query.Value.(type) { + case int64: + typ = "int" + case bool: + typ = "boolean" + } + op := "" + switch query.Op { + 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) + } + err = db.Find(&recs).Error + if err != nil { + return err + } + ret := []map[string]interface{}{} + for _, rec := range recs { + m, err := rec.Data.MarshalJSON() + if err != nil { + return err + } + ma := map[string]interface{}{} + json.Unmarshal(m, &ma) + ma["ID"] = rec.ID + ret = append(ret, ma) + } + bs, _ := json.Marshal(ret) + rsp.Records = string(bs) + return nil +} + +func (e *Db) Delete(ctx context.Context, req *db.DeleteRequest, rsp *db.DeleteResponse) error { + + return nil +} diff --git a/db/handler/parse.go b/db/handler/parse.go new file mode 100644 index 0000000..277c1f6 --- /dev/null +++ b/db/handler/parse.go @@ -0,0 +1,134 @@ +package handler + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/crufter/lexer" +) + +var quoteEscape = fmt.Sprint(0x10FFFF) + +const ( + itemIgnore = iota + + // nouns + itemAnd + itemInt + itemFieldName + itemString + itemBoolTrue + itemBoolFalse + + // ops + itemEquals + itemNotEquals + itemLessThan + itemGreaterThan + itemLessThanEquals + itemGreaterThanEquals +) + +var opToString = map[int]string{ + itemEquals: "==", + itemNotEquals: "!=", + itemLessThan: "<", + itemGreaterThan: ">", + itemLessThanEquals: "<=", + itemGreaterThanEquals: ">=", +} + +var expressions = []lexer.TokenExpr{ + {`[ ]+`, itemIgnore}, // Whitespace + {`==`, itemEquals}, + {`!=`, itemNotEquals}, + {`false`, itemBoolFalse}, + {`true`, itemBoolTrue}, + {`and`, itemAnd}, + {`<=`, itemLessThanEquals}, + {`>=`, itemGreaterThanEquals}, + {`<`, itemLessThan}, + {`>`, itemGreaterThan}, + {`[0-9]+`, itemInt}, + {`"(?:[^"\\]|\\.)*"`, itemString}, + {`[\<\>\!\=\+\-\|\&\*\/A-Za-z][A-Za-z0-9_]*`, itemFieldName}, +} + +type Query struct { + Field string + Op int + Value interface{} +} + +func Parse(q string) ([]Query, error) { + if strings.Contains(q, quoteEscape) { + return nil, errors.New("query contains illegal max rune") + } + q = strings.Replace(q, `""`, quoteEscape, -1) + tokens, err := lexer.Lex(q, expressions) + if err != nil { + return nil, err + } + queries := []Query{} + current := Query{} + for i, token := range tokens { + // and tokens should trigger a query + // save and reset + if token.Typ == itemAnd { + queries = append(queries, current) + current = Query{} + continue + } + + // is an op + if token.Typ >= itemEquals { + current.Op = token.Typ + continue + } + + // is a value + switch token.Typ { + case itemFieldName: + current.Field = token.Text + case itemString: + switch current.Op { + case itemEquals, itemNotEquals: + default: + return nil, fmt.Errorf("operator '%v' can't be used with strings", opToString[token.Typ]) + } + + if len(token.Text) < 2 { + return nil, fmt.Errorf("string literal too short: '%v'", token.Text) + } + current.Value = strings.Replace(token.Text[1:len(token.Text)-1], quoteEscape, `"`, -1) + case itemBoolTrue: + switch current.Op { + case itemEquals, itemNotEquals: + default: + return nil, fmt.Errorf("operator '%v' can't be used with bools", opToString[token.Typ]) + } + current.Value = true + case itemBoolFalse: + switch current.Op { + case itemEquals, itemNotEquals: + default: + return nil, fmt.Errorf("operator '%v' can't be used with bools", opToString[token.Typ]) + } + current.Value = false + case itemInt: + num, err := strconv.ParseInt(token.Text, 10, 64) + if err != nil { + return nil, err + } + current.Value = num + } + + // if we are at last position, save last query + if i == len(tokens)-1 { + queries = append(queries, current) + } + } + return queries, nil +} diff --git a/db/handler/parse_test.go b/db/handler/parse_test.go new file mode 100644 index 0000000..12f196f --- /dev/null +++ b/db/handler/parse_test.go @@ -0,0 +1,141 @@ +package handler + +import ( + "fmt" + "reflect" + "testing" + + "github.com/crufter/lexer" +) + +func TestLexing(t *testing.T) { + tokens, err := lexer.Lex("a == 12", expressions) + if err != nil { + t.Fatal(err) + } + if len(tokens) != 3 { + t.Fatal(tokens) + } + if tokens[0].Typ != itemFieldName || tokens[1].Typ != itemEquals || tokens[2].Typ != itemInt { + t.Fatal(tokens) + } + + tokens, err = lexer.Lex(`a == 12 and name != "nandos"`, expressions) + if tokens[0].Typ != itemFieldName || + tokens[1].Typ != itemEquals || + tokens[2].Typ != itemInt || + tokens[3].Typ != itemAnd || + tokens[4].Typ != itemFieldName || + tokens[5].Typ != itemNotEquals || + tokens[6].Typ != itemString { + t.Fatal(tokens) + } +} + +type tCase struct { + Q string + E []Query + Err error +} + +func TestParsing(t *testing.T) { + tCases := []tCase{ + tCase{ + Q: `a == 12 and name != "nandos"`, + E: []Query{ + Query{ + Field: "a", + Value: int64(12), + Op: itemEquals, + }, + Query{ + Field: "name", + Value: "nandos", + Op: itemNotEquals, + }, + }, + }, + // test escaping quotes + tCase{ + Q: `a == 12 and name != "He said ""yes""!"`, + E: []Query{ + Query{ + Field: "a", + Value: int64(12), + Op: itemEquals, + }, + Query{ + Field: "name", + Value: `He said "yes"!`, + Op: itemNotEquals, + }, + }, + }, + tCase{ + Q: `a == false and b == true`, + E: []Query{ + Query{ + Field: "a", + Value: false, + Op: itemEquals, + }, + Query{ + Field: "b", + Value: true, + Op: itemEquals, + }, + }, + }, + // a < 20 + tCase{ + Q: `a < 20`, + E: []Query{ + Query{ + Field: "a", + Value: int64(20), + Op: itemLessThan, + }, + }, + }, + tCase{ + Q: `a <= 20`, + E: []Query{ + Query{ + Field: "a", + Value: int64(20), + Op: itemLessThanEquals, + }, + }, + }, + tCase{ + Q: `a > 20`, + E: []Query{ + Query{ + Field: "a", + Value: int64(20), + Op: itemGreaterThan, + }, + }, + }, + tCase{ + Q: `a >= 20`, + E: []Query{ + Query{ + Field: "a", + Value: int64(20), + Op: itemGreaterThanEquals, + }, + }, + }, + } + for _, tCase := range tCases { + fmt.Println("Parsing", tCase.Q) + qs, err := Parse(tCase.Q) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(qs, tCase.E) { + t.Fatal("Expected", tCase.E, "got", qs) + } + } +} diff --git a/db/main.go b/db/main.go new file mode 100644 index 0000000..923cfb3 --- /dev/null +++ b/db/main.go @@ -0,0 +1,50 @@ +package main + +import ( + pb "github.com/micro/services/db/proto" + + "github.com/micro/services/db/handler" + + "github.com/micro/micro/v3/service" + "github.com/micro/micro/v3/service/logger" + + "database/sql" + + "github.com/micro/micro/v3/service/config" + + _ "github.com/jackc/pgx/v4/stdlib" +) + +var dbAddress = "postgresql://postgres:postgres@localhost:5432/db?sslmode=disable" + +func main() { + // Create service + srv := service.New( + service.Name("db"), + service.Version("latest"), + ) + + // Connect to the database + cfg, err := config.Get("micro.db.database") + if err != nil { + logger.Fatalf("Error loading config: %v", err) + } + addr := cfg.String(dbAddress) + sqlDB, err := sql.Open("pgx", addr) + if err != nil { + logger.Fatalf("Failed to open connection to DB %s", err) + } + h := &handler.Db{} + h.DBConn(sqlDB).Migrations(&handler.Record{}) + + // Register handler + pb.RegisterDbHandler(srv.Server(), h) + + // Register handler + pb.RegisterDbHandler(srv.Server(), new(handler.Db)) + + // Run service + if err := srv.Run(); err != nil { + logger.Fatal(err) + } +} diff --git a/db/micro.mu b/db/micro.mu new file mode 100644 index 0000000..7fea810 --- /dev/null +++ b/db/micro.mu @@ -0,0 +1 @@ +service db diff --git a/db/proto/db.pb.go b/db/proto/db.pb.go new file mode 100644 index 0000000..37e38a9 --- /dev/null +++ b/db/proto/db.pb.go @@ -0,0 +1,654 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.6.1 +// source: proto/db.proto + +package db + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ReadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` + // eg. 'age >= 18', 'age >= 18 and verified == true' + // comparison operators: '==', '!=', '<', '>', '<=', '>=' + // logical operator: 'and' + Query string `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"` + Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` + Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + // field name to order by + OrderBy string `protobuf:"bytes,5,opt,name=orderBy,proto3" json:"orderBy,omitempty"` + // 'asc' (default), 'desc' + Order string `protobuf:"bytes,6,opt,name=order,proto3" json:"order,omitempty"` +} + +func (x *ReadRequest) Reset() { + *x = ReadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadRequest) ProtoMessage() {} + +func (x *ReadRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. +func (*ReadRequest) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{0} +} + +func (x *ReadRequest) GetTable() string { + if x != nil { + return x.Table + } + return "" +} + +func (x *ReadRequest) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +func (x *ReadRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *ReadRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *ReadRequest) GetOrderBy() string { + if x != nil { + return x.OrderBy + } + return "" +} + +func (x *ReadRequest) GetOrder() string { + if x != nil { + return x.Order + } + return "" +} + +type ReadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // JSON encoded records + Records string `protobuf:"bytes,1,opt,name=records,proto3" json:"records,omitempty"` +} + +func (x *ReadResponse) Reset() { + *x = ReadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReadResponse) ProtoMessage() {} + +func (x *ReadResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReadResponse.ProtoReflect.Descriptor instead. +func (*ReadResponse) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{1} +} + +func (x *ReadResponse) GetRecords() string { + if x != nil { + return x.Records + } + return "" +} + +type CreateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` + // JSON encoded record or records (can be array or object) + Record string `protobuf:"bytes,2,opt,name=record,proto3" json:"record,omitempty"` +} + +func (x *CreateRequest) Reset() { + *x = CreateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateRequest) ProtoMessage() {} + +func (x *CreateRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateRequest.ProtoReflect.Descriptor instead. +func (*CreateRequest) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateRequest) GetTable() string { + if x != nil { + return x.Table + } + return "" +} + +func (x *CreateRequest) GetRecord() string { + if x != nil { + return x.Record + } + return "" +} + +type CreateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *CreateResponse) Reset() { + *x = CreateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateResponse) ProtoMessage() {} + +func (x *CreateResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateResponse.ProtoReflect.Descriptor instead. +func (*CreateResponse) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{3} +} + +type UpdateRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` + // JSON encoded record or records (can be array or object) + Record string `protobuf:"bytes,2,opt,name=record,proto3" json:"record,omitempty"` +} + +func (x *UpdateRequest) Reset() { + *x = UpdateRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRequest) ProtoMessage() {} + +func (x *UpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead. +func (*UpdateRequest) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{4} +} + +func (x *UpdateRequest) GetTable() string { + if x != nil { + return x.Table + } + return "" +} + +func (x *UpdateRequest) GetRecord() string { + if x != nil { + return x.Record + } + return "" +} + +type UpdateResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *UpdateResponse) Reset() { + *x = UpdateResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateResponse) ProtoMessage() {} + +func (x *UpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateResponse.ProtoReflect.Descriptor instead. +func (*UpdateResponse) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{5} +} + +type DeleteRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Table string `protobuf:"bytes,1,opt,name=table,proto3" json:"table,omitempty"` + // id or ids, eg. 'user-1', or comma separated ids 'user-1,user-2' + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteRequest) Reset() { + *x = DeleteRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteRequest) ProtoMessage() {} + +func (x *DeleteRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteRequest.ProtoReflect.Descriptor instead. +func (*DeleteRequest) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{6} +} + +func (x *DeleteRequest) GetTable() string { + if x != nil { + return x.Table + } + return "" +} + +func (x *DeleteRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type DeleteResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *DeleteResponse) Reset() { + *x = DeleteResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_db_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteResponse) ProtoMessage() {} + +func (x *DeleteResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_db_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteResponse.ProtoReflect.Descriptor instead. +func (*DeleteResponse) Descriptor() ([]byte, []int) { + return file_proto_db_proto_rawDescGZIP(), []int{7} +} + +var File_proto_db_proto protoreflect.FileDescriptor + +var file_proto_db_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x02, 0x64, 0x62, 0x22, 0x97, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x22, 0x28, + 0x0a, 0x0c, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x3d, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, + 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3d, 0x0a, 0x0d, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x0d, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x32, 0xca, 0x01, 0x0a, 0x02, 0x44, 0x62, 0x12, 0x31, 0x0a, 0x06, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x64, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x62, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2b, 0x0a, + 0x04, 0x52, 0x65, 0x61, 0x64, 0x12, 0x0f, 0x2e, 0x64, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x64, 0x62, 0x2e, 0x52, 0x65, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x06, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x64, 0x62, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x62, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, + 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x11, 0x2e, 0x64, 0x62, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x62, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6d, + 0x69, 0x63, 0x72, 0x6f, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x3b, 0x64, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_db_proto_rawDescOnce sync.Once + file_proto_db_proto_rawDescData = file_proto_db_proto_rawDesc +) + +func file_proto_db_proto_rawDescGZIP() []byte { + file_proto_db_proto_rawDescOnce.Do(func() { + file_proto_db_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_db_proto_rawDescData) + }) + return file_proto_db_proto_rawDescData +} + +var file_proto_db_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_proto_db_proto_goTypes = []interface{}{ + (*ReadRequest)(nil), // 0: db.ReadRequest + (*ReadResponse)(nil), // 1: db.ReadResponse + (*CreateRequest)(nil), // 2: db.CreateRequest + (*CreateResponse)(nil), // 3: db.CreateResponse + (*UpdateRequest)(nil), // 4: db.UpdateRequest + (*UpdateResponse)(nil), // 5: db.UpdateResponse + (*DeleteRequest)(nil), // 6: db.DeleteRequest + (*DeleteResponse)(nil), // 7: db.DeleteResponse +} +var file_proto_db_proto_depIdxs = []int32{ + 2, // 0: db.Db.Create:input_type -> db.CreateRequest + 0, // 1: db.Db.Read:input_type -> db.ReadRequest + 4, // 2: db.Db.Update:input_type -> db.UpdateRequest + 6, // 3: db.Db.Delete:input_type -> db.DeleteRequest + 3, // 4: db.Db.Create:output_type -> db.CreateResponse + 1, // 5: db.Db.Read:output_type -> db.ReadResponse + 5, // 6: db.Db.Update:output_type -> db.UpdateResponse + 7, // 7: db.Db.Delete:output_type -> db.DeleteResponse + 4, // [4:8] is the sub-list for method output_type + 0, // [0:4] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_proto_db_proto_init() } +func file_proto_db_proto_init() { + if File_proto_db_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_db_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_db_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_db_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_db_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_db_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_db_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UpdateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_db_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_db_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_db_proto_rawDesc, + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_db_proto_goTypes, + DependencyIndexes: file_proto_db_proto_depIdxs, + MessageInfos: file_proto_db_proto_msgTypes, + }.Build() + File_proto_db_proto = out.File + file_proto_db_proto_rawDesc = nil + file_proto_db_proto_goTypes = nil + file_proto_db_proto_depIdxs = nil +} diff --git a/db/proto/db.pb.micro.go b/db/proto/db.pb.micro.go new file mode 100644 index 0000000..48732c1 --- /dev/null +++ b/db/proto/db.pb.micro.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: proto/db.proto + +package db + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +import ( + context "context" + api "github.com/micro/micro/v3/service/api" + client "github.com/micro/micro/v3/service/client" + server "github.com/micro/micro/v3/service/server" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ api.Endpoint +var _ context.Context +var _ client.Option +var _ server.Option + +// Api Endpoints for Db service + +func NewDbEndpoints() []*api.Endpoint { + return []*api.Endpoint{} +} + +// Client API for Db service + +type DbService interface { + Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) + Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) + Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, error) + Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) +} + +type dbService struct { + c client.Client + name string +} + +func NewDbService(name string, c client.Client) DbService { + return &dbService{ + c: c, + name: name, + } +} + +func (c *dbService) Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) { + req := c.c.NewRequest(c.name, "Db.Create", in) + out := new(CreateResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dbService) Read(ctx context.Context, in *ReadRequest, opts ...client.CallOption) (*ReadResponse, error) { + req := c.c.NewRequest(c.name, "Db.Read", in) + out := new(ReadResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dbService) Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, error) { + req := c.c.NewRequest(c.name, "Db.Update", in) + out := new(UpdateResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *dbService) Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) { + req := c.c.NewRequest(c.name, "Db.Delete", in) + out := new(DeleteResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Db service + +type DbHandler interface { + Create(context.Context, *CreateRequest, *CreateResponse) error + Read(context.Context, *ReadRequest, *ReadResponse) error + Update(context.Context, *UpdateRequest, *UpdateResponse) error + Delete(context.Context, *DeleteRequest, *DeleteResponse) error +} + +func RegisterDbHandler(s server.Server, hdlr DbHandler, opts ...server.HandlerOption) error { + type db interface { + Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error + Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error + Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error + Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error + } + type Db struct { + db + } + h := &dbHandler{hdlr} + return s.Handle(s.NewHandler(&Db{h}, opts...)) +} + +type dbHandler struct { + DbHandler +} + +func (h *dbHandler) Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error { + return h.DbHandler.Create(ctx, in, out) +} + +func (h *dbHandler) Read(ctx context.Context, in *ReadRequest, out *ReadResponse) error { + return h.DbHandler.Read(ctx, in, out) +} + +func (h *dbHandler) Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error { + return h.DbHandler.Update(ctx, in, out) +} + +func (h *dbHandler) Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error { + return h.DbHandler.Delete(ctx, in, out) +} diff --git a/db/proto/db.proto b/db/proto/db.proto new file mode 100644 index 0000000..759c635 --- /dev/null +++ b/db/proto/db.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; + +package db; + +option go_package = "github.com/micro/services/proto;db"; + +service Db { + rpc Create(CreateRequest) returns (CreateResponse) {} + rpc Read(ReadRequest) returns (ReadResponse) {} + rpc Update(UpdateRequest) returns (UpdateResponse) {} + rpc Delete(DeleteRequest) returns (DeleteResponse) {} +} + + +message ReadRequest { + string table = 1; + // eg. 'age >= 18', 'age >= 18 and verified == true' + // comparison operators: '==', '!=', '<', '>', '<=', '>=' + // logical operator: 'and' + string query = 2; + int32 offset = 3; + int32 limit = 4; + // field name to order by + string orderBy = 5; + // 'asc' (default), 'desc' + string order = 6; +} + +message ReadResponse { + // JSON encoded records + string records = 1; +} + +message CreateRequest { + string table = 1; + // JSON encoded record or records (can be array or object) + string record = 2; +} + +message CreateResponse { + +} + +message UpdateRequest { + string table = 1; + // JSON encoded record or records (can be array or object) + string record = 2; +} + +message UpdateResponse { + +} + +message DeleteRequest { + string table = 1; + // id or ids, eg. 'user-1', or comma separated ids 'user-1,user-2' + string id = 2; +} + +message DeleteResponse { + +} \ No newline at end of file diff --git a/go.mod b/go.mod index 44022f2..80d332c 100644 --- a/go.mod +++ b/go.mod @@ -6,16 +6,19 @@ require ( github.com/Masterminds/semver/v3 v3.1.1 github.com/PuerkitoBio/goquery v1.6.1 github.com/SlyMarbo/rss v1.0.1 + github.com/crufter/lexer v0.0.0-20120907053443-23fe8c7add01 github.com/cdipaolo/goml v0.0.0-20190412180403-e1f51f713598 // indirect github.com/cdipaolo/sentiment v0.0.0-20200617002423-c697f64e7f10 github.com/disintegration/imaging v1.6.2 github.com/getkin/kin-openapi v0.26.0 + github.com/go-pg/pg v8.0.7+incompatible github.com/gojuno/go.osrm v0.1.1-0.20200217151037-435fc3e1d3d4 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.1 github.com/google/uuid v1.1.2 github.com/hailocab/go-geoindex v0.0.0-20160127134810-64631bfe9711 github.com/hashicorp/golang-lru v0.5.3 + github.com/jackc/pgx/v4 v4.10.1 github.com/lib/pq v1.9.0 // indirect github.com/micro/dev v0.0.0-20201117163752-d3cfc9788dfa github.com/micro/micro/v3 v3.2.2-0.20210521145845-34133d0728d2 @@ -27,8 +30,8 @@ require ( github.com/stretchr/testify v1.7.0 github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf go.opencensus.io v0.22.4 // indirect - golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 - golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a // indirect golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 // indirect google.golang.org/genproto v0.0.0-20201001141541-efaab9d3c4f7 // indirect @@ -36,8 +39,10 @@ require ( google.golang.org/protobuf v1.26.0 googlemaps.github.io/maps v1.3.1 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect - gorm.io/driver/postgres v1.0.6 - gorm.io/gorm v1.20.9 + gorm.io/datatypes v1.0.1 + gorm.io/driver/postgres v1.0.8 + gorm.io/gorm v1.21.6 + mellium.im/sasl v0.2.1 // indirect ) replace google.golang.org/grpc => google.golang.org/grpc v1.26.0 diff --git a/go.sum b/go.sum index 0f4ea66..1f4d3e4 100644 --- a/go.sum +++ b/go.sum @@ -86,9 +86,12 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crufter/lexer v0.0.0-20120907053443-23fe8c7add01 h1:3BwOWuIBrZtUqFUnGKHXhyBxF8l1BKX+5N7V5TgDLhU= +github.com/crufter/lexer v0.0.0-20120907053443-23fe8c7add01/go.mod h1:UzTRPLzI3RwH4cTSc8nYrXN2dCjwRNGZBzHSc0RE7pE= 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/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -127,6 +130,9 @@ github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-pg/pg v8.0.7+incompatible h1:ty/sXL1OZLo+47KK9N8llRcmbA9tZasqbQ/OO4ld53g= +github.com/go-pg/pg v8.0.7+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= @@ -138,6 +144,7 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/gojuno/go.osrm v0.1.1-0.20200217151037-435fc3e1d3d4 h1:ZhyiVDRMAdbMPFmzJMAK3GVbUG5abPRUMC9jySXcfCU= github.com/gojuno/go.osrm v0.1.1-0.20200217151037-435fc3e1d3d4/go.mod h1:XPCHB/Ir2/vHnqhKlfUxIiUGHFtTzgrRxD89JdkJhrs= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -276,6 +283,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E= github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= +github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -331,6 +340,7 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/micro/dev v0.0.0-20201117163752-d3cfc9788dfa h1:1BoFPE4/NTF7WKLZWsEFImOsN143QAU7Dkw9J2/qFXA= @@ -508,8 +518,10 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -524,6 +536,8 @@ golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -577,6 +591,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjN golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -624,11 +640,13 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -745,14 +763,30 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.0.1 h1:6npnXbBtjpSb7FFVA2dG/llyTN8tvZfbUqs+WyLrYgQ= +gorm.io/datatypes v1.0.1/go.mod h1:HEHoUU3/PO5ZXfAJcVWl11+zWlE16+O0X2DgJEb4Ixs= +gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI= gorm.io/driver/postgres v1.0.6 h1:9sqNcNC9PCkZ6tMzWF1cEE2PARlCONgSqRobszSTffw= gorm.io/driver/postgres v1.0.6/go.mod h1:r0nvX27yHDNbVeXMM9Y+9i5xSePcT18RfH8clP6wpwI= +gorm.io/driver/postgres v1.0.8 h1:PAgM+PaHOSAeroTjHkCHCBIHHoBIf9RgPWGo8dF2DA8= +gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg= +gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= +gorm.io/driver/sqlserver v1.0.7/go.mod h1:ng66aHI47ZIKz/vvnxzDoonzmTS8HXP+JYlgg67wOog= +gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.8/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= gorm.io/gorm v1.20.9 h1:M3aIZKXAC1PtPVu9t3WGwkBTE1le5c2telz3I/qjRNg= gorm.io/gorm v1.20.9/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +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.6 h1:xEFbH7WShsnAM+HeRNv7lOeyqmDAK+dDnf1AMf/cVPQ= +gorm.io/gorm v1.21.6/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= +gorm.io/gorm v1.21.10 h1:kBGiBsaqOQ+8f6S2U6mvGFz6aWWyCeIiuaFcaBozp4M= 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-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w= +mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=