Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16bfb1b7be | ||
|
|
ef4d4968d6 | ||
|
|
7b4a5e3ec6 | ||
|
|
e6df21e0d2 | ||
|
|
0a2c2d1eca | ||
|
|
a5fb29a6f0 | ||
|
|
f8da301e57 | ||
|
|
cb9075b737 | ||
|
|
3f389a55c2 | ||
|
|
afbd565d87 | ||
|
|
d629acc2b7 | ||
|
|
f32c6a9b28 | ||
|
|
95aa65efb9 | ||
|
|
3806e66cf1 | ||
|
|
bd430baf52 | ||
|
|
48f4154ea8 | ||
|
|
2599e0d28d | ||
|
|
12327fa07d | ||
|
|
57079bf4a4 | ||
|
|
7f6eceb5a3 | ||
|
|
7d7cb836af | ||
|
|
f87d9d1dda |
@@ -35,7 +35,7 @@ spec:
|
|||||||
- --listen-client-urls
|
- --listen-client-urls
|
||||||
- http://0.0.0.0:2379
|
- http://0.0.0.0:2379
|
||||||
- --advertise-client-urls
|
- --advertise-client-urls
|
||||||
- http://etcd0:2379
|
- http://etcd0.discov:2379
|
||||||
- --initial-cluster
|
- --initial-cluster
|
||||||
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
||||||
- --initial-cluster-state
|
- --initial-cluster-state
|
||||||
@@ -107,7 +107,7 @@ spec:
|
|||||||
- --listen-client-urls
|
- --listen-client-urls
|
||||||
- http://0.0.0.0:2379
|
- http://0.0.0.0:2379
|
||||||
- --advertise-client-urls
|
- --advertise-client-urls
|
||||||
- http://etcd1:2379
|
- http://etcd1.discov:2379
|
||||||
- --initial-cluster
|
- --initial-cluster
|
||||||
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
||||||
- --initial-cluster-state
|
- --initial-cluster-state
|
||||||
@@ -179,7 +179,7 @@ spec:
|
|||||||
- --listen-client-urls
|
- --listen-client-urls
|
||||||
- http://0.0.0.0:2379
|
- http://0.0.0.0:2379
|
||||||
- --advertise-client-urls
|
- --advertise-client-urls
|
||||||
- http://etcd2:2379
|
- http://etcd2.discov:2379
|
||||||
- --initial-cluster
|
- --initial-cluster
|
||||||
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
||||||
- --initial-cluster-state
|
- --initial-cluster-state
|
||||||
@@ -251,7 +251,7 @@ spec:
|
|||||||
- --listen-client-urls
|
- --listen-client-urls
|
||||||
- http://0.0.0.0:2379
|
- http://0.0.0.0:2379
|
||||||
- --advertise-client-urls
|
- --advertise-client-urls
|
||||||
- http://etcd3:2379
|
- http://etcd3.discov:2379
|
||||||
- --initial-cluster
|
- --initial-cluster
|
||||||
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
||||||
- --initial-cluster-state
|
- --initial-cluster-state
|
||||||
@@ -323,7 +323,7 @@ spec:
|
|||||||
- --listen-client-urls
|
- --listen-client-urls
|
||||||
- http://0.0.0.0:2379
|
- http://0.0.0.0:2379
|
||||||
- --advertise-client-urls
|
- --advertise-client-urls
|
||||||
- http://etcd4:2379
|
- http://etcd4.discov:2379
|
||||||
- --initial-cluster
|
- --initial-cluster
|
||||||
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
- etcd0=http://etcd0:2380,etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380,etcd4=http://etcd4:2380
|
||||||
- --initial-cluster-state
|
- --initial-cluster-state
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ func (pe *PeriodicalExecutor) Sync(fn func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pe *PeriodicalExecutor) Wait() {
|
func (pe *PeriodicalExecutor) Wait() {
|
||||||
|
pe.Flush()
|
||||||
pe.wgBarrier.Guard(func() {
|
pe.wgBarrier.Guard(func() {
|
||||||
pe.waitGroup.Wait()
|
pe.waitGroup.Wait()
|
||||||
})
|
})
|
||||||
|
|||||||
8
core/stores/cache/cachenode.go
vendored
8
core/stores/cache/cachenode.go
vendored
@@ -175,12 +175,12 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
|
|||||||
}
|
}
|
||||||
if fresh {
|
if fresh {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
|
||||||
// got the result from previous ongoing query
|
|
||||||
c.stat.IncrementTotal()
|
|
||||||
c.stat.IncrementHit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// got the result from previous ongoing query
|
||||||
|
c.stat.IncrementTotal()
|
||||||
|
c.stat.IncrementHit()
|
||||||
|
|
||||||
return jsonx.Unmarshal(val.([]byte), v)
|
return jsonx.Unmarshal(val.([]byte), v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
"github.com/tal-tech/go-zero/core/stores/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const postgreDriverName = "postgres"
|
const postgresDriverName = "postgres"
|
||||||
|
|
||||||
func NewPostgre(datasource string, opts ...sqlx.SqlOption) sqlx.SqlConn {
|
func NewPostgres(datasource string, opts ...sqlx.SqlOption) sqlx.SqlConn {
|
||||||
return sqlx.NewSqlConn(postgreDriverName, datasource, opts...)
|
return sqlx.NewSqlConn(postgresDriverName, datasource, opts...)
|
||||||
}
|
}
|
||||||
|
|||||||
8
go.mod
8
go.mod
@@ -7,6 +7,7 @@ require (
|
|||||||
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/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 // indirect
|
||||||
github.com/alicebob/miniredis v2.5.0+incompatible
|
github.com/alicebob/miniredis v2.5.0+incompatible
|
||||||
|
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
|
||||||
github.com/emicklei/proto v1.9.0
|
github.com/emicklei/proto v1.9.0
|
||||||
@@ -25,7 +26,7 @@ require (
|
|||||||
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
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.14.3 // indirect
|
github.com/grpc-ecosystem/grpc-gateway v1.14.3 // indirect
|
||||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334
|
github.com/iancoleman/strcase v0.1.2
|
||||||
github.com/justinas/alice v1.2.0
|
github.com/justinas/alice v1.2.0
|
||||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||||
@@ -40,10 +41,11 @@ require (
|
|||||||
github.com/pierrec/lz4 v2.5.1+incompatible // indirect
|
github.com/pierrec/lz4 v2.5.1+incompatible // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.5.1
|
github.com/prometheus/client_golang v1.5.1
|
||||||
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0
|
github.com/spaolacci/murmur3 v1.1.0
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
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.4
|
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
|
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
|
||||||
@@ -59,7 +61,7 @@ require (
|
|||||||
google.golang.org/protobuf v1.25.0
|
google.golang.org/protobuf v1.25.0
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||||
gopkg.in/h2non/gock.v1 v1.0.15
|
gopkg.in/h2non/gock.v1 v1.0.15
|
||||||
gopkg.in/yaml.v2 v2.2.8
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
10
go.sum
10
go.sum
@@ -42,6 +42,8 @@ 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 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
|
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=
|
||||||
@@ -147,6 +149,8 @@ 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 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
|
||||||
|
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=
|
||||||
@@ -248,6 +252,8 @@ 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 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
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 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
|
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
|
||||||
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=
|
||||||
@@ -277,6 +283,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1
|
|||||||
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 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||||
|
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 h1:YdYsPAZ2pC6Tow/nPZOPQ96O3hm/ToAkGsPLzedXERk=
|
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6 h1:YdYsPAZ2pC6Tow/nPZOPQ96O3hm/ToAkGsPLzedXERk=
|
||||||
@@ -446,6 +454,8 @@ gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
|
|||||||
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 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -10,43 +9,27 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func StartHttp(host string, port int, handler http.Handler) error {
|
func StartHttp(host string, port int, handler http.Handler) error {
|
||||||
addr := fmt.Sprintf("%s:%d", host, port)
|
return start(host, port, handler, func(srv *http.Server) error {
|
||||||
server := buildHttpServer(addr, handler)
|
return srv.ListenAndServe()
|
||||||
gracefulOnShutdown(server)
|
})
|
||||||
return server.ListenAndServe()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartHttps(host string, port int, certFile, keyFile string, handler http.Handler) error {
|
func StartHttps(host string, port int, certFile, keyFile string, handler http.Handler) error {
|
||||||
addr := fmt.Sprintf("%s:%d", host, port)
|
return start(host, port, handler, func(srv *http.Server) error {
|
||||||
if server, err := buildHttpsServer(addr, handler, certFile, keyFile); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
gracefulOnShutdown(server)
|
|
||||||
// certFile and keyFile are set in buildHttpsServer
|
// certFile and keyFile are set in buildHttpsServer
|
||||||
return server.ListenAndServeTLS("", "")
|
return srv.ListenAndServeTLS(certFile, keyFile)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildHttpServer(addr string, handler http.Handler) *http.Server {
|
|
||||||
return &http.Server{Addr: addr, Handler: handler}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildHttpsServer(addr string, handler http.Handler, certFile, keyFile string) (*http.Server, error) {
|
|
||||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
config := tls.Config{Certificates: []tls.Certificate{cert}}
|
|
||||||
return &http.Server{
|
|
||||||
Addr: addr,
|
|
||||||
Handler: handler,
|
|
||||||
TLSConfig: &config,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func gracefulOnShutdown(srv *http.Server) {
|
|
||||||
proc.AddWrapUpListener(func() {
|
|
||||||
srv.Shutdown(context.Background())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func start(host string, port int, handler http.Handler, run func(srv *http.Server) error) error {
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: fmt.Sprintf("%s:%d", host, port),
|
||||||
|
Handler: handler,
|
||||||
|
}
|
||||||
|
waitForCalled := proc.AddWrapUpListener(func() {
|
||||||
|
server.Shutdown(context.Background())
|
||||||
|
})
|
||||||
|
defer waitForCalled()
|
||||||
|
|
||||||
|
return run(server)
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func genDoc(api *spec.ApiSpec, dir string, filename string) error {
|
|||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
|
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
for index, route := range api.Service.Routes {
|
for index, route := range api.Service.Routes() {
|
||||||
routeComment, _ := util.GetAnnotationValue(route.Annotations, "doc", "summary")
|
routeComment, _ := util.GetAnnotationValue(route.Annotations, "doc", "summary")
|
||||||
if len(routeComment) == 0 {
|
if len(routeComment) == 0 {
|
||||||
routeComment = "N/A"
|
routeComment = "N/A"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package format
|
package format
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/format"
|
"go/format"
|
||||||
@@ -94,7 +95,6 @@ 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 {
|
r := reg.ReplaceAllStringFunc(data, func(m string) string {
|
||||||
parts := reg.FindStringSubmatch(m)
|
parts := reg.FindStringSubmatch(m)
|
||||||
if len(parts) < 2 {
|
if len(parts) < 2 {
|
||||||
@@ -115,7 +115,7 @@ func apiFormat(data string) (string, error) {
|
|||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.StructBody)))
|
fs, err := format.Source([]byte(strings.TrimSpace(apiStruct.Type)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
str := err.Error()
|
str := err.Error()
|
||||||
lineNumber := strings.Index(str, ":")
|
lineNumber := strings.Index(str, ":")
|
||||||
@@ -145,10 +145,28 @@ func apiFormat(data string) (string, error) {
|
|||||||
result += strings.TrimSpace(string(fs)) + "\n\n"
|
result += strings.TrimSpace(string(fs)) + "\n\n"
|
||||||
}
|
}
|
||||||
if len(strings.TrimSpace(apiStruct.Service)) > 0 {
|
if len(strings.TrimSpace(apiStruct.Service)) > 0 {
|
||||||
result += strings.TrimSpace(apiStruct.Service) + "\n\n"
|
result += formatService(apiStruct.Service) + "\n\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return strings.TrimSpace(result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatService(str string) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(str))
|
||||||
|
var tapCount = 0
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := strings.TrimSpace(scanner.Text())
|
||||||
|
if line == ")" || line == "}" {
|
||||||
|
tapCount -= 1
|
||||||
|
}
|
||||||
|
util.WriteIndent(&builder, tapCount)
|
||||||
|
builder.WriteString(line + "\n")
|
||||||
|
if strings.HasSuffix(line, "(") || strings.HasSuffix(line, "{") {
|
||||||
|
tapCount += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(builder.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func countRune(s string, r rune) int {
|
func countRune(s string, r rune) int {
|
||||||
|
|||||||
47
tools/goctl/api/format/format_test.go
Normal file
47
tools/goctl/api/format/format_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package format
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
notFormattedStr = `
|
||||||
|
type Request struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
service A-api {
|
||||||
|
@server(
|
||||||
|
handler: GreetHandler
|
||||||
|
)
|
||||||
|
get /greet/from/:name(Request) returns (Response)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
formattedStr = `type Request struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
service A-api {
|
||||||
|
@server(
|
||||||
|
handler: GreetHandler
|
||||||
|
)
|
||||||
|
get /greet/from/:name(Request) returns (Response)
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInlineTypeNotExist(t *testing.T) {
|
||||||
|
r, err := apiFormat(notFormattedStr)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, r, formattedStr)
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package gogen
|
package gogen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -30,7 +28,6 @@ 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")
|
||||||
force := c.Bool("force")
|
|
||||||
if len(apiFile) == 0 {
|
if len(apiFile) == 0 {
|
||||||
return errors.New("missing -api")
|
return errors.New("missing -api")
|
||||||
}
|
}
|
||||||
@@ -38,10 +35,10 @@ func GoCommand(c *cli.Context) error {
|
|||||||
return errors.New("missing -dir")
|
return errors.New("missing -dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
return DoGenProject(apiFile, dir, force)
|
return DoGenProject(apiFile, dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func DoGenProject(apiFile, dir string, force bool) error {
|
func DoGenProject(apiFile, dir string) error {
|
||||||
p, err := parser.NewParser(apiFile)
|
p, err := parser.NewParser(apiFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -56,11 +53,10 @@ func DoGenProject(apiFile, dir string, force bool) error {
|
|||||||
logx.Must(genConfig(dir, api))
|
logx.Must(genConfig(dir, api))
|
||||||
logx.Must(genMain(dir, api))
|
logx.Must(genMain(dir, api))
|
||||||
logx.Must(genServiceContext(dir, api))
|
logx.Must(genServiceContext(dir, api))
|
||||||
logx.Must(genTypes(dir, api, force))
|
logx.Must(genTypes(dir, api))
|
||||||
logx.Must(genHandlers(dir, api))
|
logx.Must(genHandlers(dir, api))
|
||||||
logx.Must(genRoutes(dir, api, force))
|
logx.Must(genRoutes(dir, api))
|
||||||
logx.Must(genLogic(dir, api))
|
logx.Must(genLogic(dir, api))
|
||||||
createGoModFileIfNeed(dir)
|
|
||||||
|
|
||||||
if err := backupAndSweep(apiFile); err != nil {
|
if err := backupAndSweep(apiFile); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -129,34 +125,3 @@ func sweep() error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createGoModFileIfNeed(dir string) {
|
|
||||||
absDir, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, hasGoMod := util.FindGoModPath(dir)
|
|
||||||
if hasGoMod {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gopath := os.Getenv("GOPATH")
|
|
||||||
parent := path.Join(gopath, "src")
|
|
||||||
pos := strings.Index(absDir, parent)
|
|
||||||
if pos >= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
moduleName := absDir[len(filepath.Dir(absDir))+1:]
|
|
||||||
cmd := exec.Command("go", "mod", "init", moduleName)
|
|
||||||
cmd.Dir = dir
|
|
||||||
var stdout, stderr bytes.Buffer
|
|
||||||
cmd.Stdout = &stdout
|
|
||||||
cmd.Stderr = &stderr
|
|
||||||
if err = cmd.Run(); err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
}
|
|
||||||
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
|
|
||||||
fmt.Printf(outStr + "\n" + errStr)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/rpc/execx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const testApiTemplate = `
|
const testApiTemplate = `
|
||||||
@@ -22,23 +23,28 @@ info(
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // }
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Message string ` + "`" + `json:"message"` + "`" + `
|
Message string ` + "`" + `json:"message"` + "`" + `
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@server(
|
||||||
|
// C0
|
||||||
|
group: greet/s1
|
||||||
|
)
|
||||||
|
// C1
|
||||||
service A-api {
|
service A-api {
|
||||||
@server(
|
// C2
|
||||||
|
@server( // C3
|
||||||
handler: GreetHandler
|
handler: GreetHandler
|
||||||
)
|
)
|
||||||
get /greet/from/:name(Request) returns (Response)
|
get /greet/from/:name(Request) returns (Response) // hello
|
||||||
|
|
||||||
@server(
|
// C4
|
||||||
handler: NoResponseHandler
|
@handler NoResponseHandler // C5
|
||||||
)
|
get /greet/get(Request)
|
||||||
get /greet/get(Request) returns
|
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -204,6 +210,75 @@ service A-api {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const hasCommentApiTest = `
|
||||||
|
type Inline struct {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Inline
|
||||||
|
Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // name in path
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Message string ` + "`" + `json:"msg"` + "`" + ` // message
|
||||||
|
}
|
||||||
|
|
||||||
|
service A-api {
|
||||||
|
@doc(helloworld)
|
||||||
|
@server(
|
||||||
|
handler: GreetHandler
|
||||||
|
)
|
||||||
|
get /greet/from/:name(Request) returns (Response)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const hasInlineNoExistTest = `
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Inline
|
||||||
|
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Message string ` + "`" + `json:"message"` + "`" + ` // message
|
||||||
|
}
|
||||||
|
|
||||||
|
service A-api {
|
||||||
|
@doc(helloworld)
|
||||||
|
@server(
|
||||||
|
handler: GreetHandler
|
||||||
|
)
|
||||||
|
get /greet/from/:name(Request) returns (Response)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const importApi = `
|
||||||
|
type ImportData struct {
|
||||||
|
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const hasImportApi = `
|
||||||
|
import "importApi.api"
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||||
|
}
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Message string ` + "`" + `json:"message"` + "`" + ` // message
|
||||||
|
}
|
||||||
|
|
||||||
|
service A-api {
|
||||||
|
@server(
|
||||||
|
handler: GreetHandler
|
||||||
|
)
|
||||||
|
get /greet/from/:name(Request) returns (Response)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
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)
|
||||||
@@ -217,13 +292,13 @@ func TestParser(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, len(api.Types), 2)
|
assert.Equal(t, len(api.Types), 2)
|
||||||
assert.Equal(t, len(api.Service.Routes), 2)
|
assert.Equal(t, len(api.Service.Routes()), 2)
|
||||||
|
|
||||||
assert.Equal(t, api.Service.Routes[0].Path, "/greet/from/:name")
|
assert.Equal(t, api.Service.Routes()[0].Path, "/greet/from/:name")
|
||||||
assert.Equal(t, api.Service.Routes[1].Path, "/greet/get")
|
assert.Equal(t, api.Service.Routes()[1].Path, "/greet/get")
|
||||||
|
|
||||||
assert.Equal(t, api.Service.Routes[1].RequestType.Name, "Request")
|
assert.Equal(t, api.Service.Routes()[1].RequestType.Name, "Request")
|
||||||
assert.Equal(t, api.Service.Routes[1].ResponseType.Name, "")
|
assert.Equal(t, api.Service.Routes()[1].ResponseType.Name, "")
|
||||||
|
|
||||||
validate(t, filename)
|
validate(t, filename)
|
||||||
}
|
}
|
||||||
@@ -240,7 +315,7 @@ func TestMultiService(t *testing.T) {
|
|||||||
api, err := parser.Parse()
|
api, err := parser.Parse()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, len(api.Service.Routes), 2)
|
assert.Equal(t, len(api.Service.Routes()), 2)
|
||||||
assert.Equal(t, len(api.Service.Groups), 2)
|
assert.Equal(t, len(api.Service.Groups), 2)
|
||||||
|
|
||||||
validate(t, filename)
|
validate(t, filename)
|
||||||
@@ -267,10 +342,7 @@ func TestInvalidApiFile(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
defer os.Remove(filename)
|
defer os.Remove(filename)
|
||||||
|
|
||||||
parser, err := parser.NewParser(filename)
|
_, err = parser.NewParser(filename)
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
_, err = parser.Parse()
|
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,8 +358,8 @@ func TestAnonymousAnnotation(t *testing.T) {
|
|||||||
api, err := parser.Parse()
|
api, err := parser.Parse()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t, len(api.Service.Routes), 1)
|
assert.Equal(t, len(api.Service.Routes()), 1)
|
||||||
assert.Equal(t, api.Service.Routes[0].Annotations[0].Value, "GreetHandler")
|
assert.Equal(t, api.Service.Routes()[0].Annotations[0].Value, "GreetHandler")
|
||||||
|
|
||||||
validate(t, filename)
|
validate(t, filename)
|
||||||
}
|
}
|
||||||
@@ -367,9 +439,68 @@ func TestApiRoutes(t *testing.T) {
|
|||||||
validate(t, filename)
|
validate(t, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHasCommentRoutes(t *testing.T) {
|
||||||
|
filename := "greet.api"
|
||||||
|
err := ioutil.WriteFile(filename, []byte(hasCommentApiTest), os.ModePerm)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.Remove(filename)
|
||||||
|
|
||||||
|
parser, err := parser.NewParser(filename)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = parser.Parse()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
validate(t, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInlineTypeNotExist(t *testing.T) {
|
||||||
|
filename := "greet.api"
|
||||||
|
err := ioutil.WriteFile(filename, []byte(hasInlineNoExistTest), os.ModePerm)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.Remove(filename)
|
||||||
|
|
||||||
|
parser, err := parser.NewParser(filename)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
_, err = parser.Parse()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
validate(t, filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasImportApi(t *testing.T) {
|
||||||
|
filename := "greet.api"
|
||||||
|
err := ioutil.WriteFile(filename, []byte(hasImportApi), os.ModePerm)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.Remove(filename)
|
||||||
|
|
||||||
|
importApiName := "importApi.api"
|
||||||
|
err = ioutil.WriteFile(importApiName, []byte(importApi), os.ModePerm)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer os.Remove(importApiName)
|
||||||
|
|
||||||
|
parser, err := parser.NewParser(filename)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
api, err := parser.Parse()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
var hasInline bool
|
||||||
|
for _, ty := range api.Types {
|
||||||
|
if ty.Name == "ImportData" {
|
||||||
|
hasInline = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.True(t, hasInline)
|
||||||
|
validate(t, filename)
|
||||||
|
}
|
||||||
|
|
||||||
func validate(t *testing.T, api string) {
|
func validate(t *testing.T, api string) {
|
||||||
dir := "_go"
|
dir := "_go"
|
||||||
err := DoGenProject(api, dir, true)
|
os.RemoveAll(dir)
|
||||||
|
err := DoGenProject(api, dir)
|
||||||
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 {
|
||||||
@@ -380,6 +511,9 @@ func validate(t *testing.T, api string) {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
_, err = execx.Run("go test ./...", dir)
|
||||||
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCode(code string) error {
|
func validateCode(code string) error {
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ func genConfig(dir string, api *spec.ApiSpec) error {
|
|||||||
"auth": strings.Join(auths, "\n"),
|
"auth": strings.Join(auths, "\n"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
_, err = fp.WriteString(formatCode)
|
_, err = fp.WriteString(formatCode)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ func genEtc(dir string, api *spec.ApiSpec) error {
|
|||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
|
|
||||||
service := api.Service
|
service := api.Service
|
||||||
host, ok := util.GetAnnotationValue(service.Annotations, "server", "host")
|
host, ok := util.GetAnnotationValue(service.Groups[0].Annotations, "server", "host")
|
||||||
if !ok {
|
if !ok {
|
||||||
host = "0.0.0.0"
|
host = "0.0.0.0"
|
||||||
}
|
}
|
||||||
port, ok := util.GetAnnotationValue(service.Annotations, "server", "port")
|
port, ok := util.GetAnnotationValue(service.Groups[0].Annotations, "server", "port")
|
||||||
if !ok {
|
if !ok {
|
||||||
port = strconv.Itoa(defaultPort)
|
port = strconv.Itoa(defaultPort)
|
||||||
}
|
}
|
||||||
@@ -55,6 +55,7 @@ func genEtc(dir string, api *spec.ApiSpec) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
_, err = fp.WriteString(formatCode)
|
_, err = fp.WriteString(formatCode)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func doGenToFile(dir, handler string, group spec.Group, route spec.Route, handle
|
|||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
err = template.Must(template.New("handlerTemplate").Parse(text)).Execute(buffer, handleObj)
|
err = template.Must(template.New("handlerTemplate").Parse(text)).Execute(buffer, handleObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
|
|||||||
@@ -72,8 +72,9 @@ func genMain(dir string, api *spec.ApiSpec) error {
|
|||||||
"serviceName": api.Service.Name,
|
"serviceName": api.Service.Name,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
_, err = fp.WriteString(formatCode)
|
_, err = fp.WriteString(formatCode)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -49,8 +49,9 @@ func genMiddleware(dir string, middlewares []string) error {
|
|||||||
"name": strings.Title(name),
|
"name": strings.Title(name),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
_, err = fp.WriteString(formatCode)
|
_, err = fp.WriteString(formatCode)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package gogen
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -52,7 +53,7 @@ type (
|
|||||||
jwtEnabled bool
|
jwtEnabled bool
|
||||||
signatureEnabled bool
|
signatureEnabled bool
|
||||||
authName string
|
authName string
|
||||||
middleware []string
|
middlewares []string
|
||||||
}
|
}
|
||||||
route struct {
|
route struct {
|
||||||
method string
|
method string
|
||||||
@@ -61,7 +62,7 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func genRoutes(dir string, api *spec.ApiSpec, force bool) error {
|
func genRoutes(dir string, 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 {
|
||||||
@@ -92,9 +93,9 @@ func genRoutes(dir string, api *spec.ApiSpec, force bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var routes string
|
var routes string
|
||||||
if len(g.middleware) > 0 {
|
if len(g.middlewares) > 0 {
|
||||||
gbuilder.WriteString("\n}...,")
|
gbuilder.WriteString("\n}...,")
|
||||||
var params = g.middleware
|
var params = g.middlewares
|
||||||
for i := range params {
|
for i := range params {
|
||||||
params[i] = "serverCtx." + params[i]
|
params[i] = "serverCtx." + params[i]
|
||||||
}
|
}
|
||||||
@@ -121,11 +122,7 @@ func genRoutes(dir string, api *spec.ApiSpec, force bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filename := path.Join(dir, handlerDir, routesFilename)
|
filename := path.Join(dir, handlerDir, routesFilename)
|
||||||
if !force {
|
os.Remove(filename)
|
||||||
if err := util.RemoveOrQuit(filename); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routesFilename)
|
fp, created, err := apiutil.MaybeCreateFile(dir, handlerDir, routesFilename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -143,8 +140,9 @@ func genRoutes(dir string, api *spec.ApiSpec, force bool) error {
|
|||||||
"routesAdditions": strings.TrimSpace(builder.String()),
|
"routesAdditions": strings.TrimSpace(builder.String()),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
_, err = fp.WriteString(formatCode)
|
_, err = fp.WriteString(formatCode)
|
||||||
return err
|
return err
|
||||||
@@ -162,8 +160,7 @@ func genRouteImports(parentPkg string, api *spec.ApiSpec) string {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
importSet.AddStr(fmt.Sprintf("%s \"%s\"", folder,
|
importSet.AddStr(fmt.Sprintf("%s \"%s\"", toPrefix(folder), util.JoinPackages(parentPkg, handlerDir, folder)))
|
||||||
util.JoinPackages(parentPkg, handlerDir, folder)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
imports := importSet.KeysStr()
|
imports := importSet.KeysStr()
|
||||||
@@ -186,11 +183,11 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
|
|||||||
handler = getHandlerBaseName(handler) + "Handler(serverCtx)"
|
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 = folder + "." + strings.ToUpper(handler[:1]) + handler[1:]
|
handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]
|
||||||
} else {
|
} else {
|
||||||
folder, ok = apiutil.GetAnnotationValue(g.Annotations, "server", groupProperty)
|
folder, ok = apiutil.GetAnnotationValue(g.Annotations, "server", groupProperty)
|
||||||
if ok {
|
if ok {
|
||||||
handler = folder + "." + strings.ToUpper(handler[:1]) + handler[1:]
|
handler = toPrefix(folder) + "." + strings.ToUpper(handler[:1]) + handler[1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
groupedRoutes.routes = append(groupedRoutes.routes, route{
|
groupedRoutes.routes = append(groupedRoutes.routes, route{
|
||||||
@@ -206,7 +203,7 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
|
|||||||
}
|
}
|
||||||
if value, ok := apiutil.GetAnnotationValue(g.Annotations, "server", "middleware"); ok {
|
if value, ok := apiutil.GetAnnotationValue(g.Annotations, "server", "middleware"); ok {
|
||||||
for _, item := range strings.Split(value, ",") {
|
for _, item := range strings.Split(value, ",") {
|
||||||
groupedRoutes.middleware = append(groupedRoutes.middleware, item)
|
groupedRoutes.middlewares = append(groupedRoutes.middlewares, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
routes = append(routes, groupedRoutes)
|
routes = append(routes, groupedRoutes)
|
||||||
@@ -214,3 +211,7 @@ func getRoutes(api *spec.ApiSpec) ([]group, error) {
|
|||||||
|
|
||||||
return routes, nil
|
return routes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toPrefix(folder string) string {
|
||||||
|
return strings.ReplaceAll(folder, "/", "")
|
||||||
|
}
|
||||||
|
|||||||
@@ -90,8 +90,9 @@ func genServiceContext(dir string, api *spec.ApiSpec) error {
|
|||||||
"middlewareAssignment": middlewareAssignment,
|
"middlewareAssignment": middlewareAssignment,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
_, err = fp.WriteString(formatCode)
|
_, err = fp.WriteString(formatCode)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
@@ -42,18 +43,14 @@ func BuildTypes(types []spec.Type) (string, error) {
|
|||||||
return builder.String(), nil
|
return builder.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func genTypes(dir string, api *spec.ApiSpec, force bool) error {
|
func genTypes(dir string, 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)
|
filename := path.Join(dir, typesDir, typesFile)
|
||||||
if !force {
|
os.Remove(filename)
|
||||||
if err := util.RemoveOrQuit(filename); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typesFile)
|
fp, created, err := apiutil.MaybeCreateFile(dir, typesDir, typesFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -71,8 +68,9 @@ func genTypes(dir string, api *spec.ApiSpec, force bool) error {
|
|||||||
"containsTime": api.ContainsTime(),
|
"containsTime": api.ContainsTime(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
formatCode := formatCode(buffer.String())
|
formatCode := formatCode(buffer.String())
|
||||||
_, err = fp.WriteString(formatCode)
|
_, err = fp.WriteString(formatCode)
|
||||||
return err
|
return err
|
||||||
@@ -90,12 +88,6 @@ func convertTypeCase(types []spec.Type, t string) (string, error) {
|
|||||||
if typ.Name == tp {
|
if typ.Name == tp {
|
||||||
defTypes = append(defTypes, tp)
|
defTypes = append(defTypes, tp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(typ.Annotations) > 0 {
|
|
||||||
if value, ok := apiutil.GetAnnotationValue(typ.Annotations, "serverReplacer", tp); ok {
|
|
||||||
t = strings.ReplaceAll(t, tp, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,8 @@ func getParentPackage(dir string) (string, error) {
|
|||||||
return filepath.ToSlash(filepath.Join(projectCtx.Path, strings.TrimPrefix(projectCtx.WorkDir, projectCtx.Dir))), nil
|
return filepath.ToSlash(filepath.Join(projectCtx.Path, strings.TrimPrefix(projectCtx.WorkDir, projectCtx.Dir))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeIndent(writer io.Writer, indent int) {
|
|
||||||
for i := 0; i < indent; i++ {
|
|
||||||
fmt.Fprint(writer, "\t")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeProperty(writer io.Writer, name, tp, tag, comment string, indent int) error {
|
func writeProperty(writer io.Writer, name, tp, tag, comment string, indent int) error {
|
||||||
writeIndent(writer, indent)
|
util.WriteIndent(writer, indent)
|
||||||
var err error
|
var err error
|
||||||
if len(comment) > 0 {
|
if len(comment) > 0 {
|
||||||
comment = strings.TrimPrefix(comment, "//")
|
comment = strings.TrimPrefix(comment, "//")
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class {{.packetName}} extends HttpRequestPacket<{{.packetName}}.{{.packet
|
|||||||
`
|
`
|
||||||
|
|
||||||
func genPacket(dir, packetName string, api *spec.ApiSpec) error {
|
func genPacket(dir, packetName string, api *spec.ApiSpec) error {
|
||||||
for _, route := range api.Service.Routes {
|
for _, route := range api.Service.Routes() {
|
||||||
if err := createWith(dir, api, route, packetName); err != nil {
|
if err := createWith(dir, api, route, packetName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
const apiTemplate = `
|
const apiTemplate = `
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + ` // 框架自动验证请求参数是否合法
|
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||||
}
|
}
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
@@ -27,9 +27,9 @@ service {{.name}}-api {
|
|||||||
|
|
||||||
func NewService(c *cli.Context) error {
|
func NewService(c *cli.Context) error {
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
dirName := "greet"
|
dirName := args.First()
|
||||||
if len(args) > 0 {
|
if len(dirName) == 0 {
|
||||||
dirName = args.First()
|
dirName = "greet"
|
||||||
}
|
}
|
||||||
|
|
||||||
abs, err := filepath.Abs(dirName)
|
abs, err := filepath.Abs(dirName)
|
||||||
@@ -58,6 +58,6 @@ func NewService(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = gogen.DoGenProject(apiFilePath, abs, true)
|
err = gogen.DoGenProject(apiFilePath, abs)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
219
tools/goctl/api/parser/apifileparser.go
Normal file
219
tools/goctl/api/parser/apifileparser.go
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tokenInfo = "info"
|
||||||
|
tokenImport = "import"
|
||||||
|
tokenType = "type"
|
||||||
|
tokenService = "service"
|
||||||
|
tokenServiceAnnotation = "@server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
ApiStruct struct {
|
||||||
|
Info string
|
||||||
|
Type string
|
||||||
|
Service string
|
||||||
|
Imports string
|
||||||
|
serviceBeginLine int
|
||||||
|
}
|
||||||
|
|
||||||
|
apiFileState interface {
|
||||||
|
process(api *ApiStruct, token string) (apiFileState, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
apiRootState struct {
|
||||||
|
*baseState
|
||||||
|
}
|
||||||
|
|
||||||
|
apiInfoState struct {
|
||||||
|
*baseState
|
||||||
|
}
|
||||||
|
|
||||||
|
apiImportState struct {
|
||||||
|
*baseState
|
||||||
|
}
|
||||||
|
|
||||||
|
apiTypeState struct {
|
||||||
|
*baseState
|
||||||
|
}
|
||||||
|
|
||||||
|
apiServiceState struct {
|
||||||
|
*baseState
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseApi(src string) (*ApiStruct, error) {
|
||||||
|
var buffer = new(bytes.Buffer)
|
||||||
|
buffer.WriteString(src)
|
||||||
|
api := new(ApiStruct)
|
||||||
|
var lineNumber = api.serviceBeginLine
|
||||||
|
apiFile := baseState{r: bufio.NewReader(buffer), lineNumber: &lineNumber}
|
||||||
|
st := apiRootState{&apiFile}
|
||||||
|
for {
|
||||||
|
st, err := st.process(api, "")
|
||||||
|
if err == io.EOF {
|
||||||
|
return api, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("near line: %d, %s", lineNumber, err.Error())
|
||||||
|
}
|
||||||
|
if st == nil {
|
||||||
|
return api, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *apiRootState) process(api *ApiStruct, token string) (apiFileState, error) {
|
||||||
|
var builder strings.Builder
|
||||||
|
for {
|
||||||
|
ch, err := s.readSkipComment()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case isSpace(ch) || isNewline(ch) || ch == leftParenthesis:
|
||||||
|
token := builder.String()
|
||||||
|
token = strings.TrimSpace(token)
|
||||||
|
if len(token) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Reset()
|
||||||
|
switch token {
|
||||||
|
case tokenInfo:
|
||||||
|
info := apiInfoState{s.baseState}
|
||||||
|
return info.process(api, token+string(ch))
|
||||||
|
case tokenImport:
|
||||||
|
tp := apiImportState{s.baseState}
|
||||||
|
return tp.process(api, token+string(ch))
|
||||||
|
case tokenType:
|
||||||
|
ty := apiTypeState{s.baseState}
|
||||||
|
return ty.process(api, token+string(ch))
|
||||||
|
case tokenService:
|
||||||
|
server := apiServiceState{s.baseState}
|
||||||
|
return server.process(api, token+string(ch))
|
||||||
|
case tokenServiceAnnotation:
|
||||||
|
server := apiServiceState{s.baseState}
|
||||||
|
return server.process(api, token+string(ch))
|
||||||
|
default:
|
||||||
|
if strings.HasPrefix(token, "//") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, errors.New(fmt.Sprintf("invalid token %s at line %d", token, *s.lineNumber))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
builder.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *apiInfoState) process(api *ApiStruct, token string) (apiFileState, error) {
|
||||||
|
for {
|
||||||
|
line, err := s.readLine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
api.Info += "\n" + token + line
|
||||||
|
token = ""
|
||||||
|
if strings.TrimSpace(line) == string(rightParenthesis) {
|
||||||
|
return &apiRootState{s.baseState}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *apiImportState) process(api *ApiStruct, token string) (apiFileState, error) {
|
||||||
|
line, err := s.readLine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
line = token + line
|
||||||
|
if len(strings.Fields(line)) != 2 {
|
||||||
|
return nil, errors.New("import syntax error: " + line)
|
||||||
|
}
|
||||||
|
|
||||||
|
api.Imports += "\n" + line
|
||||||
|
return &apiRootState{s.baseState}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *apiTypeState) process(api *ApiStruct, token string) (apiFileState, error) {
|
||||||
|
var blockCount = 0
|
||||||
|
for {
|
||||||
|
line, err := s.readLine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
api.Type += "\n\n" + token + line
|
||||||
|
token = ""
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
line = removeComment(line)
|
||||||
|
if strings.HasSuffix(line, leftBrace) {
|
||||||
|
blockCount++
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(line, string(leftParenthesis)) {
|
||||||
|
blockCount++
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(line, string(rightBrace)) {
|
||||||
|
blockCount--
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(line, string(rightParenthesis)) {
|
||||||
|
blockCount--
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockCount == 0 {
|
||||||
|
return &apiRootState{s.baseState}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *apiServiceState) process(api *ApiStruct, token string) (apiFileState, error) {
|
||||||
|
var blockCount = 0
|
||||||
|
for {
|
||||||
|
line, err := s.readLineSkipComment()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
line = token + line
|
||||||
|
token = ""
|
||||||
|
api.Service += "\n" + line
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
line = removeComment(line)
|
||||||
|
if strings.HasSuffix(line, leftBrace) {
|
||||||
|
blockCount++
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(line, string(leftParenthesis)) {
|
||||||
|
blockCount++
|
||||||
|
}
|
||||||
|
if line == string(rightBrace) {
|
||||||
|
blockCount--
|
||||||
|
}
|
||||||
|
if line == string(rightParenthesis) {
|
||||||
|
blockCount--
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockCount == 0 {
|
||||||
|
return &apiRootState{s.baseState}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeComment(line string) string {
|
||||||
|
var commentIdx = strings.Index(line, "//")
|
||||||
|
if commentIdx >= 0 {
|
||||||
|
return line[:commentIdx]
|
||||||
|
}
|
||||||
|
return line
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ func (s *baseState) parseProperties() (map[string]string, error) {
|
|||||||
var st = startState
|
var st = startState
|
||||||
|
|
||||||
for {
|
for {
|
||||||
ch, err := s.read()
|
ch, err := s.readSkipComment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -164,6 +164,60 @@ func (s *baseState) read() (rune, error) {
|
|||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *baseState) readSkipComment() (rune, error) {
|
||||||
|
ch, err := s.read()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSlash(ch) {
|
||||||
|
value, err := s.mayReadToEndOfLine()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if value > 0 {
|
||||||
|
ch = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *baseState) mayReadToEndOfLine() (rune, error) {
|
||||||
|
ch, err := s.read()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSlash(ch) {
|
||||||
|
for {
|
||||||
|
value, err := s.read()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isNewline(value) {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = s.unread()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *baseState) readLineSkipComment() (string, error) {
|
||||||
|
line, err := s.readLine()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var commentIdx = strings.Index(line, "//")
|
||||||
|
if commentIdx >= 0 {
|
||||||
|
return line[:commentIdx], nil
|
||||||
|
}
|
||||||
|
return line, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *baseState) readLine() (string, error) {
|
func (s *baseState) readLine() (string, error) {
|
||||||
line, _, err := s.r.ReadLine()
|
line, _, err := s.r.ReadLine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func newEntity(state *baseState, api *spec.ApiSpec, parser entityParser) entity
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *entity) process() error {
|
func (s *entity) process() error {
|
||||||
line, err := s.state.readLine()
|
line, err := s.state.readLineSkipComment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ func (s *entity) process() error {
|
|||||||
var annos []spec.Annotation
|
var annos []spec.Annotation
|
||||||
memberLoop:
|
memberLoop:
|
||||||
for {
|
for {
|
||||||
ch, err := s.state.read()
|
ch, err := s.state.readSkipComment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -70,13 +70,13 @@ memberLoop:
|
|||||||
case ch == at:
|
case ch == at:
|
||||||
annotationLoop:
|
annotationLoop:
|
||||||
for {
|
for {
|
||||||
next, err := s.state.read()
|
next, err := s.state.readSkipComment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case isSpace(next):
|
case isSpace(next):
|
||||||
if builder.Len() > 0 {
|
if builder.Len() > 0 && annoName == "" {
|
||||||
annoName = builder.String()
|
annoName = builder.String()
|
||||||
builder.Reset()
|
builder.Reset()
|
||||||
}
|
}
|
||||||
@@ -84,6 +84,7 @@ memberLoop:
|
|||||||
if builder.Len() == 0 {
|
if builder.Len() == 0 {
|
||||||
return errors.New("invalid annotation format")
|
return errors.New("invalid annotation format")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(annoName) > 0 {
|
if len(annoName) > 0 {
|
||||||
value := builder.String()
|
value := builder.String()
|
||||||
if value != string(leftParenthesis) {
|
if value != string(leftParenthesis) {
|
||||||
@@ -127,7 +128,7 @@ memberLoop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
var line string
|
var line string
|
||||||
line, err = s.state.readLine()
|
line, err = s.state.readLineSkipComment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package parser
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -34,28 +35,46 @@ func NewParser(filename string) (*Parser, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range strings.Split(apiStruct.Imports, "\n") {
|
for _, item := range strings.Split(apiStruct.Imports, "\n") {
|
||||||
ip := strings.TrimSpace(item)
|
importLine := strings.TrimSpace(item)
|
||||||
if len(ip) > 0 {
|
if len(importLine) > 0 {
|
||||||
item := strings.TrimPrefix(item, "import")
|
item := strings.TrimPrefix(importLine, "import")
|
||||||
item = strings.TrimSpace(item)
|
item = strings.TrimSpace(item)
|
||||||
|
item = strings.TrimPrefix(item, `"`)
|
||||||
|
item = strings.TrimSuffix(item, `"`)
|
||||||
var path = item
|
var path = item
|
||||||
if !util.FileExists(item) {
|
if !util.FileExists(item) {
|
||||||
path = filepath.Join(filepath.Dir(apiAbsPath), item)
|
path = filepath.Join(filepath.Dir(apiAbsPath), item)
|
||||||
}
|
}
|
||||||
content, err := ioutil.ReadFile(path)
|
content, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("import api file not exist: " + item)
|
||||||
|
}
|
||||||
|
|
||||||
|
importStruct, err := ParseApi(string(content))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
apiStruct.StructBody += "\n" + string(content)
|
|
||||||
|
if len(importStruct.Imports) > 0 {
|
||||||
|
return nil, errors.New("import api should not import another api file recursive")
|
||||||
|
}
|
||||||
|
|
||||||
|
apiStruct.Type += "\n" + importStruct.Type
|
||||||
|
apiStruct.Service += "\n" + importStruct.Service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(strings.TrimSpace(apiStruct.Service)) == 0 {
|
||||||
|
return nil, errors.New("api has no service defined")
|
||||||
|
}
|
||||||
|
|
||||||
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.StructBody,
|
typeDef: apiStruct.Type,
|
||||||
api: apiStruct,
|
api: apiStruct,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@@ -67,6 +86,7 @@ func (p *Parser) Parse() (api *spec.ApiSpec, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
api.Types = types
|
api.Types = types
|
||||||
var lineNumber = p.api.serviceBeginLine
|
var lineNumber = p.api.serviceBeginLine
|
||||||
st := newRootState(p.r, &lineNumber)
|
st := newRootState(p.r, &lineNumber)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (s rootState) process(api *spec.ApiSpec) (state, error) {
|
|||||||
var annos []spec.Annotation
|
var annos []spec.Annotation
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
for {
|
for {
|
||||||
ch, err := s.read()
|
ch, err := s.readSkipComment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,7 @@ func (s rootState) process(api *spec.ApiSpec) (state, error) {
|
|||||||
if builder.Len() == 0 {
|
if builder.Len() == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
token := builder.String()
|
token := builder.String()
|
||||||
builder.Reset()
|
builder.Reset()
|
||||||
return s.processToken(token, annos)
|
return s.processToken(token, annos)
|
||||||
@@ -44,10 +45,11 @@ func (s rootState) process(api *spec.ApiSpec) (state, error) {
|
|||||||
var annoName string
|
var annoName string
|
||||||
annoLoop:
|
annoLoop:
|
||||||
for {
|
for {
|
||||||
next, err := s.read()
|
next, err := s.readSkipComment()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case isSpace(next):
|
case isSpace(next):
|
||||||
if builder.Len() > 0 {
|
if builder.Len() > 0 {
|
||||||
@@ -58,6 +60,7 @@ func (s rootState) process(api *spec.ApiSpec) (state, error) {
|
|||||||
if err := s.unread(); err != nil {
|
if err := s.unread(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if builder.Len() > 0 {
|
if builder.Len() > 0 {
|
||||||
annoName = builder.String()
|
annoName = builder.String()
|
||||||
builder.Reset()
|
builder.Reset()
|
||||||
@@ -66,6 +69,7 @@ func (s rootState) process(api *spec.ApiSpec) (state, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
annos = append(annos, spec.Annotation{
|
annos = append(annos, spec.Annotation{
|
||||||
Name: annoName,
|
Name: annoName,
|
||||||
Properties: attrs,
|
Properties: attrs,
|
||||||
@@ -79,9 +83,11 @@ func (s rootState) process(api *spec.ApiSpec) (state, error) {
|
|||||||
if builder.Len() == 0 {
|
if builder.Len() == 0 {
|
||||||
return nil, fmt.Errorf("incorrect %q at the beginning of the line", leftParenthesis)
|
return nil, fmt.Errorf("incorrect %q at the beginning of the line", leftParenthesis)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.unread(); err != nil {
|
if err := s.unread(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
token := builder.String()
|
token := builder.String()
|
||||||
builder.Reset()
|
builder.Reset()
|
||||||
return s.processToken(token, annos)
|
return s.processToken(token, annos)
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ func (s *serviceState) process(api *spec.ApiSpec) (state, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
api.Service = spec.Service{
|
api.Service = spec.Service{
|
||||||
Name: name,
|
Name: name,
|
||||||
Annotations: append(api.Service.Annotations, s.annos...),
|
|
||||||
Routes: append(api.Service.Routes, routes...),
|
|
||||||
Groups: append(api.Service.Groups, spec.Group{
|
Groups: append(api.Service.Groups, spec.Group{
|
||||||
Annotations: s.annos,
|
Annotations: s.annos,
|
||||||
Routes: routes,
|
Routes: routes,
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
type typeState struct {
|
|
||||||
*baseState
|
|
||||||
annos []spec.Annotation
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTypeState(state *baseState, annos []spec.Annotation) state {
|
|
||||||
return &typeState{
|
|
||||||
baseState: state,
|
|
||||||
annos: annos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *typeState) process(api *spec.ApiSpec) (state, error) {
|
|
||||||
var name string
|
|
||||||
var members []spec.Member
|
|
||||||
parser := &typeEntityParser{
|
|
||||||
acceptName: func(n string) {
|
|
||||||
name = n
|
|
||||||
},
|
|
||||||
acceptMember: func(member spec.Member) {
|
|
||||||
members = append(members, member)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
ent := newEntity(s.baseState, api, parser)
|
|
||||||
if err := ent.process(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
api.Types = append(api.Types, spec.Type{
|
|
||||||
Name: name,
|
|
||||||
Annotations: s.annos,
|
|
||||||
Members: members,
|
|
||||||
})
|
|
||||||
|
|
||||||
return newRootState(s.r, s.lineNumber), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type typeEntityParser struct {
|
|
||||||
acceptName func(name string)
|
|
||||||
acceptMember func(member spec.Member)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *typeEntityParser) parseLine(line string, api *spec.ApiSpec, annos []spec.Annotation) error {
|
|
||||||
index := strings.Index(line, "//")
|
|
||||||
comment := ""
|
|
||||||
if index >= 0 {
|
|
||||||
comment = line[index+2:]
|
|
||||||
line = strings.TrimSpace(line[:index])
|
|
||||||
}
|
|
||||||
fields := strings.Fields(line)
|
|
||||||
if len(fields) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(fields) == 1 {
|
|
||||||
p.acceptMember(spec.Member{
|
|
||||||
Annotations: annos,
|
|
||||||
Name: fields[0],
|
|
||||||
Type: fields[0],
|
|
||||||
IsInline: true,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
name := fields[0]
|
|
||||||
tp := fields[1]
|
|
||||||
var tag string
|
|
||||||
if len(fields) > 2 {
|
|
||||||
tag = fields[2]
|
|
||||||
} else {
|
|
||||||
tag = fmt.Sprintf("`json:\"%s\"`", util.Untitle(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
p.acceptMember(spec.Member{
|
|
||||||
Annotations: annos,
|
|
||||||
Name: name,
|
|
||||||
Type: tp,
|
|
||||||
Tag: tag,
|
|
||||||
Comment: comment,
|
|
||||||
IsInline: false,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *typeEntityParser) setEntityName(name string) {
|
|
||||||
p.acceptName(name)
|
|
||||||
}
|
|
||||||
@@ -2,22 +2,12 @@ package parser
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
var emptyType spec.Type
|
var emptyType spec.Type
|
||||||
|
|
||||||
type ApiStruct struct {
|
|
||||||
Info string
|
|
||||||
StructBody string
|
|
||||||
Service string
|
|
||||||
Imports string
|
|
||||||
serviceBeginLine int
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetType(api *spec.ApiSpec, t string) spec.Type {
|
func GetType(api *spec.ApiSpec, t string) spec.Type {
|
||||||
for _, tp := range api.Types {
|
for _, tp := range api.Types {
|
||||||
if tp.Name == t {
|
if tp.Name == t {
|
||||||
@@ -36,6 +26,10 @@ func isSpace(r rune) bool {
|
|||||||
return r == ' ' || r == '\t'
|
return r == ' ' || r == '\t'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isSlash(r rune) bool {
|
||||||
|
return r == '/'
|
||||||
|
}
|
||||||
|
|
||||||
func isNewline(r rune) bool {
|
func isNewline(r rune) bool {
|
||||||
return r == '\n' || r == '\r'
|
return r == '\n' || r == '\r'
|
||||||
}
|
}
|
||||||
@@ -69,82 +63,3 @@ func skipSpaces(r *bufio.Reader) error {
|
|||||||
func unread(r *bufio.Reader) error {
|
func unread(r *bufio.Reader) error {
|
||||||
return r.UnreadRune()
|
return r.UnreadRune()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseApi(api string) (*ApiStruct, error) {
|
|
||||||
var result ApiStruct
|
|
||||||
scanner := bufio.NewScanner(strings.NewReader(api))
|
|
||||||
var parseInfo = false
|
|
||||||
var parseImport = false
|
|
||||||
var parseType = false
|
|
||||||
var parseService = false
|
|
||||||
var segment string
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
|
|
||||||
if line == "info(" {
|
|
||||||
parseInfo = true
|
|
||||||
}
|
|
||||||
if line == ")" && parseInfo {
|
|
||||||
parseInfo = false
|
|
||||||
result.Info = segment + ")"
|
|
||||||
segment = ""
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if isImportBeginLine(line) {
|
|
||||||
parseImport = true
|
|
||||||
}
|
|
||||||
if parseImport && (isTypeBeginLine(line) || isServiceBeginLine(line)) {
|
|
||||||
parseImport = false
|
|
||||||
result.Imports = segment
|
|
||||||
segment = line + "\n"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if isTypeBeginLine(line) {
|
|
||||||
parseType = true
|
|
||||||
}
|
|
||||||
if isServiceBeginLine(line) {
|
|
||||||
parseService = true
|
|
||||||
if parseType {
|
|
||||||
parseType = false
|
|
||||||
result.StructBody = segment
|
|
||||||
segment = line + "\n"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
segment += scanner.Text() + "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
if !parseService {
|
|
||||||
return nil, errors.New("no service defined")
|
|
||||||
}
|
|
||||||
result.Service = segment
|
|
||||||
result.serviceBeginLine = lineBeginOfService(api)
|
|
||||||
return &result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isImportBeginLine(line string) bool {
|
|
||||||
return strings.HasPrefix(line, "import") && strings.HasSuffix(line, ".api")
|
|
||||||
}
|
|
||||||
|
|
||||||
func isTypeBeginLine(line string) bool {
|
|
||||||
return strings.HasPrefix(line, "type")
|
|
||||||
}
|
|
||||||
|
|
||||||
func isServiceBeginLine(line string) bool {
|
|
||||||
return strings.HasPrefix(line, "@server") || (strings.HasPrefix(line, "service") && strings.HasSuffix(line, "{"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func lineBeginOfService(api string) int {
|
|
||||||
scanner := bufio.NewScanner(strings.NewReader(api))
|
|
||||||
var number = 0
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if isServiceBeginLine(line) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
number++
|
|
||||||
}
|
|
||||||
return number
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func (p *Parser) validateDuplicateProperty(tp spec.Type) (bool, string) {
|
|||||||
|
|
||||||
func (p *Parser) validateDuplicateRouteHandler(api *spec.ApiSpec) (bool, string) {
|
func (p *Parser) validateDuplicateRouteHandler(api *spec.ApiSpec) (bool, string) {
|
||||||
var names []string
|
var names []string
|
||||||
for _, r := range api.Service.Routes {
|
for _, r := range api.Service.Routes() {
|
||||||
handler, ok := util.GetAnnotationValue(r.Annotations, "server", "handler")
|
handler, ok := util.GetAnnotationValue(r.Annotations, "server", "handler")
|
||||||
if !ok {
|
if !ok {
|
||||||
return false, fmt.Sprintf("missing handler annotation for %s", r.Path)
|
return false, fmt.Sprintf("missing handler annotation for %s", r.Path)
|
||||||
|
|||||||
@@ -27,6 +27,14 @@ type Attribute struct {
|
|||||||
value string
|
value string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Service) Routes() []Route {
|
||||||
|
var result []Route
|
||||||
|
for _, group := range s.Groups {
|
||||||
|
result = append(result, group.Routes...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
func (m Member) IsOptional() bool {
|
func (m Member) IsOptional() bool {
|
||||||
var option string
|
var option string
|
||||||
|
|
||||||
|
|||||||
@@ -57,10 +57,8 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
Service struct {
|
Service struct {
|
||||||
Name string
|
Name string
|
||||||
Annotations []Annotation
|
Groups []Group
|
||||||
Routes []Route
|
|
||||||
Groups []Group
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Type struct {
|
Type struct {
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func genHandler(dir, webApi, caller string, api *spec.ApiSpec, unwrapApi bool) e
|
|||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
|
|
||||||
var localTypes []spec.Type
|
var localTypes []spec.Type
|
||||||
for _, route := range api.Service.Routes {
|
for _, route := range api.Service.Routes() {
|
||||||
rts := apiutil.GetLocalTypes(api, route)
|
rts := apiutil.GetLocalTypes(api, route)
|
||||||
localTypes = append(localTypes, rts...)
|
localTypes = append(localTypes, rts...)
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ func genTypes(localTypes []spec.Type, inlineType func(string) (*spec.Type, error
|
|||||||
|
|
||||||
func genApi(api *spec.ApiSpec, localTypes []spec.Type, caller string, prefixForType func(string) string) (string, error) {
|
func genApi(api *spec.ApiSpec, localTypes []spec.Type, caller string, prefixForType func(string) string) (string, error) {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
for _, route := range api.Service.Routes {
|
for _, route := range api.Service.Routes() {
|
||||||
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
|
handler, ok := apiutil.GetAnnotationValue(route.Annotations, "server", "handler")
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("missing handler annotation for route %q", route.Path)
|
return "", fmt.Errorf("missing handler annotation for route %q", route.Path)
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ func GetSharedTypes(api *spec.ApiSpec) []spec.Type {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, route := range api.Service.Routes {
|
for _, route := range api.Service.Routes() {
|
||||||
var rts []spec.Type
|
var rts []spec.Type
|
||||||
getTypeRecursive(route.RequestType, types, &rts)
|
getTypeRecursive(route.RequestType, types, &rts)
|
||||||
getTypeRecursive(route.ResponseType, types, &rts)
|
getTypeRecursive(route.ResponseType, types, &rts)
|
||||||
|
|||||||
@@ -75,3 +75,9 @@ func ComponentName(api *spec.ApiSpec) string {
|
|||||||
}
|
}
|
||||||
return name + "Components"
|
return name + "Components"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WriteIndent(writer io.Writer, indent int) {
|
||||||
|
for i := 0; i < indent; i++ {
|
||||||
|
fmt.Fprint(writer, "\t")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,16 +2,113 @@ package docker
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/gen"
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
ctlutil "github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
etcDir = "etc"
|
||||||
|
yamlEtx = ".yaml"
|
||||||
|
)
|
||||||
|
|
||||||
func DockerCommand(c *cli.Context) error {
|
func DockerCommand(c *cli.Context) error {
|
||||||
goFile := c.String("go")
|
goFile := c.String("go")
|
||||||
if len(goFile) == 0 {
|
if len(goFile) == 0 {
|
||||||
return errors.New("-go can't be empty")
|
return errors.New("-go can't be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
return gen.GenerateDockerfile(goFile, "-f", "etc/config.yaml")
|
cfg, err := findConfig(goFile, etcDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateDockerfile(goFile, "-f", "etc/"+cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findConfig(file, dir string) (string, error) {
|
||||||
|
var files []string
|
||||||
|
err := filepath.Walk(dir, func(path string, f os.FileInfo, _ error) error {
|
||||||
|
if !f.IsDir() {
|
||||||
|
if filepath.Ext(f.Name()) == yamlEtx {
|
||||||
|
files = append(files, f.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
return "", errors.New("no yaml file")
|
||||||
|
}
|
||||||
|
|
||||||
|
name := strings.TrimSuffix(filepath.Base(file), ".go")
|
||||||
|
for _, f := range files {
|
||||||
|
if strings.Index(f, name) == 0 {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateDockerfile(goFile string, args ...string) error {
|
||||||
|
projPath, err := getFilePath(filepath.Dir(goFile))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := strings.IndexByte(projPath, '/')
|
||||||
|
if pos >= 0 {
|
||||||
|
projPath = projPath[pos+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := util.CreateIfNotExist("Dockerfile")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
text, err := ctlutil.LoadTemplate(category, dockerTemplateFile, dockerTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
for _, arg := range args {
|
||||||
|
builder.WriteString(`, "` + arg + `"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := template.Must(template.New("dockerfile").Parse(text))
|
||||||
|
return t.Execute(out, map[string]string{
|
||||||
|
"goRelPath": projPath,
|
||||||
|
"goFile": goFile,
|
||||||
|
"exeFile": util.FileNameWithoutExt(filepath.Base(goFile)),
|
||||||
|
"argument": builder.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilePath(file string) (string, error) {
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
projPath, ok := util.FindGoModPath(filepath.Join(wd, file))
|
||||||
|
if !ok {
|
||||||
|
projPath, err = util.PathFromGoSrc()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("no go.mod found, or not in GOPATH")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return projPath, nil
|
||||||
}
|
}
|
||||||
|
|||||||
44
tools/goctl/docker/template.go
Normal file
44
tools/goctl/docker/template.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package docker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
category = "docker"
|
||||||
|
dockerTemplateFile = "docker.tpl"
|
||||||
|
dockerTemplate = `FROM golang:alpine AS builder
|
||||||
|
|
||||||
|
LABEL stage=gobuilder
|
||||||
|
|
||||||
|
ENV CGO_ENABLED 0
|
||||||
|
ENV GOOS linux
|
||||||
|
ENV GOPROXY https://goproxy.cn,direct
|
||||||
|
|
||||||
|
WORKDIR /build/zero
|
||||||
|
COPY . .
|
||||||
|
COPY {{.goRelPath}}/etc /app/etc
|
||||||
|
RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}}
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine
|
||||||
|
|
||||||
|
RUN apk update --no-cache
|
||||||
|
RUN apk add --no-cache ca-certificates
|
||||||
|
RUN apk add --no-cache tzdata
|
||||||
|
ENV TZ Asia/Shanghai
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /app/{{.exeFile}} /app/{{.exeFile}}
|
||||||
|
COPY --from=builder /app/etc /app/etc
|
||||||
|
|
||||||
|
CMD ["./{{.exeFile}}"{{.argument}}]
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenTemplates(_ *cli.Context) error {
|
||||||
|
return util.InitTemplates(category, map[string]string{
|
||||||
|
dockerTemplateFile: dockerTemplate,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package gen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GenerateDockerfile(goFile string, args ...string) error {
|
|
||||||
projPath, err := getFilePath(filepath.Dir(goFile))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
pos := strings.IndexByte(projPath, '/')
|
|
||||||
if pos >= 0 {
|
|
||||||
projPath = projPath[pos+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := util.CreateIfNotExist("Dockerfile")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
var builder strings.Builder
|
|
||||||
for _, arg := range args {
|
|
||||||
builder.WriteString(`, "` + arg + `"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
t := template.Must(template.New("dockerfile").Parse(dockerTemplate))
|
|
||||||
return t.Execute(out, map[string]string{
|
|
||||||
"projectName": vars.ProjectName,
|
|
||||||
"goRelPath": projPath,
|
|
||||||
"goFile": goFile,
|
|
||||||
"exeFile": util.FileNameWithoutExt(goFile),
|
|
||||||
"argument": builder.String(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package gen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getFilePath(file string) (string, error) {
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
projPath, ok := util.FindGoModPath(filepath.Join(wd, file))
|
|
||||||
if !ok {
|
|
||||||
projPath, err = util.PathFromGoSrc()
|
|
||||||
if err != nil {
|
|
||||||
return "", errors.New("no go.mod found, or not in GOPATH")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return projPath, nil
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package gen
|
|
||||||
|
|
||||||
const dockerTemplate = `FROM golang:alpine AS builder
|
|
||||||
|
|
||||||
LABEL stage=gobuilder
|
|
||||||
|
|
||||||
ENV CGO_ENABLED 0
|
|
||||||
ENV GOOS linux
|
|
||||||
ENV GOPROXY https://goproxy.cn,direct
|
|
||||||
|
|
||||||
WORKDIR $GOPATH/src/{{.projectName}}
|
|
||||||
COPY . .
|
|
||||||
RUN go build -ldflags="-s -w" -o /app/{{.exeFile}} {{.goRelPath}}/{{.goFile}}
|
|
||||||
|
|
||||||
|
|
||||||
FROM alpine
|
|
||||||
|
|
||||||
RUN apk update --no-cache
|
|
||||||
RUN apk add --no-cache ca-certificates
|
|
||||||
RUN apk add --no-cache tzdata
|
|
||||||
ENV TZ Asia/Shanghai
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
COPY --from=builder /app/{{.exeFile}} /app/{{.exeFile}}
|
|
||||||
|
|
||||||
CMD ["./{{.exeFile}}"{{.argument}}]
|
|
||||||
`
|
|
||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BuildVersion = "20201021"
|
BuildVersion = "20201108"
|
||||||
commands = []cli.Command{
|
commands = []cli.Command{
|
||||||
{
|
{
|
||||||
Name: "api",
|
Name: "api",
|
||||||
@@ -98,10 +98,6 @@ var (
|
|||||||
Name: "api",
|
Name: "api",
|
||||||
Usage: "the api file",
|
Usage: "the api file",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "force",
|
|
||||||
Usage: "force override the exist files",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Action: gogen.GoCommand,
|
Action: gogen.GoCommand,
|
||||||
},
|
},
|
||||||
@@ -188,16 +184,12 @@ var (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "docker",
|
Name: "docker",
|
||||||
Usage: "generate Dockerfile and Makefile",
|
Usage: "generate Dockerfile",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "go",
|
Name: "go",
|
||||||
Usage: "the file that contains main function",
|
Usage: "the file that contains main function",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
|
||||||
Name: "namespace, n",
|
|
||||||
Usage: "which namespace of kubernetes to deploy the service",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Action: docker.DockerCommand,
|
Action: docker.DockerCommand,
|
||||||
},
|
},
|
||||||
@@ -224,10 +216,6 @@ var (
|
|||||||
Name: "out, o",
|
Name: "out, o",
|
||||||
Usage: "the target path of proto",
|
Usage: "the target path of proto",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "idea",
|
|
||||||
Usage: "whether the command execution environment is from idea plugin. [optional]",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Action: rpc.RpcTemplate,
|
Action: rpc.RpcTemplate,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
|||||||
|
|
||||||
* 生成代码示例
|
* 生成代码示例
|
||||||
|
|
||||||
``` go
|
```go
|
||||||
|
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -48,9 +49,9 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
|||||||
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
|
userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",")
|
||||||
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
|
userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?"
|
||||||
|
|
||||||
cacheUserMobilePrefix = "cache#User#mobile#"
|
|
||||||
cacheUserIdPrefix = "cache#User#id#"
|
cacheUserIdPrefix = "cache#User#id#"
|
||||||
cacheUserNamePrefix = "cache#User#name#"
|
cacheUserNamePrefix = "cache#User#name#"
|
||||||
|
cacheUserMobilePrefix = "cache#User#mobile#"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -71,23 +72,28 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserModel {
|
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) *UserModel {
|
||||||
return &UserModel{
|
return &UserModel{
|
||||||
CachedConn: sqlc.NewConn(conn, c),
|
CachedConn: sqlc.NewConn(conn, c),
|
||||||
table: table,
|
table: "user",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserModel) Insert(data User) (sql.Result, error) {
|
func (m *UserModel) Insert(data User) (sql.Result, error) {
|
||||||
query := `insert into ` + m.table + `(` + userRowsExpectAutoSet + `) value (?, ?, ?, ?, ?)`
|
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
|
||||||
return m.ExecNoCache(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
|
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) {
|
func (m *UserModel) FindOne(id int64) (*User, error) {
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
||||||
var resp User
|
var resp User
|
||||||
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
|
err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error {
|
||||||
query := `select ` + userRows + ` from ` + m.table + ` where id = ? limit 1`
|
query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table)
|
||||||
return conn.QueryRow(v, query, id)
|
return conn.QueryRow(v, query, id)
|
||||||
})
|
})
|
||||||
switch err {
|
switch err {
|
||||||
@@ -103,18 +109,13 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
|||||||
func (m *UserModel) FindOneByName(name string) (*User, error) {
|
func (m *UserModel) FindOneByName(name string) (*User, error) {
|
||||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
|
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
|
||||||
var resp User
|
var resp User
|
||||||
err := m.QueryRowIndex(&resp, userNameKey, func(primary interface{}) string {
|
err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||||
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
|
query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table)
|
||||||
}, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
|
||||||
query := `select ` + userRows + ` from ` + m.table + ` where name = ? limit 1`
|
|
||||||
if err := conn.QueryRow(&resp, query, name); err != nil {
|
if err := conn.QueryRow(&resp, query, name); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp.Id, nil
|
return resp.Id, nil
|
||||||
}, func(conn sqlx.SqlConn, v, primary interface{}) error {
|
}, m.queryPrimary)
|
||||||
query := `select ` + userRows + ` from ` + m.table + ` where id = ? limit 1`
|
|
||||||
return conn.QueryRow(v, query, primary)
|
|
||||||
})
|
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
@@ -128,18 +129,13 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
|||||||
func (m *UserModel) FindOneByMobile(mobile string) (*User, error) {
|
func (m *UserModel) FindOneByMobile(mobile string) (*User, error) {
|
||||||
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
|
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
|
||||||
var resp User
|
var resp User
|
||||||
err := m.QueryRowIndex(&resp, userMobileKey, func(primary interface{}) string {
|
err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
||||||
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
|
query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table)
|
||||||
}, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
|
|
||||||
query := `select ` + userRows + ` from ` + m.table + ` where mobile = ? limit 1`
|
|
||||||
if err := conn.QueryRow(&resp, query, mobile); err != nil {
|
if err := conn.QueryRow(&resp, query, mobile); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp.Id, nil
|
return resp.Id, nil
|
||||||
}, func(conn sqlx.SqlConn, v, primary interface{}) error {
|
}, m.queryPrimary)
|
||||||
query := `select ` + userRows + ` from ` + m.table + ` where id = ? limit 1`
|
|
||||||
return conn.QueryRow(v, query, primary)
|
|
||||||
})
|
|
||||||
switch err {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
return &resp, nil
|
return &resp, nil
|
||||||
@@ -153,7 +149,7 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
|||||||
func (m *UserModel) Update(data User) error {
|
func (m *UserModel) Update(data User) error {
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
|
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
|
||||||
_, 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 ` + userRowsWithPlaceHolder + ` where id = ?`
|
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)
|
return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id)
|
||||||
}, userIdKey)
|
}, userIdKey)
|
||||||
return err
|
return err
|
||||||
@@ -164,16 +160,26 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
|
||||||
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
|
||||||
userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
|
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) {
|
_, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||||
query := `delete from ` + m.table + ` where id = ?`
|
query := fmt.Sprintf("delete from %s where id = ?", m.table)
|
||||||
return conn.Exec(query, id)
|
return conn.Exec(query, id)
|
||||||
}, userIdKey, userNameKey, userMobileKey)
|
}, userMobileKey, userIdKey, userNameKey)
|
||||||
return err
|
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)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## 用法
|
## 用法
|
||||||
|
|
||||||
@@ -212,16 +218,18 @@ OPTIONS:
|
|||||||
|
|
||||||
```
|
```
|
||||||
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:
|
||||||
|
--src value, -s value the path or path globbing patterns of the ddl
|
||||||
|
--dir value, -d value the target dir
|
||||||
|
--style value the file naming style, lower|camel|underline,default is lower
|
||||||
|
--cache, -c generate code with cache [optional]
|
||||||
|
--idea for idea plugin [optional]
|
||||||
|
|
||||||
OPTIONS:
|
|
||||||
--src value, -s value the path or path globbing patterns of the ddl
|
|
||||||
--dir value, -d value the target dir
|
|
||||||
--cache, -c generate code with cache [optional]
|
|
||||||
--idea for idea plugin [optional]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
* datasource
|
* datasource
|
||||||
@@ -233,22 +241,26 @@ OPTIONS:
|
|||||||
help
|
help
|
||||||
|
|
||||||
```
|
```
|
||||||
NAME:
|
NAME:
|
||||||
goctl model mysql datasource - generate model from datasource
|
goctl model mysql datasource - generate model from datasource
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
goctl model mysql datasource [command options] [arguments...]
|
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]
|
||||||
|
|
||||||
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
|
|
||||||
--idea for idea plugin [optional]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
示例用法请参考[用法](./example/generator.sh)
|
示例用法请参考[用法](./example/generator.sh)
|
||||||
|
|
||||||
|
> NOTE: goctl model mysql ddl/datasource 均新增了一个`--style`参数,用于标记文件命名风格。
|
||||||
|
|
||||||
目前仅支持redis缓存,如果选择带缓存模式,即生成的`FindOne(ByXxx)`&`Delete`代码会生成带缓存逻辑的代码,目前仅支持单索引字段(除全文索引外),对于联合索引我们默认认为不需要带缓存,且不属于通用型代码,因此没有放在代码生成行列,如example中user表中的`id`、`name`、`mobile`字段均属于单字段索引。
|
目前仅支持redis缓存,如果选择带缓存模式,即生成的`FindOne(ByXxx)`&`Delete`代码会生成带缓存逻辑的代码,目前仅支持单索引字段(除全文索引外),对于联合索引我们默认认为不需要带缓存,且不属于通用型代码,因此没有放在代码生成行列,如example中user表中的`id`、`name`、`mobile`字段均属于单字段索引。
|
||||||
|
|
||||||
* 不带缓存模式
|
* 不带缓存模式
|
||||||
|
|||||||
@@ -26,26 +26,49 @@ Goctl Rpc是`goctl`脚手架下的一个rpc服务代码生成模块,支持prot
|
|||||||
|
|
||||||
```golang
|
```golang
|
||||||
.
|
.
|
||||||
├── etc // 配置文件
|
├── etc // yaml配置文件
|
||||||
│ └── greet.yaml
|
│ └── greet.yaml
|
||||||
├── go.mod
|
├── go.mod
|
||||||
├── greet // client call
|
├── greet // pb.go文件夹①
|
||||||
│ └── greet.go
|
│ └── greet.pb.go
|
||||||
├── greet.go // main entry
|
├── greet.go // main函数
|
||||||
├── greet.proto
|
├── greet.proto // proto 文件
|
||||||
└── internal
|
├── greetclient // call logic ②
|
||||||
├── config // 配置声明
|
│ └── greet.go
|
||||||
│ └── config.go
|
└── internal
|
||||||
├── greet // pb.go
|
├── config // yaml配置对应的实体
|
||||||
│ └── greet.pb.go
|
│ └── config.go
|
||||||
├── logic // logic
|
├── logic // 业务代码
|
||||||
│ └── pinglogic.go
|
│ └── pinglogic.go
|
||||||
├── server // pb invoker
|
├── server // rpc server
|
||||||
│ └── greetserver.go
|
│ └── greetserver.go
|
||||||
└── svc // resource dependency
|
└── svc // 依赖资源
|
||||||
└── servicecontext.go
|
└── servicecontext.go
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ① pb文件夹名(老版本文件夹固定为pb)称取自于proto文件中option go_package的值最后一层级按照一定格式进行转换,若无此声明,则取自于package的值,大致代码如下:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if option.Name == "go_package" {
|
||||||
|
ret.GoPackage = option.Constant.Source
|
||||||
|
}
|
||||||
|
...
|
||||||
|
if len(ret.GoPackage) == 0 {
|
||||||
|
ret.GoPackage = ret.Package.Name
|
||||||
|
}
|
||||||
|
ret.PbPackage = GoSanitized(filepath.Base(ret.GoPackage))
|
||||||
|
...
|
||||||
|
```
|
||||||
|
> GoSanitized方法请参考google.golang.org/protobuf@v1.25.0/internal/strs/strings.go:71
|
||||||
|
|
||||||
|
> ② call 层文件夹名称取自于proto中service的名称,如该sercice的名称和pb文件夹名称相等,则会在srervice后面补充client进行区分,使pb和call分隔。
|
||||||
|
|
||||||
|
```go
|
||||||
|
if strings.ToLower(proto.Service.Name) == strings.ToLower(proto.GoPackage) {
|
||||||
|
callDir = filepath.Join(ctx.WorkDir, strings.ToLower(stringx.From(proto.Service.Name+"_client").ToCamel()))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
rpc一键生成常见问题解决,见 <a href="#常见问题解决">常见问题解决</a>
|
rpc一键生成常见问题解决,见 <a href="#常见问题解决">常见问题解决</a>
|
||||||
|
|
||||||
### 方式二:通过指定proto生成rpc服务
|
### 方式二:通过指定proto生成rpc服务
|
||||||
@@ -110,8 +133,8 @@ USAGE:
|
|||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
--src value, -s value the file path of the proto source file
|
--src value, -s value the file path of the proto source file
|
||||||
--proto_path value, -I value native command of protoc,specify the directory in which to search for imports. [optional]
|
--proto_path value, -I value native command of protoc, specify the directory in which to search for imports. [optional]
|
||||||
--dir value, -d value the target path of the code,default path is "${pwd}". [optional]
|
--dir value, -d value the target path of the code
|
||||||
--idea whether the command execution environment is from idea plugin. [optional]
|
--idea whether the command execution environment is from idea plugin. [optional]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -59,9 +59,10 @@ func RpcNew(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RpcTemplate(c *cli.Context) error {
|
func RpcTemplate(c *cli.Context) error {
|
||||||
name := c.Args().First()
|
protoFile := c.String("o")
|
||||||
if len(name) == 0 {
|
if len(protoFile) == 0 {
|
||||||
name = "greet.proto"
|
return errors.New("missing -o")
|
||||||
}
|
}
|
||||||
return generator.ProtoTmpl(name)
|
|
||||||
|
return generator.ProtoTmpl(protoFile)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora"
|
||||||
"github.com/tal-tech/go-zero/core/errorx"
|
"github.com/tal-tech/go-zero/core/errorx"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/gogen"
|
"github.com/tal-tech/go-zero/tools/goctl/api/gogen"
|
||||||
|
"github.com/tal-tech/go-zero/tools/goctl/docker"
|
||||||
modelgen "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
|
modelgen "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
|
||||||
rpcgen "github.com/tal-tech/go-zero/tools/goctl/rpc/generator"
|
rpcgen "github.com/tal-tech/go-zero/tools/goctl/rpc/generator"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
@@ -25,6 +26,9 @@ func GenTemplates(ctx *cli.Context) error {
|
|||||||
func() error {
|
func() error {
|
||||||
return rpcgen.GenTemplates(ctx)
|
return rpcgen.GenTemplates(ctx)
|
||||||
},
|
},
|
||||||
|
func() error {
|
||||||
|
return docker.GenTemplates(ctx)
|
||||||
|
},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +1,11 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import "google.golang.org/grpc"
|
||||||
"context"
|
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WithStreamClientInterceptors(interceptors ...grpc.StreamClientInterceptor) grpc.DialOption {
|
func WithStreamClientInterceptors(interceptors ...grpc.StreamClientInterceptor) grpc.DialOption {
|
||||||
return grpc.WithStreamInterceptor(chainStreamClientInterceptors(interceptors...))
|
return grpc.WithChainStreamInterceptor(interceptors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithUnaryClientInterceptors(interceptors ...grpc.UnaryClientInterceptor) grpc.DialOption {
|
func WithUnaryClientInterceptors(interceptors ...grpc.UnaryClientInterceptor) grpc.DialOption {
|
||||||
return grpc.WithUnaryInterceptor(chainUnaryClientInterceptors(interceptors...))
|
return grpc.WithChainUnaryInterceptor(interceptors...)
|
||||||
}
|
|
||||||
|
|
||||||
func chainStreamClientInterceptors(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor {
|
|
||||||
switch len(interceptors) {
|
|
||||||
case 0:
|
|
||||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string,
|
|
||||||
streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
return streamer(ctx, desc, cc, method, opts...)
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return interceptors[0]
|
|
||||||
default:
|
|
||||||
last := len(interceptors) - 1
|
|
||||||
return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn,
|
|
||||||
method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
var chainStreamer grpc.Streamer
|
|
||||||
var current int
|
|
||||||
|
|
||||||
chainStreamer = func(curCtx context.Context, curDesc *grpc.StreamDesc, curCc *grpc.ClientConn,
|
|
||||||
curMethod string, curOpts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
if current == last {
|
|
||||||
return streamer(curCtx, curDesc, curCc, curMethod, curOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
current++
|
|
||||||
clientStream, err := interceptors[current](curCtx, curDesc, curCc, curMethod, chainStreamer, curOpts...)
|
|
||||||
current--
|
|
||||||
|
|
||||||
return clientStream, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return interceptors[0](ctx, desc, cc, method, chainStreamer, opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func chainUnaryClientInterceptors(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor {
|
|
||||||
switch len(interceptors) {
|
|
||||||
case 0:
|
|
||||||
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
|
|
||||||
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
|
||||||
return invoker(ctx, method, req, reply, cc, opts...)
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return interceptors[0]
|
|
||||||
default:
|
|
||||||
last := len(interceptors) - 1
|
|
||||||
return func(ctx context.Context, method string, req, reply interface{},
|
|
||||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
|
||||||
var chainInvoker grpc.UnaryInvoker
|
|
||||||
var current int
|
|
||||||
|
|
||||||
chainInvoker = func(curCtx context.Context, curMethod string, curReq, curReply interface{},
|
|
||||||
curCc *grpc.ClientConn, curOpts ...grpc.CallOption) error {
|
|
||||||
if current == last {
|
|
||||||
return invoker(curCtx, curMethod, curReq, curReply, curCc, curOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
current++
|
|
||||||
err := interceptors[current](curCtx, curMethod, curReq, curReply, curCc, chainInvoker, curOpts...)
|
|
||||||
current--
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return interceptors[0](ctx, method, req, reply, cc, chainInvoker, opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWithStreamClientInterceptors(t *testing.T) {
|
func TestWithStreamClientInterceptors(t *testing.T) {
|
||||||
@@ -16,108 +14,4 @@ func TestWithStreamClientInterceptors(t *testing.T) {
|
|||||||
func TestWithUnaryClientInterceptors(t *testing.T) {
|
func TestWithUnaryClientInterceptors(t *testing.T) {
|
||||||
opts := WithUnaryClientInterceptors()
|
opts := WithUnaryClientInterceptors()
|
||||||
assert.NotNil(t, opts)
|
assert.NotNil(t, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChainStreamClientInterceptors_zero(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainStreamClientInterceptors()
|
|
||||||
_, err := interceptors(context.Background(), nil, new(grpc.ClientConn), "/foo",
|
|
||||||
func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string,
|
|
||||||
opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainStreamClientInterceptors_one(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainStreamClientInterceptors(func(ctx context.Context, desc *grpc.StreamDesc,
|
|
||||||
cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (
|
|
||||||
grpc.ClientStream, error) {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return streamer(ctx, desc, cc, method, opts...)
|
|
||||||
})
|
|
||||||
_, err := interceptors(context.Background(), nil, new(grpc.ClientConn), "/foo",
|
|
||||||
func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string,
|
|
||||||
opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainStreamClientInterceptors_more(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainStreamClientInterceptors(func(ctx context.Context, desc *grpc.StreamDesc,
|
|
||||||
cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (
|
|
||||||
grpc.ClientStream, error) {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return streamer(ctx, desc, cc, method, opts...)
|
|
||||||
}, func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string,
|
|
||||||
streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return streamer(ctx, desc, cc, method, opts...)
|
|
||||||
})
|
|
||||||
_, err := interceptors(context.Background(), nil, new(grpc.ClientConn), "/foo",
|
|
||||||
func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string,
|
|
||||||
opts ...grpc.CallOption) (grpc.ClientStream, error) {
|
|
||||||
vals = append(vals, 3)
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2, 3}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithUnaryClientInterceptors_zero(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainUnaryClientInterceptors()
|
|
||||||
err := interceptors(context.Background(), "/foo", nil, nil, new(grpc.ClientConn),
|
|
||||||
func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
|
|
||||||
opts ...grpc.CallOption) error {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithUnaryClientInterceptors_one(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainUnaryClientInterceptors(func(ctx context.Context, method string, req,
|
|
||||||
reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return invoker(ctx, method, req, reply, cc, opts...)
|
|
||||||
})
|
|
||||||
err := interceptors(context.Background(), "/foo", nil, nil, new(grpc.ClientConn),
|
|
||||||
func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
|
|
||||||
opts ...grpc.CallOption) error {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithUnaryClientInterceptors_more(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainUnaryClientInterceptors(func(ctx context.Context, method string, req,
|
|
||||||
reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return invoker(ctx, method, req, reply, cc, opts...)
|
|
||||||
}, func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
|
|
||||||
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return invoker(ctx, method, req, reply, cc, opts...)
|
|
||||||
})
|
|
||||||
err := interceptors(context.Background(), "/foo", nil, nil, new(grpc.ClientConn),
|
|
||||||
func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
|
|
||||||
opts ...grpc.CallOption) error {
|
|
||||||
vals = append(vals, 3)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2, 3}, vals)
|
|
||||||
}
|
|
||||||
@@ -1,81 +1,11 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import "google.golang.org/grpc"
|
||||||
"context"
|
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
func WithStreamServerInterceptors(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption {
|
func WithStreamServerInterceptors(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption {
|
||||||
return grpc.StreamInterceptor(chainStreamServerInterceptors(interceptors...))
|
return grpc.ChainStreamInterceptor(interceptors...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithUnaryServerInterceptors(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption {
|
func WithUnaryServerInterceptors(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption {
|
||||||
return grpc.UnaryInterceptor(chainUnaryServerInterceptors(interceptors...))
|
return grpc.ChainUnaryInterceptor(interceptors...)
|
||||||
}
|
|
||||||
|
|
||||||
func chainStreamServerInterceptors(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor {
|
|
||||||
switch len(interceptors) {
|
|
||||||
case 0:
|
|
||||||
return func(srv interface{}, stream grpc.ServerStream, _ *grpc.StreamServerInfo,
|
|
||||||
handler grpc.StreamHandler) error {
|
|
||||||
return handler(srv, stream)
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return interceptors[0]
|
|
||||||
default:
|
|
||||||
last := len(interceptors) - 1
|
|
||||||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo,
|
|
||||||
handler grpc.StreamHandler) error {
|
|
||||||
var chainHandler grpc.StreamHandler
|
|
||||||
var current int
|
|
||||||
|
|
||||||
chainHandler = func(curSrv interface{}, curStream grpc.ServerStream) error {
|
|
||||||
if current == last {
|
|
||||||
return handler(curSrv, curStream)
|
|
||||||
}
|
|
||||||
|
|
||||||
current++
|
|
||||||
err := interceptors[current](curSrv, curStream, info, chainHandler)
|
|
||||||
current--
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return interceptors[0](srv, stream, info, chainHandler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func chainUnaryServerInterceptors(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
|
|
||||||
switch len(interceptors) {
|
|
||||||
case 0:
|
|
||||||
return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (
|
|
||||||
interface{}, error) {
|
|
||||||
return handler(ctx, req)
|
|
||||||
}
|
|
||||||
case 1:
|
|
||||||
return interceptors[0]
|
|
||||||
default:
|
|
||||||
last := len(interceptors) - 1
|
|
||||||
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (
|
|
||||||
interface{}, error) {
|
|
||||||
var chainHandler grpc.UnaryHandler
|
|
||||||
var current int
|
|
||||||
|
|
||||||
chainHandler = func(curCtx context.Context, curReq interface{}) (interface{}, error) {
|
|
||||||
if current == last {
|
|
||||||
return handler(curCtx, curReq)
|
|
||||||
}
|
|
||||||
|
|
||||||
current++
|
|
||||||
resp, err := interceptors[current](curCtx, curReq, info, chainHandler)
|
|
||||||
current--
|
|
||||||
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return interceptors[0](ctx, req, info, chainHandler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"google.golang.org/grpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWithStreamServerInterceptors(t *testing.T) {
|
func TestWithStreamServerInterceptors(t *testing.T) {
|
||||||
@@ -16,96 +14,4 @@ func TestWithStreamServerInterceptors(t *testing.T) {
|
|||||||
func TestWithUnaryServerInterceptors(t *testing.T) {
|
func TestWithUnaryServerInterceptors(t *testing.T) {
|
||||||
opts := WithUnaryServerInterceptors()
|
opts := WithUnaryServerInterceptors()
|
||||||
assert.NotNil(t, opts)
|
assert.NotNil(t, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChainStreamServerInterceptors_zero(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainStreamServerInterceptors()
|
|
||||||
err := interceptors(nil, nil, nil, func(srv interface{}, stream grpc.ServerStream) error {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainStreamServerInterceptors_one(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainStreamServerInterceptors(func(srv interface{}, ss grpc.ServerStream,
|
|
||||||
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return handler(srv, ss)
|
|
||||||
})
|
|
||||||
err := interceptors(nil, nil, nil, func(srv interface{}, stream grpc.ServerStream) error {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainStreamServerInterceptors_more(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainStreamServerInterceptors(func(srv interface{}, ss grpc.ServerStream,
|
|
||||||
info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return handler(srv, ss)
|
|
||||||
}, func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return handler(srv, ss)
|
|
||||||
})
|
|
||||||
err := interceptors(nil, nil, nil, func(srv interface{}, stream grpc.ServerStream) error {
|
|
||||||
vals = append(vals, 3)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2, 3}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainUnaryServerInterceptors_zero(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainUnaryServerInterceptors()
|
|
||||||
_, err := interceptors(context.Background(), nil, nil,
|
|
||||||
func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainUnaryServerInterceptors_one(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainUnaryServerInterceptors(func(ctx context.Context, req interface{},
|
|
||||||
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return handler(ctx, req)
|
|
||||||
})
|
|
||||||
_, err := interceptors(context.Background(), nil, nil,
|
|
||||||
func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2}, vals)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainUnaryServerInterceptors_more(t *testing.T) {
|
|
||||||
var vals []int
|
|
||||||
interceptors := chainUnaryServerInterceptors(func(ctx context.Context, req interface{},
|
|
||||||
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
||||||
vals = append(vals, 1)
|
|
||||||
return handler(ctx, req)
|
|
||||||
}, func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
|
|
||||||
handler grpc.UnaryHandler) (resp interface{}, err error) {
|
|
||||||
vals = append(vals, 2)
|
|
||||||
return handler(ctx, req)
|
|
||||||
})
|
|
||||||
_, err := interceptors(context.Background(), nil, nil,
|
|
||||||
func(ctx context.Context, req interface{}) (interface{}, error) {
|
|
||||||
vals = append(vals, 3)
|
|
||||||
return nil, nil
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.ElementsMatch(t, []int{1, 2, 3}, vals)
|
|
||||||
}
|
|
||||||
@@ -68,13 +68,12 @@ func (s *rpcServer) Start(register RegisterFn) error {
|
|||||||
register(server)
|
register(server)
|
||||||
// we need to make sure all others are wrapped up
|
// we need to make sure all others are wrapped up
|
||||||
// so we do graceful stop at shutdown phase instead of wrap up phase
|
// so we do graceful stop at shutdown phase instead of wrap up phase
|
||||||
shutdownCalled := proc.AddShutdownListener(func() {
|
waitForCalled := proc.AddWrapUpListener(func() {
|
||||||
server.GracefulStop()
|
server.GracefulStop()
|
||||||
})
|
})
|
||||||
err = server.Serve(lis)
|
defer waitForCalled()
|
||||||
shutdownCalled()
|
|
||||||
|
|
||||||
return err
|
return server.Serve(lis)
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithMetrics(metrics *stat.Metrics) ServerOption {
|
func WithMetrics(metrics *stat.Metrics) ServerOption {
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const envPodIp = "POD_IP"
|
const (
|
||||||
|
allEths = "0.0.0.0"
|
||||||
|
envPodIp = "POD_IP"
|
||||||
|
)
|
||||||
|
|
||||||
type RpcServer struct {
|
type RpcServer struct {
|
||||||
server internal.Server
|
server internal.Server
|
||||||
@@ -96,7 +99,7 @@ func figureOutListenOn(listenOn string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
host := fields[0]
|
host := fields[0]
|
||||||
if len(host) > 0 && host != "0.0.0.0" {
|
if len(host) > 0 && host != allEths {
|
||||||
return listenOn
|
return listenOn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user