From babf0dfc14872fafd146fd0e0583d5e804dbc0b5 Mon Sep 17 00:00:00 2001 From: Ming Deng Date: Sun, 3 Jan 2021 21:45:20 +0800 Subject: [PATCH 1/6] Make compatible --- adapter/app.go | 2 +- adapter/router.go | 2 +- server/web/admin.go | 14 +++++++------- server/web/flash_test.go | 2 +- server/web/namespace.go | 2 +- server/web/router.go | 12 ++++++------ server/web/router_test.go | 36 +++++++++++++++++------------------ server/web/server.go | 14 +++++++++++--- server/web/unregroute_test.go | 24 +++++++++++------------ 9 files changed, 58 insertions(+), 50 deletions(-) diff --git a/adapter/app.go b/adapter/app.go index 565a9795..8502256b 100644 --- a/adapter/app.go +++ b/adapter/app.go @@ -74,7 +74,7 @@ func oldMiddlewareToNew(mws []MiddleWare) []web.MiddleWare { // beego.Router("/api/update",&RestController{},"put:UpdateFood") // beego.Router("/api/delete",&RestController{},"delete:DeleteFood") func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { - return (*App)(web.Router(rootpath, c, web.SetRouterMethods(c, mappingMethods...))) + return (*App)(web.Router(rootpath, c, mappingMethods...)) } // UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful diff --git a/adapter/router.go b/adapter/router.go index 17e270ca..9a615efe 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -87,7 +87,7 @@ func NewControllerRegister() *ControllerRegister { // Add("/api",&RestController{},"get,post:ApiFunc" // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { - (*web.ControllerRegister)(p).Add(pattern, c, web.SetRouterMethods(c, mappingMethods...)) + (*web.ControllerRegister)(p).Add(pattern, c, web.WithRouterMethods(c, mappingMethods...)) } // Include only when the Runmode is dev will generate router file in the router/auto.go from the controller diff --git a/server/web/admin.go b/server/web/admin.go index d640c1be..89c9ddb9 100644 --- a/server/web/admin.go +++ b/server/web/admin.go @@ -112,13 +112,13 @@ func registerAdmin() error { HttpServer: NewHttpServerWithCfg(BConfig), } // keep in mind that all data should be html escaped to avoid XSS attack - beeAdminApp.Router("/", c, SetRouterMethods(c, "get:AdminIndex")) - beeAdminApp.Router("/qps", c, SetRouterMethods(c, "get:QpsIndex")) - beeAdminApp.Router("/prof", c, SetRouterMethods(c, "get:ProfIndex")) - beeAdminApp.Router("/healthcheck", c, SetRouterMethods(c, "get:Healthcheck")) - beeAdminApp.Router("/task", c, SetRouterMethods(c, "get:TaskStatus")) - beeAdminApp.Router("/listconf", c, SetRouterMethods(c, "get:ListConf")) - beeAdminApp.Router("/metrics", c, SetRouterMethods(c, "get:PrometheusMetrics")) + beeAdminApp.Router("/", c, "get:AdminIndex") + beeAdminApp.Router("/qps", c, "get:QpsIndex") + beeAdminApp.Router("/prof", c, "get:ProfIndex") + beeAdminApp.Router("/healthcheck", c, "get:Healthcheck") + beeAdminApp.Router("/task", c, "get:TaskStatus") + beeAdminApp.Router("/listconf", c, "get:ListConf") + beeAdminApp.Router("/metrics", c, "get:PrometheusMetrics") go beeAdminApp.Run() } diff --git a/server/web/flash_test.go b/server/web/flash_test.go index c1ca9554..3e20c8fb 100644 --- a/server/web/flash_test.go +++ b/server/web/flash_test.go @@ -40,7 +40,7 @@ func TestFlashHeader(t *testing.T) { // setup the handler handler := NewControllerRegister() - handler.Add("/", &TestFlashController{}, SetRouterMethods(&TestFlashController{}, "get:TestWriteFlash")) + handler.Add("/", &TestFlashController{}, WithRouterMethods(&TestFlashController{}, "get:TestWriteFlash")) handler.ServeHTTP(w, r) // get the Set-Cookie value diff --git a/server/web/namespace.go b/server/web/namespace.go index 3598a222..892f648a 100644 --- a/server/web/namespace.go +++ b/server/web/namespace.go @@ -99,7 +99,7 @@ func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace { // Router same as beego.Rourer // refer: https://godoc.org/github.com/beego/beego/v2#Router func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { - n.handlers.Add(rootpath, c, SetRouterMethods(c, mappingMethods...)) + n.handlers.Add(rootpath, c, WithRouterMethods(c, mappingMethods...)) return n } diff --git a/server/web/router.go b/server/web/router.go index ba85ad6e..613c4172 100644 --- a/server/web/router.go +++ b/server/web/router.go @@ -121,19 +121,19 @@ type ControllerInfo struct { sessionOn bool } -type ControllerOptions func(*ControllerInfo) +type ControllerOption func(*ControllerInfo) func (c *ControllerInfo) GetPattern() string { return c.pattern } -func SetRouterMethods(ctrlInterface ControllerInterface, mappingMethod ...string) ControllerOptions { +func WithRouterMethods(ctrlInterface ControllerInterface, mappingMethod ...string) ControllerOption { return func(c *ControllerInfo) { c.methods = parseMappingMethods(ctrlInterface, mappingMethod) } } -func SetRouterSessionOn(sessionOn bool) ControllerOptions { +func WithRouterSessionOn(sessionOn bool) ControllerOption { return func(c *ControllerInfo) { c.sessionOn = sessionOn } @@ -186,7 +186,7 @@ func NewControllerRegisterWithCfg(cfg *Config) *ControllerRegister { // Add("/api/delete",&RestController{},"delete:DeleteFood") // Add("/api",&RestController{},"get,post:ApiFunc" // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") -func (p *ControllerRegister) Add(pattern string, c ControllerInterface, opts ...ControllerOptions) { +func (p *ControllerRegister) Add(pattern string, c ControllerInterface, opts ...ControllerOption) { p.addWithMethodParams(pattern, c, nil, opts...) } @@ -239,7 +239,7 @@ func (p *ControllerRegister) addRouterForMethod(route *ControllerInfo) { } } -func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, opts ...ControllerOptions) { +func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, opts ...ControllerOption) { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() @@ -311,7 +311,7 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { p.InsertFilter(f.Pattern, f.Pos, f.Filter, WithReturnOnOutput(f.ReturnOnOutput), WithResetParams(f.ResetParams)) } - p.addWithMethodParams(a.Router, c, a.MethodParams, SetRouterMethods(c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)) + p.addWithMethodParams(a.Router, c, a.MethodParams, WithRouterMethods(c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method)) } } } diff --git a/server/web/router_test.go b/server/web/router_test.go index bd3953ba..474405e8 100644 --- a/server/web/router_test.go +++ b/server/web/router_test.go @@ -97,7 +97,7 @@ func (jc *JSONController) Get() { func TestPrefixUrlFor(t *testing.T){ handler := NewControllerRegister() - handler.Add("/my/prefix/list", &PrefixTestController{}, "get:PrefixList") + handler.Add("/my/prefix/list", &PrefixTestController{}, WithRouterMethods(&PrefixTestController{}, "get:PrefixList")) if a := handler.URLFor(`PrefixTestController.PrefixList`); a != `/my/prefix/list` { logs.Info(a) @@ -111,8 +111,8 @@ func TestPrefixUrlFor(t *testing.T){ func TestUrlFor(t *testing.T) { handler := NewControllerRegister() - handler.Add("/api/list", &TestController{}, SetRouterMethods(&TestController{}, "*:List")) - handler.Add("/person/:last/:first", &TestController{}, SetRouterMethods(&TestController{}, "*:Param")) + handler.Add("/api/list", &TestController{}, WithRouterMethods(&TestController{}, "*:List")) + handler.Add("/person/:last/:first", &TestController{}, WithRouterMethods(&TestController{}, "*:Param")) if a := handler.URLFor("TestController.List"); a != "/api/list" { logs.Info(a) t.Errorf("TestController.List must equal to /api/list") @@ -135,9 +135,9 @@ func TestUrlFor3(t *testing.T) { func TestUrlFor2(t *testing.T) { handler := NewControllerRegister() - handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, SetRouterMethods(&TestController{}, "*:List")) - handler.Add("/v1/:username/edit", &TestController{}, SetRouterMethods(&TestController{}, "get:GetURL")) - handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, SetRouterMethods(&TestController{}, "*:Param")) + handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, WithRouterMethods(&TestController{}, "*:List")) + handler.Add("/v1/:username/edit", &TestController{}, WithRouterMethods(&TestController{}, "get:GetURL")) + handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, WithRouterMethods(&TestController{}, "*:Param")) handler.Add("/:year:int/:month:int/:title/:entid", &TestController{}) if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" { logs.Info(handler.URLFor("TestController.GetURL")) @@ -167,7 +167,7 @@ func TestUserFunc(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/api/list", &TestController{}, SetRouterMethods(&TestController{}, "*:List")) + handler.Add("/api/list", &TestController{}, WithRouterMethods(&TestController{}, "*:List")) handler.ServeHTTP(w, r) if w.Body.String() != "i am list" { t.Errorf("user define func can't run") @@ -257,7 +257,7 @@ func TestRouteOk(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/person/:last/:first", &TestController{}, SetRouterMethods(&TestController{}, "get:GetParams")) + handler.Add("/person/:last/:first", &TestController{}, WithRouterMethods(&TestController{}, "get:GetParams")) handler.ServeHTTP(w, r) body := w.Body.String() if body != "anderson+thomas+kungfu" { @@ -271,7 +271,7 @@ func TestManyRoute(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, SetRouterMethods(&TestController{}, "get:GetManyRouter")) + handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, WithRouterMethods(&TestController{}, "get:GetManyRouter")) handler.ServeHTTP(w, r) body := w.Body.String() @@ -288,7 +288,7 @@ func TestEmptyResponse(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/beego-empty.html", &TestController{}, SetRouterMethods(&TestController{}, "get:GetEmptyBody")) + handler.Add("/beego-empty.html", &TestController{}, WithRouterMethods(&TestController{}, "get:GetEmptyBody")) handler.ServeHTTP(w, r) if body := w.Body.String(); body != "" { @@ -783,8 +783,8 @@ func TestRouterSessionSet(t *testing.T) { r, _ := http.NewRequest("GET", "/user", nil) w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), - SetRouterSessionOn(false)) + handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"), + WithRouterSessionOn(false)) handler.ServeHTTP(w, r) if w.Header().Get("Set-Cookie") != "" { t.Errorf("TestRotuerSessionSet failed") @@ -794,8 +794,8 @@ func TestRouterSessionSet(t *testing.T) { r, _ = http.NewRequest("GET", "/user", nil) w = httptest.NewRecorder() handler = NewControllerRegister() - handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), - SetRouterSessionOn(true)) + handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"), + WithRouterSessionOn(true)) handler.ServeHTTP(w, r) if w.Header().Get("Set-Cookie") != "" { t.Errorf("TestRotuerSessionSet failed") @@ -809,8 +809,8 @@ func TestRouterSessionSet(t *testing.T) { r, _ = http.NewRequest("GET", "/user", nil) w = httptest.NewRecorder() handler = NewControllerRegister() - handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), - SetRouterSessionOn(false)) + handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"), + WithRouterSessionOn(false)) handler.ServeHTTP(w, r) if w.Header().Get("Set-Cookie") != "" { t.Errorf("TestRotuerSessionSet failed") @@ -820,8 +820,8 @@ func TestRouterSessionSet(t *testing.T) { r, _ = http.NewRequest("GET", "/user", nil) w = httptest.NewRecorder() handler = NewControllerRegister() - handler.Add("/user", &TestController{}, SetRouterMethods(&TestController{}, "get:Get"), - SetRouterSessionOn(true)) + handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"), + WithRouterSessionOn(true)) handler.ServeHTTP(w, r) if w.Header().Get("Set-Cookie") == "" { t.Errorf("TestRotuerSessionSet failed") diff --git a/server/web/server.go b/server/web/server.go index 280828ff..6c4eaf9e 100644 --- a/server/web/server.go +++ b/server/web/server.go @@ -266,8 +266,12 @@ func (app *HttpServer) Run(addr string, mws ...MiddleWare) { } // Router see HttpServer.Router -func Router(rootpath string, c ControllerInterface, opts ...ControllerOptions) *HttpServer { - return BeeApp.Router(rootpath, c, opts...) +func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *HttpServer { + return RouterWithOpts(rootpath, c, WithRouterMethods(c, mappingMethods...)) +} + +func RouterWithOpts(rootpath string, c ControllerInterface, opts ...ControllerOption) *HttpServer { + return BeeApp.RouterWithOpts(rootpath, c, opts...) } // Router adds a patterned controller handler to BeeApp. @@ -286,7 +290,11 @@ func Router(rootpath string, c ControllerInterface, opts ...ControllerOptions) * // beego.Router("/api/create",&RestController{},"post:CreateFood") // beego.Router("/api/update",&RestController{},"put:UpdateFood") // beego.Router("/api/delete",&RestController{},"delete:DeleteFood") -func (app *HttpServer) Router(rootPath string, c ControllerInterface, opts ...ControllerOptions) *HttpServer { +func (app *HttpServer) Router(rootPath string, c ControllerInterface, mappingMethods ...string) *HttpServer { + return app.RouterWithOpts(rootPath, c, WithRouterMethods(c, mappingMethods...)) +} + +func (app *HttpServer) RouterWithOpts(rootPath string, c ControllerInterface, opts ...ControllerOption) *HttpServer { app.Handlers.Add(rootPath, c, opts...) return app } diff --git a/server/web/unregroute_test.go b/server/web/unregroute_test.go index 9745dbac..226cffb8 100644 --- a/server/web/unregroute_test.go +++ b/server/web/unregroute_test.go @@ -75,9 +75,9 @@ func TestUnregisterFixedRouteRoot(t *testing.T) { var method = "GET" handler := NewControllerRegister() - handler.Add("/", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) - handler.Add("/level1", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) - handler.Add("/level1/level2", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) + handler.Add("/", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) + handler.Add("/level1", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) + handler.Add("/level1/level2", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) // Test original root testHelperFnContentCheck(t, handler, "Test original root", @@ -96,7 +96,7 @@ func TestUnregisterFixedRouteRoot(t *testing.T) { // Replace the root path TestPreUnregController action with the action from // TestPostUnregController - handler.Add("/", &TestPostUnregController{}, SetRouterMethods(&TestPostUnregController{}, "get:GetFixedRoot")) + handler.Add("/", &TestPostUnregController{}, WithRouterMethods(&TestPostUnregController{}, "get:GetFixedRoot")) // Test replacement root (expect change) testHelperFnContentCheck(t, handler, "Test replacement root (expect change)", method, "/", contentRootReplacement) @@ -117,9 +117,9 @@ func TestUnregisterFixedRouteLevel1(t *testing.T) { var method = "GET" handler := NewControllerRegister() - handler.Add("/", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) - handler.Add("/level1", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) - handler.Add("/level1/level2", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) + handler.Add("/", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) + handler.Add("/level1", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) + handler.Add("/level1/level2", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) // Test original root testHelperFnContentCheck(t, handler, @@ -146,7 +146,7 @@ func TestUnregisterFixedRouteLevel1(t *testing.T) { // Replace the "level1" path TestPreUnregController action with the action from // TestPostUnregController - handler.Add("/level1", &TestPostUnregController{}, SetRouterMethods(&TestPostUnregController{}, "get:GetFixedLevel1")) + handler.Add("/level1", &TestPostUnregController{}, WithRouterMethods(&TestPostUnregController{}, "get:GetFixedLevel1")) // Test replacement root (expect no change from the original) testHelperFnContentCheck(t, handler, "Test replacement root (expect no change from the original)", method, "/", contentRootOriginal) @@ -167,9 +167,9 @@ func TestUnregisterFixedRouteLevel2(t *testing.T) { var method = "GET" handler := NewControllerRegister() - handler.Add("/", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) - handler.Add("/level1", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) - handler.Add("/level1/level2", &TestPreUnregController{}, SetRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) + handler.Add("/", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedRoot")) + handler.Add("/level1", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel1")) + handler.Add("/level1/level2", &TestPreUnregController{}, WithRouterMethods(&TestPreUnregController{}, "get:GetFixedLevel2")) // Test original root testHelperFnContentCheck(t, handler, @@ -196,7 +196,7 @@ func TestUnregisterFixedRouteLevel2(t *testing.T) { // Replace the "/level1/level2" path TestPreUnregController action with the action from // TestPostUnregController - handler.Add("/level1/level2", &TestPostUnregController{}, SetRouterMethods(&TestPostUnregController{}, "get:GetFixedLevel2")) + handler.Add("/level1/level2", &TestPostUnregController{}, WithRouterMethods(&TestPostUnregController{}, "get:GetFixedLevel2")) // Test replacement root (expect no change from the original) testHelperFnContentCheck(t, handler, "Test replacement root (expect no change from the original)", method, "/", contentRootOriginal) From c51a81222b1ccf8c75ddb10ec12161fefa1e148a Mon Sep 17 00:00:00 2001 From: Ming Deng Date: Sun, 3 Jan 2021 19:28:06 +0800 Subject: [PATCH 2/6] Add golint action --- .github/workflows/golangci-lint.yml | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/golangci-lint.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 00000000..f6224ff1 --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,32 @@ +name: golangci-lint +on: + push: + tags: + - v* + branches: + - master + - main + pull_request: +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 + with: + # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + version: v1.29 + + # Optional: working directory, useful for monorepos +# working-directory: ./ + + # Optional: golangci-lint command line arguments. + args: --timeout=5m + + # Optional: show only new issues if it's a pull request. The default value is `false`. + only-new-issues: true + + # Optional: if set to true then the action will use pre-installed Go + # skip-go-installation: true \ No newline at end of file From 30dbf8fc3a0ad0c2bfcbe7e2de63746201b654bd Mon Sep 17 00:00:00 2001 From: AllenX2018 Date: Mon, 4 Jan 2021 16:29:03 +0800 Subject: [PATCH 3/6] 1.support dynamic registration model 2.support aggregete func --- client/orm/db.go | 32 +++++++++++------- client/orm/models.go | 17 +++------- client/orm/models_test.go | 16 +++++++++ client/orm/orm_queryset.go | 7 ++++ client/orm/orm_test.go | 69 ++++++++++++++++++++++++++++++++++++++ client/orm/types.go | 9 +++++ 6 files changed, 124 insertions(+), 26 deletions(-) diff --git a/client/orm/db.go b/client/orm/db.go index 4080f292..c994469f 100644 --- a/client/orm/db.go +++ b/client/orm/db.go @@ -948,9 +948,10 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi val := reflect.ValueOf(container) ind := reflect.Indirect(val) - errTyp := true + unregister := true one := true isPtr := true + name := "" if val.Kind() == reflect.Ptr { fn := "" @@ -963,19 +964,17 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi case reflect.Struct: isPtr = false fn = getFullName(typ) + name = getTableName(reflect.New(typ)) } } else { fn = getFullName(ind.Type()) + name = getTableName(ind) } - errTyp = fn != mi.fullName + unregister = fn != mi.fullName } - if errTyp { - if one { - panic(fmt.Errorf("wrong object type `%s` for rows scan, need *%s", val.Type(), mi.fullName)) - } else { - panic(fmt.Errorf("wrong object type `%s` for rows scan, need *[]*%s or *[]%s", val.Type(), mi.fullName, mi.fullName)) - } + if unregister { + RegisterModel(container) } rlimit := qs.limit @@ -1040,6 +1039,9 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi if qs.distinct { sqlSelect += " DISTINCT" } + if qs.aggregate != "" { + sels = qs.aggregate + } query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, specifyIndexes, join, where, groupBy, orderBy, limit) @@ -1064,16 +1066,20 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi } } + defer rs.Close() + + slice := ind + if unregister { + mi, _ = modelCache.get(name) + tCols = mi.fields.dbcols + colsNum = len(tCols) + } + refs := make([]interface{}, colsNum) for i := range refs { var ref interface{} refs[i] = &ref } - - defer rs.Close() - - slice := ind - var cnt int64 for rs.Next() { if one && cnt == 0 || !one { diff --git a/client/orm/models.go b/client/orm/models.go index 64dfab09..0f07e24d 100644 --- a/client/orm/models.go +++ b/client/orm/models.go @@ -332,10 +332,6 @@ end: // register register models to model cache func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, models ...interface{}) (err error) { - if mc.done { - err = fmt.Errorf("register must be run before BootStrap") - return - } for _, model := range models { val := reflect.ValueOf(model) @@ -352,7 +348,9 @@ func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, m err = fmt.Errorf(" only allow ptr model struct, it looks you use two reference to the struct `%s`", typ) return } - + if val.Elem().Kind() == reflect.Slice { + val = reflect.New(val.Elem().Type().Elem()) + } table := getTableName(val) if prefixOrSuffixStr != "" { @@ -371,8 +369,7 @@ func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, m } if _, ok := mc.get(table); ok { - err = fmt.Errorf(" table name `%s` repeat register, must be unique\n", table) - return + return nil } mi := newModelInfo(val) @@ -389,12 +386,6 @@ func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, m } } } - - if mi.fields.pk == nil { - err = fmt.Errorf(" `%s` needs a primary key field, default is to use 'id' if not set\n", name) - return - } - } mi.table = table diff --git a/client/orm/models_test.go b/client/orm/models_test.go index e3f74c0b..5add6e45 100644 --- a/client/orm/models_test.go +++ b/client/orm/models_test.go @@ -255,6 +255,22 @@ func NewTM() *TM { return obj } +type DeptInfo struct { + ID int `orm:"column(id)"` + Created time.Time `orm:"auto_now_add"` + DeptName string + EmployeeName string + Salary int +} + +type UnregisterModel struct { + ID int `orm:"column(id)"` + Created time.Time `orm:"auto_now_add"` + DeptName string + EmployeeName string + Salary int +} + type User struct { ID int `orm:"column(id)"` UserName string `orm:"size(30);unique"` diff --git a/client/orm/orm_queryset.go b/client/orm/orm_queryset.go index 177cfc3a..293e4d29 100644 --- a/client/orm/orm_queryset.go +++ b/client/orm/orm_queryset.go @@ -79,6 +79,7 @@ type querySet struct { orm *ormBase ctx context.Context forContext bool + aggregate string } var _ QuerySeter = new(querySet) @@ -323,3 +324,9 @@ func newQuerySet(orm *ormBase, mi *modelInfo) QuerySeter { o.orm = orm return o } + +// aggregate func +func (o querySet) Aggregate(s string) QuerySeter { + o.aggregate = s + return &o +} \ No newline at end of file diff --git a/client/orm/orm_test.go b/client/orm/orm_test.go index f1074973..91f2f929 100644 --- a/client/orm/orm_test.go +++ b/client/orm/orm_test.go @@ -205,6 +205,7 @@ func TestSyncDb(t *testing.T) { RegisterModel(new(Index)) RegisterModel(new(StrPk)) RegisterModel(new(TM)) + RegisterModel(new(DeptInfo)) err := RunSyncdb("default", true, Debug) throwFail(t, err) @@ -232,6 +233,7 @@ func TestRegisterModels(t *testing.T) { RegisterModel(new(Index)) RegisterModel(new(StrPk)) RegisterModel(new(TM)) + RegisterModel(new(DeptInfo)) BootStrap() @@ -333,6 +335,73 @@ func TestTM(t *testing.T) { throwFail(t, AssertIs(recTM.TMPrecision2.String(), "2020-08-07 02:07:04.1235 +0000 UTC")) } +func TestUnregisterModel(t *testing.T) { + data := []*DeptInfo{ + { + DeptName: "A", + EmployeeName: "A1", + Salary: 1000, + }, + { + DeptName: "A", + EmployeeName: "A2", + Salary: 2000, + }, + { + DeptName: "B", + EmployeeName: "B1", + Salary: 2000, + }, + { + DeptName: "B", + EmployeeName: "B2", + Salary: 4000, + }, + { + DeptName: "B", + EmployeeName: "B3", + Salary: 3000, + }, + } + qs := dORM.QueryTable("dept_info") + i, _ := qs.PrepareInsert() + for _, d := range data { + _, err := i.Insert(d) + if err != nil { + throwFail(t, err) + } + } + + f := func() { + var res []UnregisterModel + n, err := dORM.QueryTable("dept_info").All(&res) + throwFail(t, err) + throwFail(t, AssertIs(n, 5)) + throwFail(t, AssertIs(res[0].EmployeeName, "A1")) + + type Sum struct { + DeptName string + Total int + } + var sun []Sum + qs.Aggregate("dept_name,sum(salary) as total").GroupBy("dept_name").OrderBy("dept_name").All(&sun) + throwFail(t, AssertIs(sun[0].DeptName, "A")) + throwFail(t, AssertIs(sun[0].Total, 3000)) + + type Max struct { + DeptName string + Max float64 + } + var max []Max + qs.Aggregate("dept_name,max(salary) as max").GroupBy("dept_name").OrderBy("dept_name").All(&max) + throwFail(t, AssertIs(max[1].DeptName, "B")) + throwFail(t, AssertIs(max[1].Max, 4000)) + } + for i := 0; i < 5; i++ { + f() + } +} + func TestNullDataTypes(t *testing.T) { d := DataNull{} diff --git a/client/orm/types.go b/client/orm/types.go index cb735ac8..60889ede 100644 --- a/client/orm/types.go +++ b/client/orm/types.go @@ -405,6 +405,15 @@ type QuerySeter interface { // Found int // } RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) + // aggregate func. + // for example: + // type result struct { + // DeptName string + // Total int + // } + // var res []result + // o.QueryTable("dept_info").Aggregate("dept_name,sum(salary) as total").GroupBy("dept_name").All(&res) + Aggregate(s string) QuerySeter } // QueryM2Mer model to model query struct From 5575cc1a5ce44f29b05b72db531d24abdc2907e1 Mon Sep 17 00:00:00 2001 From: Ming Deng Date: Mon, 4 Jan 2021 20:43:34 +0800 Subject: [PATCH 4/6] Add more golint action parameter --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index f6224ff1..85b159db 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -23,7 +23,7 @@ jobs: # working-directory: ./ # Optional: golangci-lint command line arguments. - args: --timeout=5m + args: --timeout=5m --print-issued-lines=true --print-linter-name=true --uniq-by-line=true # Optional: show only new issues if it's a pull request. The default value is `false`. only-new-issues: true From d29b0a589ccb90542fe5901d30e72e04f36b3697 Mon Sep 17 00:00:00 2001 From: AllenX2018 Date: Thu, 7 Jan 2021 11:40:32 +0800 Subject: [PATCH 5/6] Fix orm many2many generated table error --- client/orm/models_utils.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/orm/models_utils.go b/client/orm/models_utils.go index 950ca243..b2e5760e 100644 --- a/client/orm/models_utils.go +++ b/client/orm/models_utils.go @@ -109,6 +109,9 @@ func getTableUnique(val reflect.Value) [][]string { // get whether the table needs to be created for the database alias func isApplicableTableForDB(val reflect.Value, db string) bool { + if !val.IsValid() { + return true + } fun := val.MethodByName("IsApplicableTableForDB") if fun.IsValid() { vals := fun.Call([]reflect.Value{reflect.ValueOf(db)}) From 9105402f8ce1ad21523bc913d9dbd48bdfd7230c Mon Sep 17 00:00:00 2001 From: Ming Deng Date: Fri, 8 Jan 2021 20:52:36 +0800 Subject: [PATCH 6/6] reverse filter chain sort and add test to ensure the FIFO --- client/httplib/httplib_test.go | 22 +++++++++++++++++++ server/web/filter_chain_test.go | 38 ++++++++++++++++++++++++++++++++- server/web/router.go | 32 ++++++++++++++++++++++----- server/web/server.go | 2 ++ 4 files changed, 88 insertions(+), 6 deletions(-) diff --git a/client/httplib/httplib_test.go b/client/httplib/httplib_test.go index 1763b1b5..b8cd1112 100644 --- a/client/httplib/httplib_test.go +++ b/client/httplib/httplib_test.go @@ -301,6 +301,28 @@ func TestAddFilter(t *testing.T) { assert.Equal(t, 1, len(req.setting.FilterChains)-len(r.setting.FilterChains)) } +func TestFilterChainOrder(t *testing.T) { + req := Get("http://beego.me") + req.AddFilters(func(next Filter) Filter { + return func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) { + return NewHttpResponseWithJsonBody("first"), nil + } + }) + + + req.AddFilters(func(next Filter) Filter { + return func(ctx context.Context, req *BeegoHTTPRequest) (*http.Response, error) { + return NewHttpResponseWithJsonBody("second"), nil + } + }) + + resp, err := req.DoRequestWithCtx(context.Background()) + assert.Nil(t, err) + data := make([]byte, 5) + _, _ = resp.Body.Read(data) + assert.Equal(t, "first", string(data)) +} + func TestHead(t *testing.T) { req := Head("http://beego.me") assert.NotNil(t, req) diff --git a/server/web/filter_chain_test.go b/server/web/filter_chain_test.go index 2a428b78..5dd38fc5 100644 --- a/server/web/filter_chain_test.go +++ b/server/web/filter_chain_test.go @@ -15,9 +15,12 @@ package web import ( + "fmt" "net/http" "net/http/httptest" + "strconv" "testing" + "time" "github.com/stretchr/testify/assert" @@ -36,13 +39,46 @@ func TestControllerRegister_InsertFilterChain(t *testing.T) { ns := NewNamespace("/chain") ns.Get("/*", func(ctx *context.Context) { - ctx.Output.Body([]byte("hello")) + _ = ctx.Output.Body([]byte("hello")) }) r, _ := http.NewRequest("GET", "/chain/user", nil) w := httptest.NewRecorder() + BeeApp.Handlers.Init() BeeApp.Handlers.ServeHTTP(w, r) assert.Equal(t, "filter-chain", w.Header().Get("filter")) } + +func TestControllerRegister_InsertFilterChain_Order(t *testing.T) { + InsertFilterChain("/abc", func(next FilterFunc) FilterFunc { + return func(ctx *context.Context) { + ctx.Output.Header("first", fmt.Sprintf("%d", time.Now().UnixNano())) + time.Sleep(time.Millisecond * 10) + next(ctx) + } + }) + + + InsertFilterChain("/abc", func(next FilterFunc) FilterFunc { + return func(ctx *context.Context) { + ctx.Output.Header("second", fmt.Sprintf("%d", time.Now().UnixNano())) + time.Sleep(time.Millisecond * 10) + next(ctx) + } + }) + + r, _ := http.NewRequest("GET", "/abc", nil) + w := httptest.NewRecorder() + + BeeApp.Handlers.Init() + BeeApp.Handlers.ServeHTTP(w, r) + first := w.Header().Get("first") + second := w.Header().Get("second") + + ft, _ := strconv.ParseInt(first, 10, 64) + st, _ := strconv.ParseInt(second, 10, 64) + + assert.True(t, st > ft) +} diff --git a/server/web/router.go b/server/web/router.go index 613c4172..9ba078bf 100644 --- a/server/web/router.go +++ b/server/web/router.go @@ -139,6 +139,12 @@ func WithRouterSessionOn(sessionOn bool) ControllerOption { } } +type filterChainConfig struct { + pattern string + chain FilterChain + opts []FilterOpt +} + // ControllerRegister containers registered router rules, controller handlers and filters. type ControllerRegister struct { routers map[string]*Tree @@ -151,6 +157,9 @@ type ControllerRegister struct { // the filter created by FilterChain chainRoot *FilterRouter + // keep registered chain and build it when serve http + filterChains []filterChainConfig + cfg *Config } @@ -171,11 +180,23 @@ func NewControllerRegisterWithCfg(cfg *Config) *ControllerRegister { }, }, cfg: cfg, + filterChains: make([]filterChainConfig, 0, 4), } res.chainRoot = newFilterRouter("/*", res.serveHttp, WithCaseSensitive(false)) return res } +// Init will be executed when HttpServer start running +func (p *ControllerRegister) Init() { + for i := len(p.filterChains) - 1; i >= 0 ; i -- { + fc := p.filterChains[i] + root := p.chainRoot + filterFunc := fc.chain(root.filterFunc) + p.chainRoot = newFilterRouter(fc.pattern, filterFunc, fc.opts...) + p.chainRoot.next = root + } +} + // Add controller handler and pattern rules to ControllerRegister. // usage: // default methods is the same name as method @@ -513,12 +534,13 @@ func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter Filter // } // } func (p *ControllerRegister) InsertFilterChain(pattern string, chain FilterChain, opts ...FilterOpt) { - root := p.chainRoot - filterFunc := chain(root.filterFunc) - opts = append(opts, WithCaseSensitive(p.cfg.RouterCaseSensitive)) - p.chainRoot = newFilterRouter(pattern, filterFunc, opts...) - p.chainRoot.next = root + opts = append(opts, WithCaseSensitive(p.cfg.RouterCaseSensitive)) + p.filterChains = append(p.filterChains, filterChainConfig{ + pattern: pattern, + chain: chain, + opts: opts, + }) } // add Filter into diff --git a/server/web/server.go b/server/web/server.go index 6c4eaf9e..548b906b 100644 --- a/server/web/server.go +++ b/server/web/server.go @@ -84,7 +84,9 @@ func (app *HttpServer) Run(addr string, mws ...MiddleWare) { initBeforeHTTPRun() + // init... app.initAddr(addr) + app.Handlers.Init() addr = app.Cfg.Listen.HTTPAddr