Compare commits

...

26 Commits

Author SHA1 Message Date
Kevin Wan
e5c560e8ba simplify code, format makefile (#233) 2020-11-28 22:27:58 +08:00
xuezonggui
bed494d904 optimization (#221) 2020-11-28 19:43:39 +08:00
Keson
2dfecda465 modify the service name from proto (#230) 2020-11-28 11:48:44 +08:00
voidint
3ebb1e0221 Improve Makefile robustness (#224) 2020-11-27 23:40:07 +08:00
kingxt
348184904c set default handler value (#228)
* set default value

* set default value
2020-11-26 11:57:02 +08:00
Keson
7a27fa50a1 update version (#226) 2020-11-25 12:04:22 +08:00
Kevin Wan
8d4951c990 check go.mod before build docker image (#225) 2020-11-24 23:19:31 +08:00
Keson
6e57f6c527 feature model interface (#222)
* make variable declaration more concise

* add model interface

* optimize interface methods

* fix: go test failed

* warp returns

* optimize
2020-11-24 22:36:23 +08:00
kingxt
b9ac51b6c3 feature: file namestyle (#223)
* add api filename style

* new feature: config.yaml

* optimize

* optimize logic generation

* check hanlder valid

* optimize

* reactor naming style

* optimize

* optimize test

* optimize gen middleware

* format

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-24 15:11:18 +08:00
kevin
702e8d79ce fix doc errors 2020-11-24 10:39:38 +08:00
kevin
95a9dabf8b format import 2020-11-23 16:35:39 +08:00
Chris
bae66c49c2 1.use local variable i; 2.make sure limiter larger than timer period (#218)
Co-authored-by: chris <feilee1987@163.com>
2020-11-23 16:34:51 +08:00
kingxt
e0afe0b4bb optimize api new (#216) 2020-11-19 16:48:48 +08:00
Keson
24fb29a356 patch model&rpc (#207)
* change column to read from information_schema

* reactor generate mode from datasource

* reactor generate mode from datasource

* add primary key check logic

* resolve rebase conflicts

* add naming style

* add filename test case

* resolve rebase conflicts

* reactor test

* add test case

* change shell script to makefile

* update rpc new

* update gen_test.go

* format code

* format code

* update test

* generates alias
2020-11-18 15:32:53 +08:00
kevin
71083b5e64 update readme 2020-11-17 19:01:14 +08:00
kingxt
1174f17bd9 modify image url (#213) 2020-11-17 18:50:22 +08:00
kingxt
d6d8fc21d8 type should not define nested (#212)
* nest type should not supported

* nest type should not supported

* nest type should not supported

* nest type should not supported

* new test

* new test
2020-11-17 18:08:55 +08:00
kevin
9592639cb4 add error handle tests 2020-11-17 18:04:48 +08:00
kevin
abcb28e506 support error customization 2020-11-17 17:11:06 +08:00
kingxt
a92f65580c support type def without struct token (#210)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* support type def without struct token

* optimized

* optimized

* optimized

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-17 15:25:13 +08:00
bittoy
3819f67cf4 add redis geospatial (#209)
* add redis geospatial

* fix go test error
2020-11-16 19:45:43 +08:00
kevin
295c8d2934 fix issue #205 2020-11-16 19:23:24 +08:00
kingxt
88da8685dd optimize parser (#206)
* add comment support

* add comment support

* 1. group support multi level folder
2. remove force flag

* bug fix

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* refactor parser and remove deprecated code

* optimized parser

Co-authored-by: kim <xutao@xiaoheiban.cn>
2020-11-16 10:08:28 +08:00
kevin
c7831ac96d update goctl readme 2020-11-15 21:18:02 +08:00
kevin
e898761762 update example 2020-11-15 21:15:29 +08:00
kevin
13d1c5cd00 update example 2020-11-14 22:01:35 +08:00
121 changed files with 2327 additions and 1710 deletions

View File

@@ -76,7 +76,6 @@ func (rw *RollingWindow) updateOffset() {
span := rw.span() span := rw.span()
if span > 0 { if span > 0 {
offset := rw.offset offset := rw.offset
// reset expired buckets
start := offset + 1 start := offset + 1
steps := start + span steps := start + span
var remainder int var remainder int
@@ -84,15 +83,16 @@ func (rw *RollingWindow) updateOffset() {
remainder = steps - rw.size remainder = steps - rw.size
steps = rw.size steps = rw.size
} }
// reset expired buckets
for i := start; i < steps; i++ { for i := start; i < steps; i++ {
rw.win.resetBucket(i) rw.win.resetBucket(i)
offset = i
} }
for i := 0; i < remainder; i++ { for i := 0; i < remainder; i++ {
rw.win.resetBucket(i) rw.win.resetBucket(i)
offset = i
} }
rw.offset = offset
rw.offset = (offset + span) % rw.size
rw.lastTime = timex.Now() rw.lastTime = timex.Now()
} }
} }

View File

@@ -204,6 +204,7 @@ func (tw *TimingWheel) removeTask(key interface{}) {
timer := val.(*positionEntry) timer := val.(*positionEntry)
timer.item.removed = true timer.item.removed = true
tw.timers.Del(key)
} }
func (tw *TimingWheel) run() { func (tw *TimingWheel) run() {
@@ -248,7 +249,6 @@ func (tw *TimingWheel) scanAndRunTasks(l *list.List) {
if task.removed { if task.removed {
next := e.Next() next := e.Next()
l.Remove(e) l.Remove(e)
tw.timers.Del(task.key)
e = next e = next
continue continue
} else if task.circle > 0 { } else if task.circle > 0 {
@@ -301,6 +301,7 @@ func (tw *TimingWheel) setTask(task *timingEntry) {
func (tw *TimingWheel) setTimerPosition(pos int, task *timingEntry) { func (tw *TimingWheel) setTimerPosition(pos int, task *timingEntry) {
if val, ok := tw.timers.Get(task.key); ok { if val, ok := tw.timers.Get(task.key); ok {
timer := val.(*positionEntry) timer := val.(*positionEntry)
timer.item = task
timer.pos = pos timer.pos = pos
} else { } else {
tw.timers.Set(task.key, &positionEntry{ tw.timers.Set(task.key, &positionEntry{

View File

@@ -594,6 +594,31 @@ func TestTimingWheel_ElapsedAndSetThenMove(t *testing.T) {
} }
} }
func TestMoveAndRemoveTask(t *testing.T) {
ticker := timex.NewFakeTicker()
tick := func(v int) {
for i := 0; i < v; i++ {
ticker.Tick()
}
}
var keys []int
tw, _ := newTimingWheelWithClock(testStep, 10, func(k, v interface{}) {
assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int))
keys = append(keys, v.(int))
ticker.Done()
}, ticker)
defer tw.Stop()
tw.SetTimer("any", 3, testStep*8)
tick(6)
tw.MoveTimer("any", testStep*7)
tick(3)
tw.RemoveTimer("any")
tick(30)
time.Sleep(time.Millisecond)
assert.Equal(t, 0, len(keys))
}
func BenchmarkTimingWheel(b *testing.B) { func BenchmarkTimingWheel(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()

View File

@@ -3,7 +3,7 @@ package limit
import ( import (
"testing" "testing"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/stores/redis" "github.com/tal-tech/go-zero/core/stores/redis"
"github.com/tal-tech/go-zero/core/stores/redis/redistest" "github.com/tal-tech/go-zero/core/stores/redis/redistest"

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/redis" "github.com/tal-tech/go-zero/core/stores/redis"

View File

@@ -9,7 +9,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/mathx" "github.com/tal-tech/go-zero/core/mathx"

View File

@@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/hash" "github.com/tal-tech/go-zero/core/hash"
"github.com/tal-tech/go-zero/core/stores/cache" "github.com/tal-tech/go-zero/core/stores/cache"

View File

@@ -42,6 +42,12 @@ type (
red.Cmdable red.Cmdable
} }
// GeoLocation is used with GeoAdd to add geospatial location.
GeoLocation = red.GeoLocation
// GeoRadiusQuery is used with GeoRadius to query geospatial index.
GeoRadiusQuery = red.GeoRadiusQuery
GeoPos = red.GeoPos
Pipeliner = red.Pipeliner Pipeliner = red.Pipeliner
// Z represents sorted set member. // Z represents sorted set member.
@@ -173,6 +179,107 @@ func (s *Redis) Expireat(key string, expireTime int64) error {
}, acceptable) }, acceptable)
} }
func (s *Redis) GeoAdd(key string, geoLocation ...*GeoLocation) (val int64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoAdd(key, geoLocation...).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoDist(key string, member1, member2, unit string) (val float64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoDist(key, member1, member2, unit).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoHash(key string, members ...string) (val []string, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoHash(key, members...).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) (val []GeoLocation, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoRadius(key, longitude, latitude, query).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) (val []GeoLocation, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoRadiusByMember(key, member, query).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) GeoPos(key string, members ...string) (val []*GeoPos, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
if v, err := conn.GeoPos(key, members...).Result(); err != nil {
return err
} else {
val = v
return nil
}
}, acceptable)
return
}
func (s *Redis) Get(key string) (val string, err error) { func (s *Redis) Get(key string) (val string, err error) {
err = s.brk.DoWithAcceptable(func() error { err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s) conn, err := getRedis(s)

View File

@@ -6,7 +6,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis/v2"
red "github.com/go-redis/redis" red "github.com/go-redis/redis"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -816,6 +816,38 @@ func TestRedisBlpopEx(t *testing.T) {
}) })
} }
func TestRedisGeo(t *testing.T) {
runOnRedis(t, func(client *Redis) {
client.Ping()
var geoLocation = []*GeoLocation{{Longitude: 13.361389, Latitude: 38.115556, Name: "Palermo"}, {Longitude: 15.087269, Latitude: 37.502669, Name: "Catania"}}
v, err := client.GeoAdd("sicily", geoLocation...)
assert.Nil(t, err)
assert.Equal(t, int64(2), v)
v2, err := client.GeoDist("sicily", "Palermo", "Catania", "m")
assert.Nil(t, err)
assert.Equal(t, 166274, int(v2))
// GeoHash not support
v3, err := client.GeoPos("sicily", "Palermo", "Catania")
assert.Nil(t, err)
assert.Equal(t, int64(v3[0].Longitude), int64(13))
assert.Equal(t, int64(v3[0].Latitude), int64(38))
assert.Equal(t, int64(v3[1].Longitude), int64(15))
assert.Equal(t, int64(v3[1].Latitude), int64(37))
v4, err := client.GeoRadius("sicily", 15, 37, &red.GeoRadiusQuery{WithDist: true, Unit: "km", Radius: 200})
assert.Nil(t, err)
assert.Equal(t, int64(v4[0].Dist), int64(190))
assert.Equal(t, int64(v4[1].Dist), int64(56))
var geoLocation2 = []*GeoLocation{{Longitude: 13.583333, Latitude: 37.316667, Name: "Agrigento"}}
v5, err := client.GeoAdd("sicily", geoLocation2...)
assert.Nil(t, err)
assert.Equal(t, int64(1), v5)
v6, err := client.GeoRadiusByMember("sicily", "Agrigento", &red.GeoRadiusQuery{Unit: "km", Radius: 100})
assert.Nil(t, err)
assert.Equal(t, v6[0].Name, "Agrigento")
assert.Equal(t, v6[1].Name, "Palermo")
})
}
func runOnRedis(t *testing.T, fn func(client *Redis)) { func runOnRedis(t *testing.T, fn func(client *Redis)) {
s, err := miniredis.Run() s, err := miniredis.Run()
assert.Nil(t, err) assert.Nil(t, err)

View File

@@ -3,7 +3,7 @@ package redistest
import ( import (
"time" "time"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis/v2"
"github.com/tal-tech/go-zero/core/lang" "github.com/tal-tech/go-zero/core/lang"
"github.com/tal-tech/go-zero/core/stores/redis" "github.com/tal-tech/go-zero/core/stores/redis"
) )

View File

@@ -14,7 +14,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis/v2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/fx" "github.com/tal-tech/go-zero/core/fx"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"

View File

@@ -5,7 +5,7 @@ go 1.15
require ( require (
github.com/golang/mock v1.4.3 github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/tal-tech/go-zero v1.0.16 github.com/tal-tech/go-zero v1.0.27
golang.org/x/net v0.0.0-20200707034311-ab3426394381 golang.org/x/net v0.0.0-20200707034311-ab3426394381
google.golang.org/grpc v1.29.1 google.golang.org/grpc v1.29.1
) )

View File

@@ -36,6 +36,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28= github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -46,6 +47,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm
github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y= github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -122,9 +124,11 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
@@ -175,6 +179,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -210,6 +215,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -232,11 +238,14 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8= github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8=
github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8= github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8=
github.com/tal-tech/go-zero v1.0.27 h1:QMIbaTxibMc/OsO5RTAuKZ8ndbl2dGN6pITQEtp2x/A=
github.com/tal-tech/go-zero v1.0.27/go.mod h1:JtNXlsh/CgeIHyQnt5C5M2IcSevW7V0NAnqO93TQgm8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
@@ -382,6 +391,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -393,6 +403,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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-20190523083050-ea95bdfd59fc/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= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

View File

@@ -18,7 +18,7 @@ var (
bookRowsExpectAutoSet = strings.Join(stringx.Remove(bookFieldNames, "create_time", "update_time"), ",") bookRowsExpectAutoSet = strings.Join(stringx.Remove(bookFieldNames, "create_time", "update_time"), ",")
bookRowsWithPlaceHolder = strings.Join(stringx.Remove(bookFieldNames, "book", "create_time", "update_time"), "=?,") + "=?" bookRowsWithPlaceHolder = strings.Join(stringx.Remove(bookFieldNames, "book", "create_time", "update_time"), "=?,") + "=?"
cacheBookBookPrefix = "cache#Book#book#" bookPrefix = "cache#Book#book#"
) )
type ( type (
@@ -46,9 +46,9 @@ func (m *BookModel) Insert(data Book) (sql.Result, error) {
} }
func (m *BookModel) FindOne(book string) (*Book, error) { func (m *BookModel) FindOne(book string) (*Book, error) {
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, book) bookKey := fmt.Sprintf("%s%v", bookPrefix, book)
var resp Book var resp Book
err := m.QueryRow(&resp, bookBookKey, func(conn sqlx.SqlConn, v interface{}) error { err := m.QueryRow(&resp, bookKey, func(conn sqlx.SqlConn, v interface{}) error {
query := `select ` + bookRows + ` from ` + m.table + ` where book = ? limit 1` query := `select ` + bookRows + ` from ` + m.table + ` where book = ? limit 1`
return conn.QueryRow(v, query, book) return conn.QueryRow(v, query, book)
}) })
@@ -63,20 +63,19 @@ func (m *BookModel) FindOne(book string) (*Book, error) {
} }
func (m *BookModel) Update(data Book) error { func (m *BookModel) Update(data Book) error {
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, data.Book) bookKey := fmt.Sprintf("%s%v", bookPrefix, data.Book)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := `update ` + m.table + ` set ` + bookRowsWithPlaceHolder + ` where book = ?` query := `update ` + m.table + ` set ` + bookRowsWithPlaceHolder + ` where book = ?`
return conn.Exec(query, data.Price, data.Book) return conn.Exec(query, data.Price, data.Book)
}, bookBookKey) }, bookKey)
return err return err
} }
func (m *BookModel) Delete(book string) error { func (m *BookModel) Delete(book string) error {
bookKey := fmt.Sprintf("%s%v", bookPrefix, book)
bookBookKey := fmt.Sprintf("%s%v", cacheBookBookPrefix, book)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := `delete from ` + m.table + ` where book = ?` query := `delete from ` + m.table + ` where book = ?`
return conn.Exec(query, book) return conn.Exec(query, book)
}, bookBookKey) }, bookKey)
return err return err
} }

View File

@@ -41,6 +41,7 @@ func main() {
var allowed, denied int32 var allowed, denied int32
var wait sync.WaitGroup var wait sync.WaitGroup
for i := 0; i < *threads; i++ { for i := 0; i < *threads; i++ {
i := i
wait.Add(1) wait.Add(1)
go func() { go func() {
for { for {

View File

@@ -5,7 +5,7 @@ go 1.15
require ( require (
github.com/golang/mock v1.4.3 github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/tal-tech/go-zero v1.0.16 github.com/tal-tech/go-zero v1.0.27
golang.org/x/net v0.0.0-20200707034311-ab3426394381 golang.org/x/net v0.0.0-20200707034311-ab3426394381
google.golang.org/grpc v1.29.1 google.golang.org/grpc v1.29.1
) )

View File

@@ -37,6 +37,7 @@ github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQa
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -48,6 +49,7 @@ github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819 h1:9778zj477h/VauD8
github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y= github.com/dsymonds/gotoc v0.0.0-20160928043926-5aebcfc91819/go.mod h1:MvzMVHq8BH2Ji/o8TGDocVA70byvLrAgFTxkEnmjO4Y=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -124,10 +126,12 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY= github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0= github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
@@ -179,6 +183,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -215,6 +220,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
@@ -238,12 +244,15 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8= github.com/tal-tech/go-zero v1.0.16 h1:oT7sOFftEUdD/XcXF0xEugX9yhnw4DcQkeMNFLi5KO8=
github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8= github.com/tal-tech/go-zero v1.0.16/go.mod h1:y2wBHTkxNJw79K9/wCSeDKzv2pCT6x45oOmXEsJdQK8=
github.com/tal-tech/go-zero v1.0.27 h1:QMIbaTxibMc/OsO5RTAuKZ8ndbl2dGN6pITQEtp2x/A=
github.com/tal-tech/go-zero v1.0.27/go.mod h1:JtNXlsh/CgeIHyQnt5C5M2IcSevW7V0NAnqO93TQgm8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA= github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
@@ -390,6 +399,7 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -401,6 +411,8 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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-20190523083050-ea95bdfd59fc/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= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

5
go.mod
View File

@@ -5,8 +5,7 @@ go 1.13
require ( require (
github.com/ClickHouse/clickhouse-go v1.4.3 github.com/ClickHouse/clickhouse-go v1.4.3
github.com/DATA-DOG/go-sqlmock v1.4.1 github.com/DATA-DOG/go-sqlmock v1.4.1
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect github.com/alicebob/miniredis/v2 v2.14.1
github.com/alicebob/miniredis v2.5.0+incompatible
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/dchest/siphash v1.2.1 github.com/dchest/siphash v1.2.1
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
@@ -21,7 +20,6 @@ require (
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/mock v1.4.3 github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/gomodule/redigo v2.0.0+incompatible // indirect
github.com/google/gops v0.3.7 github.com/google/gops v0.3.7
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/gorilla/websocket v1.4.2 // indirect github.com/gorilla/websocket v1.4.2 // indirect
@@ -47,7 +45,6 @@ require (
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
github.com/urfave/cli v1.22.5 github.com/urfave/cli v1.22.5
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb // indirect
go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698 go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698
go.uber.org/automaxprocs v1.3.0 go.uber.org/automaxprocs v1.3.0
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect

16
go.sum
View File

@@ -11,10 +11,10 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= github.com/alicebob/miniredis/v2 v2.14.1 h1:GjlbSeoJ24bzdLRs13HoMEeaRZx9kg5nHoRW7QV/nCs=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/alicebob/miniredis/v2 v2.14.1/go.mod h1:uS970Sw5Gs9/iK3yBg0l9Uj9s25wXxSpQUE9EaJ/Blg=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -114,8 +114,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -147,8 +145,6 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U= github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
@@ -211,6 +207,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
@@ -281,8 +278,6 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
@@ -425,6 +420,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=

View File

@@ -174,7 +174,7 @@ goctl api -o greet.api
you can check it by curl: you can check it by curl:
```shell ```shell
curl -i http://localhost:8888/greet/from/you curl -i http://localhost:8888/from/you
``` ```
the response looks like: the response looks like:

View File

@@ -1,4 +1,4 @@
<img align="right" width="150px" src="doc/images/go-zero.png"> <img align="right" width="150px" src="https://gitee.com/kevwan/static/raw/master/doc/images/go-zero.png">
# go-zero # go-zero
@@ -25,7 +25,7 @@ go-zero 包含极简的 API 定义和生成工具 goctl可以根据定义的
* 自动校验客户端请求参数合法性 * 自动校验客户端请求参数合法性
* 大量微服务治理和并发工具包 * 大量微服务治理和并发工具包
<img src="doc/images/architecture.png" alt="架构图" width="1500" /> <img src="https://gitee.com/kevwan/static/raw/master/doc/images/architecture.png" alt="架构图" width="1500" />
## 1. go-zero 框架背景 ## 1. go-zero 框架背景
@@ -77,7 +77,7 @@ go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有
如下图,我们从多个层面保障了整体服务的高可用: 如下图,我们从多个层面保障了整体服务的高可用:
![弹性设计](doc/images/resilience.jpg) ![弹性设计](https://gitee.com/kevwan/static/raw/master/doc/images/resilience.jpg)
觉得不错的话,别忘 **star** 👏 觉得不错的话,别忘 **star** 👏
@@ -120,7 +120,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求: 默认侦听在 8888 端口(可以在配置文件里修改),可以通过 curl 请求:
```shell ```shell
curl -i http://localhost:8888/greet/from/you curl -i http://localhost:8888/from/you
``` ```
返回如下: 返回如下:
@@ -150,7 +150,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
## 6. Benchmark ## 6. Benchmark
![benchmark](doc/images/benchmark.png) ![benchmark](https://gitee.com/kevwan/static/raw/master/doc/images/benchmark.png)
[测试代码见这里](https://github.com/smallnest/go-web-framework-benchmark) [测试代码见这里](https://github.com/smallnest/go-web-framework-benchmark)
@@ -189,5 +189,3 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
项目地址:[https://github.com/tal-tech/go-zero](https://github.com/tal-tech/go-zero) 项目地址:[https://github.com/tal-tech/go-zero](https://github.com/tal-tech/go-zero)
码云地址:[https://gitee.com/kevwan/go-zero](https://gitee.com/kevwan/go-zero) (国内用户可访问gitee每日自动从github同步代码) 码云地址:[https://gitee.com/kevwan/go-zero](https://gitee.com/kevwan/go-zero) (国内用户可访问gitee每日自动从github同步代码)
开源中国年度评选,给 **go-zero** 投上一票:[https://www.oschina.net/p/go-zero](https://www.oschina.net/p/go-zero)

View File

@@ -26,11 +26,11 @@ func ContentSecurityHandler(decrypters map[string]codec.RsaDecrypter, tolerance
case http.MethodDelete, http.MethodGet, http.MethodPost, http.MethodPut: case http.MethodDelete, http.MethodGet, http.MethodPost, http.MethodPut:
header, err := security.ParseContentSecurity(decrypters, r) header, err := security.ParseContentSecurity(decrypters, r)
if err != nil { if err != nil {
logx.Infof("Signature parse failed, X-Content-Security: %s, error: %s", logx.Errorf("Signature parse failed, X-Content-Security: %s, error: %s",
r.Header.Get(contentSecurity), err.Error()) r.Header.Get(contentSecurity), err.Error())
executeCallbacks(w, r, next, strict, httpx.CodeSignatureInvalidHeader, callbacks) executeCallbacks(w, r, next, strict, httpx.CodeSignatureInvalidHeader, callbacks)
} else if code := security.VerifySignature(r, header, tolerance); code != httpx.CodeSignaturePass { } else if code := security.VerifySignature(r, header, tolerance); code != httpx.CodeSignaturePass {
logx.Infof("Signature verification failed, X-Content-Security: %s", logx.Errorf("Signature verification failed, X-Content-Security: %s",
r.Header.Get(contentSecurity)) r.Header.Get(contentSecurity))
executeCallbacks(w, r, next, strict, code, callbacks) executeCallbacks(w, r, next, strict, code, callbacks)
} else if r.ContentLength > 0 && header.Encrypted() { } else if r.ContentLength > 0 && header.Encrypted() {
@@ -54,7 +54,7 @@ func executeCallbacks(w http.ResponseWriter, r *http.Request, next http.Handler,
func handleVerificationFailure(w http.ResponseWriter, r *http.Request, next http.Handler, strict bool, code int) { func handleVerificationFailure(w http.ResponseWriter, r *http.Request, next http.Handler, strict bool, code int) {
if strict { if strict {
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusForbidden)
} else { } else {
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
} }

View File

@@ -113,7 +113,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true, strict: true,
crypt: true, crypt: true,
timestamp: time.Now().Add(timeDiff).Unix(), timestamp: time.Now().Add(timeDiff).Unix(),
statusCode: http.StatusUnauthorized, statusCode: http.StatusForbidden,
}, },
{ {
method: http.MethodPost, method: http.MethodPost,
@@ -122,7 +122,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true, strict: true,
crypt: true, crypt: true,
timestamp: time.Now().Add(-timeDiff).Unix(), timestamp: time.Now().Add(-timeDiff).Unix(),
statusCode: http.StatusUnauthorized, statusCode: http.StatusForbidden,
}, },
{ {
method: http.MethodPost, method: http.MethodPost,
@@ -148,7 +148,7 @@ func TestContentSecurityHandler(t *testing.T) {
crypt: true, crypt: true,
timestamp: time.Now().Add(-timeDiff).Unix(), timestamp: time.Now().Add(-timeDiff).Unix(),
fingerprint: "badone", fingerprint: "badone",
statusCode: http.StatusUnauthorized, statusCode: http.StatusForbidden,
}, },
{ {
method: http.MethodPost, method: http.MethodPost,
@@ -157,7 +157,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true, strict: true,
crypt: true, crypt: true,
missHeader: true, missHeader: true,
statusCode: http.StatusUnauthorized, statusCode: http.StatusForbidden,
}, },
{ {
method: http.MethodHead, method: http.MethodHead,
@@ -171,7 +171,7 @@ func TestContentSecurityHandler(t *testing.T) {
strict: true, strict: true,
crypt: false, crypt: false,
signature: "badone", signature: "badone",
statusCode: http.StatusUnauthorized, statusCode: http.StatusForbidden,
}, },
} }

View File

@@ -3,12 +3,33 @@ package httpx
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"sync"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
) )
var (
errorHandler func(error) (int, interface{})
lock sync.RWMutex
)
func Error(w http.ResponseWriter, err error) { func Error(w http.ResponseWriter, err error) {
http.Error(w, err.Error(), http.StatusBadRequest) lock.RLock()
handler := errorHandler
lock.RUnlock()
if handler == nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
code, body := errorHandler(err)
e, ok := body.(error)
if ok {
http.Error(w, e.Error(), code)
} else {
WriteJson(w, code, body)
}
} }
func Ok(w http.ResponseWriter) { func Ok(w http.ResponseWriter) {
@@ -19,6 +40,12 @@ func OkJson(w http.ResponseWriter, v interface{}) {
WriteJson(w, http.StatusOK, v) WriteJson(w, http.StatusOK, v)
} }
func SetErrorHandler(handler func(error) (int, interface{})) {
lock.Lock()
defer lock.Unlock()
errorHandler = handler
}
func WriteJson(w http.ResponseWriter, code int, v interface{}) { func WriteJson(w http.ResponseWriter, code int, v interface{}) {
w.Header().Set(ContentType, ApplicationJson) w.Header().Set(ContentType, ApplicationJson)
w.WriteHeader(code) w.WriteHeader(code)

View File

@@ -19,13 +19,65 @@ func init() {
} }
func TestError(t *testing.T) { func TestError(t *testing.T) {
const body = "foo" const (
w := tracedResponseWriter{ body = "foo"
headers: make(map[string][]string), wrappedBody = `"foo"`
)
tests := []struct {
name string
input string
errorHandler func(error) (int, interface{})
expectBody string
expectCode int
}{
{
name: "default error handler",
input: body,
expectBody: body,
expectCode: http.StatusBadRequest,
},
{
name: "customized error handler return string",
input: body,
errorHandler: func(err error) (int, interface{}) {
return http.StatusForbidden, err.Error()
},
expectBody: wrappedBody,
expectCode: http.StatusForbidden,
},
{
name: "customized error handler return error",
input: body,
errorHandler: func(err error) (int, interface{}) {
return http.StatusForbidden, err
},
expectBody: body,
expectCode: http.StatusForbidden,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
w := tracedResponseWriter{
headers: make(map[string][]string),
}
if test.errorHandler != nil {
lock.RLock()
prev := errorHandler
lock.RUnlock()
SetErrorHandler(test.errorHandler)
defer func() {
lock.Lock()
errorHandler = prev
lock.Unlock()
}()
}
Error(&w, errors.New(test.input))
assert.Equal(t, test.expectCode, w.code)
assert.Equal(t, test.expectBody, strings.TrimSpace(w.builder.String()))
})
} }
Error(&w, errors.New(body))
assert.Equal(t, http.StatusBadRequest, w.code)
assert.Equal(t, body, strings.TrimSpace(w.builder.String()))
} }
func TestOk(t *testing.T) { func TestOk(t *testing.T) {

View File

@@ -2,13 +2,13 @@ version := $(shell /bin/date "+%Y-%m-%d %H:%M")
build: build:
go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" goctl.go go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" goctl.go
command -v upx &> /dev/null && upx goctl $(if $(shell command -v upx), upx goctl)
mac: mac:
GOOS=darwin go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-darwin goctl.go GOOS=darwin go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-darwin goctl.go
command -v upx &> /dev/null && upx goctl-darwin $(if $(shell command -v upx), upx goctl-darwin)
win: win:
GOOS=windows go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl.exe goctl.go GOOS=windows go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl.exe goctl.go
command -v upx &> /dev/null && upx goctl.exe $(if $(shell command -v upx), upx goctl.exe)
linux: linux:
GOOS=linux go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-linux goctl.go GOOS=linux go build -ldflags="-s -w" -ldflags="-X 'main.BuildTime=$(version)'" -o goctl-linux goctl.go
command -v upx &> /dev/null && upx goctl-linux $(if $(shell command -v upx), upx goctl-linux)

View File

@@ -28,10 +28,10 @@ type response struct {
} }
service {{.serviceName}} { service {{.serviceName}} {
@handler // TODO: set handler name and delete this comment @handler GetUser // TODO: set handler name and delete this comment
get /users/id/:userId(request) returns(response) get /users/id/:userId(request) returns(response)
@handler // TODO: set handler name and delete this comment @handler CreateUser // TODO: set handler name and delete this comment
post /users/create(request) post /users/create(request)
} }
` `

View File

@@ -4,13 +4,10 @@ import (
"bufio" "bufio"
"errors" "errors"
"fmt" "fmt"
"go/format"
"go/scanner" "go/scanner"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strconv"
"strings" "strings"
"github.com/tal-tech/go-zero/core/errorx" "github.com/tal-tech/go-zero/core/errorx"
@@ -19,8 +16,11 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var ( const (
reg = regexp.MustCompile("type (?P<name>.*)[\\s]+{") leftParenthesis = "("
rightParenthesis = ")"
leftBrace = "{"
rightBrace = "}"
) )
func GoFormatApi(c *cli.Context) error { func GoFormatApi(c *cli.Context) error {
@@ -95,86 +95,32 @@ func ApiFormatByPath(apiFilePath string) error {
} }
func apiFormat(data string) (string, error) { func apiFormat(data string) (string, error) {
r := reg.ReplaceAllStringFunc(data, func(m string) string { _, err := parser.ParseApi(data)
parts := reg.FindStringSubmatch(m)
if len(parts) < 2 {
return m
}
if !strings.Contains(m, "struct") {
return "type " + parts[1] + " struct {"
}
return m
})
apiStruct, err := parser.ParseApi(r)
if err != nil { if err != nil {
return "", err return "", err
} }
info := strings.TrimSpace(apiStruct.Info)
if len(apiStruct.Service) == 0 {
return data, nil
}
fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.Type)))
if err != nil {
str := err.Error()
lineNumber := strings.Index(str, ":")
if lineNumber > 0 {
ln, err := strconv.ParseInt(str[:lineNumber], 10, 64)
if err != nil {
return "", err
}
pn := 0
if len(info) > 0 {
pn = countRune(info, '\n') + 1
}
number := int(ln) + pn + 1
return "", errors.New(fmt.Sprintf("line: %d, %s", number, str[lineNumber+1:]))
}
return "", err
}
var result string
if len(strings.TrimSpace(info)) > 0 {
result += strings.TrimSpace(info) + "\n\n"
}
if len(strings.TrimSpace(apiStruct.Imports)) > 0 {
result += strings.TrimSpace(apiStruct.Imports) + "\n\n"
}
if len(strings.TrimSpace(string(fs))) > 0 {
result += strings.TrimSpace(string(fs)) + "\n\n"
}
if len(strings.TrimSpace(apiStruct.Service)) > 0 {
result += formatService(apiStruct.Service) + "\n\n"
}
return strings.TrimSpace(result), nil
}
func formatService(str string) string {
var builder strings.Builder var builder strings.Builder
scanner := bufio.NewScanner(strings.NewReader(str)) s := bufio.NewScanner(strings.NewReader(data))
var tapCount = 0 var tapCount = 0
for scanner.Scan() { for s.Scan() {
line := strings.TrimSpace(scanner.Text()) line := strings.TrimSpace(s.Text())
if line == ")" || line == "}" { noCommentLine := util.RemoveComment(line)
if noCommentLine == rightParenthesis || noCommentLine == rightBrace {
tapCount -= 1 tapCount -= 1
} }
if tapCount < 0 {
line = strings.TrimSuffix(line, rightBrace)
line = strings.TrimSpace(line)
if strings.HasSuffix(line, leftBrace) {
tapCount += 1
}
}
util.WriteIndent(&builder, tapCount) util.WriteIndent(&builder, tapCount)
builder.WriteString(line + "\n") builder.WriteString(line + "\n")
if strings.HasSuffix(line, "(") || strings.HasSuffix(line, "{") { if strings.HasSuffix(noCommentLine, leftParenthesis) || strings.HasSuffix(noCommentLine, leftBrace) {
tapCount += 1 tapCount += 1
} }
} }
return strings.TrimSpace(builder.String()) return strings.TrimSpace(builder.String()), nil
}
func countRune(s string, r rune) int {
count := 0
for _, c := range s {
if c == r {
count++
}
}
return count
} }

View File

@@ -12,13 +12,13 @@ import (
"time" "time"
"github.com/logrusorgru/aurora" "github.com/logrusorgru/aurora"
"github.com/urfave/cli"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format" apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format"
"github.com/tal-tech/go-zero/tools/goctl/api/parser" "github.com/tal-tech/go-zero/tools/goctl/api/parser"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
) )
const tmpFile = "%s-%d" const tmpFile = "%s-%d"
@@ -28,6 +28,8 @@ var tmpDir = path.Join(os.TempDir(), "goctl")
func GoCommand(c *cli.Context) error { func GoCommand(c *cli.Context) error {
apiFile := c.String("api") apiFile := c.String("api")
dir := c.String("dir") dir := c.String("dir")
namingStyle := c.String("style")
if len(apiFile) == 0 { if len(apiFile) == 0 {
return errors.New("missing -api") return errors.New("missing -api")
} }
@@ -35,10 +37,10 @@ func GoCommand(c *cli.Context) error {
return errors.New("missing -dir") return errors.New("missing -dir")
} }
return DoGenProject(apiFile, dir) return DoGenProject(apiFile, dir, namingStyle)
} }
func DoGenProject(apiFile, dir string) error { func DoGenProject(apiFile, dir, style string) error {
p, err := parser.NewParser(apiFile) p, err := parser.NewParser(apiFile)
if err != nil { if err != nil {
return err return err
@@ -48,15 +50,21 @@ func DoGenProject(apiFile, dir string) error {
return err return err
} }
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
logx.Must(util.MkdirIfNotExist(dir)) logx.Must(util.MkdirIfNotExist(dir))
logx.Must(genEtc(dir, api)) logx.Must(genEtc(dir, cfg, api))
logx.Must(genConfig(dir, api)) logx.Must(genConfig(dir, cfg, api))
logx.Must(genMain(dir, api)) logx.Must(genMain(dir, cfg, api))
logx.Must(genServiceContext(dir, api)) logx.Must(genServiceContext(dir, cfg, api))
logx.Must(genTypes(dir, api)) logx.Must(genTypes(dir, cfg, api))
logx.Must(genHandlers(dir, api)) logx.Must(genRoutes(dir, cfg, api))
logx.Must(genRoutes(dir, api)) logx.Must(genHandlers(dir, cfg, api))
logx.Must(genLogic(dir, api)) logx.Must(genLogic(dir, cfg, api))
logx.Must(genMiddleware(dir, cfg, api))
if err := backupAndSweep(apiFile); err != nil { if err := backupAndSweep(apiFile); err != nil {
return err return err

View File

@@ -22,10 +22,14 @@ info(
version: 1.0 version: 1.0
) )
type Request struct { // TODO: test
// {
type Request struct { // TODO: test
// TOOD
Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // } Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // }
} } // TODO: test
// TODO: test
type Response struct { type Response struct {
Message string ` + "`" + `json:"message"` + "`" + ` Message string ` + "`" + `json:"message"` + "`" + `
} }
@@ -279,6 +283,42 @@ service A-api {
} }
` `
const noStructTagApi = `
type Request {
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
}
type XXX {}
type (
Response {
Message string ` + "`" + `json:"message"` + "`" + `
}
A {}
B struct {}
)
service A-api {
@handler GreetHandler
get /greet/from/:name(Request) returns (Response)
}
`
const nestTypeApi = `
type Request {
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
XXX struct {
}
}
service A-api {
@handler GreetHandler
get /greet/from/:name(Request)
}
`
func TestParser(t *testing.T) { func TestParser(t *testing.T) {
filename := "greet.api" filename := "greet.api"
err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm) err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
@@ -494,13 +534,55 @@ func TestHasImportApi(t *testing.T) {
} }
} }
assert.True(t, hasInline) assert.True(t, hasInline)
validate(t, filename) validate(t, filename)
} }
func TestNoStructApi(t *testing.T) {
filename := "greet.api"
err := ioutil.WriteFile(filename, []byte(noStructTagApi), os.ModePerm)
assert.Nil(t, err)
defer os.Remove(filename)
parser, err := parser.NewParser(filename)
assert.Nil(t, err)
spec, err := parser.Parse()
assert.Nil(t, err)
assert.Equal(t, len(spec.Types), 5)
validate(t, filename)
}
func TestNestTypeApi(t *testing.T) {
filename := "greet.api"
err := ioutil.WriteFile(filename, []byte(nestTypeApi), os.ModePerm)
assert.Nil(t, err)
defer os.Remove(filename)
_, err = parser.NewParser(filename)
assert.NotNil(t, err)
}
func TestCamelStyle(t *testing.T) {
filename := "greet.api"
err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
assert.Nil(t, err)
defer os.Remove(filename)
_, err = parser.NewParser(filename)
assert.Nil(t, err)
validateWithCamel(t, filename, "GoZero")
}
func validate(t *testing.T, api string) { func validate(t *testing.T, api string) {
validateWithCamel(t, api, "gozero")
}
func validateWithCamel(t *testing.T, api, camel string) {
dir := "_go" dir := "_go"
os.RemoveAll(dir) os.RemoveAll(dir)
err := DoGenProject(api, dir) err := DoGenProject(api, dir, camel)
defer os.RemoveAll(dir) defer os.RemoveAll(dir)
assert.Nil(t, err) assert.Nil(t, err)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {

View File

@@ -8,12 +8,14 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
const ( const (
configFile = "config.go" configFile = "config"
configTemplate = `package config configTemplate = `package config
import {{.authImport}} import {{.authImport}}
@@ -31,8 +33,13 @@ type Config struct {
` `
) )
func genConfig(dir string, api *spec.ApiSpec) error { func genConfig(dir string, cfg *config.Config, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, configDir, configFile) filename, err := format.FileNamingFormat(cfg.NamingFormat, configFile)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, configDir, filename+".go")
if err != nil { if err != nil {
return err return err
} }

View File

@@ -8,7 +8,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
) )
const ( const (
@@ -20,8 +22,13 @@ Port: {{.port}}
` `
) )
func genEtc(dir string, api *spec.ApiSpec) error { func genEtc(dir string, cfg *config.Config, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", api.Service.Name)) filename, err := format.FileNamingFormat(cfg.NamingFormat, api.Service.Name)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, etcDir, fmt.Sprintf("%s.yaml", filename))
if err != nil { if err != nil {
return err return err
} }

View File

@@ -2,14 +2,18 @@ package gogen
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"path" "path"
"strings" "strings"
"text/template" "text/template"
"unicode"
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
@@ -50,13 +54,8 @@ type Handler struct {
HasRequest bool HasRequest bool
} }
func genHandler(dir string, group spec.Group, route spec.Route) error { func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler") handler := getHandlerName(route)
if !ok {
return fmt.Errorf("missing handler annotation for %q", route.Path)
}
handler = getHandlerName(handler)
if getHandlerFolderPath(group, route) != handlerDir { if getHandlerFolderPath(group, route) != handlerDir {
handler = strings.Title(handler) handler = strings.Title(handler)
} }
@@ -65,27 +64,24 @@ func genHandler(dir string, group spec.Group, route spec.Route) error {
return err return err
} }
return doGenToFile(dir, handler, group, route, Handler{ return doGenToFile(dir, handler, cfg, group, route, Handler{
ImportPackages: genHandlerImports(group, route, parentPkg), ImportPackages: genHandlerImports(group, route, parentPkg),
HandlerName: handler, HandlerName: handler,
RequestType: util.Title(route.RequestType.Name), RequestType: util.Title(route.RequestType.Name),
LogicType: strings.TrimSuffix(strings.Title(handler), "Handler") + "Logic", LogicType: strings.Title(getLogicName(route)),
Call: strings.Title(strings.TrimSuffix(handler, "Handler")), Call: strings.Title(strings.TrimSuffix(handler, "Handler")),
HasResp: len(route.ResponseType.Name) > 0, HasResp: len(route.ResponseType.Name) > 0,
HasRequest: len(route.RequestType.Name) > 0, HasRequest: len(route.RequestType.Name) > 0,
}) })
} }
func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handleObj Handler) error { func doGenToFile(dir, handler string, cfg *config.Config, group spec.Group, route spec.Route, handleObj Handler) error {
if getHandlerFolderPath(group, route) != handlerDir { filename, err := format.FileNamingFormat(cfg.NamingFormat, handler)
handler = strings.Title(handler) if err != nil {
} return err
filename := strings.ToLower(handler)
if strings.HasSuffix(filename, "handler") {
filename = filename + ".go"
} else {
filename = filename + "handler.go"
} }
filename = filename + ".go"
fp, created, err := apiutil.MaybeCreateFile(dir, getHandlerFolderPath(group, route), filename) fp, created, err := apiutil.MaybeCreateFile(dir, getHandlerFolderPath(group, route), filename)
if err != nil { if err != nil {
return err return err
@@ -111,10 +107,10 @@ func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handle
return err return err
} }
func genHandlers(dir string, api *spec.ApiSpec) error { func genHandlers(dir string, cfg *config.Config, api *spec.ApiSpec) error {
for _, group := range api.Service.Groups { for _, group := range api.Service.Groups {
for _, route := range group.Routes { for _, route := range group.Routes {
if err := genHandler(dir, group, route); err != nil { if err := genHandler(dir, cfg, group, route); err != nil {
return err return err
} }
} }
@@ -136,14 +132,23 @@ func genHandlerImports(group spec.Group, route spec.Route, parentPkg string) str
return strings.Join(imports, "\n\t") return strings.Join(imports, "\n\t")
} }
func getHandlerBaseName(handler string) string { func getHandlerBaseName(route spec.Route) (string, error) {
handlerName := util.Untitle(handler) handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
if strings.HasSuffix(handlerName, "handler") { if !ok {
handlerName = strings.ReplaceAll(handlerName, "handler", "") return "", fmt.Errorf("missing handler annotation for %q", route.Path)
} else if strings.HasSuffix(handlerName, "Handler") {
handlerName = strings.ReplaceAll(handlerName, "Handler", "")
} }
return handlerName
for _, char := range handler {
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
return "", errors.New(fmt.Sprintf("route [%s] handler [%s] invalid, handler name should only contains letter or digit",
route.Path, handler))
}
}
handler = strings.TrimSpace(handler)
handler = strings.TrimSuffix(handler, "handler")
handler = strings.TrimSuffix(handler, "Handler")
return handler, nil
} }
func getHandlerFolderPath(group spec.Group, route spec.Route) string { func getHandlerFolderPath(group spec.Group, route spec.Route) string {
@@ -159,6 +164,20 @@ func getHandlerFolderPath(group spec.Group, route spec.Route) string {
return path.Join(handlerDir, folder) return path.Join(handlerDir, folder)
} }
func getHandlerName(handler string) string { func getHandlerName(route spec.Route) string {
return getHandlerBaseName(handler) + "Handler" handler, err := getHandlerBaseName(route)
if err != nil {
panic(err)
}
return handler + "Handler"
}
func getLogicName(route spec.Route) string {
handler, err := getHandlerBaseName(route)
if err != nil {
panic(err)
}
return handler + "Logic"
} }

View File

@@ -9,7 +9,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
@@ -40,10 +42,10 @@ func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
} }
` `
func genLogic(dir string, api *spec.ApiSpec) error { func genLogic(dir string, cfg *config.Config, api *spec.ApiSpec) error {
for _, g := range api.Service.Groups { for _, g := range api.Service.Groups {
for _, r := range g.Routes { for _, r := range g.Routes {
err := genLogicByRoute(dir, g, r) err := genLogicByRoute(dir, cfg, g, r)
if err != nil { if err != nil {
return err return err
} }
@@ -52,16 +54,14 @@ func genLogic(dir string, api *spec.ApiSpec) error {
return nil return nil
} }
func genLogicByRoute(dir string, group spec.Group, route spec.Route) error { func genLogicByRoute(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
handler, ok := util.GetAnnotationValue(route.Annotations, "server", "handler") logic := getLogicName(route)
if !ok { goFile, err := format.FileNamingFormat(cfg.NamingFormat, logic)
return fmt.Errorf("missing handler annotation for %q", route.Path) if err != nil {
return err
} }
handler = strings.TrimSuffix(handler, "handler") goFile = goFile + ".go"
handler = strings.TrimSuffix(handler, "Handler")
filename := strings.ToLower(handler)
goFile := filename + "logic.go"
fp, created, err := util.MaybeCreateFile(dir, getLogicFolderPath(group, route), goFile) fp, created, err := util.MaybeCreateFile(dir, getLogicFolderPath(group, route), goFile)
if err != nil { if err != nil {
return err return err
@@ -102,8 +102,8 @@ func genLogicByRoute(dir string, group spec.Group, route spec.Route) error {
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
err = t.Execute(fp, map[string]string{ err = t.Execute(fp, map[string]string{
"imports": imports, "imports": imports,
"logic": strings.Title(handler) + "Logic", "logic": strings.Title(logic),
"function": strings.Title(strings.TrimSuffix(handler, "Handler")), "function": strings.Title(strings.TrimSuffix(logic, "Logic")),
"responseType": responseString, "responseType": responseString,
"returnString": returnString, "returnString": returnString,
"request": requestString, "request": requestString,

View File

@@ -8,7 +8,9 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
@@ -40,12 +42,17 @@ func main() {
} }
` `
func genMain(dir string, api *spec.ApiSpec) error { func genMain(dir string, cfg *config.Config, api *spec.ApiSpec) error {
name := strings.ToLower(api.Service.Name) name := strings.ToLower(api.Service.Name)
if strings.HasSuffix(name, "-api") { if strings.HasSuffix(name, "-api") {
name = strings.ReplaceAll(name, "-api", "") name = strings.ReplaceAll(name, "-api", "")
} }
goFile := name + ".go" filename, err := format.FileNamingFormat(cfg.NamingFormat, name)
if err != nil {
return err
}
goFile := filename + ".go"
fp, created, err := util.MaybeCreateFile(dir, "", goFile) fp, created, err := util.MaybeCreateFile(dir, "", goFile)
if err != nil { if err != nil {
return err return err

View File

@@ -5,7 +5,10 @@ import (
"strings" "strings"
"text/template" "text/template"
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
) )
var middlewareImplementCode = ` var middlewareImplementCode = `
@@ -30,9 +33,16 @@ func (m *{{.name}})Handle(next http.HandlerFunc) http.HandlerFunc {
} }
` `
func genMiddleware(dir string, middlewares []string) error { func genMiddleware(dir string, cfg *config.Config, api *spec.ApiSpec) error {
var middlewares = getMiddleware(api)
for _, item := range middlewares { for _, item := range middlewares {
filename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "middleware" + ".go" middlewareFilename := strings.TrimSuffix(strings.ToLower(item), "middleware") + "_middleware"
formatName, err := format.FileNamingFormat(cfg.NamingFormat, middlewareFilename)
if err != nil {
return err
}
filename := formatName + ".go"
fp, created, err := util.MaybeCreateFile(dir, middlewareDir, filename) fp, created, err := util.MaybeCreateFile(dir, middlewareDir, filename)
if err != nil { if err != nil {
return err return err

View File

@@ -12,12 +12,14 @@ import (
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
const ( const (
routesFilename = "routes.go" routesFilename = "routes"
routesTemplate = `// Code generated by goctl. DO NOT EDIT. routesTemplate = `// Code generated by goctl. DO NOT EDIT.
package handler package handler
@@ -62,7 +64,7 @@ type (
} }
) )
func genRoutes(dir string, api *spec.ApiSpec) error { func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
var builder strings.Builder var builder strings.Builder
groups, err := getRoutes(api) groups, err := getRoutes(api)
if err != nil { if err != nil {
@@ -121,10 +123,16 @@ func genRoutes(dir string, api *spec.ApiSpec) error {
return err return err
} }
filename := path.Join(dir, handlerDir, routesFilename) routeFilename, err := format.FileNamingFormat(cfg.NamingFormat, routesFilename)
if err != nil {
return err
}
routeFilename = routeFilename + ".go"
filename := path.Join(dir, handlerDir, routeFilename)
os.Remove(filename) os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routesFilename) fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routeFilename)
if err != nil { if err != nil {
return err return err
} }
@@ -176,11 +184,8 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
for _, g := range api.Service.Groups { for _, g := range api.Service.Groups {
var groupedRoutes group var groupedRoutes group
for _, r := range g.Routes { for _, r := range g.Routes {
handler, ok := apiutil.GetAnnotationValue(r.Annotations, "server", "handler") handler := getHandlerName(r)
if !ok { handler = handler + "(serverCtx)"
return nil, fmt.Errorf("missing handler annotation for route %q", r.Path)
}
handler = getHandlerBaseName(handler) + "Handler(serverCtx)"
folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", groupProperty) folder, ok := apiutil.GetAnnotationValue(r.Annotations, "server", groupProperty)
if ok { if ok {
handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:] handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]

View File

@@ -8,12 +8,14 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
"github.com/tal-tech/go-zero/tools/goctl/api/util" "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util" ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/vars" "github.com/tal-tech/go-zero/tools/goctl/vars"
) )
const ( const (
contextFilename = "servicecontext.go" contextFilename = "service_context"
contextTemplate = `package svc contextTemplate = `package svc
import ( import (
@@ -35,8 +37,13 @@ func NewServiceContext(c {{.config}}) *ServiceContext {
` `
) )
func genServiceContext(dir string, api *spec.ApiSpec) error { func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error {
fp, created, err := util.MaybeCreateFile(dir, contextDir, contextFilename) filename, err := format.FileNamingFormat(cfg.NamingFormat, contextFilename)
if err != nil {
return err
}
fp, created, err := util.MaybeCreateFile(dir, contextDir, filename+".go")
if err != nil { if err != nil {
return err return err
} }
@@ -64,10 +71,6 @@ func genServiceContext(dir string, api *spec.ApiSpec) error {
var middlewareStr string var middlewareStr string
var middlewareAssignment string var middlewareAssignment string
var middlewares = getMiddleware(api) var middlewares = getMiddleware(api)
err = genMiddleware(dir, middlewares)
if err != nil {
return err
}
for _, item := range middlewares { for _, item := range middlewares {
middlewareStr += fmt.Sprintf("%s rest.Middleware\n", item) middlewareStr += fmt.Sprintf("%s rest.Middleware\n", item)

View File

@@ -12,11 +12,13 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/api/spec" "github.com/tal-tech/go-zero/tools/goctl/api/spec"
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util" apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
) )
const ( const (
typesFile = "types.go" typesFile = "types"
typesTemplate = `// Code generated by goctl. DO NOT EDIT. typesTemplate = `// Code generated by goctl. DO NOT EDIT.
package types{{if .containsTime}} package types{{if .containsTime}}
import ( import (
@@ -43,19 +45,25 @@ func BuildTypes(types []spec.Type) (string, error) {
return builder.String(), nil return builder.String(), nil
} }
func genTypes(dir string, api *spec.ApiSpec) error { func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
val, err := BuildTypes(api.Types) val, err := BuildTypes(api.Types)
if err != nil { if err != nil {
return err return err
} }
filename := path.Join(dir, typesDir, typesFile) typeFilename, err := format.FileNamingFormat(cfg.NamingFormat, typesFile)
os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typesFile)
if err != nil { if err != nil {
return err return err
} }
typeFilename = typeFilename + ".go"
filename := path.Join(dir, typesDir, typeFilename)
os.Remove(filename)
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typeFilename)
if err != nil {
return err
}
if !created { if !created {
return nil return nil
} }

View File

@@ -3,25 +3,27 @@ package new
import ( import (
"os" "os"
"path/filepath" "path/filepath"
"strings"
"text/template" "text/template"
"github.com/tal-tech/go-zero/tools/goctl/api/gogen" "github.com/tal-tech/go-zero/tools/goctl/api/gogen"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
const apiTemplate = ` const apiTemplate = `
type Request struct { type Request {
Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
} }
type Response struct { type Response {
Message string ` + "`" + `json:"message"` + "`" + ` Message string ` + "`" + `json:"message"` + "`" + `
} }
service {{.name}}-api { service {{.name}}-api {
@handler GreetHandler @handler {{.handler}}Handler
get /greet/from/:name(Request) returns (Response); get /from/:name(Request) returns (Response);
} }
` `
@@ -53,11 +55,12 @@ func NewService(c *cli.Context) error {
defer fp.Close() defer fp.Close()
t := template.Must(template.New("template").Parse(apiTemplate)) t := template.Must(template.New("template").Parse(apiTemplate))
if err := t.Execute(fp, map[string]string{ if err := t.Execute(fp, map[string]string{
"name": dirName, "name": dirName,
"handler": strings.Title(dirName),
}); err != nil { }); err != nil {
return err return err
} }
err = gogen.DoGenProject(apiFilePath, abs) err = gogen.DoGenProject(apiFilePath, abs, conf.DefaultFormat)
return err return err
} }

View File

@@ -7,6 +7,9 @@ import (
"fmt" "fmt"
"io" "io"
"strings" "strings"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/api/util"
) )
const ( const (
@@ -15,6 +18,7 @@ const (
tokenType = "type" tokenType = "type"
tokenService = "service" tokenService = "service"
tokenServiceAnnotation = "@server" tokenServiceAnnotation = "@server"
tokenStruct = "struct"
) )
type ( type (
@@ -72,7 +76,7 @@ func ParseApi(src string) (*ApiStruct, error) {
} }
} }
func (s *apiRootState) process(api *ApiStruct, token string) (apiFileState, error) { func (s *apiRootState) process(api *ApiStruct, _ string) (apiFileState, error) {
var builder strings.Builder var builder strings.Builder
for { for {
ch, err := s.readSkipComment() ch, err := s.readSkipComment()
@@ -124,7 +128,7 @@ func (s *apiInfoState) process(api *ApiStruct, token string) (apiFileState, erro
return nil, err return nil, err
} }
api.Info += "\n" + token + line api.Info += newline + token + line
token = "" token = ""
if strings.TrimSpace(line) == string(rightParenthesis) { if strings.TrimSpace(line) == string(rightParenthesis) {
return &apiRootState{s.baseState}, nil return &apiRootState{s.baseState}, nil
@@ -139,39 +143,60 @@ func (s *apiImportState) process(api *ApiStruct, token string) (apiFileState, er
} }
line = token + line line = token + line
line = util.RemoveComment(line)
if len(strings.Fields(line)) != 2 { if len(strings.Fields(line)) != 2 {
return nil, errors.New("import syntax error: " + line) return nil, errors.New("import syntax error: " + line)
} }
api.Imports += "\n" + line api.Imports += newline + line
return &apiRootState{s.baseState}, nil return &apiRootState{s.baseState}, nil
} }
func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) { func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) {
var blockCount = 0 var blockCount = 0
var braceCount = 0
for { for {
line, err := s.readLine() line, err := s.readLine()
if err != nil { if err != nil {
return nil, err return nil, err
} }
api.Type += "\n\n" + token + line line = token + line
token = "" if braceCount == 0 {
line = mayInsertStructKeyword(line)
}
api.Type += newline + newline + line
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
line = removeComment(line) line = util.RemoveComment(line)
token = ""
if strings.HasSuffix(line, leftBrace) { if strings.HasSuffix(line, leftBrace) {
blockCount++ blockCount++
braceCount++
} }
if strings.HasSuffix(line, string(leftParenthesis)) { if strings.HasSuffix(line, string(leftParenthesis)) {
blockCount++ blockCount++
} }
if strings.HasSuffix(line, string(rightBrace)) { if strings.HasSuffix(line, string(rightBrace)) {
blockCount-- blockCount--
braceCount--
} }
if strings.HasSuffix(line, string(rightParenthesis)) { if strings.HasSuffix(line, string(rightParenthesis)) {
blockCount-- blockCount--
} }
if braceCount >= 2 {
return nil, errors.New("nested type not supported: " + line)
}
if braceCount < 0 {
line = strings.TrimSuffix(line, string(rightBrace))
line = strings.TrimSpace(line)
if strings.HasSuffix(line, leftBrace) {
blockCount++
braceCount++
}
}
if blockCount == 0 { if blockCount == 0 {
return &apiRootState{s.baseState}, nil return &apiRootState{s.baseState}, nil
} }
@@ -188,9 +213,10 @@ func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, e
line = token + line line = token + line
token = "" token = ""
api.Service += "\n" + line api.Service += newline + line
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
line = removeComment(line) line = util.RemoveComment(line)
if strings.HasSuffix(line, leftBrace) { if strings.HasSuffix(line, leftBrace) {
blockCount++ blockCount++
} }
@@ -210,10 +236,33 @@ func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, e
} }
} }
func removeComment(line string) string { func mayInsertStructKeyword(line string) string {
var commentIdx = strings.Index(line, "//") line = util.RemoveComment(line)
if commentIdx >= 0 { if !strings.HasSuffix(line, leftBrace) && !strings.HasSuffix(line, string(rightBrace)) {
return line[:commentIdx] return line
} }
return line
fields := strings.Fields(line)
if stringx.Contains(fields, tokenStruct) ||
stringx.Contains(fields, tokenStruct+leftBrace) ||
stringx.Contains(fields, tokenStruct+leftBrace+string(rightBrace)) ||
len(fields) <= 1 {
return line
}
var insertIndex int
if fields[0] == tokenType {
insertIndex = 2
} else {
insertIndex = 1
}
if insertIndex >= len(fields) {
return line
}
var result []string
result = append(result, fields[:insertIndex]...)
result = append(result, tokenStruct)
result = append(result, fields[insertIndex:]...)
return strings.Join(result, " ")
} }

View File

@@ -15,9 +15,8 @@ import (
) )
type Parser struct { type Parser struct {
r *bufio.Reader r *bufio.Reader
typeDef string api *ApiStruct
api *ApiStruct
} }
func NewParser(filename string) (*Parser, error) { func NewParser(filename string) (*Parser, error) {
@@ -73,15 +72,14 @@ func NewParser(filename string) (*Parser, error) {
var buffer = new(bytes.Buffer) var buffer = new(bytes.Buffer)
buffer.WriteString(apiStruct.Service) buffer.WriteString(apiStruct.Service)
return &Parser{ return &Parser{
r: bufio.NewReader(buffer), r: bufio.NewReader(buffer),
typeDef: apiStruct.Type, api: apiStruct,
api: apiStruct,
}, nil }, nil
} }
func (p *Parser) Parse() (api *spec.ApiSpec, err error) { func (p *Parser) Parse() (api *spec.ApiSpec, err error) {
api = new(spec.ApiSpec) api = new(spec.ApiSpec)
var sp = StructParser{Src: p.typeDef} var sp = StructParser{Src: p.api.Type}
types, err := sp.Parse() types, err := sp.Parse()
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -16,4 +16,5 @@ const (
multilineBeginTag = '>' multilineBeginTag = '>'
multilineEndTag = '<' multilineEndTag = '<'
semicolon = ';' semicolon = ';'
newline = "\n"
) )

View File

@@ -26,19 +26,6 @@ func MaybeCreateFile(dir, subdir, file string) (fp *os.File, created bool, err e
return return
} }
func ClearAndOpenFile(fpath string) (*os.File, error) {
f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return nil, err
}
_, err = f.WriteString("")
if err != nil {
return nil, err
}
return f, nil
}
func WrapErr(err error, message string) error { func WrapErr(err error, message string) error {
return errors.New(message + ", " + err.Error()) return errors.New(message + ", " + err.Error())
} }
@@ -81,3 +68,11 @@ func WriteIndent(writer io.Writer, indent int) {
fmt.Fprint(writer, "\t") fmt.Fprint(writer, "\t")
} }
} }
func RemoveComment(line string) string {
var commentIdx = strings.Index(line, "//")
if commentIdx >= 0 {
return strings.TrimSpace(line[:commentIdx])
}
return strings.TrimSpace(line)
}

View File

@@ -0,0 +1,122 @@
package config
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util"
"gopkg.in/yaml.v2"
)
const (
configFile = "config.yaml"
configFolder = "config"
DefaultFormat = "gozero"
)
const defaultYaml = `# namingFormat is used to define the naming format of the generated file name.
# just like time formatting, you can specify the formatting style through the
# two format characters go, and zero. for example: snake format you can
# define as go_zero, camel case format you can it is defined as goZero,
# and even split characters can be specified, such as go#zero. in theory,
# any combination can be used, but the prerequisite must meet the naming conventions
# of each operating system file name. if you want to independently control the file
# naming style of the api, rpc, and model layers, you can set it through apiNamingFormat,
# rpcNamingFormat, modelNamingFormat, and independent control is not enabled by default.
# for more information, please see #{apiNamingFormat},#{rpcNamingFormat},#{modelNamingFormat}
# Note: namingFormat is based on snake or camel string
namingFormat: gozero
`
type Config struct {
// NamingFormat is used to define the naming format of the generated file name.
// just like time formatting, you can specify the formatting style through the
// two format characters go, and zero. for example: snake format you can
// define as go_zero, camel case format you can it is defined as goZero,
// and even split characters can be specified, such as go#zero. in theory,
// any combination can be used, but the prerequisite must meet the naming conventions
// of each operating system file name.
// Note: NamingFormat is based on snake or camel string
NamingFormat string `yaml:"namingFormat"`
}
func NewConfig(format string) (*Config, error) {
if len(format) == 0 {
format = DefaultFormat
}
cfg := &Config{NamingFormat: format}
err := validate(cfg)
return cfg, err
}
func InitOrGetConfig() (*Config, error) {
var (
defaultConfig Config
)
err := yaml.Unmarshal([]byte(defaultYaml), &defaultConfig)
if err != nil {
return nil, err
}
goctlHome, err := util.GetGoctlHome()
if err != nil {
return nil, err
}
configDir := filepath.Join(goctlHome, configFolder)
configFilename := filepath.Join(configDir, configFile)
if util.FileExists(configFilename) {
data, err := ioutil.ReadFile(configFilename)
if err != nil {
return nil, err
}
err = yaml.Unmarshal(data, &defaultConfig)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
err = util.MkdirIfNotExist(configDir)
if err != nil {
return nil, err
}
f, err := os.Create(configFilename)
if err != nil {
return nil, err
}
defer func() {
_ = f.Close()
}()
_, err = f.WriteString(defaultYaml)
if err != nil {
return nil, err
}
err = validate(&defaultConfig)
if err != nil {
return nil, err
}
return &defaultConfig, nil
}
func validate(cfg *Config) error {
if len(strings.TrimSpace(cfg.NamingFormat)) == 0 {
return errors.New("missing namingFormat")
}
return nil
}

View File

@@ -0,0 +1,50 @@
# 配置项管理
| 名称 | 是否可选 | 说明 |
|-------------------|----------|-----------------------------------------------|
| namingFormat | YES | 文件名称格式化符 |
# naming-format
`namingFormat`可以用于对生成代码的文件名称进行格式化和日期格式化符yyyy-MM-dd类似在代码生成时可以根据这些配置项的格式化符进行格式化。
## 格式化符(gozero)
格式化符有`go`,`zero`组成,如常见的三种格式化风格你可以这样编写:
* lower: `gozero`
* camel: `goZero`
* snake: `go_zero`
常见格式化符生成示例
源字符welcome_to_go_zero
| 格式化符 | 格式化结果 | 说明 |
|------------|-----------------------|---------------------------|
| gozero | welcometogozero | 小写 |
| goZero | welcomeToGoZero | 驼峰 |
| go_zero | welcome_to_go_zero | snake |
| Go#zero | Welcome#to#go#zero | #号分割Title类型 |
| GOZERO | WELCOMETOGOZERO | 大写 |
| \_go#zero_ | \_welcome#to#go#zero_ | 下划线做前后缀,并且#分割 |
错误格式化符示例
* go
* gOZero
* zero
* goZEro
* goZERo
* goZeRo
* tal
# 使用方法
目前可通过在生成api、rpc、model时通过`--style`参数指定format格式
```shell script
goctl api go test.api -dir . -style gozero
```
```shell script
goctl rpc proto -src test.proto -dir . -style go_zero
```
```shell script
goctl model mysql datasource -url="" -table="*" -dir ./snake -style GoZero
```
# 默认值
当不指定-style时默认值为`gozero`

View File

@@ -18,6 +18,7 @@ ENV GOPROXY https://goproxy.cn,direct
WORKDIR /build/zero WORKDIR /build/zero
COPY . . COPY . .
RUN sh -c "[ -f go.mod ]" || exit
COPY {{.goRelPath}}/etc /app/etc COPY {{.goRelPath}}/etc /app/etc
RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}} RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}}

View File

@@ -25,7 +25,7 @@ import (
) )
var ( var (
BuildVersion = "20201108" BuildVersion = "20201125"
commands = []cli.Command{ commands = []cli.Command{
{ {
Name: "api", Name: "api",
@@ -98,6 +98,11 @@ var (
Name: "api", Name: "api",
Usage: "the api file", Usage: "the api file",
}, },
cli.StringFlag{
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
}, },
Action: gogen.GoCommand, Action: gogen.GoCommand,
}, },
@@ -201,6 +206,11 @@ var (
Name: "new", Name: "new",
Usage: `generate rpc demo service`, Usage: `generate rpc demo service`,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "idea", Name: "idea",
Usage: "whether the command execution environment is from idea plugin. [optional]", Usage: "whether the command execution environment is from idea plugin. [optional]",
@@ -235,6 +245,11 @@ var (
Name: "dir, d", Name: "dir, d",
Usage: `the target path of the code`, Usage: `the target path of the code`,
}, },
cli.StringFlag{
Name: "style",
Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.BoolFlag{ cli.BoolFlag{
Name: "idea", Name: "idea",
Usage: "whether the command execution environment is from idea plugin. [optional]", Usage: "whether the command execution environment is from idea plugin. [optional]",
@@ -265,8 +280,9 @@ var (
Usage: "the target dir", Usage: "the target dir",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "style", Name: "style",
Usage: "the file naming style, lower|camel|underline,default is lower", Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "cache, c", Name: "cache, c",
@@ -300,8 +316,9 @@ var (
Usage: "the target dir", Usage: "the target dir",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "style", Name: "style",
Usage: "the file naming style, lower|camel|snake, default is lower", Required: false,
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "idea", Name: "idea",

View File

@@ -7,7 +7,7 @@ goctl model 为go-zero下的工具模块中的组件之一目前支持识别m
* 通过ddl生成 * 通过ddl生成
```shell script ```shell script
goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c=true goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c
``` ```
执行上述命令后即可快速生成CURD代码。 执行上述命令后即可快速生成CURD代码。
@@ -29,156 +29,191 @@ goctl model 为go-zero下的工具模块中的组件之一目前支持识别m
```go ```go
package model package model
import (
"database/sql"
"fmt"
"strings"
"time"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/sqlc"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)
var (
userFieldNames = builderx.FieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
cacheUserPrefix = "cache#User#user#"
cacheUserNamePrefix = "cache#User#name#"
cacheUserMobilePrefix = "cache#User#mobile#"
cacheUserIdPrefix = "cache#User#id#"
)
type (
UserModel interface {
Insert(data User) (sql.Result, error)
FindOne(id int64) (*User, error)
FindOneByUser(user string) (*User, error)
FindOneByName(name string) (*User, error)
FindOneByMobile(mobile string) (*User, error)
Update(data User) error
Delete(id int64) error
}
defaultUserModel struct {
sqlc.CachedConn
table string
}
User struct {
Id int64 `db:"id"`
User string `db:"user"` // 用户
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Mobile string `db:"mobile"` // 手机号
Gender string `db:"gender"` // 男|女|未公开
Nickname string `db:"nickname"` // 用户昵称
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}
)
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
return &defaultUserModel{
CachedConn: sqlc.NewConn(conn, c),
table: "user",
}
}
func (m *defaultUserModel) Insert(data User) (sql.Result, error) {
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
}, userMobileKey, userKey, userNameKey)
return ret, err
}
func (m *defaultUserModel) FindOne(id int64) (*User, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
var resp User
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByUser(user string) (*User, error) {
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, user)
var resp User
err := m.QueryRowIndex(&resp, userKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where user = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, user); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByName(name string) (*User, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
var resp User
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, name); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByMobile(mobile string) (*User, error) {
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
var resp User
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, mobile); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) Update(data User) error {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
}, userIdKey)
return err
}
func (m *defaultUserModel) Delete(id int64) error {
data, err := m.FindOne(id)
if err != nil {
return err
}
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where id = ?", m.table)
return conn.Exec(query, id)
}, userIdKey, userKey, userNameKey, userMobileKey)
return err
}
func (m *defaultUserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
}
func (m *defaultUserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, primary)
}
import (
"database/sql"
"fmt"
"strings"
"time"
"github.com/tal-tech/go-zero/core/stores/cache"
"github.com/tal-tech/go-zero/core/stores/sqlc"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/core/stringx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
)
var (
userFieldNames = builderx.FieldNames(&User{})
userRows = strings.Join(userFieldNames, ",")
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
cacheUserIdPrefix = "cache#User#id#"
cacheUserNamePrefix = "cache#User#name#"
cacheUserMobilePrefix = "cache#User#mobile#"
)
type (
UserModel struct {
sqlc.CachedConn
table string
}
User struct {
Id int64 `db:"id"`
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Mobile string `db:"mobile"` // 手机号
Gender string `db:"gender"` // 男|女|未公开
Nickname string `db:"nickname"` // 用户昵称
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}
)
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) *UserModel {
return &UserModel{
CachedConn: sqlc.NewConn(conn, c),
table: "user",
}
}
func (m *UserModel) Insert(data User) (sql.Result, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
}, userNameKey, userMobileKey)
return ret, err
}
func (m *UserModel) FindOne(id int64) (*User, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
var resp User
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) FindOneByName(name string) (*User, error) {
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
var resp User
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, name); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) FindOneByMobile(mobile string) (*User, error) {
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
var resp User
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
if err := conn.QueryRow(&resp, query, mobile); err != nil {
return nil, err
}
return resp.Id, nil
}, m.queryPrimary)
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *UserModel) Update(data User) error {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder)
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
}, userIdKey)
return err
}
func (m *UserModel) Delete(id int64) error {
data, err := m.FindOne(id)
if err != nil {
return err
}
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where id = ?", m.table)
return conn.Exec(query, id)
}, userMobileKey, userIdKey, userNameKey)
return err
}
func (m *UserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
}
func (m *UserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
return conn.QueryRow(v, query, primary)
}
``` ```
## 用法 ## 用法
@@ -211,25 +246,24 @@ OPTIONS:
* ddl * ddl
```shell script ```shell script
goctl model mysql -src={patterns} -dir={dir} -cache=true goctl model mysql -src={patterns} -dir={dir} -cache
``` ```
help help
``` ```
NAME: NAME:
goctl model mysql ddl - generate mysql model from ddl goctl model mysql ddl - generate mysql model from ddl
USAGE: USAGE:
goctl model mysql ddl [command options] [arguments...] goctl model mysql ddl [command options] [arguments...]
OPTIONS: OPTIONS:
--src value, -s value the path or path globbing patterns of the ddl --src value, -s value the path or path globbing patterns of the ddl
--dir value, -d value the target dir --dir value, -d value the target dir
--style value the file naming style, lower|camel|underline,default is lower --style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
--cache, -c generate code with cache [optional] --cache, -c generate code with cache [optional]
--idea for idea plugin [optional] --idea for idea plugin [optional]
``` ```
* datasource * datasource
@@ -242,18 +276,19 @@ OPTIONS:
``` ```
NAME: NAME:
goctl model mysql datasource - generate model from datasource goctl model mysql datasource - generate model from datasource
USAGE:
goctl model mysql datasource [command options] [arguments...]
OPTIONS:
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
--table value, -t value the table or table globbing patterns in the database
--cache, -c generate code with cache [optional]
--dir value, -d value the target dir
--style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
--idea for idea plugin [optional]
USAGE:
goctl model mysql datasource [command options] [arguments...]
OPTIONS:
--url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
--table value, -t value the table or table globbing patterns in the database
--cache, -c generate code with cache [optional]
--dir value, -d value the target dir
--style value the file naming style, lower|camel|snake, default is lower
--idea for idea plugin [optional]
``` ```
@@ -281,13 +316,13 @@ OPTIONS:
* ddl * ddl
```shell script ```shell script
goctl model -src={patterns} -dir={dir} -cache=false goctl model -src={patterns} -dir={dir}
``` ```
* datasource * datasource
```shell script ```shell script
goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir} -cache=false goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir}
``` ```
生成代码仅基本的CURD结构。 生成代码仅基本的CURD结构。

View File

@@ -68,30 +68,3 @@ func FieldNames(in interface{}) []string {
} }
return out return out
} }
func FieldNamesAlias(in interface{}, alias string) []string {
out := make([]string, 0)
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
// we only accept structs
if v.Kind() != reflect.Struct {
panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
}
typ := v.Type()
for i := 0; i < v.NumField(); i++ {
// gets us a StructField
fi := typ.Field(i)
tagName := ""
if tagv := fi.Tag.Get(dbTag); tagv != "" {
tagName = tagv
} else {
tagName = fi.Name
}
if len(alias) > 0 {
tagName = alias + "." + tagName
}
out = append(out, tagName)
}
return out
}

View File

@@ -2,7 +2,6 @@ package command
import ( import (
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -10,6 +9,7 @@ import (
"github.com/go-sql-driver/mysql" "github.com/go-sql-driver/mysql"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/sqlx" "github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen" "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model" "github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util" "github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
@@ -17,14 +17,16 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var errNotMatched = errors.New("sql not matched")
const ( const (
flagSrc = "src" flagSrc = "src"
flagDir = "dir" flagDir = "dir"
flagCache = "cache" flagCache = "cache"
flagIdea = "idea" flagIdea = "idea"
flagStyle = "style"
flagUrl = "url" flagUrl = "url"
flagTable = "table" flagTable = "table"
flagStyle = "style"
) )
func MysqlDDL(ctx *cli.Context) error { func MysqlDDL(ctx *cli.Context) error {
@@ -32,49 +34,64 @@ func MysqlDDL(ctx *cli.Context) error {
dir := ctx.String(flagDir) dir := ctx.String(flagDir)
cache := ctx.Bool(flagCache) cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea) idea := ctx.Bool(flagIdea)
namingStyle := strings.TrimSpace(ctx.String(flagStyle)) style := ctx.String(flagStyle)
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDDl(src, dir, cfg, cache, idea)
}
func MyDataSource(ctx *cli.Context) error {
url := strings.TrimSpace(ctx.String(flagUrl))
dir := strings.TrimSpace(ctx.String(flagDir))
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)
style := ctx.String(flagStyle)
pattern := strings.TrimSpace(ctx.String(flagTable))
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDataSource(url, pattern, dir, cfg, cache, idea)
}
func fromDDl(src, dir string, cfg *config.Config, cache, idea bool) error {
log := console.NewConsole(idea) log := console.NewConsole(idea)
src = strings.TrimSpace(src) src = strings.TrimSpace(src)
if len(src) == 0 { if len(src) == 0 {
return errors.New("expected path or path globbing patterns, but nothing found") return errors.New("expected path or path globbing patterns, but nothing found")
} }
switch namingStyle {
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
case "":
namingStyle = gen.NamingLower
default:
return fmt.Errorf("unexpected naming style: %s", namingStyle)
}
files, err := util.MatchFiles(src) files, err := util.MatchFiles(src)
if err != nil { if err != nil {
return err return err
} }
if len(files) == 0 {
return errNotMatched
}
var source []string var source []string
for _, file := range files { for _, file := range files {
data, err := ioutil.ReadFile(file) data, err := ioutil.ReadFile(file)
if err != nil { if err != nil {
return err return err
} }
source = append(source, string(data)) source = append(source, string(data))
} }
generator := gen.NewDefaultGenerator(strings.Join(source, "\n"), dir, namingStyle, gen.WithConsoleOption(log)) generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
err = generator.Start(cache)
if err != nil { if err != nil {
log.Error("%v", err) return err
} }
return nil
err = generator.StartFromDDL(strings.Join(source, "\n"), cache)
return err
} }
func MyDataSource(ctx *cli.Context) error { func fromDataSource(url, pattern, dir string, cfg *config.Config, cache, idea bool) error {
url := strings.TrimSpace(ctx.String(flagUrl))
dir := strings.TrimSpace(ctx.String(flagDir))
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)
namingStyle := strings.TrimSpace(ctx.String(flagStyle))
pattern := strings.TrimSpace(ctx.String(flagTable))
log := console.NewConsole(idea) log := console.NewConsole(idea)
if len(url) == 0 { if len(url) == 0 {
log.Error("%v", "expected data source of mysql, but nothing found") log.Error("%v", "expected data source of mysql, but nothing found")
@@ -86,32 +103,22 @@ func MyDataSource(ctx *cli.Context) error {
return nil return nil
} }
switch namingStyle { dsn, err := mysql.ParseDSN(url)
case gen.NamingLower, gen.NamingCamel, gen.NamingSnake:
case "":
namingStyle = gen.NamingLower
default:
return fmt.Errorf("unexpected naming style: %s", namingStyle)
}
cfg, err := mysql.ParseDSN(url)
if err != nil { if err != nil {
return err return err
} }
logx.Disable() logx.Disable()
conn := sqlx.NewMysql(url) databaseSource := strings.TrimSuffix(url, "/"+dsn.DBName) + "/information_schema"
databaseSource := strings.TrimSuffix(url, "/"+cfg.DBName) + "/information_schema"
db := sqlx.NewMysql(databaseSource) db := sqlx.NewMysql(databaseSource)
m := model.NewDDLModel(conn)
im := model.NewInformationSchemaModel(db) im := model.NewInformationSchemaModel(db)
tables, err := im.GetAllTables(cfg.DBName) tables, err := im.GetAllTables(dsn.DBName)
if err != nil { if err != nil {
return err return err
} }
var matchTables []string matchTables := make(map[string][]*model.Column)
for _, item := range tables { for _, item := range tables {
match, err := filepath.Match(pattern, item) match, err := filepath.Match(pattern, item)
if err != nil { if err != nil {
@@ -121,24 +128,22 @@ func MyDataSource(ctx *cli.Context) error {
if !match { if !match {
continue continue
} }
columns, err := im.FindByTableName(dsn.DBName, item)
matchTables = append(matchTables, item) if err != nil {
return err
}
matchTables[item] = columns
} }
if len(matchTables) == 0 { if len(matchTables) == 0 {
return errors.New("no tables matched") return errors.New("no tables matched")
} }
ddl, err := m.ShowDDL(matchTables...) generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
if err != nil { if err != nil {
log.Error("%v", err) return err
return nil
} }
generator := gen.NewDefaultGenerator(strings.Join(ddl, "\n"), dir, namingStyle, gen.WithConsoleOption(log)) err = generator.StartFromInformationSchema(dsn.DBName, matchTables, cache)
err = generator.Start(cache) return err
if err != nil {
log.Error("%v", err)
}
return nil
} }

View File

@@ -0,0 +1,71 @@
package command
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
var sql = "-- 用户表 --\nCREATE TABLE `user` (\n `id` bigint(10) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `name_index` (`name`),\n UNIQUE KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;\n\n"
var cfg = &config.Config{
NamingFormat: "gozero",
}
func TestFromDDl(t *testing.T) {
err := fromDDl("./user.sql", t.TempDir(), cfg, true, false)
assert.Equal(t, errNotMatched, err)
// case dir is not exists
unknownDir := filepath.Join(t.TempDir(), "test", "user.sql")
err = fromDDl(unknownDir, t.TempDir(), cfg, true, false)
assert.True(t, func() bool {
switch err.(type) {
case *os.PathError:
return true
default:
return false
}
}())
// case empty src
err = fromDDl("", t.TempDir(), cfg, true, false)
if err != nil {
assert.Equal(t, "expected path or path globbing patterns, but nothing found", err.Error())
}
tempDir := filepath.Join(t.TempDir(), "test")
err = util.MkdirIfNotExist(tempDir)
if err != nil {
return
}
user1Sql := filepath.Join(tempDir, "user1.sql")
user2Sql := filepath.Join(tempDir, "user2.sql")
err = ioutil.WriteFile(user1Sql, []byte(sql), os.ModePerm)
if err != nil {
return
}
err = ioutil.WriteFile(user2Sql, []byte(sql), os.ModePerm)
if err != nil {
return
}
_, err = os.Stat(user1Sql)
assert.Nil(t, err)
_, err = os.Stat(user2Sql)
assert.Nil(t, err)
err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, cfg, true, false)
assert.Nil(t, err)
_, err = os.Stat(filepath.Join(tempDir, "usermodel.go"))
assert.Nil(t, err)
}

View File

@@ -1,11 +0,0 @@
#!/bin/bash
# generate model with cache from ddl
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -c
# generate model with cache from data source
#user=root
#password=password
#datasource=127.0.0.1:3306
#database=test
#goctl model mysql datasource -url="${user}:${password}@tcp(${datasource})/${database}" -table="*" -dir ./model

View File

@@ -0,0 +1,15 @@
#!/bin/bash
# generate model with cache from ddl
fromDDL:
goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -cache
# generate model with cache from data source
user=root
password=password
datasource=127.0.0.1:3306
database=gozero
fromDataSource:
goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style gozero

View File

@@ -1,6 +1,7 @@
-- 用户表 -- -- 用户表 --
CREATE TABLE `user` ( CREATE TABLE `user` (
`id` bigint(10) NOT NULL AUTO_INCREMENT, `id` bigint(10) NOT NULL AUTO_INCREMENT,
`user` varchar(50) NOT NULL DEFAULT '' COMMENT '用户',
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称', `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码', `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号', `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
@@ -10,6 +11,7 @@ CREATE TABLE `user` (
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`), UNIQUE KEY `name_index` (`name`),
UNIQUE KEY `user_index` (`user`),
UNIQUE KEY `mobile_index` (`mobile`) UNIQUE KEY `mobile_index` (`mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genDelete(table Table, withCache bool) (string, error) { func genDelete(table Table, withCache bool) (string, string, error) {
keySet := collection.NewSet() keySet := collection.NewSet()
keyVariableSet := collection.NewSet() keyVariableSet := collection.NewSet()
for fieldName, key := range table.CacheKey { for fieldName, key := range table.CacheKey {
@@ -24,7 +24,7 @@ func genDelete(table Table, withCache bool) (string, error) {
camel := table.Name.ToCamel() camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete) text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("delete"). output, err := util.With("delete").
@@ -40,7 +40,23 @@ func genDelete(table Table, withCache bool) (string, error) {
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "), "keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
}) })
if err != nil { if err != nil {
return "", err return "", "", err
} }
return output.String(), nil
// interface method
text, err = util.LoadTemplate(category, deleteMethodTemplateFile, template.DeleteMethod)
if err != nil {
return "", "", err
}
deleteMethodOut, err := util.With("deleteMethod").
Parse(text).
Execute(map[string]interface{}{
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
"dataType": table.PrimaryKey.DataType,
})
if err != nil {
return "", "", err
}
return output.String(), deleteMethodOut.String(), nil
} }

View File

@@ -15,6 +15,7 @@ func genFields(fields []parser.Field) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
list = append(list, result) list = append(list, result)
} }
return strings.Join(list, "\n"), nil return strings.Join(list, "\n"), nil
@@ -43,5 +44,6 @@ func genField(field parser.Field) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return output.String(), nil return output.String(), nil
} }

View File

@@ -6,11 +6,11 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genFindOne(table Table, withCache bool) (string, error) { func genFindOne(table Table, withCache bool) (string, string, error) {
camel := table.Name.ToCamel() camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne) text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("findOne"). output, err := util.With("findOne").
@@ -26,7 +26,23 @@ func genFindOne(table Table, withCache bool) (string, error) {
"cacheKeyVariable": table.CacheKey[table.PrimaryKey.Name.Source()].Variable, "cacheKeyVariable": table.CacheKey[table.PrimaryKey.Name.Source()].Variable,
}) })
if err != nil { if err != nil {
return "", err return "", "", err
} }
return output.String(), nil
text, err = util.LoadTemplate(category, findOneMethodTemplateFile, template.FindOneMethod)
if err != nil {
return "", "", err
}
findOneMethod, err := util.With("findOneMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camel,
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(),
"dataType": table.PrimaryKey.DataType,
})
if err != nil {
return "", "", err
}
return output.String(), findOneMethod.String(), nil
} }

View File

@@ -9,10 +9,16 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genFindOneByField(table Table, withCache bool) (string, string, error) { type findOneCode struct {
findOneMethod string
findOneInterfaceMethod string
cacheExtra string
}
func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField) text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField)
if err != nil { if err != nil {
return "", "", err return nil, err
} }
t := util.With("findOneByField").Parse(text) t := util.With("findOneByField").Parse(text)
@@ -36,15 +42,40 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
"originalField": field.Name.Source(), "originalField": field.Name.Source(),
}) })
if err != nil { if err != nil {
return "", "", err return nil, err
} }
list = append(list, output.String()) list = append(list, output.String())
} }
text, err = util.LoadTemplate(category, findOneByFieldMethodTemplateFile, template.FindOneByFieldMethod)
if err != nil {
return nil, err
}
t = util.With("findOneByFieldMethod").Parse(text)
var listMethod []string
for _, field := range table.Fields {
if field.IsPrimaryKey || !field.IsUniqueKey {
continue
}
camelFieldName := field.Name.ToCamel()
output, err := t.Execute(map[string]interface{}{
"upperStartCamelObject": camelTableName,
"upperField": camelFieldName,
"in": fmt.Sprintf("%s %s", stringx.From(camelFieldName).UnTitle(), field.DataType),
})
if err != nil {
return nil, err
}
listMethod = append(listMethod, output.String())
}
if withCache { if withCache {
text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod) text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod)
if err != nil { if err != nil {
return "", "", err return nil, err
} }
out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{ out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{
@@ -54,11 +85,18 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) {
"originalPrimaryField": table.PrimaryKey.Name.Source(), "originalPrimaryField": table.PrimaryKey.Name.Source(),
}) })
if err != nil { if err != nil {
return "", "", err return nil, err
} }
return strings.Join(list, "\n"), out.String(), nil return &findOneCode{
findOneMethod: strings.Join(list, util.NL),
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
cacheExtra: out.String(),
}, nil
} }
return strings.Join(list, "\n"), "", nil
return &findOneCode{
findOneMethod: strings.Join(list, util.NL),
findOneInterfaceMethod: strings.Join(listMethod, util.NL),
}, nil
} }

View File

@@ -7,10 +7,14 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser" "github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/template" "github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
modelutil "github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/console" "github.com/tal-tech/go-zero/tools/goctl/util/console"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -24,27 +28,39 @@ const (
type ( type (
defaultGenerator struct { defaultGenerator struct {
source string //source string
dir string dir string
console.Console console.Console
pkg string pkg string
namingStyle string cfg *config.Config
} }
Option func(generator *defaultGenerator) Option func(generator *defaultGenerator)
) )
func NewDefaultGenerator(source, dir, namingStyle string, opt ...Option) *defaultGenerator { func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaultGenerator, error) {
if dir == "" { if dir == "" {
dir = pwd dir = pwd
} }
generator := &defaultGenerator{source: source, dir: dir, namingStyle: namingStyle} dirAbs, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
dir = dirAbs
pkg := filepath.Base(dirAbs)
err = util.MkdirIfNotExist(dir)
if err != nil {
return nil, err
}
generator := &defaultGenerator{dir: dir, cfg: cfg, pkg: pkg}
var optionList []Option var optionList []Option
optionList = append(optionList, newDefaultOption()) optionList = append(optionList, newDefaultOption())
optionList = append(optionList, opt...) optionList = append(optionList, opt...)
for _, fn := range optionList { for _, fn := range optionList {
fn(generator) fn(generator)
} }
return generator return generator, nil
} }
func WithConsoleOption(c console.Console) Option { func WithConsoleOption(c console.Console) Option {
@@ -59,31 +75,54 @@ func newDefaultOption() Option {
} }
} }
func (g *defaultGenerator) Start(withCache bool) error { func (g *defaultGenerator) StartFromDDL(source string, withCache bool) error {
modelList, err := g.genFromDDL(source, withCache)
if err != nil {
return err
}
return g.createFile(modelList)
}
func (g *defaultGenerator) StartFromInformationSchema(db string, columns map[string][]*model.Column, withCache bool) error {
m := make(map[string]string)
for tableName, column := range columns {
table, err := parser.ConvertColumn(db, tableName, column)
if err != nil {
return err
}
code, err := g.genModel(*table, withCache)
if err != nil {
return err
}
m[table.Name.Source()] = code
}
return g.createFile(m)
}
func (g *defaultGenerator) createFile(modelList map[string]string) error {
dirAbs, err := filepath.Abs(g.dir) dirAbs, err := filepath.Abs(g.dir)
if err != nil { if err != nil {
return err return err
} }
g.dir = dirAbs g.dir = dirAbs
g.pkg = filepath.Base(dirAbs) g.pkg = filepath.Base(dirAbs)
err = util.MkdirIfNotExist(dirAbs) err = util.MkdirIfNotExist(dirAbs)
if err != nil { if err != nil {
return err return err
} }
modelList, err := g.genFromDDL(withCache)
if err != nil {
return err
}
for tableName, code := range modelList { for tableName, code := range modelList {
tn := stringx.From(tableName) tn := stringx.From(tableName)
name := fmt.Sprintf("%smodel.go", strings.ToLower(tn.ToCamel())) modelFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, fmt.Sprintf("%s_model", tn.Source()))
switch g.namingStyle { if err != nil {
case NamingCamel: return err
name = fmt.Sprintf("%sModel.go", tn.ToCamel())
case NamingSnake:
name = fmt.Sprintf("%s_model.go", tn.ToSnake())
} }
name := modelFilename + ".go"
filename := filepath.Join(dirAbs, name) filename := filepath.Join(dirAbs, name)
if util.FileExists(filename) { if util.FileExists(filename) {
g.Warning("%s already exists, ignored.", name) g.Warning("%s already exists, ignored.", name)
@@ -95,7 +134,12 @@ func (g *defaultGenerator) Start(withCache bool) error {
} }
} }
// generate error file // generate error file
filename := filepath.Join(dirAbs, "vars.go") varFilename, err := format.FileNamingFormat(g.cfg.NamingFormat, "vars")
if err != nil {
return err
}
filename := filepath.Join(dirAbs, varFilename+".go")
text, err := util.LoadTemplate(category, errTemplateFile, template.Error) text, err := util.LoadTemplate(category, errTemplateFile, template.Error)
if err != nil { if err != nil {
return err return err
@@ -113,8 +157,8 @@ func (g *defaultGenerator) Start(withCache bool) error {
} }
// ret1: key-table name,value-code // ret1: key-table name,value-code
func (g *defaultGenerator) genFromDDL(withCache bool) (map[string]string, error) { func (g *defaultGenerator) genFromDDL(source string, withCache bool) (map[string]string, error) {
ddlList := g.split() ddlList := g.split(source)
m := make(map[string]string) m := make(map[string]string)
for _, ddl := range ddlList { for _, ddl := range ddlList {
table, err := parser.Parse(ddl) table, err := parser.Parse(ddl)
@@ -139,10 +183,15 @@ type (
) )
func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, error) { func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, error) {
if len(in.PrimaryKey.Name.Source()) == 0 {
return "", fmt.Errorf("table %s: missing primary key", in.Name.Source())
}
text, err := util.LoadTemplate(category, modelTemplateFile, template.Model) text, err := util.LoadTemplate(category, modelTemplateFile, template.Model)
if err != nil { if err != nil {
return "", err return "", err
} }
t := util.With("model"). t := util.With("model").
Parse(text). Parse(text).
GoFmt(true) GoFmt(true)
@@ -174,39 +223,41 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return "", err return "", err
} }
typesCode, err := genTypes(table, withCache) insertCode, insertCodeMethod, err := genInsert(table, withCache)
if err != nil {
return "", err
}
newCode, err := genNew(table, withCache)
if err != nil {
return "", err
}
insertCode, err := genInsert(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
var findCode = make([]string, 0) var findCode = make([]string, 0)
findOneCode, err := genFindOne(table, withCache) findOneCode, findOneCodeMethod, err := genFindOne(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
findOneByFieldCode, extraMethod, err := genFindOneByField(table, withCache) ret, err := genFindOneByField(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
findCode = append(findCode, findOneCode, findOneByFieldCode) findCode = append(findCode, findOneCode, ret.findOneMethod)
updateCode, err := genUpdate(table, withCache) updateCode, updateCodeMethod, err := genUpdate(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
deleteCode, err := genDelete(table, withCache) deleteCode, deleteCodeMethod, err := genDelete(table, withCache)
if err != nil {
return "", err
}
var list []string
list = append(list, insertCodeMethod, findOneCodeMethod, ret.findOneInterfaceMethod, updateCodeMethod, deleteCodeMethod)
typesCode, err := genTypes(table, strings.Join(modelutil.TrimStringSlice(list), util.NL), withCache)
if err != nil {
return "", err
}
newCode, err := genNew(table, withCache)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -221,7 +272,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
"find": strings.Join(findCode, "\n"), "find": strings.Join(findCode, "\n"),
"update": updateCode, "update": updateCode,
"delete": deleteCode, "delete": deleteCode,
"extraMethod": extraMethod, "extraMethod": ret.cacheExtra,
}) })
if err != nil { if err != nil {
return "", err return "", err

View File

@@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx" "github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/tools/goctl/config"
) )
var ( var (
@@ -22,15 +23,23 @@ func TestCacheModel(t *testing.T) {
defer func() { defer func() {
_ = os.RemoveAll(dir) _ = os.RemoveAll(dir)
}() }()
g := NewDefaultGenerator(source, cacheDir, NamingLower) g, err := NewDefaultGenerator(cacheDir, &config.Config{
err := g.Start(true) NamingFormat: "GoZero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, func() bool { assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(cacheDir, "testuserinfomodel.go")) _, err := os.Stat(filepath.Join(cacheDir, "TestUserInfoModel.go"))
return err == nil return err == nil
}()) }())
g = NewDefaultGenerator(source, noCacheDir, NamingLower) g, err = NewDefaultGenerator(noCacheDir, &config.Config{
err = g.Start(false) NamingFormat: "gozero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, false)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, func() bool { assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(noCacheDir, "testuserinfomodel.go")) _, err := os.Stat(filepath.Join(noCacheDir, "testuserinfomodel.go"))
@@ -47,15 +56,23 @@ func TestNamingModel(t *testing.T) {
defer func() { defer func() {
_ = os.RemoveAll(dir) _ = os.RemoveAll(dir)
}() }()
g := NewDefaultGenerator(source, camelDir, NamingCamel) g, err := NewDefaultGenerator(camelDir, &config.Config{
err := g.Start(true) NamingFormat: "GoZero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, func() bool { assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(camelDir, "TestUserInfoModel.go")) _, err := os.Stat(filepath.Join(camelDir, "TestUserInfoModel.go"))
return err == nil return err == nil
}()) }())
g = NewDefaultGenerator(source, snakeDir, NamingSnake) g, err = NewDefaultGenerator(snakeDir, &config.Config{
err = g.Start(true) NamingFormat: "go_zero",
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, func() bool { assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(snakeDir, "test_user_info_model.go")) _, err := os.Stat(filepath.Join(snakeDir, "test_user_info_model.go"))

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genInsert(table Table, withCache bool) (string, error) { func genInsert(table Table, withCache bool) (string, string, error) {
keySet := collection.NewSet() keySet := collection.NewSet()
keyVariableSet := collection.NewSet() keyVariableSet := collection.NewSet()
for fieldName, key := range table.CacheKey { for fieldName, key := range table.CacheKey {
@@ -36,7 +36,7 @@ func genInsert(table Table, withCache bool) (string, error) {
camel := table.Name.ToCamel() camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert) text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("insert"). output, err := util.With("insert").
@@ -52,8 +52,23 @@ func genInsert(table Table, withCache bool) (string, error) {
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "), "keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
}) })
if err != nil { if err != nil {
return "", err return "", "", err
} }
return output.String(), nil // interface method
text, err = util.LoadTemplate(category, insertTemplateMethodFile, template.InsertMethod)
if err != nil {
return "", "", err
}
insertMethodOutput, err := util.With("insertMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camel,
})
if err != nil {
return "", "", err
}
return output.String(), insertMethodOutput.String(), nil
} }

View File

@@ -2,6 +2,7 @@ package gen
import ( import (
"fmt" "fmt"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/parser" "github.com/tal-tech/go-zero/tools/goctl/model/sql/parser"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
@@ -33,8 +34,14 @@ func genCacheKeys(table parser.Table) (map[string]Key, error) {
camelFieldName := field.Name.ToCamel() camelFieldName := field.Name.ToCamel()
lowerStartCamelFieldName := stringx.From(camelFieldName).UnTitle() lowerStartCamelFieldName := stringx.From(camelFieldName).UnTitle()
left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName) left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName)
if strings.ToLower(camelFieldName) == strings.ToLower(camelTableName) {
left = fmt.Sprintf("cache%sPrefix", camelTableName)
}
right := fmt.Sprintf("cache#%s#%s#", camelTableName, lowerStartCamelFieldName) right := fmt.Sprintf("cache#%s#%s#", camelTableName, lowerStartCamelFieldName)
variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName) variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName)
if strings.ToLower(lowerStartCamelTableName) == strings.ToLower(camelFieldName) {
variable = fmt.Sprintf("%sKey", lowerStartCamelTableName)
}
m[field.Name.Source()] = Key{ m[field.Name.Source()] = Key{
VarExpression: fmt.Sprintf(`%s = "%s"`, left, right), VarExpression: fmt.Sprintf(`%s = "%s"`, left, right),
Left: left, Left: left,

View File

@@ -17,7 +17,6 @@ func TestGenCacheKeys(t *testing.T) {
Name: stringx.From("id"), Name: stringx.From("id"),
DataBaseType: "bigint", DataBaseType: "bigint",
DataType: "int64", DataType: "int64",
IsKey: false,
IsPrimaryKey: true, IsPrimaryKey: true,
IsUniqueKey: false, IsUniqueKey: false,
Comment: "自增id", Comment: "自增id",
@@ -29,7 +28,6 @@ func TestGenCacheKeys(t *testing.T) {
Name: stringx.From("mobile"), Name: stringx.From("mobile"),
DataBaseType: "varchar", DataBaseType: "varchar",
DataType: "string", DataType: "string",
IsKey: false,
IsPrimaryKey: false, IsPrimaryKey: false,
IsUniqueKey: true, IsUniqueKey: true,
Comment: "手机号", Comment: "手机号",
@@ -38,7 +36,6 @@ func TestGenCacheKeys(t *testing.T) {
Name: stringx.From("name"), Name: stringx.From("name"),
DataBaseType: "varchar", DataBaseType: "varchar",
DataType: "string", DataType: "string",
IsKey: false,
IsPrimaryKey: false, IsPrimaryKey: false,
IsUniqueKey: true, IsUniqueKey: true,
Comment: "姓名", Comment: "姓名",
@@ -47,7 +44,6 @@ func TestGenCacheKeys(t *testing.T) {
Name: stringx.From("createTime"), Name: stringx.From("createTime"),
DataBaseType: "timestamp", DataBaseType: "timestamp",
DataType: "time.Time", DataType: "time.Time",
IsKey: false,
IsPrimaryKey: false, IsPrimaryKey: false,
IsUniqueKey: false, IsUniqueKey: false,
Comment: "创建时间", Comment: "创建时间",
@@ -56,7 +52,6 @@ func TestGenCacheKeys(t *testing.T) {
Name: stringx.From("updateTime"), Name: stringx.From("updateTime"),
DataBaseType: "timestamp", DataBaseType: "timestamp",
DataType: "time.Time", DataType: "time.Time",
IsKey: false,
IsPrimaryKey: false, IsPrimaryKey: false,
IsUniqueKey: false, IsUniqueKey: false,
Comment: "更新时间", Comment: "更新时间",

View File

@@ -4,11 +4,10 @@ import (
"regexp" "regexp"
) )
func (g *defaultGenerator) split() []string { func (g *defaultGenerator) split(source string) []string {
reg := regexp.MustCompile(createTableFlag) reg := regexp.MustCompile(createTableFlag)
index := reg.FindAllStringIndex(g.source, -1) index := reg.FindAllStringIndex(source, -1)
list := make([]string, 0) list := make([]string, 0)
source := g.source
for i := len(index) - 1; i >= 0; i-- { for i := len(index) - 1; i >= 0; i-- {
subIndex := index[i] subIndex := index[i]
if len(subIndex) == 0 { if len(subIndex) == 0 {

View File

@@ -22,5 +22,6 @@ func genTag(in string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
return output.String(), nil return output.String(), nil
} }

View File

@@ -11,31 +11,40 @@ import (
const ( const (
category = "model" category = "model"
deleteTemplateFile = "delete.tpl" deleteTemplateFile = "delete.tpl"
deleteMethodTemplateFile = "interface-delete.tpl"
fieldTemplateFile = "filed.tpl" fieldTemplateFile = "filed.tpl"
findOneTemplateFile = "find-one.tpl" findOneTemplateFile = "find-one.tpl"
findOneMethodTemplateFile = "interface-find-one.tpl"
findOneByFieldTemplateFile = "find-one-by-field.tpl" findOneByFieldTemplateFile = "find-one-by-field.tpl"
findOneByFieldMethodTemplateFile = "interface-find-one-by-field.tpl"
findOneByFieldExtraMethodTemplateFile = "find-one-by-filed-extra-method.tpl" findOneByFieldExtraMethodTemplateFile = "find-one-by-filed-extra-method.tpl"
importsTemplateFile = "import.tpl" importsTemplateFile = "import.tpl"
importsWithNoCacheTemplateFile = "import-no-cache.tpl" importsWithNoCacheTemplateFile = "import-no-cache.tpl"
insertTemplateFile = "insert.tpl" insertTemplateFile = "insert.tpl"
insertTemplateMethodFile = "interface-insert.tpl"
modelTemplateFile = "model.tpl" modelTemplateFile = "model.tpl"
modelNewTemplateFile = "model-new.tpl" modelNewTemplateFile = "model-new.tpl"
tagTemplateFile = "tag.tpl" tagTemplateFile = "tag.tpl"
typesTemplateFile = "types.tpl" typesTemplateFile = "types.tpl"
updateTemplateFile = "update.tpl" updateTemplateFile = "update.tpl"
updateMethodTemplateFile = "interface-update.tpl"
varTemplateFile = "var.tpl" varTemplateFile = "var.tpl"
errTemplateFile = "err.tpl" errTemplateFile = "err.tpl"
) )
var templates = map[string]string{ var templates = map[string]string{
deleteTemplateFile: template.Delete, deleteTemplateFile: template.Delete,
deleteMethodTemplateFile: template.DeleteMethod,
fieldTemplateFile: template.Field, fieldTemplateFile: template.Field,
findOneTemplateFile: template.FindOne, findOneTemplateFile: template.FindOne,
findOneMethodTemplateFile: template.FindOneMethod,
findOneByFieldTemplateFile: template.FindOneByField, findOneByFieldTemplateFile: template.FindOneByField,
findOneByFieldMethodTemplateFile: template.FindOneByFieldMethod,
findOneByFieldExtraMethodTemplateFile: template.FindOneByFieldExtraMethod, findOneByFieldExtraMethodTemplateFile: template.FindOneByFieldExtraMethod,
importsTemplateFile: template.Imports, importsTemplateFile: template.Imports,
importsWithNoCacheTemplateFile: template.ImportsNoCache, importsWithNoCacheTemplateFile: template.ImportsNoCache,
insertTemplateFile: template.Insert, insertTemplateFile: template.Insert,
insertTemplateMethodFile: template.InsertMethod,
modelTemplateFile: template.Model, modelTemplateFile: template.Model,
modelNewTemplateFile: template.New, modelNewTemplateFile: template.New,
tagTemplateFile: template.Tag, tagTemplateFile: template.Tag,

View File

@@ -5,7 +5,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
) )
func genTypes(table Table, withCache bool) (string, error) { func genTypes(table Table, methods string, withCache bool) (string, error) {
fields := table.Fields fields := table.Fields
fieldsString, err := genFields(fields) fieldsString, err := genFields(fields)
if err != nil { if err != nil {
@@ -21,6 +21,7 @@ func genTypes(table Table, withCache bool) (string, error) {
Parse(text). Parse(text).
Execute(map[string]interface{}{ Execute(map[string]interface{}{
"withCache": withCache, "withCache": withCache,
"method": methods,
"upperStartCamelObject": table.Name.ToCamel(), "upperStartCamelObject": table.Name.ToCamel(),
"fields": fieldsString, "fields": fieldsString,
}) })

View File

@@ -8,7 +8,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
func genUpdate(table Table, withCache bool) (string, error) { func genUpdate(table Table, withCache bool) (string, string, error) {
expressionValues := make([]string, 0) expressionValues := make([]string, 0)
for _, filed := range table.Fields { for _, filed := range table.Fields {
camel := filed.Name.ToCamel() camel := filed.Name.ToCamel()
@@ -24,7 +24,7 @@ func genUpdate(table Table, withCache bool) (string, error) {
camelTableName := table.Name.ToCamel() camelTableName := table.Name.ToCamel()
text, err := util.LoadTemplate(category, updateTemplateFile, template.Update) text, err := util.LoadTemplate(category, updateTemplateFile, template.Update)
if err != nil { if err != nil {
return "", err return "", "", err
} }
output, err := util.With("update"). output, err := util.With("update").
@@ -39,8 +39,23 @@ func genUpdate(table Table, withCache bool) (string, error) {
"expressionValues": strings.Join(expressionValues, ", "), "expressionValues": strings.Join(expressionValues, ", "),
}) })
if err != nil { if err != nil {
return "", nil return "", "", nil
} }
return output.String(), nil // update interface method
text, err = util.LoadTemplate(category, updateMethodTemplateFile, template.UpdateMethod)
if err != nil {
return "", "", err
}
updateMethodOutput, err := util.With("updateMethod").
Parse(text).
Execute(map[string]interface{}{
"upperStartCamelObject": camelTableName,
})
if err != nil {
return "", "", nil
}
return output.String(), updateMethodOutput.String(), nil
} }

View File

@@ -27,6 +27,7 @@ func (m *DDLModel) ShowDDL(table ...string) ([]string, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
ddl = append(ddl, resp.DDL) ddl = append(ddl, resp.DDL)
} }
return ddl, nil return ddl, nil

View File

@@ -8,6 +8,13 @@ type (
InformationSchemaModel struct { InformationSchemaModel struct {
conn sqlx.SqlConn conn sqlx.SqlConn
} }
Column struct {
Name string `db:"COLUMN_NAME"`
DataType string `db:"DATA_TYPE"`
Key string `db:"COLUMN_KEY"`
Extra string `db:"EXTRA"`
Comment string `db:"COLUMN_COMMENT"`
}
) )
func NewInformationSchemaModel(conn sqlx.SqlConn) *InformationSchemaModel { func NewInformationSchemaModel(conn sqlx.SqlConn) *InformationSchemaModel {
@@ -21,5 +28,13 @@ func (m *InformationSchemaModel) GetAllTables(database string) ([]string, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return tables, nil return tables, nil
} }
func (m *InformationSchemaModel) FindByTableName(db, table string) ([]*Column, error) {
querySql := `select COLUMN_NAME,DATA_TYPE,COLUMN_KEY,EXTRA,COLUMN_COMMENT from COLUMNS where TABLE_SCHEMA = ? and TABLE_NAME = ?`
var reply []*Column
err := m.conn.QueryRows(&reply, querySql, db, table)
return reply, err
}

View File

@@ -2,8 +2,10 @@ package parser
import ( import (
"fmt" "fmt"
"strings"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/converter" "github.com/tal-tech/go-zero/tools/goctl/model/sql/converter"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
"github.com/xwb1989/sqlparser" "github.com/xwb1989/sqlparser"
) )
@@ -34,7 +36,6 @@ type (
Name stringx.String Name stringx.String
DataBaseType string DataBaseType string
DataType string DataType string
IsKey bool
IsPrimaryKey bool IsPrimaryKey bool
IsUniqueKey bool IsUniqueKey bool
Comment string Comment string
@@ -123,7 +124,6 @@ func Parse(ddl string) (*Table, error) {
field.Comment = comment field.Comment = comment
key, ok := keyMap[column.Name.String()] key, ok := keyMap[column.Name.String()]
if ok { if ok {
field.IsKey = true
field.IsPrimaryKey = key == primary field.IsPrimaryKey = key == primary
field.IsUniqueKey = key == unique field.IsUniqueKey = key == unique
if field.IsPrimaryKey { if field.IsPrimaryKey {
@@ -151,3 +151,62 @@ func (t *Table) ContainsTime() bool {
} }
return false return false
} }
func ConvertColumn(db, table string, in []*model.Column) (*Table, error) {
var reply Table
reply.Name = stringx.From(table)
keyMap := make(map[string][]*model.Column)
for _, column := range in {
keyMap[column.Key] = append(keyMap[column.Key], column)
}
primaryColumns := keyMap["PRI"]
if len(primaryColumns) == 0 {
return nil, fmt.Errorf("database:%s, table %s: missing primary key", db, table)
}
if len(primaryColumns) > 1 {
return nil, fmt.Errorf("database:%s, table %s: only one primary key expected", db, table)
}
primaryColumn := primaryColumns[0]
primaryFt, err := converter.ConvertDataType(primaryColumn.DataType)
if err != nil {
return nil, err
}
primaryField := Field{
Name: stringx.From(primaryColumn.Name),
DataBaseType: primaryColumn.DataType,
DataType: primaryFt,
IsUniqueKey: true,
IsPrimaryKey: true,
Comment: primaryColumn.Comment,
}
reply.PrimaryKey = Primary{
Field: primaryField,
AutoIncrement: strings.Contains(primaryColumn.Extra, "auto_increment"),
}
for key, columns := range keyMap {
for _, item := range columns {
dt, err := converter.ConvertDataType(item.DataType)
if err != nil {
return nil, err
}
f := Field{
Name: stringx.From(item.Name),
DataBaseType: item.DataType,
DataType: dt,
IsPrimaryKey: primaryColumn.Name == item.Name,
Comment: item.Comment,
}
if key == "UNI" {
f.IsUniqueKey = true
}
reply.Fields = append(reply.Fields, f)
}
}
return &reply, nil
}

View File

@@ -4,6 +4,7 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
) )
func TestParsePlainText(t *testing.T) { func TestParsePlainText(t *testing.T) {
@@ -23,3 +24,61 @@ func TestParseCreateTable(t *testing.T) {
assert.Equal(t, "id", table.PrimaryKey.Name.Source()) assert.Equal(t, "id", table.PrimaryKey.Name.Source())
assert.Equal(t, true, table.ContainsTime()) assert.Equal(t, true, table.ContainsTime())
} }
func TestConvertColumn(t *testing.T) {
_, err := ConvertColumn("user", "user", []*model.Column{
{
Name: "id",
DataType: "bigint",
Key: "",
Extra: "",
Comment: "",
},
})
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "missing primary key")
_, err = ConvertColumn("user", "user", []*model.Column{
{
Name: "id",
DataType: "bigint",
Key: "PRI",
Extra: "",
Comment: "",
},
{
Name: "mobile",
DataType: "varchar",
Key: "PRI",
Extra: "",
Comment: "手机号",
},
})
assert.NotNil(t, err)
assert.Contains(t, err.Error(), "only one primary key expected")
table, err := ConvertColumn("user", "user", []*model.Column{
{
Name: "id",
DataType: "bigint",
Key: "PRI",
Extra: "auto_increment",
Comment: "",
},
{
Name: "mobile",
DataType: "varchar",
Key: "UNI",
Extra: "",
Comment: "手机号",
},
})
assert.Nil(t, err)
assert.True(t, table.PrimaryKey.AutoIncrement && table.PrimaryKey.IsPrimaryKey)
assert.Equal(t, "id", table.PrimaryKey.Name.Source())
for _, item := range table.Fields {
if item.Name.Source() == "mobile" {
assert.True(t, item.IsUniqueKey)
}
}
}

View File

@@ -1,7 +1,7 @@
package template package template
var Delete = ` var Delete = `
func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error { func (m *default{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error {
{{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne({{.lowerStartCamelPrimaryKey}}) {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne({{.lowerStartCamelPrimaryKey}})
if err!=nil{ if err!=nil{
return err return err
@@ -16,3 +16,5 @@ func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}}
return err return err
} }
` `
var DeleteMethod = `Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error`

View File

@@ -2,7 +2,7 @@ package template
// 通过id查询 // 通过id查询
var FindOne = ` var FindOne = `
func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) { func (m *default{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) {
{{if .withCache}}{{.cacheKey}} {{if .withCache}}{{.cacheKey}}
var resp {{.upperStartCamelObject}} var resp {{.upperStartCamelObject}}
err := m.QueryRow(&resp, {{.cacheKeyVariable}}, func(conn sqlx.SqlConn, v interface{}) error { err := m.QueryRow(&resp, {{.cacheKeyVariable}}, func(conn sqlx.SqlConn, v interface{}) error {
@@ -32,7 +32,7 @@ func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}}
// 通过指定字段查询 // 通过指定字段查询
var FindOneByField = ` var FindOneByField = `
func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) { func (m *default{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) {
{{if .withCache}}{{.cacheKey}} {{if .withCache}}{{.cacheKey}}
var resp {{.upperStartCamelObject}} var resp {{.upperStartCamelObject}}
err := m.QueryRowIndex(&resp, {{.cacheKeyVariable}}, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { err := m.QueryRowIndex(&resp, {{.cacheKeyVariable}}, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
@@ -64,12 +64,15 @@ func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{
}{{end}} }{{end}}
` `
var FindOneByFieldExtraMethod = ` var FindOneByFieldExtraMethod = `
func (m *{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string { func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary) return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary)
} }
func (m *{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error { func (m *default{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table ) query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table )
return conn.QueryRow(v, query, primary) return conn.QueryRow(v, query, primary)
} }
` `
var FindOneMethod = `FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error)`
var FindOneByFieldMethod = `FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) `

View File

@@ -1,7 +1,7 @@
package template package template
var Insert = ` var Insert = `
func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) { func (m *default{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) {
{{if .withCache}}{{if .containsIndexCache}}{{.keys}} {{if .withCache}}{{if .containsIndexCache}}{{.keys}}
ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet) query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet)
@@ -13,3 +13,5 @@ func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}
return ret,err return ret,err
} }
` `
var InsertMethod = `Insert(data {{.upperStartCamelObject}}) (sql.Result,error)`

View File

@@ -1,8 +1,8 @@
package template package template
var New = ` var New = `
func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) *{{.upperStartCamelObject}}Model { func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) {{.upperStartCamelObject}}Model {
return &{{.upperStartCamelObject}}Model{ return &default{{.upperStartCamelObject}}Model{
{{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}}, {{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}},
table: "{{.table}}", table: "{{.table}}",
} }

View File

@@ -2,7 +2,11 @@ package template
var Types = ` var Types = `
type ( type (
{{.upperStartCamelObject}}Model struct { {{.upperStartCamelObject}}Model interface{
{{.method}}
}
default{{.upperStartCamelObject}}Model struct {
{{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}} {{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}}
table string table string
} }

View File

@@ -1,7 +1,7 @@
package template package template
var Update = ` var Update = `
func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error { func (m *default{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error {
{{if .withCache}}{{.primaryCacheKey}} {{if .withCache}}{{.primaryCacheKey}}
_, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = ?", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder) query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = ?", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder)
@@ -11,3 +11,5 @@ func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}
return err return err
} }
` `
var UpdateMethod = `Update(data {{.upperStartCamelObject}}) error`

View File

@@ -0,0 +1,12 @@
package util
func TrimStringSlice(list []string) []string {
var out []string
for _, item := range list {
if len(item) == 0 {
continue
}
out = append(out, item)
}
return out
}

View File

@@ -157,10 +157,10 @@ OPTIONS:
### 注意事项 ### 注意事项
* `google.golang.org/grpc`需要降级到v1.26.0,且protoc-gen-go版本不能高于v1.3.2see [https://github.com/grpc/grpc-go/issues/3347](https://github.com/grpc/grpc-go/issues/3347))即 * `google.golang.org/grpc`需要降级到 `v1.29.1`且protoc-gen-go版本不能高于v1.3.2see [https://github.com/grpc/grpc-go/issues/3347](https://github.com/grpc/grpc-go/issues/3347))即
```shell script ```shell script
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0 replace google.golang.org/grpc => google.golang.org/grpc v1.29.1
``` ```
* proto不支持暂多文件同时生成 * proto不支持暂多文件同时生成
@@ -261,5 +261,5 @@ service Greet {
解决方法: 解决方法:
```golang ```golang
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0 replace google.golang.org/grpc => google.golang.org/grpc v1.29.1
``` ```

View File

@@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/generator" "github.com/tal-tech/go-zero/tools/goctl/rpc/generator"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@@ -16,6 +15,7 @@ import (
func Rpc(c *cli.Context) error { func Rpc(c *cli.Context) error {
src := c.String("src") src := c.String("src")
out := c.String("dir") out := c.String("dir")
style := c.String("style")
protoImportPath := c.StringSlice("proto_path") protoImportPath := c.StringSlice("proto_path")
if len(src) == 0 { if len(src) == 0 {
return errors.New("missing -src") return errors.New("missing -src")
@@ -23,21 +23,27 @@ func Rpc(c *cli.Context) error {
if len(out) == 0 { if len(out) == 0 {
return errors.New("missing -dir") return errors.New("missing -dir")
} }
g := generator.NewDefaultRpcGenerator()
g, err := generator.NewDefaultRpcGenerator(style)
if err != nil {
return err
}
return g.Generate(src, out, protoImportPath) return g.Generate(src, out, protoImportPath)
} }
// RpcNew is to generate rpc greet service, this greet service can speed // RpcNew is to generate rpc greet service, this greet service can speed
// up your understanding of the zrpc service structure // up your understanding of the zrpc service structure
func RpcNew(c *cli.Context) error { func RpcNew(c *cli.Context) error {
name := c.Args().First() rpcname := c.Args().First()
ext := filepath.Ext(name) ext := filepath.Ext(rpcname)
if len(ext) > 0 { if len(ext) > 0 {
return fmt.Errorf("unexpected ext: %s", ext) return fmt.Errorf("unexpected ext: %s", ext)
} }
style := c.String("style")
protoName := name + ".proto" protoName := rpcname + ".proto"
filename := filepath.Join(".", name, protoName) filename := filepath.Join(".", rpcname, protoName)
src, err := filepath.Abs(filename) src, err := filepath.Abs(filename)
if err != nil { if err != nil {
return err return err
@@ -48,13 +54,11 @@ func RpcNew(c *cli.Context) error {
return err return err
} }
workDir := filepath.Dir(src) g, err := generator.NewDefaultRpcGenerator(style)
_, err = execx.Run("go mod init "+name, workDir)
if err != nil { if err != nil {
return err return err
} }
g := generator.NewDefaultRpcGenerator()
return g.Generate(src, filepath.Dir(src), nil) return g.Generate(src, filepath.Dir(src), nil)
} }

View File

@@ -1,75 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: common.proto
package common
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// 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
type User struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *User) Reset() { *m = User{} }
func (m *User) String() string { return proto.CompactTextString(m) }
func (*User) ProtoMessage() {}
func (*User) Descriptor() ([]byte, []int) {
return fileDescriptor_555bd8c177793206, []int{0}
}
func (m *User) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_User.Unmarshal(m, b)
}
func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_User.Marshal(b, m, deterministic)
}
func (m *User) XXX_Merge(src proto.Message) {
xxx_messageInfo_User.Merge(m, src)
}
func (m *User) XXX_Size() int {
return xxx_messageInfo_User.Size(m)
}
func (m *User) XXX_DiscardUnknown() {
xxx_messageInfo_User.DiscardUnknown(m)
}
var xxx_messageInfo_User proto.InternalMessageInfo
func (m *User) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func init() {
proto.RegisterType((*User)(nil), "common.User")
}
func init() { proto.RegisterFile("common.proto", fileDescriptor_555bd8c177793206) }
var fileDescriptor_555bd8c177793206 = []byte{
// 72 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x49, 0xce, 0xcf, 0xcd,
0xcd, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0x94, 0xa4, 0xb8, 0x58,
0x42, 0x8b, 0x53, 0x8b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18, 0x15, 0x18,
0x35, 0x38, 0x83, 0xc0, 0xec, 0x24, 0x36, 0xb0, 0x52, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff,
0x2c, 0x6d, 0x58, 0x59, 0x3a, 0x00, 0x00, 0x00,
}

View File

@@ -1,11 +0,0 @@
package generator
import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func formatFilename(filename string) string {
return strings.ToLower(stringx.From(filename).ToCamel())
}

View File

@@ -3,6 +3,7 @@ package generator
import ( import (
"path/filepath" "path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/console" "github.com/tal-tech/go-zero/tools/goctl/util/console"
@@ -10,16 +11,22 @@ import (
) )
type RpcGenerator struct { type RpcGenerator struct {
g Generator g Generator
cfg *conf.Config
} }
func NewDefaultRpcGenerator() *RpcGenerator { func NewDefaultRpcGenerator(style string) (*RpcGenerator, error) {
return NewRpcGenerator(NewDefaultGenerator()) cfg, err := conf.NewConfig(style)
if err != nil {
return nil, err
}
return NewRpcGenerator(NewDefaultGenerator(), cfg), nil
} }
func NewRpcGenerator(g Generator) *RpcGenerator { func NewRpcGenerator(g Generator, cfg *conf.Config) *RpcGenerator {
return &RpcGenerator{ return &RpcGenerator{
g: g, g: g,
cfg: cfg,
} }
} }
@@ -55,42 +62,42 @@ func (g *RpcGenerator) Generate(src, target string, protoImportPath []string) er
return err return err
} }
err = g.g.GenEtc(dirCtx, proto) err = g.g.GenEtc(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenPb(dirCtx, protoImportPath, proto) err = g.g.GenPb(dirCtx, protoImportPath, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenConfig(dirCtx, proto) err = g.g.GenConfig(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenSvc(dirCtx, proto) err = g.g.GenSvc(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenLogic(dirCtx, proto) err = g.g.GenLogic(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenServer(dirCtx, proto) err = g.g.GenServer(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenMain(dirCtx, proto) err = g.g.GenMain(dirCtx, proto, g.cfg)
if err != nil { if err != nil {
return err return err
} }
err = g.g.GenCall(dirCtx, proto) err = g.g.GenCall(dirCtx, proto, g.cfg)
console.NewColorConsole().MarkDone() console.NewColorConsole().MarkDone()

View File

@@ -1,128 +1,79 @@
package generator package generator
import ( import (
"go/build"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stringx"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx" "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
) )
func TestRpcGenerateCaseNilImport(t *testing.T) { var cfg = &conf.Config{
_ = Clean() NamingFormat: "gozero",
dispatcher := NewDefaultGenerator()
if err := dispatcher.Prepare(); err == nil {
g := NewRpcGenerator(dispatcher)
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
err = g.Generate("./test_stream.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
}
} }
func TestRpcGenerateCaseOption(t *testing.T) { func TestRpcGenerate(t *testing.T) {
_ = Clean() _ = Clean()
dispatcher := NewDefaultGenerator() dispatcher := NewDefaultGenerator()
if err := dispatcher.Prepare(); err == nil { err := dispatcher.Prepare()
g := NewRpcGenerator(dispatcher) if err != nil {
abs, err := filepath.Abs("./test") logx.Error(err)
assert.Nil(t, err) return
err = g.Generate("./test_option.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
} }
} projectName := stringx.Rand()
g := NewRpcGenerator(dispatcher, cfg)
func TestRpcGenerateCaseWordOption(t *testing.T) { // case go path
_ = Clean() src := filepath.Join(build.Default.GOPATH, "src")
dispatcher := NewDefaultGenerator() _, err = os.Stat(src)
if err := dispatcher.Prepare(); err == nil { if err != nil {
g := NewRpcGenerator(dispatcher) return
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
err = g.Generate("./test_word_option.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
} }
}
// test keyword go projectDir := filepath.Join(src, projectName)
func TestRpcGenerateCaseGoOption(t *testing.T) { srcDir := projectDir
_ = Clean() defer func() {
dispatcher := NewDefaultGenerator() _ = os.RemoveAll(srcDir)
if err := dispatcher.Prepare(); err == nil { }()
g := NewRpcGenerator(dispatcher) err = g.Generate("./test.proto", projectDir, []string{src})
abs, err := filepath.Abs("./test") assert.Nil(t, err)
assert.Nil(t, err) _, err = execx.Run("go test "+projectName, projectDir)
if err != nil {
err = g.Generate("./test_go_option.proto", abs, nil) assert.Contains(t, err.Error(), "not in GOROOT")
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
} }
}
func TestRpcGenerateCaseImport(t *testing.T) { // case go mod
_ = Clean() workDir := t.TempDir()
dispatcher := NewDefaultGenerator() name := filepath.Base(workDir)
if err := dispatcher.Prepare(); err == nil { _, err = execx.Run("go mod init "+name, workDir)
g := NewRpcGenerator(dispatcher) if err != nil {
abs, err := filepath.Abs("./test") logx.Error(err)
assert.Nil(t, err) return
err = g.Generate("./test_import.proto", abs, []string{"./base"})
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.True(t, func() bool {
return strings.Contains(err.Error(), "package base is not in GOROOT")
}())
} }
}
func TestRpcGenerateCaseServiceRpcNamingSnake(t *testing.T) { projectDir = filepath.Join(workDir, projectName)
_ = Clean() err = g.Generate("./test.proto", projectDir, []string{src})
dispatcher := NewDefaultGenerator() assert.Nil(t, err)
if err := dispatcher.Prepare(); err == nil { _, err = execx.Run("go test "+projectName, projectDir)
g := NewRpcGenerator(dispatcher) if err != nil {
abs, err := filepath.Abs("./test") assert.Contains(t, err.Error(), "not in GOROOT")
assert.Nil(t, err)
err = g.Generate("./test_service_rpc_naming_snake.proto", abs, nil)
defer func() {
_ = os.RemoveAll(abs)
}()
assert.Nil(t, err)
_, err = execx.Run("go test "+abs, abs)
assert.Nil(t, err)
} }
// case not in go mod and go path
err = g.Generate("./test.proto", projectDir, []string{src})
assert.Nil(t, err)
_, err = execx.Run("go test "+projectName, projectDir)
if err != nil {
assert.Contains(t, err.Error(), "not in GOROOT")
}
// invalid directory
projectDir = filepath.Join(t.TempDir(), ".....")
err = g.Generate("./test.proto", projectDir, nil)
assert.NotNil(t, err)
} }

View File

@@ -6,8 +6,10 @@ import (
"strings" "strings"
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -59,12 +61,17 @@ func (m *default{{.serviceName}}) {{.method}}(ctx context.Context,in *{{.pbReque
` `
) )
func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto) error { func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetCall() dir := ctx.GetCall()
service := proto.Service service := proto.Service
head := util.GetHead(proto.Name) head := util.GetHead(proto.Name)
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", formatFilename(service.Name))) callFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name)
if err != nil {
return err
}
filename := filepath.Join(dir.Filename, fmt.Sprintf("%s.go", callFilename))
functions, err := g.genFunction(proto.PbPackage, service) functions, err := g.genFunction(proto.PbPackage, service)
if err != nil { if err != nil {
return err return err
@@ -81,13 +88,12 @@ func (g *defaultGenerator) GenCall(ctx DirContext, proto parser.Proto) error {
} }
var alias = collection.NewSet() var alias = collection.NewSet()
for _, item := range service.RPC { for _, item := range proto.Message {
alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(item.RequestType), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(item.RequestType)))) alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(item.Name), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(item.Name))))
alias.AddStr(fmt.Sprintf("%s = %s", parser.CamelCase(item.ReturnsType), fmt.Sprintf("%s.%s", proto.PbPackage, parser.CamelCase(item.ReturnsType))))
} }
err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{ err = util.With("shared").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"name": formatFilename(service.Name), "name": callFilename,
"alias": strings.Join(alias.KeysStr(), util.NL), "alias": strings.Join(alias.KeysStr(), util.NL),
"head": head, "head": head,
"filePackage": dir.Base, "filePackage": dir.Base,

View File

@@ -1,44 +0,0 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateCall(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenCall(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -5,8 +5,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
) )
const configTemplate = `package config const configTemplate = `package config
@@ -18,9 +20,14 @@ type Config struct {
} }
` `
func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto) error { func (g *defaultGenerator) GenConfig(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetConfig() dir := ctx.GetConfig()
fileName := filepath.Join(dir.Filename, formatFilename("config")+".go") configFilename, err := format.FileNamingFormat(cfg.NamingFormat, "config")
if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, configFilename+".go")
if util.FileExists(fileName) { if util.FileExists(fileName) {
return nil return nil
} }

View File

@@ -1,48 +0,0 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateConfig(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenConfig(dirCtx, proto)
assert.Nil(t, err)
// test file exists
err = g.GenConfig(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -1,15 +1,18 @@
package generator package generator
import "github.com/tal-tech/go-zero/tools/goctl/rpc/parser" import (
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
)
type Generator interface { type Generator interface {
Prepare() error Prepare() error
GenMain(ctx DirContext, proto parser.Proto) error GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenCall(ctx DirContext, proto parser.Proto) error GenCall(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenEtc(ctx DirContext, proto parser.Proto) error GenEtc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenConfig(ctx DirContext, proto parser.Proto) error GenConfig(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenLogic(ctx DirContext, proto parser.Proto) error GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenServer(ctx DirContext, proto parser.Proto) error GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenSvc(ctx DirContext, proto parser.Proto) error GenSvc(ctx DirContext, proto parser.Proto, cfg *conf.Config) error
GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto) error GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, cfg *conf.Config) error
} }

View File

@@ -3,9 +3,13 @@ package generator
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
const etcTemplate = `Name: {{.serviceName}}.rpc const etcTemplate = `Name: {{.serviceName}}.rpc
@@ -16,10 +20,14 @@ Etcd:
Key: {{.serviceName}}.rpc Key: {{.serviceName}}.rpc
` `
func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto) error { func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto, cfg *conf.Config) error {
dir := ctx.GetEtc() dir := ctx.GetEtc()
serviceNameLower := formatFilename(ctx.GetMain().Base) etcFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.yaml", serviceNameLower)) if err != nil {
return err
}
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.yaml", etcFilename))
text, err := util.LoadTemplate(category, etcTemplateFileFile, etcTemplate) text, err := util.LoadTemplate(category, etcTemplateFileFile, etcTemplate)
if err != nil { if err != nil {
@@ -27,6 +35,6 @@ func (g *defaultGenerator) GenEtc(ctx DirContext, _ parser.Proto) error {
} }
return util.With("etc").Parse(text).SaveTo(map[string]interface{}{ return util.With("etc").Parse(text).SaveTo(map[string]interface{}{
"serviceName": serviceNameLower, "serviceName": strings.ToLower(stringx.From(ctx.GetServiceName().Source()).ToCamel()),
}, fileName, false) }, fileName, false)
} }

View File

@@ -1,45 +0,0 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateEtc(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenEtc(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -6,8 +6,10 @@ import (
"strings" "strings"
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -46,10 +48,15 @@ func (l *{{.logicName}}) {{.method}} (in {{.request}}) ({{.response}}, error) {
` `
) )
func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto) error { func (g *defaultGenerator) GenLogic(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetLogic() dir := ctx.GetLogic()
for _, rpc := range proto.Service.RPC { for _, rpc := range proto.Service.RPC {
filename := filepath.Join(dir.Filename, formatFilename(rpc.Name+"_logic")+".go") logicFilename, err := format.FileNamingFormat(cfg.NamingFormat, rpc.Name+"_logic")
if err != nil {
return err
}
filename := filepath.Join(dir.Filename, logicFilename+".go")
functions, err := g.genLogicFunction(proto.PbPackage, rpc) functions, err := g.genLogicFunction(proto.PbPackage, rpc)
if err != nil { if err != nil {
return err return err

View File

@@ -1,44 +0,0 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateLogic(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenLogic(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -5,8 +5,10 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -45,10 +47,13 @@ func main() {
} }
` `
func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto) error { func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetMain() mainFilename, err := format.FileNamingFormat(cfg.NamingFormat, ctx.GetServiceName().Source())
serviceNameLower := formatFilename(ctx.GetMain().Base) if err != nil {
fileName := filepath.Join(dir.Filename, fmt.Sprintf("%v.go", serviceNameLower)) return err
}
fileName := filepath.Join(ctx.GetMain().Filename, fmt.Sprintf("%v.go", mainFilename))
imports := make([]string, 0) imports := make([]string, 0)
pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package) pbImport := fmt.Sprintf(`"%v"`, ctx.GetPb().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package) svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -63,7 +68,7 @@ func (g *defaultGenerator) GenMain(ctx DirContext, proto parser.Proto) error {
return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{ return util.With("main").GoFmt(true).Parse(text).SaveTo(map[string]interface{}{
"head": head, "head": head,
"serviceName": serviceNameLower, "serviceName": strings.ToLower(ctx.GetServiceName().ToCamel()),
"imports": strings.Join(imports, util.NL), "imports": strings.Join(imports, util.NL),
"pkg": proto.PbPackage, "pkg": proto.PbPackage,
"serviceNew": stringx.From(proto.Service.Name).ToCamel(), "serviceNew": stringx.From(proto.Service.Name).ToCamel(),

View File

@@ -1,45 +0,0 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateMain(t *testing.T) {
_ = Clean()
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
err = g.Prepare()
if err != nil {
return
}
err = g.GenMain(dirCtx, proto)
assert.Nil(t, err)
}

View File

@@ -5,11 +5,12 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx" "github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
) )
func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto) error { func (g *defaultGenerator) GenPb(ctx DirContext, protoImportPath []string, proto parser.Proto, _ *conf.Config) error {
dir := ctx.GetPb() dir := ctx.GetPb()
cw := new(bytes.Buffer) cw := new(bytes.Buffer)
base := filepath.Dir(proto.Src) base := filepath.Dir(proto.Src)

View File

@@ -1,184 +0,0 @@
package generator
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/ctx"
)
func TestGenerateCaseNilImport(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
//_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_stream.pb.go")
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
func TestGenerateCaseImport(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_stream.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_stream.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
func TestGenerateCasePathOption(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_option.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_option.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
func TestGenerateCaseWordOption(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_word_option.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_word_option.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}
// test keyword go
func TestGenerateCaseGoOption(t *testing.T) {
project := "stream"
abs, err := filepath.Abs("./test")
assert.Nil(t, err)
dir := filepath.Join(abs, project)
err = util.MkdirIfNotExist(dir)
assert.Nil(t, err)
defer func() {
_ = os.RemoveAll(abs)
}()
projectCtx, err := ctx.Prepare(dir)
assert.Nil(t, err)
p := parser.NewDefaultProtoParser()
proto, err := p.Parse("./test_go_option.proto")
assert.Nil(t, err)
dirCtx, err := mkdir(projectCtx, proto)
assert.Nil(t, err)
g := NewDefaultGenerator()
if err := g.Prepare(); err == nil {
err = g.GenPb(dirCtx, nil, proto)
assert.Nil(t, err)
targetPb := filepath.Join(dirCtx.GetPb().Filename, "test_go_option.pb.go")
assert.True(t, func() bool {
return util.FileExists(targetPb)
}())
}
}

View File

@@ -6,8 +6,10 @@ import (
"strings" "strings"
"github.com/tal-tech/go-zero/core/collection" "github.com/tal-tech/go-zero/core/collection"
conf "github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/rpc/parser" "github.com/tal-tech/go-zero/tools/goctl/rpc/parser"
"github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/format"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx" "github.com/tal-tech/go-zero/tools/goctl/util/stringx"
) )
@@ -43,7 +45,7 @@ func (s *{{.server}}Server) {{.method}} (ctx context.Context, in {{.request}}) (
` `
) )
func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto) error { func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto, cfg *conf.Config) error {
dir := ctx.GetServer() dir := ctx.GetServer()
logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package) logicImport := fmt.Sprintf(`"%v"`, ctx.GetLogic().Package)
svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package) svcImport := fmt.Sprintf(`"%v"`, ctx.GetSvc().Package)
@@ -54,7 +56,12 @@ func (g *defaultGenerator) GenServer(ctx DirContext, proto parser.Proto) error {
head := util.GetHead(proto.Name) head := util.GetHead(proto.Name)
service := proto.Service service := proto.Service
serverFile := filepath.Join(dir.Filename, formatFilename(service.Name+"_server")+".go") serverFilename, err := format.FileNamingFormat(cfg.NamingFormat, service.Name+"_server")
if err != nil {
return err
}
serverFile := filepath.Join(dir.Filename, serverFilename+".go")
funcList, err := g.genFunctions(proto.PbPackage, service) funcList, err := g.genFunctions(proto.PbPackage, service)
if err != nil { if err != nil {
return err return err

Some files were not shown because too many files have changed in this diff Show More