From b788d74fd1bd1f6300b7923d3e4455bc165339cf Mon Sep 17 00:00:00 2001 From: codejuan Date: Tue, 6 Dec 2016 12:44:00 +0800 Subject: [PATCH 001/163] set perm of rotated log to 440 --- logs/file.go | 1 + 1 file changed, 1 insertion(+) diff --git a/logs/file.go b/logs/file.go index 42146dae..bd3c22a9 100644 --- a/logs/file.go +++ b/logs/file.go @@ -270,6 +270,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { // Rename the file to its new found name // even if occurs error,we MUST guarantee to restart new logger err = os.Rename(w.Filename, fName) + err = os.Chmod(fName, os.FileMode(440)) // re-start logger RESTART_LOGGER: From c0c113036b5443092e21dbf92d9bbab418cdb505 Mon Sep 17 00:00:00 2001 From: mengskysama Date: Tue, 6 Dec 2016 14:57:15 +0800 Subject: [PATCH 002/163] statistics lock --- toolbox/statistics.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolbox/statistics.go b/toolbox/statistics.go index 69b88772..c6a9489f 100644 --- a/toolbox/statistics.go +++ b/toolbox/statistics.go @@ -117,7 +117,9 @@ func (m *URLMap) GetMap() map[string]interface{} { // GetMapData return all mapdata func (m *URLMap) GetMapData() []map[string]interface{} { - + m.lock.Lock() + defer m.lock.Unlock() + var resultLists []map[string]interface{} for k, v := range m.urlmap { From fc4801494dde1bfc2c6f00cbbbefbb8e9711335e Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Tue, 6 Dec 2016 13:36:59 +0100 Subject: [PATCH 003/163] Added hookable signals --- grace/grace.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/grace/grace.go b/grace/grace.go index af4e9068..6ebf8455 100644 --- a/grace/grace.go +++ b/grace/grace.go @@ -85,23 +85,31 @@ var ( isChild bool socketOrder string - once sync.Once + + hookableSignals []os.Signal ) -func onceInit() { - regLock = &sync.Mutex{} +func init() { flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)") flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started") + + regLock = &sync.Mutex{} runningServers = make(map[string]*Server) runningServersOrder = []string{} socketPtrOffsetMap = make(map[string]uint) + + hookableSignals = []os.Signal{ + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + } } // NewServer returns a new graceServer. func NewServer(addr string, handler http.Handler) (srv *Server) { - once.Do(onceInit) regLock.Lock() defer regLock.Unlock() + if !flag.Parsed() { flag.Parse() } From eb50221a154fe3732c23364a944063a07215cdba Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Tue, 6 Dec 2016 13:38:18 +0100 Subject: [PATCH 004/163] Added method to register Pre/Post signal handlers --- grace/server.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/grace/server.go b/grace/server.go index 101bda56..cc985552 100644 --- a/grace/server.go +++ b/grace/server.go @@ -162,9 +162,7 @@ func (srv *Server) handleSignals() { signal.Notify( srv.sigChan, - syscall.SIGHUP, - syscall.SIGINT, - syscall.SIGTERM, + hookableSignals..., ) pid := syscall.Getpid() @@ -290,3 +288,19 @@ func (srv *Server) fork() (err error) { return } + +// RegisterSignalHook registers a function to be run PreSignal or PostSignal for a given signal. +func (srv *Server) RegisterSignalHook(ppFlag int, sig os.Signal, f func()) (err error) { + if ppFlag != PreSignal && ppFlag != PostSignal { + err = fmt.Errorf("Invalid ppFlag argument. Must be either grace.PreSignal or grace.PostSignal.") + return + } + for _, s := range hookableSignals { + if s == sig { + srv.SignalHooks[ppFlag][sig] = append(srv.SignalHooks[ppFlag][sig], f) + return + } + } + err = fmt.Errorf("Signal '%v' is not supported.", sig) + return +} From e90f4bee1a33646edee0dcb88546f2173543a5f3 Mon Sep 17 00:00:00 2001 From: mlgd Date: Fri, 9 Dec 2016 09:37:10 +0100 Subject: [PATCH 005/163] Remove a regression on AppPath The application path is incorrect on Windows with the command line "go run". AppPath is assigned to the temp directory instead the folder project --- config.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/config.go b/config.go index 36bf445c..52e647db 100644 --- a/config.go +++ b/config.go @@ -144,9 +144,6 @@ func init() { if err = parseConfig(appConfigPath); err != nil { panic(err) } - if err = os.Chdir(AppPath); err != nil { - panic(err) - } } func recoverPanic(ctx *context.Context) { From 8fac2d8d58474812affd5ab10c088a4d0b90f4c7 Mon Sep 17 00:00:00 2001 From: Amine KABAB Date: Wed, 14 Dec 2016 17:19:31 +0000 Subject: [PATCH 006/163] Don't rewrite content-type --- controller.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/controller.go b/controller.go index e484ce49..a6fbdd23 100644 --- a/controller.go +++ b/controller.go @@ -185,7 +185,11 @@ func (c *Controller) Render() error { if err != nil { return err } - c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") + + if c.Ctx.ResponseWriter.Header().Get("Content-Type") == "" { + c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") + } + return c.Ctx.Output.Body(rb) } From c9c284be27b372be59f203367ea01f1d93d97ca2 Mon Sep 17 00:00:00 2001 From: legendtkl Date: Sun, 25 Dec 2016 21:09:06 +0800 Subject: [PATCH 007/163] Modify func camelString to be more robust 1. In previous edition, for case "pic_url_1", the func will return "PicUrl_1", but "PicUrl1" seems to be more reasonable. 2. More test cases please refer to utils_test.go --- orm/utils.go | 21 ++++++++------------- orm/utils_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 orm/utils_test.go diff --git a/orm/utils.go b/orm/utils.go index bf43ceb0..6aac8e5d 100644 --- a/orm/utils.go +++ b/orm/utils.go @@ -219,22 +219,17 @@ func snakeString(s string) string { // camel string, xx_yy to XxYy func camelString(s string) string { data := make([]byte, 0, len(s)) - j := false - k := false - num := len(s) - 1 + flag, num := true, len(s)-1 for i := 0; i <= num; i++ { d := s[i] - if k == false && d >= 'A' && d <= 'Z' { - k = true - } - if d >= 'a' && d <= 'z' && (j || k == false) { - d = d - 32 - j = false - k = true - } - if k && d == '_' && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' { - j = true + if d == '_' { + flag = true continue + } else if flag == true { + if d >= 'a' && d <= 'z' { + d = d - 32 + } + flag = false } data = append(data, d) } diff --git a/orm/utils_test.go b/orm/utils_test.go new file mode 100644 index 00000000..8c7c5008 --- /dev/null +++ b/orm/utils_test.go @@ -0,0 +1,36 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package orm + +import ( + "testing" +) + +func TestCamelString(t *testing.T) { + snake := []string{"pic_url", "hello_world_", "hello__World", "_HelLO_Word", "pic_url_1", "pic_url__1"} + camel := []string{"PicUrl", "HelloWorld", "HelloWorld", "HelLOWord", "PicUrl1", "PicUrl1"} + + answer := make(map[string]string) + for i, v := range snake { + answer[v] = camel[i] + } + + for _, v := range snake { + res := camelString(v) + if res != answer[v] { + t.Error("Unit Test Fail:", v, res, answer[v]) + } + } +} From d736d0ca87a0e86d05324f4d3f642cec275f140e Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Wed, 28 Dec 2016 12:36:39 +0100 Subject: [PATCH 008/163] Adds Count method to BeeMap struct This adds a Count() method to BeeMap struct that returns the number of items within the safe map. --- utils/safemap.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/utils/safemap.go b/utils/safemap.go index 2e438f2c..a834a55a 100644 --- a/utils/safemap.go +++ b/utils/safemap.go @@ -84,3 +84,10 @@ func (m *BeeMap) Items() map[interface{}]interface{} { } return r } + +// Count returns the number of items within the map. +func (m *BeeMap) Count() int { + m.lock.RLock() + defer m.lock.RUnlock() + return len(m.bm) +} From 75ec8d33a289a99f248e2104742dc97f7fc78cde Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Wed, 28 Dec 2016 12:39:21 +0100 Subject: [PATCH 009/163] Rewrite safemap_test suite --- utils/safemap_test.go | 55 +++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/utils/safemap_test.go b/utils/safemap_test.go index fb271d18..1bfe8699 100644 --- a/utils/safemap_test.go +++ b/utils/safemap_test.go @@ -14,25 +14,44 @@ package utils -import ( - "testing" -) +import "testing" -func Test_beemap(t *testing.T) { - bm := NewBeeMap() - if !bm.Set("astaxie", 1) { - t.Error("set Error") - } - if !bm.Check("astaxie") { - t.Error("check err") - } +var safeMap *BeeMap - if v := bm.Get("astaxie"); v.(int) != 1 { - t.Error("get err") - } - - bm.Delete("astaxie") - if bm.Check("astaxie") { - t.Error("delete err") +func TestNewBeeMap(t *testing.T) { + safeMap = NewBeeMap() + if safeMap == nil { + t.Fatal("expected to return non-nil BeeMap", "got", safeMap) + } +} + +func TestSet(t *testing.T) { + if ok := safeMap.Set("astaxie", 1); !ok { + t.Error("expected", true, "got", false) + } +} + +func TestCheck(t *testing.T) { + if exists := safeMap.Check("astaxie"); !exists { + t.Error("expected", true, "got", false) + } +} + +func TestGet(t *testing.T) { + if val := safeMap.Get("astaxie"); val.(int) != 1 { + t.Error("expected value", 1, "got", val) + } +} + +func TestDelete(t *testing.T) { + safeMap.Delete("astaxie") + if exists := safeMap.Check("astaxie"); exists { + t.Error("expected element to be deleted") + } +} + +func TestCount(t *testing.T) { + if count := safeMap.Count(); count != 0 { + t.Error("expected count to be", 0, "got", count) } } From fe21305bb3eae3ec585a7ae8d7955112d3fe94e8 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 29 Dec 2016 11:05:35 +0100 Subject: [PATCH 010/163] Removes redundant check if key exists in BeeMap --- utils/safemap.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/utils/safemap.go b/utils/safemap.go index a834a55a..1793030a 100644 --- a/utils/safemap.go +++ b/utils/safemap.go @@ -61,10 +61,8 @@ func (m *BeeMap) Set(k interface{}, v interface{}) bool { func (m *BeeMap) Check(k interface{}) bool { m.lock.RLock() defer m.lock.RUnlock() - if _, ok := m.bm[k]; !ok { - return false - } - return true + _, ok := m.bm[k] + return ok } // Delete the given key and value. From caca5e37ba81e71e33131b3698c15ec7d65ba343 Mon Sep 17 00:00:00 2001 From: "Kariuki, Stanley (Contractor)" Date: Thu, 29 Dec 2016 12:24:27 -0500 Subject: [PATCH 011/163] fixed typo in models_boot --- orm/models_boot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/models_boot.go b/orm/models_boot.go index 319785ce..4ba5affd 100644 --- a/orm/models_boot.go +++ b/orm/models_boot.go @@ -117,7 +117,7 @@ func bootStrap() { name := getFullName(elm) mii, ok := modelCache.getByFullName(name) if !ok || mii.pkg != elm.PkgPath() { - err = fmt.Errorf("can not found rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String()) + err = fmt.Errorf("can not find rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String()) goto end } fi.relModelInfo = mii From d77160dafeac9b8a44d3f3450afa14cb801bb3a8 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Thu, 29 Dec 2016 22:30:56 +0100 Subject: [PATCH 012/163] ignore .vscode folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9806457b..e1b65291 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea +.vscode .DS_Store *.swp *.swo From 96387e9a9b9e30824637c7a153311c51cc9cbc07 Mon Sep 17 00:00:00 2001 From: kbynd Date: Sat, 31 Dec 2016 16:04:34 +0530 Subject: [PATCH 013/163] EnableGZip=true,then content-length header missing This results in responses with Content-Type as gzip as opposed to original content type. This affects ServeJSON() function. --- context/output.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/context/output.go b/context/output.go index c09b9d19..7b238ab8 100644 --- a/context/output.go +++ b/context/output.go @@ -67,9 +67,9 @@ func (output *BeegoOutput) Body(content []byte) error { } if b, n, _ := WriteBody(encoding, buf, content); b { output.Header("Content-Encoding", n) - } else { - output.Header("Content-Length", strconv.Itoa(len(content))) - } + } + output.Header("Content-Length", strconv.Itoa(len(content))) + // Write status code if it has been set manually // Set it to 0 afterwards to prevent "multiple response.WriteHeader calls" if output.Status != 0 { From f0d1d7149bfc2e4618cf5a388f795c901d9c74d0 Mon Sep 17 00:00:00 2001 From: kbynd Date: Sat, 31 Dec 2016 16:14:38 +0530 Subject: [PATCH 014/163] Update output.go --- context/output.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/context/output.go b/context/output.go index 7b238ab8..76b523d3 100644 --- a/context/output.go +++ b/context/output.go @@ -67,8 +67,11 @@ func (output *BeegoOutput) Body(content []byte) error { } if b, n, _ := WriteBody(encoding, buf, content); b { output.Header("Content-Encoding", n) - } - output.Header("Content-Length", strconv.Itoa(len(content))) + output.Header("Content-Length", strconv.Itoa(buf.Len())) + } else { + output.Header("Content-Length", strconv.Itoa(len(content))) + } + // Write status code if it has been set manually // Set it to 0 afterwards to prevent "multiple response.WriteHeader calls" From 2f6da122fdcef15780daff6c491997fde5211c1e Mon Sep 17 00:00:00 2001 From: kbynd Date: Mon, 2 Jan 2017 09:17:17 +0530 Subject: [PATCH 015/163] Update output.go --- context/output.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/context/output.go b/context/output.go index 76b523d3..4b513dd8 100644 --- a/context/output.go +++ b/context/output.go @@ -71,8 +71,6 @@ func (output *BeegoOutput) Body(content []byte) error { } else { output.Header("Content-Length", strconv.Itoa(len(content))) } - - // Write status code if it has been set manually // Set it to 0 afterwards to prevent "multiple response.WriteHeader calls" if output.Status != 0 { From 61e694f388c9ad3b81585a17c8fbdef18859411e Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 3 Jan 2017 22:50:45 +0800 Subject: [PATCH 016/163] add retry --- httplib/httplib.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 510ad75e..87513c94 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -140,6 +140,7 @@ type BeegoHTTPSettings struct { EnableCookie bool Gzip bool DumpBody bool + Retries int // if set to -1 means will retry forever } // BeegoHTTPRequest provides more useful methods for requesting one url than http.Request. @@ -189,6 +190,15 @@ func (b *BeegoHTTPRequest) Debug(isdebug bool) *BeegoHTTPRequest { return b } +// Retries sets Retries times. +// default is 0 means no retried. +// -1 means retried forever. +// others means retried times. +func (b *BeegoHTTPRequest) Retries(times int) *BeegoHTTPRequest { + b.setting.Retries = times + return b +} + // DumpBody setting whether need to Dump the Body. func (b *BeegoHTTPRequest) DumpBody(isdump bool) *BeegoHTTPRequest { b.setting.DumpBody = isdump @@ -390,7 +400,7 @@ func (b *BeegoHTTPRequest) getResponse() (*http.Response, error) { } // DoRequest will do the client.Do -func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) { +func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) { var paramBody string if len(b.params) > 0 { var buf bytes.Buffer @@ -467,7 +477,16 @@ func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) { } b.dump = dump } - return client.Do(b.req) + // retries default value is 0, it will run once. + // retries equal to -1, it will run forever until success + // retries is setted, it will retries fixed times. + for i := 0; i == 0 || b.setting.Retries == -1 || i < b.setting.Retries; i++ { + resp, err = client.Do(b.req) + if err == nil { + break + } + } + return resp, err } // String returns the body string in response. From 09c405990c8aa60f16903dfd467e603511a420af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B4=AA=E4=BA=AE?= Date: Tue, 27 Dec 2016 09:58:42 +0800 Subject: [PATCH 017/163] Add Aliyun Logger --- .travis.yml | 2 + logs/alils/alils.go | 192 +++++++ logs/alils/config.go | 13 + logs/alils/log.pb.go | 984 ++++++++++++++++++++++++++++++++++++ logs/alils/log_config.go | 39 ++ logs/alils/log_project.go | 818 ++++++++++++++++++++++++++++++ logs/alils/log_store.go | 269 ++++++++++ logs/alils/machine_group.go | 87 ++++ logs/alils/request.go | 62 +++ logs/alils/signature.go | 112 ++++ logs/log.go | 1 + 11 files changed, 2579 insertions(+) create mode 100644 logs/alils/alils.go create mode 100755 logs/alils/config.go create mode 100755 logs/alils/log.pb.go create mode 100755 logs/alils/log_config.go create mode 100755 logs/alils/log_project.go create mode 100755 logs/alils/log_store.go create mode 100755 logs/alils/machine_group.go create mode 100755 logs/alils/request.go create mode 100755 logs/alils/signature.go diff --git a/.travis.yml b/.travis.yml index 93536488..df3e923f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,8 @@ install: - go get github.com/siddontang/ledisdb/config - go get github.com/siddontang/ledisdb/ledis - go get github.com/ssdb/gossdb/ssdb + - go get github.com/cloudflare/golz4 + - go get github.com/gogo/protobuf/proto before_script: - psql --version - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" diff --git a/logs/alils/alils.go b/logs/alils/alils.go new file mode 100644 index 00000000..30a09243 --- /dev/null +++ b/logs/alils/alils.go @@ -0,0 +1,192 @@ +package alils + +import ( + "encoding/json" + "github.com/astaxie/beego/logs" + "github.com/gogo/protobuf/proto" + "strings" + "sync" + "time" +) + +const ( + CacheSize int = 64 + Delimiter string = "##" +) + +type AliLSConfig struct { + Project string `json:"project"` + Endpoint string `json:"endpoint"` + KeyID string `json:"key_id"` + KeySecret string `json:"key_secret"` + LogStore string `json:"log_store"` + Topics []string `json:"topics"` + Source string `json:"source"` + Level int `json:"level"` + FlushWhen int `json:"flush_when"` +} + +// aliLSWriter implements LoggerInterface. +// it writes messages in keep-live tcp connection. +type aliLSWriter struct { + store *LogStore + group []*LogGroup + withMap bool + groupMap map[string]*LogGroup + lock *sync.Mutex + AliLSConfig +} + +// 创建提供Logger接口的日志服务 +func NewAliLS() logs.Logger { + alils := new(aliLSWriter) + alils.Level = logs.LevelTrace + return alils +} + +// 读取配置 +// 初始化必要的数据结构 +func (c *aliLSWriter) Init(jsonConfig string) (err error) { + + json.Unmarshal([]byte(jsonConfig), c) + + if c.FlushWhen > CacheSize { + c.FlushWhen = CacheSize + } + + // 初始化Project + prj := &LogProject{ + Name: c.Project, + Endpoint: c.Endpoint, + AccessKeyId: c.KeyID, + AccessKeySecret: c.KeySecret, + } + + // 获取logstore + c.store, err = prj.GetLogStore(c.LogStore) + if err != nil { + return err + } + + // 创建默认Log Group + c.group = append(c.group, &LogGroup{ + Topic: proto.String(""), + Source: proto.String(c.Source), + Logs: make([]*Log, 0, c.FlushWhen), + }) + + // 创建其它Log Group + c.groupMap = make(map[string]*LogGroup) + for _, topic := range c.Topics { + + lg := &LogGroup{ + Topic: proto.String(topic), + Source: proto.String(c.Source), + Logs: make([]*Log, 0, c.FlushWhen), + } + + c.group = append(c.group, lg) + c.groupMap[topic] = lg + } + + if len(c.group) == 1 { + c.withMap = false + } else { + c.withMap = true + } + + c.lock = &sync.Mutex{} + + return nil +} + +// WriteMsg write message in connection. +// if connection is down, try to re-connect. +func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error) { + + if level > c.Level { + return nil + } + + var topic string + var content string + var lg *LogGroup + if c.withMap { + + // 解析出Topic,并匹配LogGroup + strs := strings.SplitN(msg, Delimiter, 2) + if len(strs) == 2 { + pos := strings.LastIndex(strs[0], " ") + topic = strs[0][pos+1 : len(strs[0])] + content = strs[0][0:pos] + strs[1] + lg = c.groupMap[topic] + } + + // 默认发到空Topic + if lg == nil { + topic = "" + content = msg + lg = c.group[0] + } + } else { + topic = "" + content = msg + lg = c.group[0] + } + + // 生成日志 + c1 := &Log_Content{ + Key: proto.String("msg"), + Value: proto.String(content), + } + + l := &Log{ + Time: proto.Uint32(uint32(when.Unix())), // 填写日志时间 + Contents: []*Log_Content{ + c1, + }, + } + + c.lock.Lock() + lg.Logs = append(lg.Logs, l) + c.lock.Unlock() + + // 满足条件则Flush + if len(lg.Logs) >= c.FlushWhen { + c.flush(lg) + } + + return nil +} + +// Flush implementing method. empty. +func (c *aliLSWriter) Flush() { + + // flush所有group + for _, lg := range c.group { + c.flush(lg) + } +} + +// Destroy destroy connection writer and close tcp listener. +func (c *aliLSWriter) Destroy() { +} + +func (c *aliLSWriter) flush(lg *LogGroup) { + + c.lock.Lock() + defer c.lock.Unlock() + + // 把以上的LogGroup推送到SLS服务器, + // SLS服务器会根据该logstore的shard个数自动进行负载均衡。 + err := c.store.PutLogs(lg) + if err != nil { + return + } + + lg.Logs = make([]*Log, 0, c.FlushWhen) +} + +func init() { + logs.Register(logs.AdapterAliLS, NewAliLS) +} diff --git a/logs/alils/config.go b/logs/alils/config.go new file mode 100755 index 00000000..e8c24448 --- /dev/null +++ b/logs/alils/config.go @@ -0,0 +1,13 @@ +package alils + +const ( + version = "0.5.0" // SDK version + signatureMethod = "hmac-sha1" // Signature method + + // OffsetNewest stands for the log head offset, i.e. the offset that will be + // assigned to the next message that will be produced to the shard. + OffsetNewest = "end" + // OffsetOldest stands for the oldest offset available on the logstore for a + // shard. + OffsetOldest = "begin" +) diff --git a/logs/alils/log.pb.go b/logs/alils/log.pb.go new file mode 100755 index 00000000..42f7e892 --- /dev/null +++ b/logs/alils/log.pb.go @@ -0,0 +1,984 @@ +package alils + +import "github.com/gogo/protobuf/proto" +import "fmt" +import "math" + +// discarding unused import gogoproto "." + +import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" + +import "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type Log struct { + Time *uint32 `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"` + Contents []*Log_Content `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Log) Reset() { *m = Log{} } +func (m *Log) String() string { return proto.CompactTextString(m) } +func (*Log) ProtoMessage() {} + +func (m *Log) GetTime() uint32 { + if m != nil && m.Time != nil { + return *m.Time + } + return 0 +} + +func (m *Log) GetContents() []*Log_Content { + if m != nil { + return m.Contents + } + return nil +} + +type Log_Content struct { + Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"` + Value *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Log_Content) Reset() { *m = Log_Content{} } +func (m *Log_Content) String() string { return proto.CompactTextString(m) } +func (*Log_Content) ProtoMessage() {} + +func (m *Log_Content) GetKey() string { + if m != nil && m.Key != nil { + return *m.Key + } + return "" +} + +func (m *Log_Content) GetValue() string { + if m != nil && m.Value != nil { + return *m.Value + } + return "" +} + +type LogGroup struct { + Logs []*Log `protobuf:"bytes,1,rep,name=Logs" json:"Logs,omitempty"` + Reserved *string `protobuf:"bytes,2,opt,name=Reserved" json:"Reserved,omitempty"` + Topic *string `protobuf:"bytes,3,opt,name=Topic" json:"Topic,omitempty"` + Source *string `protobuf:"bytes,4,opt,name=Source" json:"Source,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogGroup) Reset() { *m = LogGroup{} } +func (m *LogGroup) String() string { return proto.CompactTextString(m) } +func (*LogGroup) ProtoMessage() {} + +func (m *LogGroup) GetLogs() []*Log { + if m != nil { + return m.Logs + } + return nil +} + +func (m *LogGroup) GetReserved() string { + if m != nil && m.Reserved != nil { + return *m.Reserved + } + return "" +} + +func (m *LogGroup) GetTopic() string { + if m != nil && m.Topic != nil { + return *m.Topic + } + return "" +} + +func (m *LogGroup) GetSource() string { + if m != nil && m.Source != nil { + return *m.Source + } + return "" +} + +type LogGroupList struct { + LogGroups []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *LogGroupList) Reset() { *m = LogGroupList{} } +func (m *LogGroupList) String() string { return proto.CompactTextString(m) } +func (*LogGroupList) ProtoMessage() {} + +func (m *LogGroupList) GetLogGroups() []*LogGroup { + if m != nil { + return m.LogGroups + } + return nil +} + +func (m *Log) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *Log) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Time == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time") + } else { + data[i] = 0x8 + i++ + i = encodeVarintLog(data, i, uint64(*m.Time)) + } + if len(m.Contents) > 0 { + for _, msg := range m.Contents { + data[i] = 0x12 + i++ + i = encodeVarintLog(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.XXX_unrecognized != nil { + i += copy(data[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *Log_Content) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *Log_Content) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Key == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key") + } else { + data[i] = 0xa + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Key))) + i += copy(data[i:], *m.Key) + } + if m.Value == nil { + return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value") + } else { + data[i] = 0x12 + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Value))) + i += copy(data[i:], *m.Value) + } + if m.XXX_unrecognized != nil { + i += copy(data[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *LogGroup) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *LogGroup) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Logs) > 0 { + for _, msg := range m.Logs { + data[i] = 0xa + i++ + i = encodeVarintLog(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.Reserved != nil { + data[i] = 0x12 + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Reserved))) + i += copy(data[i:], *m.Reserved) + } + if m.Topic != nil { + data[i] = 0x1a + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Topic))) + i += copy(data[i:], *m.Topic) + } + if m.Source != nil { + data[i] = 0x22 + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Source))) + i += copy(data[i:], *m.Source) + } + if m.XXX_unrecognized != nil { + i += copy(data[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *LogGroupList) Marshal() (data []byte, err error) { + size := m.Size() + data = make([]byte, size) + n, err := m.MarshalTo(data) + if err != nil { + return nil, err + } + return data[:n], nil +} + +func (m *LogGroupList) MarshalTo(data []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.LogGroups) > 0 { + for _, msg := range m.LogGroups { + data[i] = 0xa + i++ + i = encodeVarintLog(data, i, uint64(msg.Size())) + n, err := msg.MarshalTo(data[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.XXX_unrecognized != nil { + i += copy(data[i:], m.XXX_unrecognized) + } + return i, nil +} + +func encodeFixed64Log(data []byte, offset int, v uint64) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + data[offset+4] = uint8(v >> 32) + data[offset+5] = uint8(v >> 40) + data[offset+6] = uint8(v >> 48) + data[offset+7] = uint8(v >> 56) + return offset + 8 +} +func encodeFixed32Log(data []byte, offset int, v uint32) int { + data[offset] = uint8(v) + data[offset+1] = uint8(v >> 8) + data[offset+2] = uint8(v >> 16) + data[offset+3] = uint8(v >> 24) + return offset + 4 +} +func encodeVarintLog(data []byte, offset int, v uint64) int { + for v >= 1<<7 { + data[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + data[offset] = uint8(v) + return offset + 1 +} +func (m *Log) Size() (n int) { + var l int + _ = l + if m.Time != nil { + n += 1 + sovLog(uint64(*m.Time)) + } + if len(m.Contents) > 0 { + for _, e := range m.Contents { + l = e.Size() + n += 1 + l + sovLog(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Log_Content) Size() (n int) { + var l int + _ = l + if m.Key != nil { + l = len(*m.Key) + n += 1 + l + sovLog(uint64(l)) + } + if m.Value != nil { + l = len(*m.Value) + n += 1 + l + sovLog(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *LogGroup) Size() (n int) { + var l int + _ = l + if len(m.Logs) > 0 { + for _, e := range m.Logs { + l = e.Size() + n += 1 + l + sovLog(uint64(l)) + } + } + if m.Reserved != nil { + l = len(*m.Reserved) + n += 1 + l + sovLog(uint64(l)) + } + if m.Topic != nil { + l = len(*m.Topic) + n += 1 + l + sovLog(uint64(l)) + } + if m.Source != nil { + l = len(*m.Source) + n += 1 + l + sovLog(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *LogGroupList) Size() (n int) { + var l int + _ = l + if len(m.LogGroups) > 0 { + for _, e := range m.LogGroups { + l = e.Size() + n += 1 + l + sovLog(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovLog(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozLog(x uint64) (n int) { + return sovLog(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Log) Unmarshal(data []byte) error { + var hasFields [1]uint64 + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Log: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Log: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType) + } + var v uint32 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + v |= (uint32(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Time = &v + hasFields[0] |= uint64(0x00000001) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Contents = append(m.Contents, &Log_Content{}) + if err := m.Contents[len(m.Contents)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + if hasFields[0]&uint64(0x00000001) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time") + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Log_Content) Unmarshal(data []byte) error { + var hasFields [1]uint64 + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Content: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Key = &s + iNdEx = postIndex + hasFields[0] |= uint64(0x00000001) + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Value = &s + iNdEx = postIndex + hasFields[0] |= uint64(0x00000002) + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + if hasFields[0]&uint64(0x00000001) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key") + } + if hasFields[0]&uint64(0x00000002) == 0 { + return github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value") + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogGroup) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LogGroup: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogGroup: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Logs", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Logs = append(m.Logs, &Log{}) + if err := m.Logs[len(m.Logs)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reserved", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Reserved = &s + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Topic", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Topic = &s + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(data[iNdEx:postIndex]) + m.Source = &s + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LogGroupList) Unmarshal(data []byte) error { + l := len(data) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LogGroupList: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LogGroupList: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LogGroups", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLog + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLog + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LogGroups = append(m.LogGroups, &LogGroup{}) + if err := m.LogGroups[len(m.LogGroups)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLog(data[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthLog + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipLog(data []byte) (n int, err error) { + l := len(data) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if data[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthLog + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowLog + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := data[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipLog(data[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowLog = fmt.Errorf("proto: integer overflow") +) diff --git a/logs/alils/log_config.go b/logs/alils/log_config.go new file mode 100755 index 00000000..41fa0959 --- /dev/null +++ b/logs/alils/log_config.go @@ -0,0 +1,39 @@ +package alils + +type InputDetail struct { + LogType string `json:"logType"` + LogPath string `json:"logPath"` + FilePattern string `json:"filePattern"` + LocalStorage bool `json:"localStorage"` + TimeFormat string `json:"timeFormat"` + LogBeginRegex string `json:"logBeginRegex"` + Regex string `json:"regex"` + Keys []string `json:"key"` + FilterKeys []string `json:"filterKey"` + FilterRegex []string `json:"filterRegex"` + TopicFormat string `json:"topicFormat"` +} + +type OutputDetail struct { + Endpoint string `json:"endpoint"` + LogStoreName string `json:"logstoreName"` +} + +type LogConfig struct { + Name string `json:"configName"` + InputType string `json:"inputType"` + InputDetail InputDetail `json:"inputDetail"` + OutputType string `json:"outputType"` + OutputDetail OutputDetail `json:"outputDetail"` + + CreateTime uint32 + LastModifyTime uint32 + + project *LogProject +} + +// GetAppliedMachineGroup returns applied machine group of this config. +func (c *LogConfig) GetAppliedMachineGroup(confName string) (groupNames []string, err error) { + groupNames, err = c.project.GetAppliedMachineGroups(c.Name) + return +} diff --git a/logs/alils/log_project.go b/logs/alils/log_project.go new file mode 100755 index 00000000..63ab07f8 --- /dev/null +++ b/logs/alils/log_project.go @@ -0,0 +1,818 @@ +/* +Package sls implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS). + +For more description about SLS, please read this article: +http://gitlab.alibaba-inc.com/sls/doc. +*/ +package alils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" +) + +// Error message in SLS HTTP response. +type errorMessage struct { + Code string `json:"errorCode"` + Message string `json:"errorMessage"` +} + +type LogProject struct { + Name string // Project name + Endpoint string // IP or hostname of SLS endpoint + AccessKeyId string + AccessKeySecret string +} + +// NewLogProject creates a new SLS project. +func NewLogProject(name, endpoint, accessKeyId, accessKeySecret string) (p *LogProject, err error) { + p = &LogProject{ + Name: name, + Endpoint: endpoint, + AccessKeyId: accessKeyId, + AccessKeySecret: accessKeySecret, + } + return p, nil +} + +// ListLogStore returns all logstore names of project p. +func (p *LogProject) ListLogStore() (storeNames []string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/logstores") + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to list logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Count int + LogStores []string + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + storeNames = body.LogStores + + return +} + +// GetLogStore returns logstore according by logstore name. +func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "GET", "/logstores/"+name, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + s = &LogStore{} + err = json.Unmarshal(buf, s) + if err != nil { + return + } + s.project = p + return +} + +// CreateLogStore creates a new logstore in SLS, +// where name is logstore name, +// and ttl is time-to-live(in day) of logs, +// and shardCnt is the number of shards. +func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error) { + + type Body struct { + Name string `json:"logstoreName"` + TTL int `json:"ttl"` + ShardCount int `json:"shardCount"` + } + + store := &Body{ + Name: name, + TTL: ttl, + ShardCount: shardCnt, + } + + body, err := json.Marshal(store) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "POST", "/logstores", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to create logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// DeleteLogStore deletes a logstore according by logstore name. +func (p *LogProject) DeleteLogStore(name string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "DELETE", "/logstores/"+name, h, nil) + if err != nil { + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// UpdateLogStore updates a logstore according by logstore name, +// obviously we can't modify the logstore name itself. +func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error) { + + type Body struct { + Name string `json:"logstoreName"` + TTL int `json:"ttl"` + ShardCount int `json:"shardCount"` + } + + store := &Body{ + Name: name, + TTL: ttl, + ShardCount: shardCnt, + } + + body, err := json.Marshal(store) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "PUT", "/logstores", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// ListMachineGroup returns machine group name list and the total number of machine groups. +// The offset starts from 0 and the size is the max number of machine groups could be returned. +func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + if size <= 0 { + size = 500 + } + + uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to list machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + MachineGroups []string + Count int + Total int + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + m = body.MachineGroups + total = body.Total + + return +} + +// GetMachineGroup retruns machine group according by machine group name. +func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "GET", "/machinegroups/"+name, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get machine group:%v", name) + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + m = &MachineGroup{} + err = json.Unmarshal(buf, m) + if err != nil { + return + } + m.project = p + return +} + +// CreateMachineGroup creates a new machine group in SLS. +func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) { + + body, err := json.Marshal(m) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "POST", "/machinegroups", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to create machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// UpdateMachineGroup updates a machine group. +func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) { + + body, err := json.Marshal(m) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// DeleteMachineGroup deletes machine group according machine group name. +func (p *LogProject) DeleteMachineGroup(name string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil) + if err != nil { + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// ListConfig returns config names list and the total number of configs. +// The offset starts from 0 and the size is the max number of configs could be returned. +func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + if size <= 0 { + size = 100 + } + + uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Total int + Configs []string + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + cfgNames = body.Configs + total = body.Total + return +} + +// GetConfig returns config according by config name. +func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "GET", "/configs/"+name, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + c = &LogConfig{} + err = json.Unmarshal(buf, c) + if err != nil { + return + } + c.project = p + return +} + +// UpdateConfig updates a config. +func (p *LogProject) UpdateConfig(c *LogConfig) (err error) { + + body, err := json.Marshal(c) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "PUT", "/configs/"+c.Name, h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// CreateConfig creates a new config in SLS. +func (p *LogProject) CreateConfig(c *LogConfig) (err error) { + + body, err := json.Marshal(c) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/json", + "Accept-Encoding": "deflate", // TODO: support lz4 + } + + r, err := request(p, "POST", "/configs", h, body) + if err != nil { + return + } + + body, err = ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to update config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + return +} + +// DeleteConfig deletes a config according by config name. +func (p *LogProject) DeleteConfig(name string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + r, err := request(p, "DELETE", "/configs/"+name, h, nil) + if err != nil { + return + } + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(body, errMsg) + if err != nil { + err = fmt.Errorf("failed to delete config") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// GetAppliedMachineGroups returns applied machine group names list according config name. +func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/configs/%v/machinegroups", confName) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get applied machine groups") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Count int + Machinegroups []string + } + + body := &Body{} + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + groupNames = body.Machinegroups + return +} + +// GetAppliedConfigs returns applied config names list according machine group name groupName. +func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/configs", groupName) + r, err := request(p, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to applied configs") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Cfg struct { + Count int `json:"count"` + Configs []string `json:"configs"` + } + + body := &Cfg{} + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + confNames = body.Configs + return +} + +// ApplyConfigToMachineGroup applies config to machine group. +func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) + r, err := request(p, "PUT", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to apply config to machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// RemoveConfigFromMachineGroup removes config from machine group. +func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) + r, err := request(p, "DELETE", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to remove config from machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Printf("%s\n", dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} diff --git a/logs/alils/log_store.go b/logs/alils/log_store.go new file mode 100755 index 00000000..009e39c4 --- /dev/null +++ b/logs/alils/log_store.go @@ -0,0 +1,269 @@ +package alils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" + "strconv" + + lz4 "github.com/cloudflare/golz4" + "github.com/gogo/protobuf/proto" +) + +type LogStore struct { + Name string `json:"logstoreName"` + TTL int + ShardCount int + + CreateTime uint32 + LastModifyTime uint32 + + project *LogProject +} + +type Shard struct { + ShardID int `json:"shardID"` +} + +// ListShards returns shard id list of this logstore. +func (s *LogStore) ListShards() (shardIDs []int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/logstores/%v/shards", s.Name) + r, err := request(s.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to list logstore") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + var shards []*Shard + err = json.Unmarshal(buf, &shards) + if err != nil { + return + } + + for _, v := range shards { + shardIDs = append(shardIDs, v.ShardID) + } + return +} + +// PutLogs put logs into logstore. +// The callers should transform user logs into LogGroup. +func (s *LogStore) PutLogs(lg *LogGroup) (err error) { + body, err := proto.Marshal(lg) + if err != nil { + return + } + + // Compresse body with lz4 + out := make([]byte, lz4.CompressBound(body)) + n, err := lz4.Compress(body, out) + if err != nil { + return + } + + h := map[string]string{ + "x-sls-compresstype": "lz4", + "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), + "Content-Type": "application/x-protobuf", + } + + uri := fmt.Sprintf("/logstores/%v", s.Name) + r, err := request(s.project, "POST", uri, h, out[:n]) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to put logs") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + return +} + +// GetCursor gets log cursor of one shard specified by shardId. +// The from can be in three form: a) unix timestamp in seccond, b) "begin", c) "end". +// For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore +func (s *LogStore) GetCursor(shardId int, from string) (cursor string, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v", + s.Name, shardId, from) + + r, err := request(s.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get cursor") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + type Body struct { + Cursor string + } + body := &Body{} + + err = json.Unmarshal(buf, body) + if err != nil { + return + } + cursor = body.Cursor + return +} + +// GetLogsBytes gets logs binary data from shard specified by shardId according cursor. +// The logGroupMaxCount is the max number of logGroup could be returned. +// The nextCursor is the next curosr can be used to read logs at next time. +func (s *LogStore) GetLogsBytes(shardId int, cursor string, + logGroupMaxCount int) (out []byte, nextCursor string, err error) { + + h := map[string]string{ + "x-sls-bodyrawsize": "0", + "Accept": "application/x-protobuf", + "Accept-Encoding": "lz4", + } + + uri := fmt.Sprintf("/logstores/%v/shards/%v?type=logs&cursor=%v&count=%v", + s.Name, shardId, cursor, logGroupMaxCount) + + r, err := request(s.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to get cursor") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + v, ok := r.Header["X-Sls-Compresstype"] + if !ok || len(v) == 0 { + err = fmt.Errorf("can't find 'x-sls-compresstype' header") + return + } + if v[0] != "lz4" { + err = fmt.Errorf("unexpected compress type:%v", v[0]) + return + } + + v, ok = r.Header["X-Sls-Cursor"] + if !ok || len(v) == 0 { + err = fmt.Errorf("can't find 'x-sls-cursor' header") + return + } + nextCursor = v[0] + + v, ok = r.Header["X-Sls-Bodyrawsize"] + if !ok || len(v) == 0 { + err = fmt.Errorf("can't find 'x-sls-bodyrawsize' header") + return + } + bodyRawSize, err := strconv.Atoi(v[0]) + if err != nil { + return + } + + out = make([]byte, bodyRawSize) + err = lz4.Uncompress(buf, out) + if err != nil { + return + } + + return +} + +// LogsBytesDecode decodes logs binary data retruned by GetLogsBytes API +func LogsBytesDecode(data []byte) (gl *LogGroupList, err error) { + + gl = &LogGroupList{} + err = proto.Unmarshal(data, gl) + if err != nil { + return + } + + return +} + +// GetLogs gets logs from shard specified by shardId according cursor. +// The logGroupMaxCount is the max number of logGroup could be returned. +// The nextCursor is the next curosr can be used to read logs at next time. +func (s *LogStore) GetLogs(shardId int, cursor string, + logGroupMaxCount int) (gl *LogGroupList, nextCursor string, err error) { + + out, nextCursor, err := s.GetLogsBytes(shardId, cursor, logGroupMaxCount) + if err != nil { + return + } + + gl, err = LogsBytesDecode(out) + if err != nil { + return + } + + return +} diff --git a/logs/alils/machine_group.go b/logs/alils/machine_group.go new file mode 100755 index 00000000..7a0aace1 --- /dev/null +++ b/logs/alils/machine_group.go @@ -0,0 +1,87 @@ +package alils + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" +) + +type MachinGroupAttribute struct { + ExternalName string `json:"externalName"` + TopicName string `json:"groupTopic"` +} + +type MachineGroup struct { + Name string `json:"groupName"` + Type string `json:"groupType"` + MachineIdType string `json:"machineIdentifyType"` + MachineIdList []string `json:"machineList"` + + Attribute MachinGroupAttribute `json:"groupAttribute"` + + CreateTime uint32 + LastModifyTime uint32 + + project *LogProject +} + +type Machine struct { + IP string + UniqueId string `json:"machine-uniqueid"` + UserdefinedId string `json:"userdefined-id"` +} + +type MachineList struct { + Total int + Machines []*Machine +} + +// ListMachines returns machine list of this machine group. +func (m *MachineGroup) ListMachines() (ms []*Machine, total int, err error) { + h := map[string]string{ + "x-sls-bodyrawsize": "0", + } + + uri := fmt.Sprintf("/machinegroups/%v/machines", m.Name) + r, err := request(m.project, "GET", uri, h, nil) + if err != nil { + return + } + + buf, err := ioutil.ReadAll(r.Body) + if err != nil { + return + } + + if r.StatusCode != http.StatusOK { + errMsg := &errorMessage{} + err = json.Unmarshal(buf, errMsg) + if err != nil { + err = fmt.Errorf("failed to remove config from machine group") + dump, _ := httputil.DumpResponse(r, true) + fmt.Println(dump) + return + } + err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) + return + } + + body := &MachineList{} + err = json.Unmarshal(buf, body) + if err != nil { + return + } + + ms = body.Machines + total = body.Total + + return +} + +// GetAppliedConfigs returns applied configs of this machine group. +func (m *MachineGroup) GetAppliedConfigs() (confNames []string, err error) { + confNames, err = m.project.GetAppliedConfigs(m.Name) + return +} diff --git a/logs/alils/request.go b/logs/alils/request.go new file mode 100755 index 00000000..20df45b4 --- /dev/null +++ b/logs/alils/request.go @@ -0,0 +1,62 @@ +package alils + +import ( + "bytes" + "crypto/md5" + "fmt" + "net/http" +) + +// request sends a request to SLS. +func request(project *LogProject, method, uri string, headers map[string]string, + body []byte) (resp *http.Response, err error) { + + // The caller should provide 'x-sls-bodyrawsize' header + if _, ok := headers["x-sls-bodyrawsize"]; !ok { + err = fmt.Errorf("Can't find 'x-sls-bodyrawsize' header") + return + } + + // SLS public request headers + headers["Host"] = project.Name + "." + project.Endpoint + headers["Date"] = nowRFC1123() + headers["x-sls-apiversion"] = version + headers["x-sls-signaturemethod"] = signatureMethod + if body != nil { + bodyMD5 := fmt.Sprintf("%X", md5.Sum(body)) + headers["Content-MD5"] = bodyMD5 + + if _, ok := headers["Content-Type"]; !ok { + err = fmt.Errorf("Can't find 'Content-Type' header") + return + } + } + + // Calc Authorization + // Authorization = "SLS :" + digest, err := signature(project, method, uri, headers) + if err != nil { + return + } + auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyId, digest) + headers["Authorization"] = auth + + // Initialize http request + reader := bytes.NewReader(body) + urlStr := fmt.Sprintf("http://%v.%v%v", project.Name, project.Endpoint, uri) + req, err := http.NewRequest(method, urlStr, reader) + if err != nil { + return + } + for k, v := range headers { + req.Header.Add(k, v) + } + + // Get ready to do request + resp, err = http.DefaultClient.Do(req) + if err != nil { + return + } + + return +} diff --git a/logs/alils/signature.go b/logs/alils/signature.go new file mode 100755 index 00000000..e0e4b3f7 --- /dev/null +++ b/logs/alils/signature.go @@ -0,0 +1,112 @@ +package alils + +import ( + "crypto/hmac" + "crypto/sha1" + "encoding/base64" + "fmt" + "net/url" + "sort" + "strings" + "time" +) + +// GMT location +var gmtLoc = time.FixedZone("GMT", 0) + +// NowRFC1123 returns now time in RFC1123 format with GMT timezone, +// eg. "Mon, 02 Jan 2006 15:04:05 GMT". +func nowRFC1123() string { + return time.Now().In(gmtLoc).Format(time.RFC1123) +} + +// signature calculates a request's signature digest. +func signature(project *LogProject, method, uri string, + headers map[string]string) (digest string, err error) { + var contentMD5, contentType, date, canoHeaders, canoResource string + var slsHeaderKeys sort.StringSlice + + // SignString = VERB + "\n" + // + CONTENT-MD5 + "\n" + // + CONTENT-TYPE + "\n" + // + DATE + "\n" + // + CanonicalizedSLSHeaders + "\n" + // + CanonicalizedResource + + if val, ok := headers["Content-MD5"]; ok { + contentMD5 = val + } + + if val, ok := headers["Content-Type"]; ok { + contentType = val + } + + date, ok := headers["Date"] + if !ok { + err = fmt.Errorf("Can't find 'Date' header") + return + } + + // Calc CanonicalizedSLSHeaders + slsHeaders := make(map[string]string, len(headers)) + for k, v := range headers { + l := strings.TrimSpace(strings.ToLower(k)) + if strings.HasPrefix(l, "x-sls-") { + slsHeaders[l] = strings.TrimSpace(v) + slsHeaderKeys = append(slsHeaderKeys, l) + } + } + + sort.Sort(slsHeaderKeys) + for i, k := range slsHeaderKeys { + canoHeaders += k + ":" + slsHeaders[k] + if i+1 < len(slsHeaderKeys) { + canoHeaders += "\n" + } + } + + // Calc CanonicalizedResource + u, err := url.Parse(uri) + if err != nil { + return + } + + canoResource += url.QueryEscape(u.Path) + if u.RawQuery != "" { + var keys sort.StringSlice + + vals := u.Query() + for k, _ := range vals { + keys = append(keys, k) + } + + sort.Sort(keys) + canoResource += "?" + for i, k := range keys { + if i > 0 { + canoResource += "&" + } + + for _, v := range vals[k] { + canoResource += k + "=" + v + } + } + } + + signStr := method + "\n" + + contentMD5 + "\n" + + contentType + "\n" + + date + "\n" + + canoHeaders + "\n" + + canoResource + + // Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret)) + mac := hmac.New(sha1.New, []byte(project.AccessKeySecret)) + _, err = mac.Write([]byte(signStr)) + if err != nil { + return + } + digest = base64.StdEncoding.EncodeToString(mac.Sum(nil)) + return +} + diff --git a/logs/log.go b/logs/log.go index 806ebaa0..c351c473 100644 --- a/logs/log.go +++ b/logs/log.go @@ -71,6 +71,7 @@ const ( AdapterEs = "es" AdapterJianLiao = "jianliao" AdapterSlack = "slack" + AdapterAliLS = "alils" ) // Legacy log level constants to ensure backwards compatibility. From 9266ece7a408aa00c918db7789a6713b11695065 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 5 Jan 2017 18:27:23 +0800 Subject: [PATCH 018/163] fix the retried --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 87513c94..39480469 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -480,7 +480,7 @@ func (b *BeegoHTTPRequest) DoRequest() (resp *http.Response, err error) { // retries default value is 0, it will run once. // retries equal to -1, it will run forever until success // retries is setted, it will retries fixed times. - for i := 0; i == 0 || b.setting.Retries == -1 || i < b.setting.Retries; i++ { + for i := 0; b.setting.Retries == -1 || i <= b.setting.Retries; i++ { resp, err = client.Do(b.req) if err == nil { break From a8a2dffc5918b6439e36d9117c96efb4e1d041ca Mon Sep 17 00:00:00 2001 From: chesedo Date: Fri, 6 Jan 2017 10:11:08 +0200 Subject: [PATCH 019/163] Have Required validator trim strings to fix #2361 This will cause the Required validator not to consider fields that has only spaces or new lines to be regarded as valid. This is done by checking if the trimmed version of the string is valid. --- validation/validation_test.go | 6 ++++++ validation/validators.go | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/validation/validation_test.go b/validation/validation_test.go index ec65b6d0..d8e880df 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -35,6 +35,12 @@ func TestRequired(t *testing.T) { if valid.Required("", "string").Ok { t.Error("\"'\" string should be false") } + if valid.Required(" ", "string").Ok { + t.Error("\" \" string should be false") // For #2361 + } + if valid.Required("\n", "string").Ok { + t.Error("new line string should be false") // For #2361 + } if !valid.Required("astaxie", "string").Ok { t.Error("string should be true") } diff --git a/validation/validators.go b/validation/validators.go index 9b04c5ce..78b10373 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -18,6 +18,7 @@ import ( "fmt" "reflect" "regexp" + "strings" "time" "unicode/utf8" ) @@ -98,7 +99,7 @@ func (r Required) IsSatisfied(obj interface{}) bool { } if str, ok := obj.(string); ok { - return len(str) > 0 + return len(strings.TrimSpace(str)) > 0 } if _, ok := obj.(bool); ok { return true From 6a2ee371a5d76b0bf34307d8ec850024a029c6dc Mon Sep 17 00:00:00 2001 From: fugr Date: Mon, 9 Jan 2017 21:04:11 +0800 Subject: [PATCH 020/163] avoid creating new file to implements Config There is no need to create new file in ParseData(data []byte) (Configer, error).Tet's make code simply. --- config/ini.go | 43 +++++++++++++++++++++++-------------------- config/xml/xml.go | 28 ++++++++-------------------- config/yaml/yaml.go | 24 +++++++++++++----------- 3 files changed, 44 insertions(+), 51 deletions(-) diff --git a/config/ini.go b/config/ini.go index b3332bd8..6b78f02a 100644 --- a/config/ini.go +++ b/config/ini.go @@ -18,16 +18,13 @@ import ( "bufio" "bytes" "errors" - "fmt" "io" "io/ioutil" "os" - "path" "path/filepath" "strconv" "strings" "sync" - "time" ) var ( @@ -52,24 +49,26 @@ func (ini *IniConfig) Parse(name string) (Configer, error) { } func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { - file, err := os.Open(name) + data, err := ioutil.ReadFile(name) if err != nil { return nil, err } + return ini.parseData(data) +} + +func (ini *IniConfig) parseData(data []byte) (*IniConfigContainer, error) { cfg := &IniConfigContainer{ - file.Name(), - make(map[string]map[string]string), - make(map[string]string), - make(map[string]string), - sync.RWMutex{}, + data: make(map[string]map[string]string), + sectionComment: make(map[string]string), + keyComment: make(map[string]string), + RWMutex: sync.RWMutex{}, } cfg.Lock() defer cfg.Unlock() - defer file.Close() var comment bytes.Buffer - buf := bufio.NewReader(file) + buf := bufio.NewReader(bytes.NewBuffer(data)) // check the BOM head, err := buf.Peek(3) if err == nil && head[0] == 239 && head[1] == 187 && head[2] == 191 { @@ -130,16 +129,24 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { // handle include "other.conf" if len(keyValue) == 1 && strings.HasPrefix(key, "include") { + includefiles := strings.Fields(key) if includefiles[0] == "include" && len(includefiles) == 2 { + otherfile := strings.Trim(includefiles[1], "\"") if !filepath.IsAbs(otherfile) { - otherfile = filepath.Join(filepath.Dir(name), otherfile) + dir, err := os.Getwd() + if err != nil { + return nil, err + } + otherfile = filepath.Join(dir, otherfile) } + i, err := ini.parseFile(otherfile) if err != nil { return nil, err } + for sec, dt := range i.data { if _, ok := cfg.data[sec]; !ok { cfg.data[sec] = make(map[string]string) @@ -148,12 +155,15 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { cfg.data[sec][k] = v } } + for sec, comm := range i.sectionComment { cfg.sectionComment[sec] = comm } + for k, comm := range i.keyComment { cfg.keyComment[k] = comm } + continue } } @@ -178,19 +188,12 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { // ParseData parse ini the data func (ini *IniConfig) ParseData(data []byte) (Configer, error) { - // Save memory data to temporary file - tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond())) - os.MkdirAll(path.Dir(tmpName), os.ModePerm) - if err := ioutil.WriteFile(tmpName, data, 0655); err != nil { - return nil, err - } - return ini.Parse(tmpName) + return ini.parseData(data) } // IniConfigContainer A Config represents the ini configuration. // When set and get value, support key as section:name type. type IniConfigContainer struct { - filename string data map[string]map[string]string // section=> key:val sectionComment map[string]string // section : comment keyComment map[string]string // id: []{comment, key...}; id 1 is for main comment. diff --git a/config/xml/xml.go b/config/xml/xml.go index 66115714..b82bf403 100644 --- a/config/xml/xml.go +++ b/config/xml/xml.go @@ -35,11 +35,9 @@ import ( "fmt" "io/ioutil" "os" - "path" "strconv" "strings" "sync" - "time" "github.com/astaxie/beego/config" "github.com/beego/x2j" @@ -52,36 +50,26 @@ type Config struct{} // Parse returns a ConfigContainer with parsed xml config map. func (xc *Config) Parse(filename string) (config.Configer, error) { - file, err := os.Open(filename) + context, err := ioutil.ReadFile(filename) if err != nil { return nil, err } - defer file.Close() + return xc.ParseData(context) +} + +// ParseData xml data +func (xc *Config) ParseData(data []byte) (config.Configer, error) { x := &ConfigContainer{data: make(map[string]interface{})} - content, err := ioutil.ReadAll(file) - if err != nil { - return nil, err - } - d, err := x2j.DocToMap(string(content)) + d, err := x2j.DocToMap(string(data)) if err != nil { return nil, err } x.data = config.ExpandValueEnvForMap(d["config"].(map[string]interface{})) - return x, nil -} -// ParseData xml data -func (xc *Config) ParseData(data []byte) (config.Configer, error) { - // Save memory data to temporary file - tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond())) - os.MkdirAll(path.Dir(tmpName), os.ModePerm) - if err := ioutil.WriteFile(tmpName, data, 0655); err != nil { - return nil, err - } - return xc.Parse(tmpName) + return x, nil } // ConfigContainer A Config represents the xml configuration. diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index e3260215..bcef4a20 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -37,10 +37,8 @@ import ( "io/ioutil" "log" "os" - "path" "strings" "sync" - "time" "github.com/astaxie/beego/config" "github.com/beego/goyaml2" @@ -63,26 +61,30 @@ func (yaml *Config) Parse(filename string) (y config.Configer, err error) { // ParseData parse yaml data func (yaml *Config) ParseData(data []byte) (config.Configer, error) { - // Save memory data to temporary file - tmpName := path.Join(os.TempDir(), "beego", fmt.Sprintf("%d", time.Now().Nanosecond())) - os.MkdirAll(path.Dir(tmpName), os.ModePerm) - if err := ioutil.WriteFile(tmpName, data, 0655); err != nil { + cnf, err := parseYML(data) + if err != nil { return nil, err } - return yaml.Parse(tmpName) + + return &ConfigContainer{ + data: cnf, + }, nil } // ReadYmlReader Read yaml file to map. // if json like, use json package, unless goyaml2 package. func ReadYmlReader(path string) (cnf map[string]interface{}, err error) { - f, err := os.Open(path) + buf, err := ioutil.ReadFile(path) if err != nil { return } - defer f.Close() - buf, err := ioutil.ReadAll(f) - if err != nil || len(buf) < 3 { + return parseYML(buf) +} + +// parseYML parse yaml formatted []byte to map. +func parseYML(buf []byte) (cnf map[string]interface{}, err error) { + if len(buf) < 3 { return } From 3fa7fc6e419eb426f2b111f6e6a62dfed929e9d3 Mon Sep 17 00:00:00 2001 From: fugr Date: Wed, 11 Jan 2017 18:55:53 +0800 Subject: [PATCH 021/163] config:fix handle include other.conf When include other.conf,other.conf is either absolute directory or under beego in default temporary directory(/tmp/beego). maybe replace by current directory is better. --- config/ini.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/config/ini.go b/config/ini.go index 6b78f02a..c09bcf5c 100644 --- a/config/ini.go +++ b/config/ini.go @@ -54,10 +54,10 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { return nil, err } - return ini.parseData(data) + return ini.parseData(filepath.Dir(name), data) } -func (ini *IniConfig) parseData(data []byte) (*IniConfigContainer, error) { +func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, error) { cfg := &IniConfigContainer{ data: make(map[string]map[string]string), sectionComment: make(map[string]string), @@ -135,10 +135,6 @@ func (ini *IniConfig) parseData(data []byte) (*IniConfigContainer, error) { otherfile := strings.Trim(includefiles[1], "\"") if !filepath.IsAbs(otherfile) { - dir, err := os.Getwd() - if err != nil { - return nil, err - } otherfile = filepath.Join(dir, otherfile) } @@ -187,8 +183,13 @@ func (ini *IniConfig) parseData(data []byte) (*IniConfigContainer, error) { } // ParseData parse ini the data +// When include other.conf,other.conf is either absolute directory +// or under beego in default temporary directory(/tmp/beego). func (ini *IniConfig) ParseData(data []byte) (Configer, error) { - return ini.parseData(data) + dir := filepath.Join(os.TempDir(), "beego") + os.MkdirAll(dir, os.ModePerm) + + return ini.parseData(dir, data) } // IniConfigContainer A Config represents the ini configuration. From 82d2ace3bd35f472fef85d824275c4c383a5dc35 Mon Sep 17 00:00:00 2001 From: chesedo Date: Wed, 11 Jan 2017 12:51:32 +0200 Subject: [PATCH 022/163] Add strong relationship support to orm --- orm/db_utils.go | 2 ++ orm/models_test.go | 5 +++++ orm/orm.go | 2 ++ orm/orm_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/orm/db_utils.go b/orm/db_utils.go index 923917ec..7ae10ca5 100644 --- a/orm/db_utils.go +++ b/orm/db_utils.go @@ -41,6 +41,8 @@ func getExistPk(mi *modelInfo, ind reflect.Value) (column string, value interfac vu := v.Int() exist = true value = vu + } else if fi.fieldType&IsRelField > 0 { + _, value, exist = getExistPk(fi.relModelInfo, reflect.Indirect(v)) } else { vu := v.String() exist = vu != "" diff --git a/orm/models_test.go b/orm/models_test.go index 462370b2..9843a87d 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -406,6 +406,11 @@ type UintPk struct { Name string } +type PtrPk struct { + ID *IntegerPk `orm:"pk;rel(one)"` + Positive bool +} + var DBARGS = struct { Driver string Source string diff --git a/orm/orm.go b/orm/orm.go index 538916e4..d9d1cd77 100644 --- a/orm/orm.go +++ b/orm/orm.go @@ -153,6 +153,8 @@ func (o *orm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, i id, vid := int64(0), ind.FieldByIndex(mi.fields.pk.fieldIndex) if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 { id = int64(vid.Uint()) + } else if mi.fields.pk.rel { + return o.ReadOrCreate(vid.Interface(), mi.fields.pk.relModelInfo.fields.pk.name) } else { id = vid.Int() } diff --git a/orm/orm_test.go b/orm/orm_test.go index adfe0066..8738952b 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -193,6 +193,7 @@ func TestSyncDb(t *testing.T) { RegisterModel(new(InLineOneToOne)) RegisterModel(new(IntegerPk)) RegisterModel(new(UintPk)) + RegisterModel(new(PtrPk)) err := RunSyncdb("default", true, Debug) throwFail(t, err) @@ -216,6 +217,7 @@ func TestRegisterModels(t *testing.T) { RegisterModel(new(InLineOneToOne)) RegisterModel(new(IntegerPk)) RegisterModel(new(UintPk)) + RegisterModel(new(PtrPk)) BootStrap() @@ -2144,6 +2146,48 @@ func TestUintPk(t *testing.T) { dORM.Delete(u) } +func TestPtrPk(t *testing.T) { + parent := &IntegerPk{ID: 10, Value: "10"} + + id, _ := dORM.Insert(parent) + if !IsMysql { + // MySql does not support last_insert_id in this case: see #2382 + throwFail(t, AssertIs(id, 10)) + } + + ptr := PtrPk{ID: parent, Positive: true} + num, err := dORM.InsertMulti(2, []PtrPk{ptr}) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + throwFail(t, AssertIs(ptr.ID, parent)) + + nptr := &PtrPk{ID: parent} + created, pk, err := dORM.ReadOrCreate(nptr, "ID") + throwFail(t, err) + throwFail(t, AssertIs(created, false)) + throwFail(t, AssertIs(pk, 10)) + throwFail(t, AssertIs(nptr.ID, parent)) + throwFail(t, AssertIs(nptr.Positive, true)) + + nptr = &PtrPk{Positive: true} + created, pk, err = dORM.ReadOrCreate(nptr, "Positive") + throwFail(t, err) + throwFail(t, AssertIs(created, false)) + throwFail(t, AssertIs(pk, 10)) + throwFail(t, AssertIs(nptr.ID, parent)) + + nptr.Positive = false + num, err = dORM.Update(nptr) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + throwFail(t, AssertIs(nptr.ID, parent)) + throwFail(t, AssertIs(nptr.Positive, false)) + + num, err = dORM.Delete(nptr) + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) +} + func TestSnake(t *testing.T) { cases := map[string]string{ "i": "i", From fbc9f8e6409dd0122d49ccf8a8e7696c80de442c Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Fri, 13 Jan 2017 17:10:54 +0100 Subject: [PATCH 023/163] Package env for working with env variables inside Beego The package env makes it trivial to work with environment variables. It allows getting values with defaults as a fallback. New ENV variables can be set safely on the current process environment. --- env/env.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ env/env_test.go | 75 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 env/env.go create mode 100644 env/env_test.go diff --git a/env/env.go b/env/env.go new file mode 100644 index 00000000..014501f5 --- /dev/null +++ b/env/env.go @@ -0,0 +1,89 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// Copyright 2017 Faissal Elamraoui. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package env + +import ( + "fmt" + "os" + "strings" + "sync" +) + +var env struct { + data map[string]string + lock *sync.RWMutex +} + +func init() { + env.data = make(map[string]string) + env.lock = &sync.RWMutex{} + for _, e := range os.Environ() { + splits := strings.Split(e, "=") + env.data[splits[0]] = os.Getenv(splits[0]) + } +} + +// Get returns a value by key. +// If the key does not exist, the default value will be returned. +func Get(key string, defVal string) string { + env.lock.RLock() + defer env.lock.RUnlock() + + if val, ok := env.data[key]; ok { + return val + } + return defVal +} + +// MustGet returns a value by key. +// If the key does not exist, it will return an error. +func MustGet(key string) (string, error) { + env.lock.RLock() + defer env.lock.RUnlock() + + if val, ok := env.data[key]; ok { + return val, nil + } + return "", fmt.Errorf("no env variable with %s", key) +} + +// Set sets a value in the ENV copy. +// This does not affect the child process environment. +func Set(key string, value string) { + env.lock.Lock() + defer env.lock.Unlock() + env.data[key] = value +} + +// MustSet sets a value in the ENV copy and the child process environment. +// It returns an error in case the set operation failed. +func MustSet(key string, value string) error { + env.lock.Lock() + defer env.lock.Unlock() + + err := os.Setenv(key, value) + if err != nil { + return err + } + env.data[key] = value + return nil +} + +// GetAll returns all keys/values in the current child process environment. +func GetAll() map[string]string { + env.lock.RLock() + defer env.lock.RUnlock() + return env.data +} diff --git a/env/env_test.go b/env/env_test.go new file mode 100644 index 00000000..3f1d4dba --- /dev/null +++ b/env/env_test.go @@ -0,0 +1,75 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// Copyright 2017 Faissal Elamraoui. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package env + +import ( + "os" + "testing" +) + +func TestEnvGet(t *testing.T) { + gopath := Get("GOPATH", "") + if gopath != os.Getenv("GOPATH") { + t.Error("expected GOPATH not empty.") + } + + noExistVar := Get("NOEXISTVAR", "foo") + if noExistVar != "foo" { + t.Errorf("expected NOEXISTVAR to equal foo, got %s.", noExistVar) + } +} + +func TestEnvMustGet(t *testing.T) { + gopath, err := MustGet("GOPATH") + if err != nil { + t.Error(err) + } + + if gopath != os.Getenv("GOPATH") { + t.Errorf("expected GOPATH to be the same, got %s.", gopath) + } + + _, err = MustGet("NOEXISTVAR") + if err == nil { + t.Error("expected error to be non-nil") + } +} + +func TestEnvSet(t *testing.T) { + Set("MYVAR", "foo") + myVar := Get("MYVAR", "bar") + if myVar != "foo" { + t.Errorf("expected MYVAR to equal foo, got %s.", myVar) + } +} + +func TestEnvMustSet(t *testing.T) { + err := MustSet("FOO", "bar") + if err != nil { + t.Error(err) + } + + fooVar := os.Getenv("FOO") + if fooVar != "bar" { + t.Errorf("expected FOO variable to equal bar, got %s.", fooVar) + } +} + +func TestEnvGetAll(t *testing.T) { + envMap := GetAll() + if len(envMap) == 0 { + t.Error("expected environment not empty.") + } +} From e32d173b0d43c3987d0f2906467f999be5ba4d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E6=9C=A8?= Date: Sat, 14 Jan 2017 15:03:49 +0800 Subject: [PATCH 024/163] fix the bug to prevent rewrite t the t have paresed in line 212. --- template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.go b/template.go index 5415f5f0..343ef223 100644 --- a/template.go +++ b/template.go @@ -224,7 +224,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp if !HasTemplateExt(m[1]) { continue } - t, _, err = getTplDeep(root, m[1], file, t) + _, _, err = getTplDeep(root, m[1], file, t) if err != nil { return nil, [][]string{}, err } From 957c0630c0296e7628fdc08f7da720e597b1aa09 Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sat, 14 Jan 2017 10:15:02 +0100 Subject: [PATCH 025/163] moved the env package to config/ --- {env => config/env}/env.go | 0 {env => config/env}/env_test.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {env => config/env}/env.go (100%) rename {env => config/env}/env_test.go (100%) diff --git a/env/env.go b/config/env/env.go similarity index 100% rename from env/env.go rename to config/env/env.go diff --git a/env/env_test.go b/config/env/env_test.go similarity index 100% rename from env/env_test.go rename to config/env/env_test.go From 126dbdae2f8a4daa2bd5ddb37a07e4d6bd71281f Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Mon, 16 Jan 2017 10:08:32 +0100 Subject: [PATCH 026/163] use BeeMap instead of a regular map --- config/env/env.go | 52 ++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/config/env/env.go b/config/env/env.go index 014501f5..a819e51a 100644 --- a/config/env/env.go +++ b/config/env/env.go @@ -18,31 +18,25 @@ import ( "fmt" "os" "strings" - "sync" + + "github.com/astaxie/beego/utils" ) -var env struct { - data map[string]string - lock *sync.RWMutex -} +var env *utils.BeeMap func init() { - env.data = make(map[string]string) - env.lock = &sync.RWMutex{} + env = utils.NewBeeMap() for _, e := range os.Environ() { splits := strings.Split(e, "=") - env.data[splits[0]] = os.Getenv(splits[0]) + env.Set(splits[0], os.Getenv(splits[0])) } } // Get returns a value by key. // If the key does not exist, the default value will be returned. func Get(key string, defVal string) string { - env.lock.RLock() - defer env.lock.RUnlock() - - if val, ok := env.data[key]; ok { - return val + if val := env.Get(key); val != nil { + return val.(string) } return defVal } @@ -50,11 +44,8 @@ func Get(key string, defVal string) string { // MustGet returns a value by key. // If the key does not exist, it will return an error. func MustGet(key string) (string, error) { - env.lock.RLock() - defer env.lock.RUnlock() - - if val, ok := env.data[key]; ok { - return val, nil + if val := env.Get(key); val != nil { + return val.(string), nil } return "", fmt.Errorf("no env variable with %s", key) } @@ -62,28 +53,33 @@ func MustGet(key string) (string, error) { // Set sets a value in the ENV copy. // This does not affect the child process environment. func Set(key string, value string) { - env.lock.Lock() - defer env.lock.Unlock() - env.data[key] = value + env.Set(key, value) } // MustSet sets a value in the ENV copy and the child process environment. // It returns an error in case the set operation failed. func MustSet(key string, value string) error { - env.lock.Lock() - defer env.lock.Unlock() - err := os.Setenv(key, value) if err != nil { return err } - env.data[key] = value + env.Set(key, value) return nil } // GetAll returns all keys/values in the current child process environment. func GetAll() map[string]string { - env.lock.RLock() - defer env.lock.RUnlock() - return env.data + items := env.Items() + envs := make(map[string]string, env.Count()) + + for key, val := range items { + switch key := key.(type) { + case string: + switch val := val.(type) { + case string: + envs[key] = val + } + } + } + return envs } From 5c76f621034043bbff4b8160bbed37f1c9aa4200 Mon Sep 17 00:00:00 2001 From: kerwin Date: Wed, 18 Jan 2017 17:04:23 +0800 Subject: [PATCH 027/163] Add GetCond func to querySet --- orm/orm_queryset.go | 5 +++++ orm/types.go | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index 575f62ae..4e33646d 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -153,6 +153,11 @@ func (o querySet) SetCond(cond *Condition) QuerySeter { return &o } +// get condition from QuerySeter +func (o querySet) GetCond() *Condition { + return o.cond +} + // return QuerySeter execution result number func (o *querySet) Count() (int64, error) { return o.orm.alias.DbBaser.Count(o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ) diff --git a/orm/types.go b/orm/types.go index fd3062ab..3e6a9e87 100644 --- a/orm/types.go +++ b/orm/types.go @@ -145,6 +145,16 @@ type QuerySeter interface { // //sql-> WHERE T0.`profile_id` IS NOT NULL AND NOT T0.`Status` IN (?) OR T1.`age` > 2000 // num, err := qs.SetCond(cond1).Count() SetCond(*Condition) QuerySeter + // get condition from QuerySeter. + // sql's where condition + // cond := orm.NewCondition() + // cond = cond.And("profile__isnull", false).AndNot("status__in", 1) + // qs = qs.SetCond(cond) + // cond = qs.GetCond() + // cond := cond.Or("profile__age__gt", 2000) + // //sql-> WHERE T0.`profile_id` IS NOT NULL AND NOT T0.`Status` IN (?) OR T1.`age` > 2000 + // num, err := qs.SetCond(cond).Count() + GetCond() *Condition // add LIMIT value. // args[0] means offset, e.g. LIMIT num,offset. // if Limit <= 0 then Limit will be set to default limit ,eg 1000 From 9b714a75187e4ef066db2a6cf79c284723606c26 Mon Sep 17 00:00:00 2001 From: xhzhang Date: Mon, 6 Feb 2017 12:33:15 +0800 Subject: [PATCH 028/163] Add config field EnableErrorsRender Add config field EnableErrorsRender to disable errors output with the template data, sometimes we do not want errors output with it even in dev mode, especially in API projects. --- config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config.go b/config.go index 36bf445c..8d4b3c09 100644 --- a/config.go +++ b/config.go @@ -41,6 +41,7 @@ type Config struct { EnableGzip bool MaxMemory int64 EnableErrorsShow bool + EnableErrorsRender bool Listen Listen WebConfig WebConfig Log LogConfig @@ -174,7 +175,7 @@ func recoverPanic(ctx *context.Context) { logs.Critical(fmt.Sprintf("%s:%d", file, line)) stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line)) } - if BConfig.RunMode == DEV { + if BConfig.RunMode == DEV && BConfig.EnableErrorsRender { showErr(err, ctx, stack) } } @@ -192,6 +193,7 @@ func newBConfig() *Config { EnableGzip: false, MaxMemory: 1 << 26, //64MB EnableErrorsShow: true, + EnableErrorsRender: true, Listen: Listen{ Graceful: false, ServerTimeOut: 0, From dfa74faf431c627c44b943356a96bf00e43a4d6e Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Tue, 7 Feb 2017 18:28:03 +0200 Subject: [PATCH 029/163] support for multiple view paths --- controller.go | 16 +++++++++---- controller_test.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++ hooks.go | 2 +- template.go | 42 ++++++++++++++++++++++----------- template_test.go | 10 +++++++- 5 files changed, 109 insertions(+), 19 deletions(-) diff --git a/controller.go b/controller.go index a6fbdd23..488ffcda 100644 --- a/controller.go +++ b/controller.go @@ -69,6 +69,7 @@ type Controller struct { // template data TplName string + ViewPath string Layout string LayoutSections map[string]string // the key is the section name and the value is the template name TplPrefix string @@ -213,7 +214,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { continue } buf.Reset() - err = ExecuteTemplate(&buf, sectionTpl, c.Data) + err = ExecuteViewPathTemplate(&buf, sectionTpl, c.viewPath(), c.Data) if err != nil { return nil, err } @@ -222,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { } buf.Reset() - ExecuteTemplate(&buf, c.Layout, c.Data) + ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath() ,c.Data) } return buf.Bytes(), err } @@ -248,9 +249,16 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) { } } } - BuildTemplate(BConfig.WebConfig.ViewsPath, buildFiles...) + BuildTemplate(c.viewPath() , buildFiles...) } - return buf, ExecuteTemplate(&buf, c.TplName, c.Data) + return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data) +} + +func (c *Controller) viewPath() string { + if c.ViewPath == "" { + return BConfig.WebConfig.ViewsPath + } + return c.ViewPath } // Redirect sends the redirection response to url with status code. diff --git a/controller_test.go b/controller_test.go index 132971a1..c2025860 100644 --- a/controller_test.go +++ b/controller_test.go @@ -20,6 +20,8 @@ import ( "testing" "github.com/astaxie/beego/context" + "os" + "path/filepath" ) func TestGetInt(t *testing.T) { @@ -121,3 +123,59 @@ func TestGetUint64(t *testing.T) { t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val) } } + +func TestAdditionalViewPaths(t *testing.T) { + dir1 := "_beeTmp" + dir2 := "_beeTmp2" + defer os.RemoveAll(dir1) + defer os.RemoveAll(dir2) + + dir1file := "file1.tpl" + dir2file := "file2.tpl" + + genFile := func(dir string, name string, content string) { + os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777) + if f, err := os.Create(filepath.Join(dir, name)); err != nil { + t.Fatal(err) + } else { + defer f.Close() + f.WriteString(content) + f.Close() + } + + } + genFile(dir1, dir1file, `
{{.Content}}
`) + genFile(dir2, dir2file, `{{.Content}}`) + + AddViewPath(dir1) + AddViewPath(dir2) + + ctrl := Controller{ + TplName: "file1.tpl", + ViewPath: dir1, + } + ctrl.Data = map[interface{}]interface{}{ + "Content": "value2", + } + if result, err := ctrl.RenderString(); err != nil { + t.Fatal(err) + } else { + if result != "
value2
" { + t.Fatalf("TestAdditionalViewPaths expect %s got %s", "
value2
", result) + } + } + + func() { + ctrl.TplName = "file2.tpl" + defer func() { + if r := recover(); r == nil { + t.Fatal("TestAdditionalViewPaths expected error") + } + }() + ctrl.RenderString(); + }() + + ctrl.TplName = "file2.tpl" + ctrl.ViewPath = dir2 + ctrl.RenderString(); +} diff --git a/hooks.go b/hooks.go index b5a5e6c5..528b58cc 100644 --- a/hooks.go +++ b/hooks.go @@ -72,7 +72,7 @@ func registerSession() error { } func registerTemplate() error { - if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil { + if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil { if BConfig.RunMode == DEV { logs.Warn(err) } diff --git a/template.go b/template.go index 5415f5f0..d4d0ed12 100644 --- a/template.go +++ b/template.go @@ -32,8 +32,8 @@ import ( var ( beegoTplFuncMap = make(template.FuncMap) - // beeTemplates caching map and supported template file extensions. - beeTemplates = make(map[string]*template.Template) + // beeViewPathTemplates caching map and supported template file extensions. + beeViewPathTemplates = make(map[string]map[string]*template.Template) templatesLock sync.RWMutex // beeTemplateExt stores the template extension which will build beeTemplateExt = []string{"tpl", "html"} @@ -45,23 +45,30 @@ var ( // writing the output to wr. // A template will be executed safely in parallel. func ExecuteTemplate(wr io.Writer, name string, data interface{}) error { + return ExecuteViewPathTemplate(wr,name, BConfig.WebConfig.ViewsPath, data) +} + +func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data interface{}) error { if BConfig.RunMode == DEV { templatesLock.RLock() defer templatesLock.RUnlock() } - if t, ok := beeTemplates[name]; ok { - var err error - if t.Lookup(name) != nil { - err = t.ExecuteTemplate(wr, name, data) - } else { - err = t.Execute(wr, data) + if beeTemplates,ok := beeViewPathTemplates[viewPath]; ok { + if t, ok := beeTemplates[name]; ok { + var err error + if t.Lookup(name) != nil { + err = t.ExecuteTemplate(wr, name, data) + } else { + err = t.Execute(wr, data) + } + if err != nil { + logs.Trace("template Execute err:", err) + } + return err } - if err != nil { - logs.Trace("template Execute err:", err) - } - return err + panic("can't find templatefile in the path:" + viewPath + "/" + name) } - panic("can't find templatefile in the path:" + name) + panic("Uknown view path:" + viewPath) } func init() { @@ -149,6 +156,11 @@ func AddTemplateExt(ext string) { beeTemplateExt = append(beeTemplateExt, ext) } +func AddViewPath(viewPath string) error { + beeViewPathTemplates[viewPath] = make(map[string]*template.Template) + return BuildTemplate(viewPath) +} + // BuildTemplate will build all template files in a directory. // it makes beego can render any template file in view directory. func BuildTemplate(dir string, files ...string) error { @@ -158,6 +170,10 @@ func BuildTemplate(dir string, files ...string) error { } return errors.New("dir open err") } + beeTemplates,ok := beeViewPathTemplates[dir]; + if !ok { + panic("Unknown view path: " + dir) + } self := &templateFile{ root: dir, files: make(map[string][]string), diff --git a/template_test.go b/template_test.go index 4f13736c..17690965 100644 --- a/template_test.go +++ b/template_test.go @@ -67,9 +67,10 @@ func TestTemplate(t *testing.T) { f.Close() } } - if err := BuildTemplate(dir); err != nil { + if err := AddViewPath(dir); err != nil { t.Fatal(err) } + beeTemplates := beeViewPathTemplates[dir] if len(beeTemplates) != 3 { t.Fatalf("should be 3 but got %v", len(beeTemplates)) } @@ -103,6 +104,12 @@ var user = ` func TestRelativeTemplate(t *testing.T) { dir := "_beeTmp" + + //Just add dir to known viewPaths + if err := AddViewPath(dir); err != nil { + t.Fatal(err) + } + files := []string{ "easyui/public/menu.tpl", "easyui/rbac/user.tpl", @@ -126,6 +133,7 @@ func TestRelativeTemplate(t *testing.T) { if err := BuildTemplate(dir, files[1]); err != nil { t.Fatal(err) } + beeTemplates := beeViewPathTemplates[dir] if err := beeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil { t.Fatal(err) } From dade92d98b1164682931a8eddcbddd24c6b49357 Mon Sep 17 00:00:00 2001 From: liming <327135508@qq.com> Date: Thu, 9 Feb 2017 14:16:23 +0800 Subject: [PATCH 030/163] close mysql connection --- session/mysql/sess_mysql.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 838ec669..7683ee1f 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -143,6 +143,7 @@ func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error { // SessionRead get mysql session by sid func (mp *Provider) SessionRead(sid string) (session.Store, error) { c := mp.connectInit() + defer c.Close() row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) @@ -179,6 +180,7 @@ func (mp *Provider) SessionExist(sid string) bool { // SessionRegenerate generate new sid for mysql session func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { c := mp.connectInit() + defer c.Close() row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid) var sessiondata []byte err := row.Scan(&sessiondata) From 872a964145e3fec20d723437941f857cc3733c0e Mon Sep 17 00:00:00 2001 From: liming <327135508@qq.com> Date: Thu, 9 Feb 2017 13:47:24 +0800 Subject: [PATCH 031/163] 1.add "defer f.close()" in SessionRead to fix file handle leak if DecodeGob failed 2.rewrite SessionRegenerate --- session/sess_file.go | 85 +++++++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/session/sess_file.go b/session/sess_file.go index 132f5a00..50687c9e 100644 --- a/session/sess_file.go +++ b/session/sess_file.go @@ -15,8 +15,7 @@ package session import ( - "errors" - "io" + "fmt" "io/ioutil" "net/http" "os" @@ -135,6 +134,9 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { } else { return nil, err } + + defer f.Close() + os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now()) var kv map[interface{}]interface{} b, err := ioutil.ReadAll(f) @@ -149,7 +151,7 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { return nil, err } } - f.Close() + ss := &FileSessionStore{sid: sid, values: kv} return ss, nil } @@ -204,49 +206,58 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) { filepder.lock.Lock() defer filepder.lock.Unlock() - err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777) - if err != nil { - SLogger.Println(err.Error()) - } - err = os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777) - if err != nil { - SLogger.Println(err.Error()) - } - _, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - var newf *os.File + oldPath := path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])) + oldSidFile := path.Join(oldPath, oldsid) + newPath := path.Join(fp.savePath, string(sid[0]), string(sid[1])) + newSidFile := path.Join(newPath, sid) + + // new sid file is exist + _, err := os.Stat(newSidFile) if err == nil { - return nil, errors.New("newsid exist") - } else if os.IsNotExist(err) { - newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) + return nil, fmt.Errorf("newsid %s exist", newSidFile) } - _, err = os.Stat(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid)) - var f *os.File - if err == nil { - f, err = os.OpenFile(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid), os.O_RDWR, 0777) - io.Copy(newf, f) - } else if os.IsNotExist(err) { - newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - } else { - return nil, err - } - f.Close() - os.Remove(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]))) - os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now()) - var kv map[interface{}]interface{} - b, err := ioutil.ReadAll(newf) + err = os.MkdirAll(newPath, 0777) if err != nil { - return nil, err + SLogger.Println(err.Error()) } - if len(b) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = DecodeGob(b) + + // if old sid file exist + // 1.read and parse file content + // 2.write content to new sid file + // 3.remove old sid file, change new sid file atime and ctime + // 4.return FileSessionStore + _, err = os.Stat(oldSidFile) + if err == nil { + b, err := ioutil.ReadFile(oldSidFile) if err != nil { return nil, err } + + var kv map[interface{}]interface{} + if len(b) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = DecodeGob(b) + if err != nil { + return nil, err + } + } + + ioutil.WriteFile(newSidFile, b, 0777) + os.Remove(oldSidFile) + os.Chtimes(newSidFile, time.Now(), time.Now()) + ss := &FileSessionStore{sid: sid, values: kv} + return ss, nil } - ss := &FileSessionStore{sid: sid, values: kv} + + // if old sid file not exist, just create new sid file and return + newf, err := os.Create(newSidFile) + if err != nil { + return nil, err + } + newf.Close() + ss := &FileSessionStore{sid: sid, values: make(map[interface{}]interface{})} return ss, nil } From db67ffbb94af848025403b4ca10b476e792cfb86 Mon Sep 17 00:00:00 2001 From: liming <327135508@qq.com> Date: Fri, 10 Feb 2017 09:35:23 +0800 Subject: [PATCH 032/163] 1.simplify reading and writing file code 2.add apiauth test --- cache/file.go | 25 ++-------------- plugins/apiauth/apiauth.go | 52 ++++++++++----------------------- plugins/apiauth/apiauth_test.go | 20 +++++++++++++ 3 files changed, 39 insertions(+), 58 deletions(-) create mode 100644 plugins/apiauth/apiauth_test.go diff --git a/cache/file.go b/cache/file.go index 4b030980..691ce7cd 100644 --- a/cache/file.go +++ b/cache/file.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "os" "path/filepath" "reflect" @@ -222,33 +223,13 @@ func exists(path string) (bool, error) { // FileGetContents Get bytes to file. // if non-exist, create this file. func FileGetContents(filename string) (data []byte, e error) { - f, e := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm) - if e != nil { - return - } - defer f.Close() - stat, e := f.Stat() - if e != nil { - return - } - data = make([]byte, stat.Size()) - result, e := f.Read(data) - if e != nil || int64(result) != stat.Size() { - return nil, e - } - return + return ioutil.ReadFile(filename) } // FilePutContents Put bytes to file. // if non-exist, create this file. func FilePutContents(filename string, content []byte) error { - fp, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm) - if err != nil { - return err - } - defer fp.Close() - _, err = fp.Write(content) - return err + return ioutil.WriteFile(filename, content, os.ModePerm) } // GobEncode Gob encodes file cache item. diff --git a/plugins/apiauth/apiauth.go b/plugins/apiauth/apiauth.go index 10636d1c..f816029c 100644 --- a/plugins/apiauth/apiauth.go +++ b/plugins/apiauth/apiauth.go @@ -56,6 +56,7 @@ package apiauth import ( + "bytes" "crypto/hmac" "crypto/sha256" "encoding/base64" @@ -128,53 +129,32 @@ func APISecretAuth(f AppIDToAppSecret, timeout int) beego.FilterFunc { // Signature used to generate signature with the appsecret/method/params/RequestURI func Signature(appsecret, method string, params url.Values, RequestURL string) (result string) { - var query string + var b bytes.Buffer + keys := make([]string, len(params)) pa := make(map[string]string) for k, v := range params { pa[k] = v[0] + keys = append(keys, k) } - vs := mapSorter(pa) - vs.Sort() - for i := 0; i < vs.Len(); i++ { - if vs.Keys[i] == "signature" { + + sort.Strings(keys) + + for _, key := range keys { + if key == "signature" { continue } - if vs.Keys[i] != "" && vs.Vals[i] != "" { - query = fmt.Sprintf("%v%v%v", query, vs.Keys[i], vs.Vals[i]) + + val := pa[key] + if key != "" && val != "" { + b.WriteString(key) + b.WriteString(val) } } - stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, query, RequestURL) + + stringToSign := fmt.Sprintf("%v\n%v\n%v\n", method, b.String(), RequestURL) sha256 := sha256.New hash := hmac.New(sha256, []byte(appsecret)) hash.Write([]byte(stringToSign)) return base64.StdEncoding.EncodeToString(hash.Sum(nil)) } - -type valSorter struct { - Keys []string - Vals []string -} - -func mapSorter(m map[string]string) *valSorter { - vs := &valSorter{ - Keys: make([]string, 0, len(m)), - Vals: make([]string, 0, len(m)), - } - for k, v := range m { - vs.Keys = append(vs.Keys, k) - vs.Vals = append(vs.Vals, v) - } - return vs -} - -func (vs *valSorter) Sort() { - sort.Sort(vs) -} - -func (vs *valSorter) Len() int { return len(vs.Keys) } -func (vs *valSorter) Less(i, j int) bool { return vs.Keys[i] < vs.Keys[j] } -func (vs *valSorter) Swap(i, j int) { - vs.Vals[i], vs.Vals[j] = vs.Vals[j], vs.Vals[i] - vs.Keys[i], vs.Keys[j] = vs.Keys[j], vs.Keys[i] -} diff --git a/plugins/apiauth/apiauth_test.go b/plugins/apiauth/apiauth_test.go new file mode 100644 index 00000000..1f56cb0f --- /dev/null +++ b/plugins/apiauth/apiauth_test.go @@ -0,0 +1,20 @@ +package apiauth + +import ( + "net/url" + "testing" +) + +func TestSignature(t *testing.T) { + appsecret := "beego secret" + method := "GET" + RequestURL := "http://localhost/test/url" + params := make(url.Values) + params.Add("arg1", "hello") + params.Add("arg2", "beego") + + signature := "mFdpvLh48ca4mDVEItE9++AKKQ/IVca7O/ZyyB8hR58=" + if Signature(appsecret, method, params, RequestURL) != signature { + t.Error("Signature error") + } +} From 8a2b6976251fba8a319cf421d54e456f1469ac5e Mon Sep 17 00:00:00 2001 From: awengo Date: Fri, 10 Feb 2017 17:45:47 +0900 Subject: [PATCH 033/163] Add http methods --- router.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/router.go b/router.go index 74cf02a1..9f573f26 100644 --- a/router.go +++ b/router.go @@ -51,15 +51,22 @@ const ( var ( // HTTPMETHOD list the supported http methods. HTTPMETHOD = map[string]string{ - "GET": "GET", - "POST": "POST", - "PUT": "PUT", - "DELETE": "DELETE", - "PATCH": "PATCH", - "OPTIONS": "OPTIONS", - "HEAD": "HEAD", - "TRACE": "TRACE", - "CONNECT": "CONNECT", + "GET": "GET", + "POST": "POST", + "PUT": "PUT", + "DELETE": "DELETE", + "PATCH": "PATCH", + "OPTIONS": "OPTIONS", + "HEAD": "HEAD", + "TRACE": "TRACE", + "CONNECT": "CONNECT", + "MKCOL": "MKCOL", + "COPY": "COPY", + "MOVE": "MOVE", + "PROPFIND": "PROPFIND", + "PROPPATCH": "PROPPATCH", + "LOCK": "LOCK", + "UNLOCK": "UNLOCK", } // these beego.Controller's methods shouldn't reflect to AutoRouter exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString", From fc2c0f4fbac53d3df06c706c7d79c77fc61e0ee1 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Sat, 11 Feb 2017 22:00:30 +0200 Subject: [PATCH 034/163] Don't allow AddViewPath after beego run --- hooks.go | 1 + template.go | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/hooks.go b/hooks.go index 528b58cc..091ecbc7 100644 --- a/hooks.go +++ b/hooks.go @@ -72,6 +72,7 @@ func registerSession() error { } func registerTemplate() error { + defer lockViewPaths() if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil { if BConfig.RunMode == DEV { logs.Warn(err) diff --git a/template.go b/template.go index d4d0ed12..2dba9d1f 100644 --- a/template.go +++ b/template.go @@ -32,7 +32,8 @@ import ( var ( beegoTplFuncMap = make(template.FuncMap) - // beeViewPathTemplates caching map and supported template file extensions. + beeViewPathTemplateLocked = false + // beeViewPathTemplates caching map and supported template file extensions per view beeViewPathTemplates = make(map[string]map[string]*template.Template) templatesLock sync.RWMutex // beeTemplateExt stores the template extension which will build @@ -48,6 +49,9 @@ func ExecuteTemplate(wr io.Writer, name string, data interface{}) error { return ExecuteViewPathTemplate(wr,name, BConfig.WebConfig.ViewsPath, data) } +// ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object, +// writing the output to wr. +// A template will be executed safely in parallel. func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data interface{}) error { if BConfig.RunMode == DEV { templatesLock.RLock() @@ -156,11 +160,21 @@ func AddTemplateExt(ext string) { beeTemplateExt = append(beeTemplateExt, ext) } +// AddViewPath adds a new path to the supported view paths. +//Can later be used by setting a controller ViewPath to this folder +//will panic if called after beego.Run() func AddViewPath(viewPath string) error { + if beeViewPathTemplateLocked { + panic("Can not add new view paths after beego.Run()") + } beeViewPathTemplates[viewPath] = make(map[string]*template.Template) return BuildTemplate(viewPath) } +func lockViewPaths() { + beeViewPathTemplateLocked = true +} + // BuildTemplate will build all template files in a directory. // it makes beego can render any template file in view directory. func BuildTemplate(dir string, files ...string) error { From 393e4c4969f62c71ce4d65a68fca96ea60e84b0e Mon Sep 17 00:00:00 2001 From: jiayukun Date: Wed, 22 Feb 2017 17:38:26 +0800 Subject: [PATCH 035/163] Improve json coding performance --- context/output.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/context/output.go b/context/output.go index 4b513dd8..564ef96d 100644 --- a/context/output.go +++ b/context/output.go @@ -331,16 +331,17 @@ func (output *BeegoOutput) IsServerError() bool { func stringsToJSON(str string) string { rs := []rune(str) - jsons := "" + var jsons bytes.Buffer for _, r := range rs { rint := int(r) if rint < 128 { - jsons += string(r) + jsons.WriteRune(r) } else { - jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json + jsons.WriteString("\\u") + jsons.WriteString(strconv.FormatInt(int64(rint), 16)) } } - return jsons + return jsons.String() } // Session sets session item value with given key. From 28011a5835229ad64d90a9d8f0805571cbf07d40 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 26 Feb 2017 22:20:11 +0800 Subject: [PATCH 036/163] fix #2462 --- config.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index 52e647db..42b7c70c 100644 --- a/config.go +++ b/config.go @@ -254,6 +254,9 @@ func parseConfig(appConfigPath string) (err error) { } func assignConfig(ac config.Configer) error { + for _, i := range []interface{}{&BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { + assignSingleConfig(i, ac) + } // set the run mode first if envRunMode := os.Getenv("BEEGO_RUNMODE"); envRunMode != "" { BConfig.RunMode = envRunMode @@ -261,10 +264,6 @@ func assignConfig(ac config.Configer) error { BConfig.RunMode = runMode } - for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { - assignSingleConfig(i, ac) - } - if sd := ac.String("StaticDir"); sd != "" { BConfig.WebConfig.StaticDir = map[string]string{} sds := strings.Fields(sd) From 50e294be32da818b9fea72c42a668033f396674d Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 27 Feb 2017 09:41:15 +0800 Subject: [PATCH 037/163] fix the broken test --- config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.go b/config.go index 51ee08a1..3c202e53 100644 --- a/config.go +++ b/config.go @@ -256,7 +256,7 @@ func parseConfig(appConfigPath string) (err error) { } func assignConfig(ac config.Configer) error { - for _, i := range []interface{}{&BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { + for _, i := range []interface{}{BConfig, &BConfig.Listen, &BConfig.WebConfig, &BConfig.Log, &BConfig.WebConfig.Session} { assignSingleConfig(i, ac) } // set the run mode first From 49fffe3ebe6f54ce848c9ebf502f33f104b34cd8 Mon Sep 17 00:00:00 2001 From: Guohua Ouyang Date: Mon, 27 Feb 2017 14:43:16 +0800 Subject: [PATCH 038/163] Parse form time by its length Fix #2451 Signed-off-by: Guohua Ouyang --- templatefunc.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/templatefunc.go b/templatefunc.go index 01751717..8ecacbfe 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -26,6 +26,12 @@ import ( "time" ) +const ( + formatTime = "15:04:05" + formatDate = "2006-01-02" + formatDateTime = "2006-01-02 15:04:05" +) + // Substr returns the substr from start to length. func Substr(s string, start, length int) string { bt := []rune(s) @@ -352,11 +358,25 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e case reflect.Struct: switch fieldT.Type.String() { case "time.Time": - format := time.RFC3339 - if len(tags) > 1 { - format = tags[1] + var ( + t time.Time + err error + ) + if len(value) >= 19 { + value = value[:19] + t, err = time.ParseInLocation(formatDateTime, value, time.Local) + } else if len(value) >= 10 { + if len(value) > 10 { + value = value[:10] + } + t, err = time.ParseInLocation(formatDate, value, time.Local) + } else if len(value) >= 8 { + if len(value) > 8 { + value = value[:8] + } + t, err = time.ParseInLocation(formatTime, value, time.Local) } - t, err := time.ParseInLocation(format, value, time.Local) + if err != nil { return err } From 74045090cc834b4b13587a210ed92d7a188a2c1c Mon Sep 17 00:00:00 2001 From: Anton Khalikov Date: Mon, 27 Feb 2017 14:14:16 +0500 Subject: [PATCH 039/163] This fixes issue #2467 with ssdb cache IsExist --- cache/ssdb/ssdb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/ssdb/ssdb.go b/cache/ssdb/ssdb.go index bfee69ce..bbc43606 100644 --- a/cache/ssdb/ssdb.go +++ b/cache/ssdb/ssdb.go @@ -152,7 +152,7 @@ func (rc *Cache) IsExist(key string) bool { if err != nil { return false } - if resp[1] == "1" { + if len(resp) == 2 && resp[1] == "1" { return true } return false From e0250e287137fd061217c107b061077c98b25dd5 Mon Sep 17 00:00:00 2001 From: Mariano Fevola Date: Fri, 3 Mar 2017 16:24:02 +0000 Subject: [PATCH 040/163] Fix typo --- config/ini.go | 2 +- config/yaml/yaml.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/ini.go b/config/ini.go index 6b78f02a..416d48f0 100644 --- a/config/ini.go +++ b/config/ini.go @@ -300,7 +300,7 @@ func (c *IniConfigContainer) GetSection(section string) (map[string]string, erro if v, ok := c.data[section]; ok { return v, nil } - return nil, errors.New("not exist setction") + return nil, errors.New("not exist section") } // SaveConfigFile save the config into file. diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index bcef4a20..51fe44d3 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -252,7 +252,7 @@ func (c *ConfigContainer) GetSection(section string) (map[string]string, error) if v, ok := c.data[section]; ok { return v.(map[string]string), nil } - return nil, errors.New("not exist setction") + return nil, errors.New("not exist section") } // SaveConfigFile save the config into file From 6d997366ed4d300bebfe851dde62598d592b5d7e Mon Sep 17 00:00:00 2001 From: ysqi Date: Sat, 4 Mar 2017 20:23:55 +0800 Subject: [PATCH 041/163] Fixed 2456 and strengthen bind --- context/input.go | 11 +++- context/input_test.go | 138 +++++++++++++++++++++++------------------- 2 files changed, 87 insertions(+), 62 deletions(-) diff --git a/context/input.go b/context/input.go index 1e6eaf71..d9015ce3 100644 --- a/context/input.go +++ b/context/input.go @@ -413,7 +413,13 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error { if !value.CanSet() { return errors.New("beego: non-settable variable passed to Bind: " + key) } - rv := input.bind(key, value.Type()) + typ := value.Type() + // Get real type if dest define with interface{}. + // e.g var dest interface{} dest=1.0 + if value.Kind() == reflect.Interface { + typ = value.Elem().Type() + } + rv := input.bind(key, typ) if !rv.IsValid() { return errors.New("beego: reflect value is empty") } @@ -422,6 +428,9 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error { } func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { + if input.Context.Request.Form == nil { + input.Context.Request.ParseForm() + } rv := reflect.Zero(typ) switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: diff --git a/context/input_test.go b/context/input_test.go index e64addba..9853e398 100644 --- a/context/input_test.go +++ b/context/input_test.go @@ -15,81 +15,97 @@ package context import ( - "fmt" "net/http" "net/http/httptest" "reflect" "testing" ) -func TestParse(t *testing.T) { - r, _ := http.NewRequest("GET", "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie", nil) - beegoInput := NewInput() - beegoInput.Context = NewContext() - beegoInput.Context.Reset(httptest.NewRecorder(), r) - beegoInput.ParseFormOrMulitForm(1 << 20) +func TestBind(t *testing.T) { + type testItem struct { + field string + empty interface{} + want interface{} + } + type Human struct { + ID int + Nick string + Pwd string + Ms bool + } - var id int - err := beegoInput.Bind(&id, "id") - if id != 123 || err != nil { - t.Fatal("id should has int value") - } - fmt.Println(id) + cases := []struct { + request string + valueGp []testItem + }{ + {"/?p=str", []testItem{{"p", interface{}(""), interface{}("str")}}}, - var isok bool - err = beegoInput.Bind(&isok, "isok") - if !isok || err != nil { - t.Fatal("isok should be true") - } - fmt.Println(isok) + {"/?p=", []testItem{{"p", "", ""}}}, + {"/?p=str", []testItem{{"p", "", "str"}}}, - var float float64 - err = beegoInput.Bind(&float, "ft") - if float != 1.2 || err != nil { - t.Fatal("float should be equal to 1.2") - } - fmt.Println(float) + {"/?p=123", []testItem{{"p", 0, 123}}}, + {"/?p=123", []testItem{{"p", uint(0), uint(123)}}}, - ol := make([]int, 0, 2) - err = beegoInput.Bind(&ol, "ol") - if len(ol) != 2 || err != nil || ol[0] != 1 || ol[1] != 2 { - t.Fatal("ol should has two elements") - } - fmt.Println(ol) + {"/?p=1.0", []testItem{{"p", 0.0, 1.0}}}, + {"/?p=1", []testItem{{"p", false, true}}}, - ul := make([]string, 0, 2) - err = beegoInput.Bind(&ul, "ul") - if len(ul) != 2 || err != nil || ul[0] != "str" || ul[1] != "array" { - t.Fatal("ul should has two elements") - } - fmt.Println(ul) + {"/?p=true", []testItem{{"p", false, true}}}, + {"/?p=ON", []testItem{{"p", false, true}}}, + {"/?p=on", []testItem{{"p", false, true}}}, + {"/?p=1", []testItem{{"p", false, true}}}, + {"/?p=2", []testItem{{"p", false, false}}}, + {"/?p=false", []testItem{{"p", false, false}}}, - type User struct { - Name string - } - user := User{} - err = beegoInput.Bind(&user, "user") - if err != nil || user.Name != "astaxie" { - t.Fatal("user should has name") - } - fmt.Println(user) -} + {"/?p[a]=1&p[b]=2&p[c]=3", []testItem{{"p", map[string]int{}, map[string]int{"a": 1, "b": 2, "c": 3}}}}, + {"/?p[a]=v1&p[b]=v2&p[c]=v3", []testItem{{"p", map[string]string{}, map[string]string{"a": "v1", "b": "v2", "c": "v3"}}}}, -func TestParse2(t *testing.T) { - r, _ := http.NewRequest("GET", "/?user[0][Username]=Raph&user[1].Username=Leo&user[0].Password=123456&user[1][Password]=654321", nil) - beegoInput := NewInput() - beegoInput.Context = NewContext() - beegoInput.Context.Reset(httptest.NewRecorder(), r) - beegoInput.ParseFormOrMulitForm(1 << 20) - type User struct { - Username string - Password string + {"/?p[]=8&p[]=9&p[]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}}, + {"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []int{}, []int{8, 9, 10}}}}, + {"/?p[0]=8&p[1]=9&p[2]=10&p[5]=14", []testItem{{"p", []int{}, []int{8, 9, 10, 0, 0, 14}}}}, + {"/?p[0]=8.0&p[1]=9.0&p[2]=10.0", []testItem{{"p", []float64{}, []float64{8.0, 9.0, 10.0}}}}, + + {"/?p[]=10&p[]=9&p[]=8", []testItem{{"p", []string{}, []string{"10", "9", "8"}}}}, + {"/?p[0]=8&p[1]=9&p[2]=10", []testItem{{"p", []string{}, []string{"8", "9", "10"}}}}, + + {"/?p[0]=true&p[1]=false&p[2]=true&p[5]=1&p[6]=ON&p[7]=other", []testItem{{"p", []bool{}, []bool{true, false, true, false, false, true, true, false}}}}, + + {"/?human.Nick=astaxie", []testItem{{"human", Human{}, Human{Nick: "astaxie"}}}}, + {"/?human.ID=888&human.Nick=astaxie&human.Ms=true&human[Pwd]=pass", []testItem{{"human", Human{}, Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass"}}}}, + {"/?human[0].ID=888&human[0].Nick=astaxie&human[0].Ms=true&human[0][Pwd]=pass01&human[1].ID=999&human[1].Nick=ysqi&human[1].Ms=On&human[1].Pwd=pass02", + []testItem{{"human", []Human{}, []Human{ + Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"}, + Human{ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"}, + }}}}, + + { + "/?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&human.Nick=astaxie", + []testItem{ + {"id", 0, 123}, + {"isok", false, true}, + {"ft", 0.0, 1.2}, + {"ol", []int{}, []int{1, 2}}, + {"ul", []string{}, []string{"str", "array"}}, + {"human", Human{}, Human{Nick: "astaxie"}}, + }, + }, } - var users []User - err := beegoInput.Bind(&users, "user") - fmt.Println(users) - if err != nil || users[0].Username != "Raph" || users[0].Password != "123456" || users[1].Username != "Leo" || users[1].Password != "654321" { - t.Fatal("users info wrong") + for _, c := range cases { + r, _ := http.NewRequest("GET", c.request, nil) + beegoInput := NewInput() + beegoInput.Context = NewContext() + beegoInput.Context.Reset(httptest.NewRecorder(), r) + + for _, item := range c.valueGp { + got := item.empty + err := beegoInput.Bind(&got, item.field) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(got, item.want) { + t.Fatalf("Bind %q error,should be:\n%#v \ngot:\n%#v", item.field, item.want, got) + } + } + } } From c5f838e7856f07c96173717e4e04126ff0dc7978 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 6 Mar 2017 21:29:41 +0800 Subject: [PATCH 042/163] beego 1.8.0 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 1bc8bb85..c06b499c 100644 --- a/beego.go +++ b/beego.go @@ -23,7 +23,7 @@ import ( const ( // VERSION represent beego web framework version. - VERSION = "1.7.2" + VERSION = "1.8.0" // DEV is for develop DEV = "dev" From 8e46decc8ea690797be773b9713a1f024707d305 Mon Sep 17 00:00:00 2001 From: chendingxing Date: Fri, 10 Mar 2017 09:28:25 +0800 Subject: [PATCH 043/163] fix routing bug for splat --- tree.go | 25 +++++++++++++++---------- tree_test.go | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/tree.go b/tree.go index 25b78e50..2d6c3fc3 100644 --- a/tree.go +++ b/tree.go @@ -288,10 +288,10 @@ func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{ return nil } w := make([]string, 0, 20) - return t.match(pattern, w, ctx) + return t.match(pattern[1:], pattern, w, ctx) } -func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { +func (t *Tree) match(treePattern string, pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { if len(pattern) > 0 { i := 0 for ; i < len(pattern) && pattern[i] == '/'; i++ { @@ -301,13 +301,13 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte // Handle leaf nodes: if len(pattern) == 0 { for _, l := range t.leaves { - if ok := l.match(wildcardValues, ctx); ok { + if ok := l.match(treePattern, wildcardValues, ctx); ok { return l.runObject } } if t.wildcard != nil { for _, l := range t.wildcard.leaves { - if ok := l.match(wildcardValues, ctx); ok { + if ok := l.match(treePattern, wildcardValues, ctx); ok { return l.runObject } } @@ -327,7 +327,12 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte } for _, subTree := range t.fixrouters { if subTree.prefix == seg { - runObject = subTree.match(pattern, wildcardValues, ctx) + if len(pattern) != 0 && pattern[0] == '/' { + treePattern = pattern[1:] + } else { + treePattern = pattern + } + runObject = subTree.match(treePattern, pattern, wildcardValues, ctx) if runObject != nil { break } @@ -339,7 +344,7 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte if strings.HasSuffix(seg, str) { for _, subTree := range t.fixrouters { if subTree.prefix == seg[:len(seg)-len(str)] { - runObject = subTree.match(pattern, wildcardValues, ctx) + runObject = subTree.match(treePattern, pattern, wildcardValues, ctx) if runObject != nil { ctx.Input.SetParam(":ext", str[1:]) } @@ -349,7 +354,7 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte } } if runObject == nil && t.wildcard != nil { - runObject = t.wildcard.match(pattern, append(wildcardValues, seg), ctx) + runObject = t.wildcard.match(treePattern, pattern, append(wildcardValues, seg), ctx) } if runObject == nil && len(t.leaves) > 0 { @@ -368,7 +373,7 @@ func (t *Tree) match(pattern string, wildcardValues []string, ctx *context.Conte wildcardValues = append(wildcardValues, pattern[start:i]) } for _, l := range t.leaves { - if ok := l.match(wildcardValues, ctx); ok { + if ok := l.match(treePattern, wildcardValues, ctx); ok { return l.runObject } } @@ -386,7 +391,7 @@ type leafInfo struct { runObject interface{} } -func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok bool) { +func (leaf *leafInfo) match(treePattern string, wildcardValues []string, ctx *context.Context) (ok bool) { //fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps) if leaf.regexps == nil { if len(wildcardValues) == 0 && len(leaf.wildcards) == 0 { // static path @@ -394,7 +399,7 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b } // match * if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { - ctx.Input.SetParam(":splat", path.Join(wildcardValues...)) + ctx.Input.SetParam(":splat", treePattern) return true } // match *.* or :id diff --git a/tree_test.go b/tree_test.go index 81ff7edd..d412a348 100644 --- a/tree_test.go +++ b/tree_test.go @@ -42,7 +42,7 @@ func init() { routers = append(routers, testinfo{"/", "/", nil}) routers = append(routers, testinfo{"/customer/login", "/customer/login", nil}) routers = append(routers, testinfo{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}}) - routers = append(routers, testinfo{"/*", "/customer/123", map[string]string{":splat": "customer/123"}}) + routers = append(routers, testinfo{"/*", "/http://customer/123/", map[string]string{":splat": "http://customer/123/"}}) routers = append(routers, testinfo{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}}) routers = append(routers, testinfo{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}}) routers = append(routers, testinfo{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}}) From d41f4c0a3aa9aa6b5c621f2c72532389293ba454 Mon Sep 17 00:00:00 2001 From: miraclesu Date: Sun, 12 Mar 2017 19:23:46 +0800 Subject: [PATCH 044/163] validation: fix email valid --- validation/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validators.go b/validation/validators.go index 9b04c5ce..01aed443 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -518,7 +518,7 @@ func (a AlphaDash) GetLimitValue() interface{} { return nil } -var emailPattern = regexp.MustCompile("[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?") +var emailPattern = regexp.MustCompile("^[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?$") // Email check struct type Email struct { From b34853f8cc29c6734f543669e4914ef12f098c70 Mon Sep 17 00:00:00 2001 From: miraclesu Date: Sun, 12 Mar 2017 19:30:23 +0800 Subject: [PATCH 045/163] validattion: add test case for email valid --- validation/validation_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation/validation_test.go b/validation/validation_test.go index ec65b6d0..2b287a1e 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -214,6 +214,12 @@ func TestEmail(t *testing.T) { if !valid.Email("suchuangji@gmail.com", "email").Ok { t.Error("\"suchuangji@gmail.com\" is a valid email address should be true") } + if valid.Email("@suchuangji@gmail.com", "email").Ok { + t.Error("\"@suchuangji@gmail.com\" is a valid email address should be false") + } + if valid.Email("@suchuangji@gmail.com", "email").Ok { + t.Error("\"suchuangji@gmail.com ok\" is a valid email address should be false") + } } func TestIP(t *testing.T) { From c9cc642d37d07f19e1a0f30b175551b3743b08b8 Mon Sep 17 00:00:00 2001 From: miraclesu Date: Sun, 12 Mar 2017 20:27:09 +0800 Subject: [PATCH 046/163] validation: amend email test case --- validation/validation_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validation_test.go b/validation/validation_test.go index 2b287a1e..83e881bf 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -217,7 +217,7 @@ func TestEmail(t *testing.T) { if valid.Email("@suchuangji@gmail.com", "email").Ok { t.Error("\"@suchuangji@gmail.com\" is a valid email address should be false") } - if valid.Email("@suchuangji@gmail.com", "email").Ok { + if valid.Email("suchuangji@gmail.com ok", "email").Ok { t.Error("\"suchuangji@gmail.com ok\" is a valid email address should be false") } } From 24d4a278427159811fa83ff9fa6de504f01c86a6 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Mon, 13 Mar 2017 08:46:57 +0200 Subject: [PATCH 047/163] Don't panic during AddViewPath if adding the same path twice --- template.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/template.go b/template.go index 2dba9d1f..0524f3cb 100644 --- a/template.go +++ b/template.go @@ -72,7 +72,7 @@ func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data in } panic("can't find templatefile in the path:" + viewPath + "/" + name) } - panic("Uknown view path:" + viewPath) + panic("Unknown view path:" + viewPath) } func init() { @@ -165,6 +165,9 @@ func AddTemplateExt(ext string) { //will panic if called after beego.Run() func AddViewPath(viewPath string) error { if beeViewPathTemplateLocked { + if _, exist := beeViewPathTemplates[viewPath]; exist { + return nil //Ignore if viewpath already exists + } panic("Can not add new view paths after beego.Run()") } beeViewPathTemplates[viewPath] = make(map[string]*template.Template) From 3e37b97549f2293ede78d12850f38a62b6a6537e Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Tue, 14 Mar 2017 12:55:40 +0200 Subject: [PATCH 048/163] temple parse error write in log as trace. change to error --- template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.go b/template.go index 89d38b80..4e0d6384 100644 --- a/template.go +++ b/template.go @@ -217,7 +217,7 @@ func BuildTemplate(dir string, files ...string) error { t, err = getTemplate(self.root, file, v...) } if err != nil { - logs.Trace("parse template err:", file, err) + logs.Error("parse template err:", file, err) } else { beeTemplates[file] = t } From f5fc2edfd36e8f1aa9a88d1b728a170032343fd3 Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Fri, 17 Mar 2017 19:32:31 +0200 Subject: [PATCH 049/163] add support go1.7/8 to travis yml --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index df3e923f..4c068baf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ go: - 1.6 - 1.5.3 - 1.4.3 + - 1.7 + - 1.8 services: - redis-server - mysql From 3204d7631bfce56ad3e3a28c927ab3481f71b699 Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Fri, 17 Mar 2017 19:33:54 +0200 Subject: [PATCH 050/163] drop support go1.4/5 in travis yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index df3e923f..37cbe0c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,8 +2,6 @@ language: go go: - 1.6 - - 1.5.3 - - 1.4.3 services: - redis-server - mysql From 856fde28dc37705a4a97593abbb1f12eb37621de Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Fri, 17 Mar 2017 19:45:30 +0200 Subject: [PATCH 051/163] add unconverted support --- .travis.yml | 2 ++ cache/ssdb/ssdb.go | 4 ++-- config.go | 2 +- logs/alils/log.pb.go | 2 +- logs/smtp.go | 2 +- orm/utils.go | 8 ++++---- session/ledis/ledis_session.go | 2 +- templatefunc.go | 30 ++++++++++++------------------ utils/captcha/image.go | 2 +- 9 files changed, 25 insertions(+), 29 deletions(-) diff --git a/.travis.yml b/.travis.yml index df3e923f..fe47739a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ install: - go get github.com/ssdb/gossdb/ssdb - go get github.com/cloudflare/golz4 - go get github.com/gogo/protobuf/proto + - go get -u github.com/mdempsky/unconvert before_script: - psql --version - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" @@ -47,5 +48,6 @@ after_script: - rm -rf ./res/var/* script: - go test -v ./... + - unconvert $(go list ./... | grep -v /vendor/) addons: postgresql: "9.4" diff --git a/cache/ssdb/ssdb.go b/cache/ssdb/ssdb.go index bbc43606..479d4d9a 100644 --- a/cache/ssdb/ssdb.go +++ b/cache/ssdb/ssdb.go @@ -53,7 +53,7 @@ func (rc *Cache) GetMulti(keys []string) []interface{} { resSize := len(res) if err == nil { for i := 1; i < resSize; i += 2 { - values = append(values, string(res[i+1])) + values = append(values, res[i+1]) } return values } @@ -175,7 +175,7 @@ func (rc *Cache) ClearAll() error { } keys := []string{} for i := 1; i < size; i += 2 { - keys = append(keys, string(resp[i])) + keys = append(keys, resp[i]) } _, e := rc.conn.Do("multi_del", keys) if e != nil { diff --git a/config.go b/config.go index 3c202e53..e6e99570 100644 --- a/config.go +++ b/config.go @@ -345,7 +345,7 @@ func assignSingleConfig(p interface{}, ac config.Configer) { case reflect.String: pf.SetString(ac.DefaultString(name, pf.String())) case reflect.Int, reflect.Int64: - pf.SetInt(int64(ac.DefaultInt64(name, pf.Int()))) + pf.SetInt(ac.DefaultInt64(name, pf.Int())) case reflect.Bool: pf.SetBool(ac.DefaultBool(name, pf.Bool())) case reflect.Struct: diff --git a/logs/alils/log.pb.go b/logs/alils/log.pb.go index 42f7e892..b60ca200 100755 --- a/logs/alils/log.pb.go +++ b/logs/alils/log.pb.go @@ -395,7 +395,7 @@ func sovLog(x uint64) (n int) { return n } func sozLog(x uint64) (n int) { - return sovLog(uint64((x << 1) ^ uint64((int64(x) >> 63)))) + return sovLog((x << 1) ^ (x >> 63)) } func (m *Log) Unmarshal(data []byte) error { var hasFields [1]uint64 diff --git a/logs/smtp.go b/logs/smtp.go index 834130ef..a07947e6 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -106,7 +106,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd if err != nil { return err } - _, err = w.Write([]byte(msgContent)) + _, err = w.Write(msgContent) if err != nil { return err } diff --git a/orm/utils.go b/orm/utils.go index 6aac8e5d..3cfc1e7d 100644 --- a/orm/utils.go +++ b/orm/utils.go @@ -92,11 +92,11 @@ func (f StrTo) Int64() (int64, error) { i := new(big.Int) ni, ok := i.SetString(f.String(), 10) // octal if !ok { - return int64(v), err + return v, err } return ni.Int64(), nil } - return int64(v), err + return v, err } // Uint string to uint @@ -130,11 +130,11 @@ func (f StrTo) Uint64() (uint64, error) { i := new(big.Int) ni, ok := i.SetString(f.String(), 10) if !ok { - return uint64(v), err + return v, err } return ni.Uint64(), nil } - return uint64(v), err + return v, err } // String string to string diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go index 68f37b08..1366104e 100644 --- a/session/ledis/ledis_session.go +++ b/session/ledis/ledis_session.go @@ -150,7 +150,7 @@ func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob([]byte(kvs)) + kv, err = session.DecodeGob(kvs) if err != nil { return nil, err } diff --git a/templatefunc.go b/templatefunc.go index 01751717..b54c0b42 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -46,26 +46,25 @@ func Substr(s string, start, length int) string { // HTML2str returns escaping text convert from html. func HTML2str(html string) string { - src := string(html) re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllStringFunc(src, strings.ToLower) + html = re.ReplaceAllStringFunc(html, strings.ToLower) //remove STYLE re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") + html = re.ReplaceAllString(html, "") //remove SCRIPT re, _ = regexp.Compile("\\") - src = re.ReplaceAllString(src, "") + html = re.ReplaceAllString(html, "") re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") - src = re.ReplaceAllString(src, "\n") + html = re.ReplaceAllString(html, "\n") re, _ = regexp.Compile("\\s{2,}") - src = re.ReplaceAllString(src, "\n") + html = re.ReplaceAllString(html, "\n") - return strings.TrimSpace(src) + return strings.TrimSpace(html) } // DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat" @@ -193,7 +192,7 @@ func Str2html(raw string) template.HTML { } // Htmlquote returns quoted html string. -func Htmlquote(src string) string { +func Htmlquote(text string) string { //HTML编码为实体符号 /* Encodes `text` for raw use in HTML. @@ -201,8 +200,6 @@ func Htmlquote(src string) string { '<'&">' */ - text := string(src) - text = strings.Replace(text, "&", "&", -1) // Must be done first! text = strings.Replace(text, "<", "<", -1) text = strings.Replace(text, ">", ">", -1) @@ -216,7 +213,7 @@ func Htmlquote(src string) string { } // Htmlunquote returns unquoted html string. -func Htmlunquote(src string) string { +func Htmlunquote(text string) string { //实体符号解释为HTML /* Decodes `text` that's HTML quoted. @@ -227,7 +224,6 @@ func Htmlunquote(src string) string { // strings.Replace(s, old, new, n) // 在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换 - text := string(src) text = strings.Replace(text, " ", " ", -1) text = strings.Replace(text, "”", "”", -1) text = strings.Replace(text, "“", "“", -1) @@ -262,19 +258,17 @@ func URLFor(endpoint string, values ...interface{}) string { } // AssetsJs returns script tag with src string. -func AssetsJs(src string) template.HTML { - text := string(src) +func AssetsJs(text string) template.HTML { - text = "" + text = "" return template.HTML(text) } // AssetsCSS returns stylesheet link tag with src string. -func AssetsCSS(src string) template.HTML { - text := string(src) +func AssetsCSS(text string) template.HTML { - text = "" + text = "" return template.HTML(text) } diff --git a/utils/captcha/image.go b/utils/captcha/image.go index 0ceb8e42..c3c9a83a 100644 --- a/utils/captcha/image.go +++ b/utils/captcha/image.go @@ -474,7 +474,7 @@ func randomBrightness(c color.RGBA, max uint8) color.RGBA { uint8(int(c.R) + n), uint8(int(c.G) + n), uint8(int(c.B) + n), - uint8(c.A), + c.A, } } From 37c1ffc57a0d46ba32718712bdd27b89c6391e92 Mon Sep 17 00:00:00 2001 From: Sergey Lanzman Date: Fri, 17 Mar 2017 19:24:45 +0200 Subject: [PATCH 052/163] add go simple support --- .gosimpleignore | 4 +++ .travis.yml | 2 ++ admin.go | 22 ++++++++-------- cache/conv_test.go | 6 ++--- cache/memcache/memcache.go | 5 +--- cache/redis/redis.go | 2 +- cache/ssdb/ssdb.go | 15 +++-------- config/ini.go | 7 ++--- config/ini_test.go | 2 +- controller.go | 6 ++--- flash_test.go | 2 +- grace/listener.go | 2 +- httplib/httplib.go | 2 +- logs/file.go | 14 +++++----- logs/jianliao.go | 6 +---- logs/log.go | 8 ++---- logs/slack.go | 6 +---- logs/smtp.go | 13 ++-------- namespace_test.go | 5 +--- orm/cmd.go | 4 +-- orm/cmd_utils.go | 8 +++--- orm/db.go | 28 ++++++++++---------- orm/db_alias.go | 4 +-- orm/db_tables.go | 12 ++++----- orm/models_boot.go | 14 +++++----- orm/models_info_f.go | 6 ++--- orm/models_info_m.go | 2 +- orm/orm.go | 26 +++++-------------- orm/orm_querym2m.go | 10 +++----- orm/orm_test.go | 4 +-- orm/utils.go | 2 +- router_test.go | 40 ++++++++++++++--------------- session/ledis/ledis_session.go | 5 +--- session/memcache/sess_memcache.go | 6 +---- session/mysql/sess_mysql.go | 5 +--- session/postgres/sess_postgresql.go | 6 +---- session/sess_file.go | 5 +--- session/sess_test.go | 2 +- session/ssdb/sess_ssdb.go | 16 +++--------- staticfile.go | 4 +-- template.go | 20 +++++++-------- templatefunc_test.go | 18 ++++++------- validation/validation.go | 2 +- 43 files changed, 150 insertions(+), 228 deletions(-) create mode 100644 .gosimpleignore diff --git a/.gosimpleignore b/.gosimpleignore new file mode 100644 index 00000000..84df9b95 --- /dev/null +++ b/.gosimpleignore @@ -0,0 +1,4 @@ +github.com/astaxie/beego/*/*:S1012 +github.com/astaxie/beego/*:S1012 +github.com/astaxie/beego/*/*:S1007 +github.com/astaxie/beego/*:S1007 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index df3e923f..abee35f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ install: - go get github.com/ssdb/gossdb/ssdb - go get github.com/cloudflare/golz4 - go get github.com/gogo/protobuf/proto + - go get -u honnef.co/go/tools/cmd/gosimple before_script: - psql --version - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" @@ -47,5 +48,6 @@ after_script: - rm -rf ./res/var/* script: - go test -v ./... + - gosimple -ignore "$(cat .gosimpleignore)" $(go list ./... | grep -v /vendor/) addons: postgresql: "9.4" diff --git a/admin.go b/admin.go index a2b2f53a..b4e3068f 100644 --- a/admin.go +++ b/admin.go @@ -157,8 +157,8 @@ func listConf(rw http.ResponseWriter, r *http.Request) { resultList := new([][]string) for _, f := range bf { var result = []string{ - fmt.Sprintf("%s", f.pattern), - fmt.Sprintf("%s", utils.GetFuncName(f.filterFunc)), + f.pattern, + utils.GetFuncName(f.filterFunc), } *resultList = append(*resultList, result) } @@ -213,7 +213,7 @@ func printTree(resultList *[][]string, t *Tree) { var result = []string{ v.pattern, fmt.Sprintf("%s", v.methods), - fmt.Sprintf("%s", v.controllerType), + v.controllerType.String(), } *resultList = append(*resultList, result) } else if v.routerType == routerTypeRESTFul { @@ -287,16 +287,16 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) { for name, h := range toolbox.AdminCheckList { if err := h.Check(); err != nil { result = []string{ - fmt.Sprintf("error"), - fmt.Sprintf("%s", name), - fmt.Sprintf("%s", err.Error()), + "error", + name, + err.Error(), } } else { result = []string{ - fmt.Sprintf("success"), - fmt.Sprintf("%s", name), - fmt.Sprintf("OK"), + "success", + name, + "OK", } } @@ -341,8 +341,8 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { for tname, tk := range toolbox.AdminTaskList { result = []string{ tname, - fmt.Sprintf("%s", tk.GetSpec()), - fmt.Sprintf("%s", tk.GetStatus()), + tk.GetSpec(), + tk.GetStatus(), tk.GetPrev().String(), } *resultList = append(*resultList, result) diff --git a/cache/conv_test.go b/cache/conv_test.go index cf792fa6..b90e224a 100644 --- a/cache/conv_test.go +++ b/cache/conv_test.go @@ -118,14 +118,14 @@ func TestGetFloat64(t *testing.T) { func TestGetBool(t *testing.T) { var t1 = true - if true != GetBool(t1) { + if !GetBool(t1) { t.Error("get bool from bool error") } var t2 = "true" - if true != GetBool(t2) { + if !GetBool(t2) { t.Error("get bool from string error") } - if false != GetBool(nil) { + if GetBool(nil) { t.Error("get bool from nil error") } } diff --git a/cache/memcache/memcache.go b/cache/memcache/memcache.go index 972361f7..0624f5fa 100644 --- a/cache/memcache/memcache.go +++ b/cache/memcache/memcache.go @@ -146,10 +146,7 @@ func (rc *Cache) IsExist(key string) bool { } } _, err := rc.conn.Get(key) - if err != nil { - return false - } - return true + return !(err != nil) } // ClearAll clear all cached in memcache. diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 781e3836..3e71fb53 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -137,7 +137,7 @@ func (rc *Cache) IsExist(key string) bool { if err != nil { return false } - if v == false { + if !v { if _, err = rc.do("HDEL", rc.key, key); err != nil { return false } diff --git a/cache/ssdb/ssdb.go b/cache/ssdb/ssdb.go index bbc43606..0b59503c 100644 --- a/cache/ssdb/ssdb.go +++ b/cache/ssdb/ssdb.go @@ -71,10 +71,7 @@ func (rc *Cache) DelMulti(keys []string) error { } } _, err := rc.conn.Do("multi_del", keys) - if err != nil { - return err - } - return nil + return err } // Put put value to memcache. only support string. @@ -113,10 +110,7 @@ func (rc *Cache) Delete(key string) error { } } _, err := rc.conn.Del(key) - if err != nil { - return err - } - return nil + return err } // Incr increase counter. @@ -229,10 +223,7 @@ func (rc *Cache) connectInit() error { } var err error rc.conn, err = ssdb.Connect(host, port) - if err != nil { - return err - } - return nil + return err } func init() { diff --git a/config/ini.go b/config/ini.go index 27220f90..42d218d6 100644 --- a/config/ini.go +++ b/config/ini.go @@ -397,11 +397,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { } } } - - if _, err = buf.WriteTo(f); err != nil { - return err - } - return nil + _, err = buf.WriteTo(f) + return err } // Set writes a new value for key. diff --git a/config/ini_test.go b/config/ini_test.go index 83ff3668..ffcdb294 100644 --- a/config/ini_test.go +++ b/config/ini_test.go @@ -181,7 +181,7 @@ name=mysql cfgData := string(data) datas := strings.Split(saveResult, "\n") for _, line := range datas { - if strings.Contains(cfgData, line+"\n") == false { + if !strings.Contains(cfgData, line+"\n") { t.Fatalf("different after save ini config file. need contains %q", line) } } diff --git a/controller.go b/controller.go index 488ffcda..c2a327b3 100644 --- a/controller.go +++ b/controller.go @@ -223,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { } buf.Reset() - ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath() ,c.Data) + ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data) } return buf.Bytes(), err } @@ -249,7 +249,7 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) { } } } - BuildTemplate(c.viewPath() , buildFiles...) + BuildTemplate(c.viewPath(), buildFiles...) } return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data) } @@ -314,7 +314,7 @@ func (c *Controller) ServeJSON(encoding ...bool) { if BConfig.RunMode == PROD { hasIndent = false } - if len(encoding) > 0 && encoding[0] == true { + if len(encoding) > 0 && encoding[0] { hasEncoding = true } c.Ctx.Output.JSON(c.Data["json"], hasIndent, hasEncoding) diff --git a/flash_test.go b/flash_test.go index 640d54de..d5e9608d 100644 --- a/flash_test.go +++ b/flash_test.go @@ -48,7 +48,7 @@ func TestFlashHeader(t *testing.T) { // match for the expected header res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00") // validate the assertion - if res != true { + if !res { t.Errorf("TestFlashHeader() unable to validate flash message") } } diff --git a/grace/listener.go b/grace/listener.go index 5439d0b2..823d3cce 100644 --- a/grace/listener.go +++ b/grace/listener.go @@ -21,7 +21,7 @@ func newGraceListener(l net.Listener, srv *Server) (el *graceListener) { server: srv, } go func() { - _ = <-el.stop + <-el.stop el.stopped = true el.stop <- el.Listener.Close() }() diff --git a/httplib/httplib.go b/httplib/httplib.go index 39480469..32edc5c4 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -335,7 +335,7 @@ func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) func (b *BeegoHTTPRequest) buildURL(paramBody string) { // build GET url with query string if b.req.Method == "GET" && len(paramBody) > 0 { - if strings.Index(b.url, "?") != -1 { + if strings.Contains(b.url, "?") { b.url += "&" + paramBody } else { b.url = b.url + "?" + paramBody diff --git a/logs/file.go b/logs/file.go index bd3c22a9..d00cbfc6 100644 --- a/logs/file.go +++ b/logs/file.go @@ -193,16 +193,14 @@ func (w *fileLogWriter) dailyRotate(openTime time.Time) { y, m, d := openTime.Add(24 * time.Hour).Date() nextDay := time.Date(y, m, d, 0, 0, 0, 0, openTime.Location()) tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100)) - select { - case <-tm.C: - w.Lock() - if w.needRotate(0, time.Now().Day()) { - if err := w.doRotate(time.Now()); err != nil { - fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) - } + <-tm.C + w.Lock() + if w.needRotate(0, time.Now().Day()) { + if err := w.doRotate(time.Now()); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) } - w.Unlock() } + w.Unlock() } func (w *fileLogWriter) lines() (int, error) { diff --git a/logs/jianliao.go b/logs/jianliao.go index 16773c93..16bbdc2e 100644 --- a/logs/jianliao.go +++ b/logs/jianliao.go @@ -25,11 +25,7 @@ func newJLWriter() Logger { // Init JLWriter with json config string func (s *JLWriter) Init(jsonconfig string) error { - err := json.Unmarshal([]byte(jsonconfig), s) - if err != nil { - return err - } - return nil + return json.Unmarshal([]byte(jsonconfig), s) } // WriteMsg write message in smtp writer. diff --git a/logs/log.go b/logs/log.go index c351c473..a5e0a9ea 100644 --- a/logs/log.go +++ b/logs/log.go @@ -275,7 +275,7 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string, v ...interface{}) error line = 0 } _, filename := path.Split(file) - msg = "[" + filename + ":" + strconv.FormatInt(int64(line), 10) + "] " + msg + msg = "[" + filename + ":" + strconv.Itoa(line) + "] " + msg } //set level info in front of filename info @@ -561,11 +561,7 @@ func SetLogFuncCallDepth(d int) { // SetLogger sets a new logger. func SetLogger(adapter string, config ...string) error { - err := beeLogger.SetLogger(adapter, config...) - if err != nil { - return err - } - return nil + return beeLogger.SetLogger(adapter, config...) } // Emergency logs a message at emergency level. diff --git a/logs/slack.go b/logs/slack.go index 90f009cb..d8482b3e 100644 --- a/logs/slack.go +++ b/logs/slack.go @@ -21,11 +21,7 @@ func newSLACKWriter() Logger { // Init SLACKWriter with json config string func (s *SLACKWriter) Init(jsonconfig string) error { - err := json.Unmarshal([]byte(jsonconfig), s) - if err != nil { - return err - } - return nil + return json.Unmarshal([]byte(jsonconfig), s) } // WriteMsg write message in smtp writer. diff --git a/logs/smtp.go b/logs/smtp.go index 834130ef..bf603f2b 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -52,11 +52,7 @@ func newSMTPWriter() Logger { // "level":LevelError // } func (s *SMTPWriter) Init(jsonconfig string) error { - err := json.Unmarshal([]byte(jsonconfig), s) - if err != nil { - return err - } - return nil + return json.Unmarshal([]byte(jsonconfig), s) } func (s *SMTPWriter) getSMTPAuth(host string) smtp.Auth { @@ -116,12 +112,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd return err } - err = client.Quit() - if err != nil { - return err - } - - return nil + return client.Quit() } // WriteMsg write message in smtp writer. diff --git a/namespace_test.go b/namespace_test.go index fc02b5fb..b3f20dff 100644 --- a/namespace_test.go +++ b/namespace_test.go @@ -139,10 +139,7 @@ func TestNamespaceCond(t *testing.T) { ns := NewNamespace("/v2") ns.Cond(func(ctx *context.Context) bool { - if ctx.Input.Domain() == "beego.me" { - return true - } - return false + return ctx.Input.Domain() == "beego.me" }). AutoRouter(&TestController{}) AddNamespace(ns) diff --git a/orm/cmd.go b/orm/cmd.go index 3638a75c..0ff4dc40 100644 --- a/orm/cmd.go +++ b/orm/cmd.go @@ -150,7 +150,7 @@ func (d *commandSyncDb) Run() error { } for _, fi := range mi.fields.fieldsDB { - if _, ok := columns[fi.column]; ok == false { + if _, ok := columns[fi.column]; !ok { fields = append(fields, fi) } } @@ -175,7 +175,7 @@ func (d *commandSyncDb) Run() error { } for _, idx := range indexes[mi.table] { - if d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) == false { + if !d.al.DbBaser.IndexExists(db, idx.Table, idx.Name) { if !d.noInfo { fmt.Printf("create index `%s` for table `%s`\n", idx.Name, idx.Table) } diff --git a/orm/cmd_utils.go b/orm/cmd_utils.go index 8119b70b..de47cb02 100644 --- a/orm/cmd_utils.go +++ b/orm/cmd_utils.go @@ -89,7 +89,7 @@ checkColumn: col = T["float64"] case TypeDecimalField: s := T["float64-decimal"] - if strings.Index(s, "%d") == -1 { + if !strings.Contains(s, "%d") { col = s } else { col = fmt.Sprintf(s, fi.digits, fi.decimals) @@ -120,7 +120,7 @@ func getColumnAddQuery(al *alias, fi *fieldInfo) string { Q := al.DbBaser.TableQuote() typ := getColumnTyp(al, fi) - if fi.null == false { + if !fi.null { typ += " " + "NOT NULL" } @@ -172,7 +172,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex } else { column += col - if fi.null == false { + if !fi.null { column += " " + "NOT NULL" } @@ -192,7 +192,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex } } - if strings.Index(column, "%COL%") != -1 { + if strings.Contains(column, "%COL%") { column = strings.Replace(column, "%COL%", fi.column, -1) } diff --git a/orm/db.go b/orm/db.go index bca6071d..1544c479 100644 --- a/orm/db.go +++ b/orm/db.go @@ -48,7 +48,7 @@ var ( "lte": true, "eq": true, "nq": true, - "ne": true, + "ne": true, "startswith": true, "endswith": true, "istartswith": true, @@ -87,7 +87,7 @@ func (d *dbBase) collectValues(mi *modelInfo, ind reflect.Value, cols []string, } else { panic(fmt.Errorf("wrong db field/column name `%s` for model `%s`", column, mi.fullName)) } - if fi.dbcol == false || fi.auto && skipAuto { + if !fi.dbcol || fi.auto && skipAuto { continue } value, err := d.collectFieldValue(mi, fi, ind, insert, tz) @@ -224,7 +224,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val value = nil } } - if fi.null == false && value == nil { + if !fi.null && value == nil { return nil, fmt.Errorf("field `%s` cannot be NULL", fi.fullName) } } @@ -271,7 +271,7 @@ func (d *dbBase) PrepareInsert(q dbQuerier, mi *modelInfo) (stmtQuerier, string, dbcols := make([]string, 0, len(mi.fields.dbcols)) marks := make([]string, 0, len(mi.fields.dbcols)) for _, fi := range mi.fields.fieldsDB { - if fi.auto == false { + if !fi.auto { dbcols = append(dbcols, fi.column) marks = append(marks, "?") } @@ -326,7 +326,7 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo } else { // default use pk value as where condtion. pkColumn, pkValue, ok := getExistPk(mi, ind) - if ok == false { + if !ok { return ErrMissPK } whereCols = []string{pkColumn} @@ -601,7 +601,7 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a // execute update sql dbQuerier with given struct reflect.Value. func (d *dbBase) Update(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) { pkName, pkValue, ok := getExistPk(mi, ind) - if ok == false { + if !ok { return 0, ErrMissPK } @@ -654,7 +654,7 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time. } else { // default use pk value as where condtion. pkColumn, pkValue, ok := getExistPk(mi, ind) - if ok == false { + if !ok { return 0, ErrMissPK } whereCols = []string{pkColumn} @@ -699,7 +699,7 @@ func (d *dbBase) UpdateBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con columns := make([]string, 0, len(params)) values := make([]interface{}, 0, len(params)) for col, val := range params { - if fi, ok := mi.fields.GetByAny(col); ok == false || fi.dbcol == false { + if fi, ok := mi.fields.GetByAny(col); !ok || !fi.dbcol { panic(fmt.Errorf("wrong field/column name `%s`", col)) } else { columns = append(columns, fi.column) @@ -929,7 +929,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi if hasRel { for _, fi := range mi.fields.fieldsDB { if fi.fieldType&IsRelField > 0 { - if maps[fi.column] == false { + if !maps[fi.column] { tCols = append(tCols, fi.column) } } @@ -987,7 +987,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi var cnt int64 for rs.Next() { - if one && cnt == 0 || one == false { + if one && cnt == 0 || !one { if err := rs.Scan(refs...); err != nil { return 0, err } @@ -1067,7 +1067,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi cnt++ } - if one == false { + if !one { if cnt > 0 { ind.Set(slice) } else { @@ -1357,7 +1357,7 @@ end: func (d *dbBase) setFieldValue(fi *fieldInfo, value interface{}, field reflect.Value) (interface{}, error) { fieldType := fi.fieldType - isNative := fi.isFielder == false + isNative := !fi.isFielder setValue: switch { @@ -1533,7 +1533,7 @@ setValue: } } - if isNative == false { + if !isNative { fd := field.Addr().Interface().(Fielder) err := fd.SetRaw(value) if err != nil { @@ -1594,7 +1594,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond infos = make([]*fieldInfo, 0, len(exprs)) for _, ex := range exprs { index, name, fi, suc := tables.parseExprs(mi, strings.Split(ex, ExprSep)) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", ex)) } cols = append(cols, fmt.Sprintf("%s.%s%s%s %s%s%s", index, Q, fi.column, Q, Q, name, Q)) diff --git a/orm/db_alias.go b/orm/db_alias.go index c95d49c9..8daa4b23 100644 --- a/orm/db_alias.go +++ b/orm/db_alias.go @@ -186,7 +186,7 @@ func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) { return nil, fmt.Errorf("register db Ping `%s`, %s", aliasName, err.Error()) } - if dataBaseCache.add(aliasName, al) == false { + if !dataBaseCache.add(aliasName, al) { return nil, fmt.Errorf("DataBase alias name `%s` already registered, cannot reuse", aliasName) } @@ -244,7 +244,7 @@ end: // RegisterDriver Register a database driver use specify driver name, this can be definition the driver is which database type. func RegisterDriver(driverName string, typ DriverType) error { - if t, ok := drivers[driverName]; ok == false { + if t, ok := drivers[driverName]; !ok { drivers[driverName] = typ } else { if t != typ { diff --git a/orm/db_tables.go b/orm/db_tables.go index e4c74ace..42be5550 100644 --- a/orm/db_tables.go +++ b/orm/db_tables.go @@ -63,7 +63,7 @@ func (t *dbTables) set(names []string, mi *modelInfo, fi *fieldInfo, inner bool) // add table info to collection. func (t *dbTables) add(names []string, mi *modelInfo, fi *fieldInfo, inner bool) (*dbTable, bool) { name := strings.Join(names, ExprSep) - if _, ok := t.tablesM[name]; ok == false { + if _, ok := t.tablesM[name]; !ok { i := len(t.tables) + 1 jt := &dbTable{i, fmt.Sprintf("T%d", i), name, names, false, inner, mi, fi, nil} t.tablesM[name] = jt @@ -261,7 +261,7 @@ loopFor: fiN, okN = mmi.fields.GetByAny(exprs[i+1]) } - if isRel && (fi.mi.isThrough == false || num != i) { + if isRel && (!fi.mi.isThrough || num != i) { if fi.null || t.skipEnd { inner = false } @@ -364,7 +364,7 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe } index, _, fi, suc := t.parseExprs(mi, exprs) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(p.exprs, ExprSep))) } @@ -383,7 +383,7 @@ func (t *dbTables) getCondSQL(cond *Condition, sub bool, tz *time.Location) (whe } } - if sub == false && where != "" { + if !sub && where != "" { where = "WHERE " + where } @@ -403,7 +403,7 @@ func (t *dbTables) getGroupSQL(groups []string) (groupSQL string) { exprs := strings.Split(group, ExprSep) index, _, fi, suc := t.parseExprs(t.mi, exprs) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) } @@ -432,7 +432,7 @@ func (t *dbTables) getOrderSQL(orders []string) (orderSQL string) { exprs := strings.Split(order, ExprSep) index, _, fi, suc := t.parseExprs(t.mi, exprs) - if suc == false { + if !suc { panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) } diff --git a/orm/models_boot.go b/orm/models_boot.go index 4ba5affd..85d0917f 100644 --- a/orm/models_boot.go +++ b/orm/models_boot.go @@ -128,7 +128,7 @@ func bootStrap() { if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) { pn := fi.relThrough[:i] rmi, ok := modelCache.getByFullName(fi.relThrough) - if ok == false || pn != rmi.pkg { + if !ok || pn != rmi.pkg { err = fmt.Errorf("field `%s` wrong rel_through value `%s` cannot find table", fi.fullName, fi.relThrough) goto end } @@ -171,7 +171,7 @@ func bootStrap() { break } } - if inModel == false { + if !inModel { rmi := fi.relModelInfo ffi := new(fieldInfo) ffi.name = mi.name @@ -185,7 +185,7 @@ func bootStrap() { } else { ffi.fieldType = RelReverseMany } - if rmi.fields.Add(ffi) == false { + if !rmi.fields.Add(ffi) { added := false for cnt := 0; cnt < 5; cnt++ { ffi.name = fmt.Sprintf("%s%d", mi.name, cnt) @@ -195,7 +195,7 @@ func bootStrap() { break } } - if added == false { + if !added { panic(fmt.Errorf("cannot generate auto reverse field info `%s` to `%s`", fi.fullName, ffi.fullName)) } } @@ -248,7 +248,7 @@ func bootStrap() { break mForA } } - if found == false { + if !found { err = fmt.Errorf("reverse field `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) goto end } @@ -267,7 +267,7 @@ func bootStrap() { break mForB } } - if found == false { + if !found { mForC: for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelManyToMany] { conditions := fi.relThrough != "" && fi.relThrough == ffi.relThrough || @@ -287,7 +287,7 @@ func bootStrap() { } } } - if found == false { + if !found { err = fmt.Errorf("reverse field for `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName) goto end } diff --git a/orm/models_info_f.go b/orm/models_info_f.go index 4b3d3e27..bbb7d71f 100644 --- a/orm/models_info_f.go +++ b/orm/models_info_f.go @@ -47,7 +47,7 @@ func (f *fields) Add(fi *fieldInfo) (added bool) { } else { return } - if _, ok := f.fieldsByType[fi.fieldType]; ok == false { + if _, ok := f.fieldsByType[fi.fieldType]; !ok { f.fieldsByType[fi.fieldType] = make([]*fieldInfo, 0) } f.fieldsByType[fi.fieldType] = append(f.fieldsByType[fi.fieldType], fi) @@ -334,12 +334,12 @@ checkType: switch onDelete { case odCascade, odDoNothing: case odSetDefault: - if initial.Exist() == false { + if !initial.Exist() { err = errors.New("on_delete: set_default need set field a default value") goto end } case odSetNULL: - if fi.null == false { + if !fi.null { err = errors.New("on_delete: set_null need set field null") goto end } diff --git a/orm/models_info_m.go b/orm/models_info_m.go index d6ba1dca..4a3a37f9 100644 --- a/orm/models_info_m.go +++ b/orm/models_info_m.go @@ -78,7 +78,7 @@ func addModelFields(mi *modelInfo, ind reflect.Value, mName string, index []int) fi.fieldIndex = append(index, i) fi.mi = mi fi.inModel = true - if mi.fields.Add(fi) == false { + if !mi.fields.Add(fi) { err = fmt.Errorf("duplicate column name: %s", fi.column) break } diff --git a/orm/orm.go b/orm/orm.go index d9d1cd77..d364e621 100644 --- a/orm/orm.go +++ b/orm/orm.go @@ -122,21 +122,13 @@ func (o *orm) getFieldInfo(mi *modelInfo, name string) *fieldInfo { // read data to model func (o *orm) Read(md interface{}, cols ...string) error { mi, ind := o.getMiInd(md, true) - err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false) - if err != nil { - return err - } - return nil + return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, false) } // read data to model, like Read(), but use "SELECT FOR UPDATE" form func (o *orm) ReadForUpdate(md interface{}, cols ...string) error { mi, ind := o.getMiInd(md, true) - err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, true) - if err != nil { - return err - } - return nil + return o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols, true) } // Try to read a row from the database, or insert one if it doesn't exist @@ -238,15 +230,11 @@ func (o *orm) InsertOrUpdate(md interface{}, colConflitAndArgs ...string) (int64 // cols set the columns those want to update. func (o *orm) Update(md interface{}, cols ...string) (int64, error) { mi, ind := o.getMiInd(md, true) - num, err := o.alias.DbBaser.Update(o.db, mi, ind, o.alias.TZ, cols) - if err != nil { - return num, err - } - return num, nil + return o.alias.DbBaser.Update(o.db, mi, ind, o.alias.TZ, cols) } // delete model in database -// cols shows the delete conditions values read from. deafult is pk +// cols shows the delete conditions values read from. default is pk func (o *orm) Delete(md interface{}, cols ...string) (int64, error) { mi, ind := o.getMiInd(md, true) num, err := o.alias.DbBaser.Delete(o.db, mi, ind, o.alias.TZ, cols) @@ -361,7 +349,7 @@ func (o *orm) queryRelated(md interface{}, name string) (*modelInfo, *fieldInfo, fi := o.getFieldInfo(mi, name) _, _, exist := getExistPk(mi, ind) - if exist == false { + if !exist { panic(ErrMissPK) } @@ -489,7 +477,7 @@ func (o *orm) Begin() error { // commit transaction func (o *orm) Commit() error { - if o.isTx == false { + if !o.isTx { return ErrTxDone } err := o.db.(txEnder).Commit() @@ -504,7 +492,7 @@ func (o *orm) Commit() error { // rollback transaction func (o *orm) Rollback() error { - if o.isTx == false { + if !o.isTx { return ErrTxDone } err := o.db.(txEnder).Rollback() diff --git a/orm/orm_querym2m.go b/orm/orm_querym2m.go index b220bda6..6a270a0d 100644 --- a/orm/orm_querym2m.go +++ b/orm/orm_querym2m.go @@ -72,7 +72,7 @@ func (o *queryM2M) Add(mds ...interface{}) (int64, error) { } _, v1, exist := getExistPk(o.mi, o.ind) - if exist == false { + if !exist { panic(ErrMissPK) } @@ -87,7 +87,7 @@ func (o *queryM2M) Add(mds ...interface{}) (int64, error) { v2 = ind.Interface() } else { _, v2, exist = getExistPk(fi.relModelInfo, ind) - if exist == false { + if !exist { panic(ErrMissPK) } } @@ -104,11 +104,7 @@ func (o *queryM2M) Remove(mds ...interface{}) (int64, error) { fi := o.fi qs := o.qs.Filter(fi.reverseFieldInfo.name, o.md) - nums, err := qs.Filter(fi.reverseFieldInfoTwo.name+ExprSep+"in", mds).Delete() - if err != nil { - return nums, err - } - return nums, nil + return qs.Filter(fi.reverseFieldInfoTwo.name+ExprSep+"in", mds).Delete() } // check model is existed in relationship of origin model diff --git a/orm/orm_test.go b/orm/orm_test.go index 8738952b..a8b4fe69 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -93,14 +93,14 @@ wrongArg: } func AssertIs(a interface{}, args ...interface{}) error { - if ok, err := ValuesCompare(true, a, args...); ok == false { + if ok, err := ValuesCompare(true, a, args...); !ok { return err } return nil } func AssertNot(a interface{}, args ...interface{}) error { - if ok, err := ValuesCompare(false, a, args...); ok == false { + if ok, err := ValuesCompare(false, a, args...); !ok { return err } return nil diff --git a/orm/utils.go b/orm/utils.go index 6aac8e5d..7340715d 100644 --- a/orm/utils.go +++ b/orm/utils.go @@ -225,7 +225,7 @@ func camelString(s string) string { if d == '_' { flag = true continue - } else if flag == true { + } else if flag { if d >= 'a' && d <= 'z' { d = d - 32 } diff --git a/router_test.go b/router_test.go index 936fd5e8..25804d67 100644 --- a/router_test.go +++ b/router_test.go @@ -502,10 +502,10 @@ func TestFilterBeforeRouter(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "BeforeRouter1") == false { + if !strings.Contains(rw.Body.String(), "BeforeRouter1") { t.Errorf(testName + " BeforeRouter did not run") } - if strings.Contains(rw.Body.String(), "hello") == true { + if strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " BeforeRouter did not return properly") } } @@ -525,13 +525,13 @@ func TestFilterBeforeExec(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "BeforeExec1") == false { + if !strings.Contains(rw.Body.String(), "BeforeExec1") { t.Errorf(testName + " BeforeExec did not run") } - if strings.Contains(rw.Body.String(), "hello") == true { + if strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " BeforeExec did not return properly") } - if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + if strings.Contains(rw.Body.String(), "BeforeRouter") { t.Errorf(testName + " BeforeRouter ran in error") } } @@ -552,16 +552,16 @@ func TestFilterAfterExec(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "AfterExec1") == false { + if !strings.Contains(rw.Body.String(), "AfterExec1") { t.Errorf(testName + " AfterExec did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } - if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + if strings.Contains(rw.Body.String(), "BeforeRouter") { t.Errorf(testName + " BeforeRouter ran in error") } - if strings.Contains(rw.Body.String(), "BeforeExec") == true { + if strings.Contains(rw.Body.String(), "BeforeExec") { t.Errorf(testName + " BeforeExec ran in error") } } @@ -583,19 +583,19 @@ func TestFilterFinishRouter(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "FinishRouter1") == true { + if strings.Contains(rw.Body.String(), "FinishRouter1") { t.Errorf(testName + " FinishRouter did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } - if strings.Contains(rw.Body.String(), "AfterExec1") == true { + if strings.Contains(rw.Body.String(), "AfterExec1") { t.Errorf(testName + " AfterExec ran in error") } - if strings.Contains(rw.Body.String(), "BeforeRouter") == true { + if strings.Contains(rw.Body.String(), "BeforeRouter") { t.Errorf(testName + " BeforeRouter ran in error") } - if strings.Contains(rw.Body.String(), "BeforeExec") == true { + if strings.Contains(rw.Body.String(), "BeforeExec") { t.Errorf(testName + " BeforeExec ran in error") } } @@ -615,14 +615,14 @@ func TestFilterFinishRouterMultiFirstOnly(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "FinishRouter1") == false { + if !strings.Contains(rw.Body.String(), "FinishRouter1") { t.Errorf(testName + " FinishRouter1 did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } // not expected in body - if strings.Contains(rw.Body.String(), "FinishRouter2") == true { + if strings.Contains(rw.Body.String(), "FinishRouter2") { t.Errorf(testName + " FinishRouter2 did run") } } @@ -642,13 +642,13 @@ func TestFilterFinishRouterMulti(t *testing.T) { rw, r := testRequest("GET", url) mux.ServeHTTP(rw, r) - if strings.Contains(rw.Body.String(), "FinishRouter1") == false { + if !strings.Contains(rw.Body.String(), "FinishRouter1") { t.Errorf(testName + " FinishRouter1 did not run") } - if strings.Contains(rw.Body.String(), "hello") == false { + if !strings.Contains(rw.Body.String(), "hello") { t.Errorf(testName + " handler did not run properly") } - if strings.Contains(rw.Body.String(), "FinishRouter2") == false { + if !strings.Contains(rw.Body.String(), "FinishRouter2") { t.Errorf(testName + " FinishRouter2 did not run properly") } } diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go index 68f37b08..7dcc080d 100644 --- a/session/ledis/ledis_session.go +++ b/session/ledis/ledis_session.go @@ -125,10 +125,7 @@ func (lp *Provider) SessionRead(sid string) (session.Store, error) { // SessionExist check ledis session exist by sid func (lp *Provider) SessionExist(sid string) bool { count, _ := c.Exists([]byte(sid)) - if count == 0 { - return false - } - return true + return !(count == 0) } // SessionRegenerate generate new sid for ledis session diff --git a/session/memcache/sess_memcache.go b/session/memcache/sess_memcache.go index f1069bc9..cad02258 100644 --- a/session/memcache/sess_memcache.go +++ b/session/memcache/sess_memcache.go @@ -205,11 +205,7 @@ func (rp *MemProvider) SessionDestroy(sid string) error { } } - err := client.Delete(sid) - if err != nil { - return err - } - return nil + return client.Delete(sid) } func (rp *MemProvider) connectInit() error { diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 7683ee1f..37c45283 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -171,10 +171,7 @@ func (mp *Provider) SessionExist(sid string) bool { row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) - if err == sql.ErrNoRows { - return false - } - return true + return !(err == sql.ErrNoRows) } // SessionRegenerate generate new sid for mysql session diff --git a/session/postgres/sess_postgresql.go b/session/postgres/sess_postgresql.go index 73f9c13a..f00e0711 100644 --- a/session/postgres/sess_postgresql.go +++ b/session/postgres/sess_postgresql.go @@ -184,11 +184,7 @@ func (mp *Provider) SessionExist(sid string) bool { row := c.QueryRow("select session_data from session where session_key=$1", sid) var sessiondata []byte err := row.Scan(&sessiondata) - - if err == sql.ErrNoRows { - return false - } - return true + return !(err == sql.ErrNoRows) } // SessionRegenerate generate new sid for postgresql session diff --git a/session/sess_file.go b/session/sess_file.go index 50687c9e..12cf1f3f 100644 --- a/session/sess_file.go +++ b/session/sess_file.go @@ -163,10 +163,7 @@ func (fp *FileProvider) SessionExist(sid string) bool { defer filepder.lock.Unlock() _, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) - if err == nil { - return true - } - return false + return err == nil } // SessionDestroy Remove all files in this save path diff --git a/session/sess_test.go b/session/sess_test.go index b40865f3..60b47dd4 100644 --- a/session/sess_test.go +++ b/session/sess_test.go @@ -115,7 +115,7 @@ func TestParseConfig(t *testing.T) { if cf2.Gclifetime != 3600 { t.Fatal("parseconfig get gclifetime error") } - if cf2.EnableSetCookie != false { + if cf2.EnableSetCookie { t.Fatal("parseconfig get enableSetCookie error") } cconfig := new(cookieConfig) diff --git a/session/ssdb/sess_ssdb.go b/session/ssdb/sess_ssdb.go index 4dcf160a..c846e73a 100644 --- a/session/ssdb/sess_ssdb.go +++ b/session/ssdb/sess_ssdb.go @@ -26,10 +26,7 @@ func (p *SsdbProvider) connectInit() error { return errors.New("SessionInit First") } p.client, err = ssdb.Connect(p.host, p.port) - if err != nil { - return err - } - return nil + return err } func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error { @@ -41,11 +38,7 @@ func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error { if e != nil { return e } - err := p.connectInit() - if err != nil { - return err - } - return nil + return p.connectInit() } func (p *SsdbProvider) SessionRead(sid string) (session.Store, error) { @@ -126,10 +119,7 @@ func (p *SsdbProvider) SessionDestroy(sid string) error { } } _, err := p.client.Del(sid) - if err != nil { - return err - } - return nil + return err } func (p *SsdbProvider) SessionGC() { diff --git a/staticfile.go b/staticfile.go index b7be24f3..7c270947 100644 --- a/staticfile.go +++ b/staticfile.go @@ -109,14 +109,14 @@ var ( func openFile(filePath string, fi os.FileInfo, acceptEncoding string) (bool, string, *serveContentHolder, error) { mapKey := acceptEncoding + ":" + filePath mapLock.RLock() - mapFile, _ := staticFileMap[mapKey] + mapFile := staticFileMap[mapKey] mapLock.RUnlock() if isOk(mapFile, fi) { return mapFile.encoding != "", mapFile.encoding, mapFile, nil } mapLock.Lock() defer mapLock.Unlock() - if mapFile, _ = staticFileMap[mapKey]; !isOk(mapFile, fi) { + if mapFile = staticFileMap[mapKey]; !isOk(mapFile, fi) { file, err := os.Open(filePath) if err != nil { return false, "", nil, err diff --git a/template.go b/template.go index 4e0d6384..0d69a29e 100644 --- a/template.go +++ b/template.go @@ -31,11 +31,11 @@ import ( ) var ( - beegoTplFuncMap = make(template.FuncMap) + beegoTplFuncMap = make(template.FuncMap) beeViewPathTemplateLocked = false // beeViewPathTemplates caching map and supported template file extensions per view - beeViewPathTemplates = make(map[string]map[string]*template.Template) - templatesLock sync.RWMutex + beeViewPathTemplates = make(map[string]map[string]*template.Template) + templatesLock sync.RWMutex // beeTemplateExt stores the template extension which will build beeTemplateExt = []string{"tpl", "html"} // beeTemplatePreprocessors stores associations of extension -> preprocessor handler @@ -46,7 +46,7 @@ var ( // writing the output to wr. // A template will be executed safely in parallel. func ExecuteTemplate(wr io.Writer, name string, data interface{}) error { - return ExecuteViewPathTemplate(wr,name, BConfig.WebConfig.ViewsPath, data) + return ExecuteViewPathTemplate(wr, name, BConfig.WebConfig.ViewsPath, data) } // ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object, @@ -57,7 +57,7 @@ func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data in templatesLock.RLock() defer templatesLock.RUnlock() } - if beeTemplates,ok := beeViewPathTemplates[viewPath]; ok { + if beeTemplates, ok := beeViewPathTemplates[viewPath]; ok { if t, ok := beeTemplates[name]; ok { var err error if t.Lookup(name) != nil { @@ -160,9 +160,9 @@ func AddTemplateExt(ext string) { beeTemplateExt = append(beeTemplateExt, ext) } -// AddViewPath adds a new path to the supported view paths. +// AddViewPath adds a new path to the supported view paths. //Can later be used by setting a controller ViewPath to this folder -//will panic if called after beego.Run() +//will panic if called after beego.Run() func AddViewPath(viewPath string) error { if beeViewPathTemplateLocked { if _, exist := beeViewPathTemplates[viewPath]; exist { @@ -187,7 +187,7 @@ func BuildTemplate(dir string, files ...string) error { } return errors.New("dir open err") } - beeTemplates,ok := beeViewPathTemplates[dir]; + beeTemplates, ok := beeViewPathTemplates[dir] if !ok { panic("Unknown view path: " + dir) } @@ -296,7 +296,7 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others t, subMods1, err = getTplDeep(root, otherFile, "", t) if err != nil { logs.Trace("template parse file err:", err) - } else if subMods1 != nil && len(subMods1) > 0 { + } else if len(subMods1) > 0 { t, err = _getTemplate(t, root, subMods1, others...) } break @@ -317,7 +317,7 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others t, subMods1, err = getTplDeep(root, otherFile, "", t) if err != nil { logs.Trace("template parse file err:", err) - } else if subMods1 != nil && len(subMods1) > 0 { + } else if len(subMods1) > 0 { t, err = _getTemplate(t, root, subMods1, others...) } break diff --git a/templatefunc_test.go b/templatefunc_test.go index a1ec1136..20d9b850 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -173,7 +173,7 @@ func TestParseForm(t *testing.T) { if u.Intro != "I am an engineer!" { t.Errorf("Intro should equal `I am an engineer!` but got `%v`", u.Intro) } - if u.StrBool != true { + if !u.StrBool { t.Errorf("strboll should equal `true`, but got `%v`", u.StrBool) } y, m, d := u.Date.Date() @@ -255,43 +255,43 @@ func TestParseFormTag(t *testing.T) { objT := reflect.TypeOf(&user{}).Elem() label, name, fType, id, class, ignored, required := parseFormTag(objT.Field(0)) - if !(name == "name" && label == "年龄:" && fType == "text" && ignored == false) { + if !(name == "name" && label == "年龄:" && fType == "text" && !ignored) { t.Errorf("Form Tag with name, label and type was not correctly parsed.") } label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(1)) - if !(name == "NoName" && label == "年龄:" && fType == "hidden" && ignored == false) { + if !(name == "NoName" && label == "年龄:" && fType == "hidden" && !ignored) { t.Errorf("Form Tag with label and type but without name was not correctly parsed.") } label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(2)) - if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && ignored == false) { + if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && !ignored) { t.Errorf("Form Tag containing only label was not correctly parsed.") } label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(3)) - if !(name == "name" && label == "OnlyName: " && fType == "text" && ignored == false && + if !(name == "name" && label == "OnlyName: " && fType == "text" && !ignored && id == "name" && class == "form-name") { t.Errorf("Form Tag containing only name was not correctly parsed.") } label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(4)) - if ignored == false { + if !ignored { t.Errorf("Form Tag that should be ignored was not correctly parsed.") } label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(5)) - if !(name == "name" && required == true) { + if !(name == "name" && required) { t.Errorf("Form Tag containing only name and required was not correctly parsed.") } label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(6)) - if !(name == "name" && required == false) { + if !(name == "name" && !required) { t.Errorf("Form Tag containing only name and ignore required was not correctly parsed.") } label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(7)) - if !(name == "name" && required == false) { + if !(name == "name" && !required) { t.Errorf("Form Tag containing only name and not required was not correctly parsed.") } diff --git a/validation/validation.go b/validation/validation.go index 489dfa5e..9dc51106 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -349,7 +349,7 @@ func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { //Step 1: validate obj itself firstly // fails if objc is not struct pass, err := v.Valid(objc) - if err != nil || false == pass { + if err != nil || !pass { return pass, err // Stop recursive validation } // Step 2: Validate struct's struct fields From b0e2012a170832c899ba6264fab7373a8712995f Mon Sep 17 00:00:00 2001 From: liaoziqian Date: Tue, 21 Mar 2017 09:15:30 +0800 Subject: [PATCH 053/163] Fix set cookie bug, zero max age is not valid. --- context/output.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/output.go b/context/output.go index 564ef96d..c15f9fe7 100644 --- a/context/output.go +++ b/context/output.go @@ -105,7 +105,7 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface switch { case maxAge > 0: fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(maxAge)*time.Second).UTC().Format(time.RFC1123), maxAge) - case maxAge < 0: + case maxAge <= 0: fmt.Fprintf(&b, "; Max-Age=0") } } From ae0a75c464e3373c89f296e698a1679f80e566f9 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 21 Mar 2017 23:55:40 +0800 Subject: [PATCH 054/163] console debug use diffrent color --- logs/console.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/console.go b/logs/console.go index e6bf6c29..e75f2a1b 100644 --- a/logs/console.go +++ b/logs/console.go @@ -41,7 +41,7 @@ var colors = []brush{ newBrush("1;33"), // Warning yellow newBrush("1;32"), // Notice green newBrush("1;34"), // Informational blue - newBrush("1;34"), // Debug blue + newBrush("1;44"), // Debug Background blue } // consoleWriter implements LoggerInterface and writes messages to terminal. From 83d4385f1f94acde7972801abec35b6508db8cb6 Mon Sep 17 00:00:00 2001 From: Guohua Ouyang Date: Sat, 25 Mar 2017 07:52:43 +0800 Subject: [PATCH 055/163] Support time.RFC3339 Signed-off-by: Guohua Ouyang --- templatefunc.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/templatefunc.go b/templatefunc.go index 8ecacbfe..5bd7b73f 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -362,7 +362,10 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e t time.Time err error ) - if len(value) >= 19 { + if len(value) >= 25 { + value = value[:25] + t, err = time.ParseInLocation(time.RFC3339, value, time.Local) + } else if len(value) >= 19 { value = value[:19] t, err = time.ParseInLocation(formatDateTime, value, time.Local) } else if len(value) >= 10 { From c387aeeb363ea65ec18b5f3fecbcde06f6506c49 Mon Sep 17 00:00:00 2001 From: Jisu Kim Date: Mon, 27 Mar 2017 14:30:42 +0900 Subject: [PATCH 056/163] fix markdown formatting --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d3c92d84..c8a964f8 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific feature More info [beego.me](http://beego.me) -##Quick Start -######Download and install +## Quick Start +###### Download and install go get github.com/astaxie/beego -######Create file `hello.go` +###### Create file `hello.go` ```go package main @@ -24,12 +24,12 @@ func main(){ beego.Run() } ``` -######Build and run +###### Build and run ```bash go build hello.go ./hello ``` -######Congratulations! +###### Congratulations! You just built your first beego app. Open your browser and visit `http://localhost:8080`. Please see [Documentation](http://beego.me/docs) for more. From 7c3a99773590c37aac84ebea32364a6eeba02b3c Mon Sep 17 00:00:00 2001 From: xia hao Date: Mon, 27 Mar 2017 23:34:58 +0800 Subject: [PATCH 057/163] poolsize could set to zero sometimes we may want disable the redis pool --- session/redis/sess_redis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index c46fa7cd..14bbd4c1 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -128,7 +128,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { } if len(configs) > 1 { poolsize, err := strconv.Atoi(configs[1]) - if err != nil || poolsize <= 0 { + if err != nil || poolsize < 0 { rp.poolsize = MaxPoolSize } else { rp.poolsize = poolsize From fdb2660a2a67f923637d78b0570d62ee44ee066b Mon Sep 17 00:00:00 2001 From: Ketan Gupta Date: Tue, 28 Mar 2017 11:29:20 +0530 Subject: [PATCH 058/163] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d3c92d84..c8a964f8 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific feature More info [beego.me](http://beego.me) -##Quick Start -######Download and install +## Quick Start +###### Download and install go get github.com/astaxie/beego -######Create file `hello.go` +###### Create file `hello.go` ```go package main @@ -24,12 +24,12 @@ func main(){ beego.Run() } ``` -######Build and run +###### Build and run ```bash go build hello.go ./hello ``` -######Congratulations! +###### Congratulations! You just built your first beego app. Open your browser and visit `http://localhost:8080`. Please see [Documentation](http://beego.me/docs) for more. From 2808a13f0774880901f31714838793ef0dc0d7b8 Mon Sep 17 00:00:00 2001 From: Christoph Pech Date: Tue, 28 Mar 2017 12:38:27 +0200 Subject: [PATCH 059/163] Fix for IndexExists in SQLite driver, they added the "origin" and "partial" columns to the index_list pragma. see: https://www.sqlite.org/src/info/2743846cdba572f6 --- orm/db_sqlite.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/db_sqlite.go b/orm/db_sqlite.go index a3cb69a7..a43a5594 100644 --- a/orm/db_sqlite.go +++ b/orm/db_sqlite.go @@ -134,7 +134,7 @@ func (d *dbBaseSqlite) IndexExists(db dbQuerier, table string, name string) bool defer rows.Close() for rows.Next() { var tmp, index sql.NullString - rows.Scan(&tmp, &index, &tmp) + rows.Scan(&tmp, &index, &tmp, &tmp, &tmp) if name == index.String { return true } From bf469f0b550cda35f2c0ef543d7a7c14ed77a05b Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 29 Mar 2017 09:57:18 +0800 Subject: [PATCH 060/163] Revert "close mysql connection" --- session/mysql/sess_mysql.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 37c45283..7bcf959c 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -143,7 +143,6 @@ func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error { // SessionRead get mysql session by sid func (mp *Provider) SessionRead(sid string) (session.Store, error) { c := mp.connectInit() - defer c.Close() row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) @@ -177,7 +176,6 @@ func (mp *Provider) SessionExist(sid string) bool { // SessionRegenerate generate new sid for mysql session func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { c := mp.connectInit() - defer c.Close() row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid) var sessiondata []byte err := row.Scan(&sessiondata) From 5c7673e73da9addeede1a97c7ebd1097aee99a4f Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 29 Mar 2017 10:22:27 +0800 Subject: [PATCH 061/163] update to 1.8.1 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index c06b499c..7acf4a13 100644 --- a/beego.go +++ b/beego.go @@ -23,7 +23,7 @@ import ( const ( // VERSION represent beego web framework version. - VERSION = "1.8.0" + VERSION = "1.8.1" // DEV is for develop DEV = "dev" From fb04d3cff18fe575d7fbdc465a9f88e65bcefa5b Mon Sep 17 00:00:00 2001 From: Vladimir Shteinman Date: Thu, 30 Mar 2017 11:44:31 +0300 Subject: [PATCH 062/163] Fix example for Hander func --- app.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index 32776298..25ea2a04 100644 --- a/app.go +++ b/app.go @@ -348,9 +348,9 @@ func Any(rootpath string, f FilterFunc) *App { // Handler used to register a Handler router // usage: -// beego.Handler("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) +// beego.Handler("/api", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) { +// fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) +// })) func Handler(rootpath string, h http.Handler, options ...interface{}) *App { BeeApp.Handlers.Handler(rootpath, h, options...) return BeeApp From 6a32b048bd996790db417b40428aaa0f5b747167 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Sun, 2 Apr 2017 11:04:08 +0200 Subject: [PATCH 063/163] Update README.md --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index d3c92d84..32c8e25e 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,18 @@ -## Beego - -[![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) -[![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) -[![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) +# Beego [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org) beego is used for rapid development of RESTful APIs, web apps and backend services in Go. It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. -More info [beego.me](http://beego.me) +###### More info at [beego.me](http://beego.me). -##Quick Start -######Download and install +## Quick Start + +#### Download and install go get github.com/astaxie/beego -######Create file `hello.go` +#### Create file `hello.go` + ```go package main @@ -24,15 +22,17 @@ func main(){ beego.Run() } ``` -######Build and run -```bash - go build hello.go + +#### Build and run + + go build main.go ./hello -``` -######Congratulations! -You just built your first beego app. -Open your browser and visit `http://localhost:8080`. -Please see [Documentation](http://beego.me/docs) for more. + +#### Go to [http://localhost:8080](http://localhost:8080) + +Congratulations! You've just built your first **beego** app. + +###### Please see [Documentation](http://beego.me/docs) for more. ## Features @@ -56,7 +56,7 @@ Please see [Documentation](http://beego.me/docs) for more. * [http://beego.me/community](http://beego.me/community) * Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232) -## LICENSE +## License beego source code is licensed under the Apache Licence, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.html). From d5c03f5b8f577a702a76f735ec490488bcf62785 Mon Sep 17 00:00:00 2001 From: Liaodd Date: Mon, 10 Apr 2017 11:30:23 +0800 Subject: [PATCH 064/163] Update ini.go change the key to lowercase when set a new key for ini configer --- config/ini.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/ini.go b/config/ini.go index 27220f90..8fdbc2ea 100644 --- a/config/ini.go +++ b/config/ini.go @@ -416,7 +416,7 @@ func (c *IniConfigContainer) Set(key, value string) error { var ( section, k string - sectionKey = strings.Split(key, "::") + sectionKey = strings.Split(strings.ToLower(key), "::") ) if len(sectionKey) >= 2 { From 932019770deeb0651233e4bebfa87fa9e6b345d7 Mon Sep 17 00:00:00 2001 From: zjjott Date: Mon, 10 Apr 2017 17:36:17 +0800 Subject: [PATCH 065/163] fix: log mode 0440 should not be 440 --- logs/file.go | 2 +- logs/file_test.go | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/logs/file.go b/logs/file.go index bd3c22a9..a7276523 100644 --- a/logs/file.go +++ b/logs/file.go @@ -270,7 +270,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { // Rename the file to its new found name // even if occurs error,we MUST guarantee to restart new logger err = os.Rename(w.Filename, fName) - err = os.Chmod(fName, os.FileMode(440)) + err = os.Chmod(fName, os.FileMode(0440)) // re-start logger RESTART_LOGGER: diff --git a/logs/file_test.go b/logs/file_test.go index 69a66d84..a1b226bf 100644 --- a/logs/file_test.go +++ b/logs/file_test.go @@ -162,7 +162,27 @@ func TestFileRotate_05(t *testing.T) { testFileDailyRotate(t, fn1, fn2) os.Remove(fn) } - +func TestFileRotate_06(t *testing.T) {//test file mode + log := NewLogger(10000) + log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`) + log.Debug("debug") + log.Info("info") + log.Notice("notice") + log.Warning("warning") + log.Error("error") + log.Alert("alert") + log.Critical("critical") + log.Emergency("emergency") + rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log" + s,_:=os.Lstat(rotateName) + if s.Mode() != 0440 { + os.Remove(rotateName) + os.Remove("test3.log") + t.Fatal("rotate file mode error") + } + os.Remove(rotateName) + os.Remove("test3.log") +} func testFileRotate(t *testing.T, fn1, fn2 string) { fw := &fileLogWriter{ Daily: true, From 0cd31a247f898e782f1730beb2f542963988a73c Mon Sep 17 00:00:00 2001 From: Orefa Date: Wed, 19 Apr 2017 07:47:05 +0800 Subject: [PATCH 066/163] fix Template nesting problem --- template.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/template.go b/template.go index 17c18591..163ed620 100644 --- a/template.go +++ b/template.go @@ -227,9 +227,12 @@ func BuildTemplate(dir string, files ...string) error { func getTplDeep(root, file, parent string, t *template.Template) (*template.Template, [][]string, error) { var fileAbsPath string + var rParent string if filepath.HasPrefix(file, "../") { + rParent = filepath.Join(filepath.Dir(parent), file) fileAbsPath = filepath.Join(root, filepath.Dir(parent), file) } else { + rParent = file fileAbsPath = filepath.Join(root, file) } if e := utils.FileExists(fileAbsPath); !e { @@ -254,7 +257,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp if !HasTemplateExt(m[1]) { continue } - _, _, err = getTplDeep(root, m[1], file, t) + _, _, err = getTplDeep(root, m[1], rParent, t) if err != nil { return nil, [][]string{}, err } From 0cb8de421857b4b084d50a7df2cb24073938e3c6 Mon Sep 17 00:00:00 2001 From: PaulChen2016 <747766706@qq.com> Date: Wed, 19 Apr 2017 16:22:58 +0800 Subject: [PATCH 067/163] =?UTF-8?q?Beego=20=E8=BF=90=E8=A1=8C=E8=BF=87?= =?UTF-8?q?=E7=A8=8B=E4=B8=AD=E5=8A=A8=E6=80=81=E5=A2=9E=E5=87=8F=E5=AE=9A?= =?UTF-8?q?=E6=97=B6=E4=BB=BB=E5=8A=A1=E6=97=B6=EF=BC=8Cnow=E7=9A=84?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E9=9C=80=E8=A6=81=E6=9B=B4=E6=96=B0=EF=BC=8C?= =?UTF-8?q?=E5=90=A6=E5=88=99=E7=AD=89=E5=BE=85=E6=97=B6=E9=97=B4=E4=BC=9A?= =?UTF-8?q?=E4=B8=8D=E6=AD=A3=E7=A1=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit beego 启动时,执行toolbox.StartTask() 运行过程中,动态添加定时任务(这个是now时间已经不是starttask的时间了,需要刷新) case <-changed: now = time.Now().Local() continue --- toolbox/task.go | 1 + 1 file changed, 1 insertion(+) diff --git a/toolbox/task.go b/toolbox/task.go index abd411c8..672717cd 100644 --- a/toolbox/task.go +++ b/toolbox/task.go @@ -427,6 +427,7 @@ func run() { } continue case <-changed: + now = time.Now().Local() continue case <-stop: return From d1a2583972ffdc743a9bb87e0ccb8ae40f4d7124 Mon Sep 17 00:00:00 2001 From: Ggicci Date: Wed, 19 Apr 2017 19:50:11 +0800 Subject: [PATCH 068/163] Fix ini parsing error for multiple users on one machine. If there were multiple users working on one machine, it's common that "/tmp/beego" will be owned by one of them, and the others won't be able to access to it. So, it's better to add an "id-like" postfix to the temporary directory. --- config/ini.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/config/ini.go b/config/ini.go index 27220f90..7711b010 100644 --- a/config/ini.go +++ b/config/ini.go @@ -21,6 +21,7 @@ import ( "io" "io/ioutil" "os" + "os/user" "path/filepath" "strconv" "strings" @@ -184,10 +185,17 @@ func (ini *IniConfig) parseData(dir string, data []byte) (*IniConfigContainer, e // ParseData parse ini the data // When include other.conf,other.conf is either absolute directory -// or under beego in default temporary directory(/tmp/beego). +// or under beego in default temporary directory(/tmp/beego[-username]). func (ini *IniConfig) ParseData(data []byte) (Configer, error) { - dir := filepath.Join(os.TempDir(), "beego") - os.MkdirAll(dir, os.ModePerm) + dir := "beego" + currentUser, err := user.Current() + if err == nil { + dir = "beego-" + currentUser.Username + } + dir = filepath.Join(os.TempDir(), dir) + if err = os.MkdirAll(dir, os.ModePerm); err != nil { + return nil, err + } return ini.parseData(dir, data) } From 453691728ab73e569c32df3055e941ff35405e5c Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 20 Apr 2017 10:56:09 +0800 Subject: [PATCH 069/163] gofmt simplify --- context/input_test.go | 4 ++-- controller_test.go | 4 ++-- logs/alils/signature.go | 3 +-- logs/file_test.go | 4 ++-- toolbox/statistics.go | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/context/input_test.go b/context/input_test.go index 9853e398..db812a0f 100644 --- a/context/input_test.go +++ b/context/input_test.go @@ -73,8 +73,8 @@ func TestBind(t *testing.T) { {"/?human.ID=888&human.Nick=astaxie&human.Ms=true&human[Pwd]=pass", []testItem{{"human", Human{}, Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass"}}}}, {"/?human[0].ID=888&human[0].Nick=astaxie&human[0].Ms=true&human[0][Pwd]=pass01&human[1].ID=999&human[1].Nick=ysqi&human[1].Ms=On&human[1].Pwd=pass02", []testItem{{"human", []Human{}, []Human{ - Human{ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"}, - Human{ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"}, + {ID: 888, Nick: "astaxie", Ms: true, Pwd: "pass01"}, + {ID: 999, Nick: "ysqi", Ms: true, Pwd: "pass02"}, }}}}, { diff --git a/controller_test.go b/controller_test.go index c2025860..1e53416d 100644 --- a/controller_test.go +++ b/controller_test.go @@ -172,10 +172,10 @@ func TestAdditionalViewPaths(t *testing.T) { t.Fatal("TestAdditionalViewPaths expected error") } }() - ctrl.RenderString(); + ctrl.RenderString() }() ctrl.TplName = "file2.tpl" ctrl.ViewPath = dir2 - ctrl.RenderString(); + ctrl.RenderString() } diff --git a/logs/alils/signature.go b/logs/alils/signature.go index e0e4b3f7..2d611307 100755 --- a/logs/alils/signature.go +++ b/logs/alils/signature.go @@ -76,7 +76,7 @@ func signature(project *LogProject, method, uri string, var keys sort.StringSlice vals := u.Query() - for k, _ := range vals { + for k := range vals { keys = append(keys, k) } @@ -109,4 +109,3 @@ func signature(project *LogProject, method, uri string, digest = base64.StdEncoding.EncodeToString(mac.Sum(nil)) return } - diff --git a/logs/file_test.go b/logs/file_test.go index a1b226bf..f345ff20 100644 --- a/logs/file_test.go +++ b/logs/file_test.go @@ -162,7 +162,7 @@ func TestFileRotate_05(t *testing.T) { testFileDailyRotate(t, fn1, fn2) os.Remove(fn) } -func TestFileRotate_06(t *testing.T) {//test file mode +func TestFileRotate_06(t *testing.T) { //test file mode log := NewLogger(10000) log.SetLogger("file", `{"filename":"test3.log","maxlines":4}`) log.Debug("debug") @@ -174,7 +174,7 @@ func TestFileRotate_06(t *testing.T) {//test file mode log.Critical("critical") log.Emergency("emergency") rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log" - s,_:=os.Lstat(rotateName) + s, _ := os.Lstat(rotateName) if s.Mode() != 0440 { os.Remove(rotateName) os.Remove("test3.log") diff --git a/toolbox/statistics.go b/toolbox/statistics.go index c6a9489f..d014544c 100644 --- a/toolbox/statistics.go +++ b/toolbox/statistics.go @@ -119,7 +119,7 @@ func (m *URLMap) GetMap() map[string]interface{} { func (m *URLMap) GetMapData() []map[string]interface{} { m.lock.Lock() defer m.lock.Unlock() - + var resultLists []map[string]interface{} for k, v := range m.urlmap { From 9aedb4d05a70937533ff8cf657b365a80257406d Mon Sep 17 00:00:00 2001 From: eyalpost Date: Fri, 21 Apr 2017 15:26:41 +0300 Subject: [PATCH 070/163] phase #1 --- controller.go | 2 + param/methodparams.go | 101 ++++++++++++++++++++++++++++++++++++++++++ param/options.go | 15 +++++++ param/parsers.go | 41 +++++++++++++++++ response/renderer.go | 27 +++++++++++ response/responses.go | 44 ++++++++++++++++++ router.go | 42 ++++++++++++++---- 7 files changed, 264 insertions(+), 8 deletions(-) create mode 100644 param/methodparams.go create mode 100644 param/options.go create mode 100644 param/parsers.go create mode 100644 response/renderer.go create mode 100644 response/responses.go diff --git a/controller.go b/controller.go index c2a327b3..02e45738 100644 --- a/controller.go +++ b/controller.go @@ -28,6 +28,7 @@ import ( "strings" "github.com/astaxie/beego/context" + "github.com/astaxie/beego/param" "github.com/astaxie/beego/session" ) @@ -51,6 +52,7 @@ type ControllerComments struct { Router string AllowHTTPMethods []string Params []map[string]string + MethodParams []*param.MethodParam } // Controller defines some basic http request handler operations, such as diff --git a/param/methodparams.go b/param/methodparams.go new file mode 100644 index 00000000..2e6e6840 --- /dev/null +++ b/param/methodparams.go @@ -0,0 +1,101 @@ +package param + +import ( + "fmt" + "reflect" + + beecontext "github.com/astaxie/beego/context" +) + +//Keeps param information to be auto passed to controller methods +type MethodParam struct { + name string + parser paramParser + location paramLocation + required bool + defValue interface{} +} + +type paramLocation byte + +const ( + param paramLocation = iota + body + header +) + +type MethodParamOption func(*MethodParam) + +func Bool(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, boolParser{}, opts) +} + +func String(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, stringParser{}, opts) +} + +func Int(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, intParser{}, opts) +} + +func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { + param = &MethodParam{name: name, parser: parser} + for _, option := range opts { + option(param) + } + return +} + +func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { + result = make([]reflect.Value, 0, len(methodParams)) + i := 0 + for _, p := range methodParams { + var strValue string + var value interface{} + switch p.location { + case body: + strValue = string(ctx.Input.RequestBody) + case header: + strValue = ctx.Input.Header(p.name) + default: + strValue = ctx.Input.Query(p.name) + } + + if strValue == "" { + if p.required { + ctx.Abort(400, "Missing argument "+p.name) + } else if p.defValue != nil { + value = p.defValue + } else { + value = p.parser.zeroValue() + } + } else { + var err error + value, err = p.parser.parse(strValue) + if err != nil { + //TODO: handle err + } + } + reflectValue, err := safeConvert(reflect.ValueOf(value), methodType.In(i)) + if err != nil { + //TODO: handle err + } + result = append(result, reflectValue) + i++ + } + return +} + +func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("%v", r) + } + } + }() + result = value.Convert(t) + return +} diff --git a/param/options.go b/param/options.go new file mode 100644 index 00000000..91d01ac3 --- /dev/null +++ b/param/options.go @@ -0,0 +1,15 @@ +package param + +var InHeader MethodParamOption = func(p *MethodParam) { + p.location = header +} + +var IsRequired MethodParamOption = func(p *MethodParam) { + p.required = true +} + +func Default(defValue interface{}) MethodParamOption { + return func(p *MethodParam) { + p.defValue = defValue + } +} diff --git a/param/parsers.go b/param/parsers.go new file mode 100644 index 00000000..eec0bb79 --- /dev/null +++ b/param/parsers.go @@ -0,0 +1,41 @@ +package param + +import "strconv" + +type paramParser interface { + parse(value string) (interface{}, error) + zeroValue() interface{} +} + +type boolParser struct { +} + +func (p boolParser) parse(value string) (interface{}, error) { + return strconv.ParseBool(value) +} + +func (p boolParser) zeroValue() interface{} { + return false +} + +type stringParser struct { +} + +func (p stringParser) parse(value string) (interface{}, error) { + return value, nil +} + +func (p stringParser) zeroValue() interface{} { + return "" +} + +type intParser struct { +} + +func (p intParser) parse(value string) (interface{}, error) { + return strconv.Atoi(value) +} + +func (p intParser) zeroValue() interface{} { + return 0 +} diff --git a/response/renderer.go b/response/renderer.go new file mode 100644 index 00000000..ca5936db --- /dev/null +++ b/response/renderer.go @@ -0,0 +1,27 @@ +package response + +import ( + "strconv" + + beecontext "github.com/astaxie/beego/context" +) + +type Renderer interface { + Render(ctx *beecontext.Context) +} + +type rendererFunc func(ctx *beecontext.Context) + +func (f rendererFunc) Render(ctx *beecontext.Context) { + f(ctx) +} + +type StatusCode int + +func (s StatusCode) Error() string { + return strconv.Itoa(int(s)) +} + +func (s StatusCode) Render(ctx *beecontext.Context) { + ctx.Output.SetStatus(int(s)) +} diff --git a/response/responses.go b/response/responses.go new file mode 100644 index 00000000..aa042a35 --- /dev/null +++ b/response/responses.go @@ -0,0 +1,44 @@ +package response + +import ( + beecontext "github.com/astaxie/beego/context" +) + +func Json(value interface{}, encoding ...bool) Renderer { + return rendererFunc(func(ctx *beecontext.Context) { + var ( + hasIndent = true + hasEncoding = false + ) + //TODO: need access to BConfig :( + // if BConfig.RunMode == PROD { + // hasIndent = false + // } + if len(encoding) > 0 && encoding[0] { + hasEncoding = true + } + ctx.Output.JSON(value, hasIndent, hasEncoding) + }) +} + +func Error(err error) Renderer { + return rendererFunc(func(ctx *beecontext.Context) { + ctx.Output.SetStatus(500) + ctx.WriteString(err.Error()) + }) +} + +func RenderMethodResult(result interface{}, ctx *beecontext.Context) { + if result != nil { + renderer, ok := result.(Renderer) + if !ok { + err, ok := result.(error) + if ok { + renderer = Error(err) + } else { + renderer = Json(result) + } + } + renderer.Render(ctx) + } +} diff --git a/router.go b/router.go index 9f573f26..4937354a 100644 --- a/router.go +++ b/router.go @@ -29,6 +29,8 @@ import ( beecontext "github.com/astaxie/beego/context" "github.com/astaxie/beego/logs" + "github.com/astaxie/beego/param" + "github.com/astaxie/beego/response" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" ) @@ -116,6 +118,7 @@ type controllerInfo struct { handler http.Handler runFunction FilterFunc routerType int + methodParams []*param.MethodParam } // ControllerRegister containers registered router rules, controller handlers and filters. @@ -151,6 +154,10 @@ 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) { + p.addWithMethodParams(pattern, c, nil, mappingMethods...) +} + +func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInterface, methodParams []*param.MethodParam, mappingMethods ...string) { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() methods := make(map[string]string) @@ -181,6 +188,7 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM route.methods = methods route.routerType = routerTypeBeego route.controllerType = t + route.methodParams = methodParams if len(methods) == 0 { for _, m := range HTTPMETHOD { p.addToRouter(m, pattern, route) @@ -247,7 +255,7 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { key := t.PkgPath() + ":" + t.Name() if comm, ok := GlobalControllerRouter[key]; ok { for _, a := range comm { - p.Add(a.Router, c, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) + p.addWithMethodParams(a.Router, c, a.MethodParams, strings.Join(a.AllowHTTPMethods, ",")+":"+a.Method) } } } @@ -626,11 +634,12 @@ func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath str func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { startTime := time.Now() var ( - runRouter reflect.Type - findRouter bool - runMethod string - routerInfo *controllerInfo - isRunnable bool + runRouter reflect.Type + findRouter bool + runMethod string + methodParams []*param.MethodParam + routerInfo *controllerInfo + isRunnable bool ) context := p.pool.Get().(*beecontext.Context) context.Reset(rw, r) @@ -742,6 +751,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) routerInfo.handler.ServeHTTP(rw, r) } else { runRouter = routerInfo.controllerType + methodParams = routerInfo.methodParams method := r.Method if r.Method == "POST" && context.Input.Query("_method") == "PUT" { method = "PUT" @@ -804,9 +814,14 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) execController.Options() default: if !execController.HandlerFunc(runMethod) { - var in []reflect.Value method := vc.MethodByName(runMethod) - method.Call(in) + var in []reflect.Value = param.ConvertParams(methodParams, method.Type(), context) + out := method.Call(in) + + //For backward compatibility we only handle response if we had incoming methodParams + if methodParams != nil { + p.handleParamResponse(context, execController, out) + } } } @@ -886,6 +901,17 @@ Admin: } } +func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, execController ControllerInterface, results []reflect.Value) { + //looping in reverse order for the case when both error and value are returned and error sets the response status code + for i := len(results) - 1; i >= 0; i-- { + result := results[i] + if !result.IsNil() { + resultValue := result.Interface() + response.RenderMethodResult(resultValue, context) + } + } +} + // FindRouter Find Router info for URL func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *controllerInfo, isFind bool) { var urlPath = context.Input.URL() From 712df81c99796febb88d6c1a374bc1f1d99f9330 Mon Sep 17 00:00:00 2001 From: OlegFX Date: Fri, 21 Apr 2017 19:57:04 +0300 Subject: [PATCH 071/163] Fixed InsertOrUpdate bug --- orm/db.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/db.go b/orm/db.go index 1544c479..e9508ee0 100644 --- a/orm/db.go +++ b/orm/db.go @@ -592,7 +592,7 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a row := q.QueryRow(query, values...) var id int64 err = row.Scan(&id) - if err.Error() == `pq: syntax error at or near "ON"` { + if err != nil && err.Error() == `pq: syntax error at or near "ON"` { err = fmt.Errorf("postgres version must 9.5 or higher") } return id, err From 89e01d125c274d636f4c7eeb545be38bdefef139 Mon Sep 17 00:00:00 2001 From: eyalpost Date: Sun, 23 Apr 2017 01:33:50 +0300 Subject: [PATCH 072/163] all types implemented --- param/methodparams.go | 82 ++++++++++++++++++++++++++----------------- param/options.go | 18 +++++++--- param/parsers.go | 58 +++++++++++++++++++++--------- response/renderer.go | 9 +++++ response/responses.go | 10 ++++-- 5 files changed, 123 insertions(+), 54 deletions(-) diff --git a/param/methodparams.go b/param/methodparams.go index 2e6e6840..9cd2b1d6 100644 --- a/param/methodparams.go +++ b/param/methodparams.go @@ -5,6 +5,7 @@ import ( "reflect" beecontext "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" ) //Keeps param information to be auto passed to controller methods @@ -13,7 +14,7 @@ type MethodParam struct { parser paramParser location paramLocation required bool - defValue interface{} + defValue string } type paramLocation byte @@ -38,6 +39,18 @@ func Int(name string, opts ...MethodParamOption) *MethodParam { return newParam(name, intParser{}, opts) } +func Float(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, floatParser{}, opts) +} + +func Time(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, timeParser{}, opts) +} + +func Json(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, jsonParser{}, opts) +} + func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { param = &MethodParam{name: name, parser: parser} for _, option := range opts { @@ -46,42 +59,47 @@ func newParam(name string, parser paramParser, opts []MethodParamOption) (param return } -func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { - result = make([]reflect.Value, 0, len(methodParams)) - i := 0 - for _, p := range methodParams { - var strValue string - var value interface{} - switch p.location { - case body: - strValue = string(ctx.Input.RequestBody) - case header: - strValue = ctx.Input.Header(p.name) - default: - strValue = ctx.Input.Query(p.name) +func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { + var strValue string + var reflectValue reflect.Value + switch param.location { + case body: + strValue = string(ctx.Input.RequestBody) + case header: + strValue = ctx.Input.Header(param.name) + default: + strValue = ctx.Input.Query(param.name) + } + + if strValue == "" { + if param.required { + ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) + } else { + strValue = param.defValue + } + } + if strValue == "" { + reflectValue = reflect.Zero(paramType) + } else { + value, err := param.parser.parse(strValue, paramType) + if err != nil { + logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %s, Parser: %s, Error: %s", param.name, paramType.Name(), strValue, reflect.TypeOf(param.parser).Name(), err)) + ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %s to type %s", param.name, strValue, paramType.Name())) } - if strValue == "" { - if p.required { - ctx.Abort(400, "Missing argument "+p.name) - } else if p.defValue != nil { - value = p.defValue - } else { - value = p.parser.zeroValue() - } - } else { - var err error - value, err = p.parser.parse(strValue) - if err != nil { - //TODO: handle err - } - } - reflectValue, err := safeConvert(reflect.ValueOf(value), methodType.In(i)) + reflectValue, err = safeConvert(reflect.ValueOf(value), paramType) if err != nil { - //TODO: handle err + panic(err) } + } + return reflectValue +} + +func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { + result = make([]reflect.Value, 0, len(methodParams)) + for i := 0; i < len(methodParams); i++ { + reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) result = append(result, reflectValue) - i++ } return } diff --git a/param/options.go b/param/options.go index 91d01ac3..6fe10108 100644 --- a/param/options.go +++ b/param/options.go @@ -1,15 +1,25 @@ package param -var InHeader MethodParamOption = func(p *MethodParam) { - p.location = header -} +import ( + "fmt" +) var IsRequired MethodParamOption = func(p *MethodParam) { p.required = true } +var InHeader MethodParamOption = func(p *MethodParam) { + p.location = header +} + +var InBody MethodParamOption = func(p *MethodParam) { + p.location = body +} + func Default(defValue interface{}) MethodParamOption { return func(p *MethodParam) { - p.defValue = defValue + if defValue != nil { + p.defValue = fmt.Sprintf("%v", defValue) + } } } diff --git a/param/parsers.go b/param/parsers.go index eec0bb79..e3032e20 100644 --- a/param/parsers.go +++ b/param/parsers.go @@ -1,41 +1,67 @@ package param -import "strconv" +import ( + "encoding/json" + "reflect" + "strconv" + "time" +) type paramParser interface { - parse(value string) (interface{}, error) - zeroValue() interface{} + parse(value string, toType reflect.Type) (interface{}, error) } type boolParser struct { } -func (p boolParser) parse(value string) (interface{}, error) { +func (p boolParser) parse(value string, toType reflect.Type) (interface{}, error) { return strconv.ParseBool(value) } -func (p boolParser) zeroValue() interface{} { - return false -} - type stringParser struct { } -func (p stringParser) parse(value string) (interface{}, error) { +func (p stringParser) parse(value string, toType reflect.Type) (interface{}, error) { return value, nil } -func (p stringParser) zeroValue() interface{} { - return "" -} - type intParser struct { } -func (p intParser) parse(value string) (interface{}, error) { +func (p intParser) parse(value string, toType reflect.Type) (interface{}, error) { return strconv.Atoi(value) } -func (p intParser) zeroValue() interface{} { - return 0 +type floatParser struct { +} + +func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) { + if toType.Kind() == reflect.Float32 { + return strconv.ParseFloat(value, 32) + } + return strconv.ParseFloat(value, 64) +} + +type timeParser struct { +} + +func (p timeParser) parse(value string, toType reflect.Type) (result interface{}, err error) { + result, err = time.Parse(time.RFC3339, value) + if err != nil { + result, err = time.Parse("2006-01-02", value) + } + return +} + +type jsonParser struct { +} + +func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error) { + pResult := reflect.New(toType) + v := pResult.Interface() + err := json.Unmarshal([]byte(value), v) + if err != nil { + return nil, err + } + return pResult.Elem().Interface(), nil } diff --git a/response/renderer.go b/response/renderer.go index ca5936db..f5f9a52d 100644 --- a/response/renderer.go +++ b/response/renderer.go @@ -25,3 +25,12 @@ func (s StatusCode) Error() string { func (s StatusCode) Render(ctx *beecontext.Context) { ctx.Output.SetStatus(int(s)) } + +type statusCodeWithRender struct { + statusCode int + rendererFunc +} + +func (s statusCodeWithRender) Error() string { + return strconv.Itoa(s.statusCode) +} diff --git a/response/responses.go b/response/responses.go index aa042a35..5fbe4be1 100644 --- a/response/responses.go +++ b/response/responses.go @@ -21,20 +21,26 @@ func Json(value interface{}, encoding ...bool) Renderer { }) } -func Error(err error) Renderer { +func errorRenderer(err error) Renderer { return rendererFunc(func(ctx *beecontext.Context) { ctx.Output.SetStatus(500) ctx.WriteString(err.Error()) }) } +func Redirect(localurl string) statusCodeWithRender { + return statusCodeWithRender{302, func(ctx *beecontext.Context) { + ctx.Redirect(302, localurl) + }} +} + func RenderMethodResult(result interface{}, ctx *beecontext.Context) { if result != nil { renderer, ok := result.(Renderer) if !ok { err, ok := result.(error) if ok { - renderer = Error(err) + renderer = errorRenderer(err) } else { renderer = Json(result) } From bf6bd6b292a9c8c1d3835ef607daa1446f7b8a7d Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Sun, 23 Apr 2017 19:07:12 +0200 Subject: [PATCH 073/163] Fixes #2587 Fixes warnings and errors raised by gometalinter and gosimple. --- admin.go | 10 +++--- grace/server.go | 1 - logs/jianliao.go | 2 -- logs/slack.go | 2 -- logs/smtp.go | 2 -- namespace.go | 11 +++---- router.go | 49 +++++++++++++++-------------- router_test.go | 10 +++++- session/couchbase/sess_couchbase.go | 10 +++--- session/ledis/ledis_session.go | 47 ++++++++++++++------------- session/memcache/sess_memcache.go | 1 - session/mysql/sess_mysql.go | 1 - session/postgres/sess_postgresql.go | 1 - session/redis/sess_redis.go | 33 ++++++++----------- session/sess_cookie.go | 24 ++++++-------- session/ssdb/sess_ssdb.go | 11 +++---- staticfile.go | 2 -- 17 files changed, 99 insertions(+), 118 deletions(-) diff --git a/admin.go b/admin.go index b4e3068f..80615957 100644 --- a/admin.go +++ b/admin.go @@ -208,7 +208,7 @@ func printTree(resultList *[][]string, t *Tree) { printTree(resultList, t.wildcard) } for _, l := range t.leaves { - if v, ok := l.runObject.(*controllerInfo); ok { + if v, ok := l.runObject.(*ControllerInfo); ok { if v.routerType == routerTypeBeego { var result = []string{ v.pattern, @@ -276,8 +276,8 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { // it's in "/healthcheck" pattern in admin module. func healthcheck(rw http.ResponseWriter, req *http.Request) { var ( + result []string data = make(map[interface{}]interface{}) - result = []string{} resultList = new([][]string) content = map[string]interface{}{ "Fields": []string{"Name", "Message", "Status"}, @@ -291,17 +291,16 @@ func healthcheck(rw http.ResponseWriter, req *http.Request) { name, err.Error(), } - } else { result = []string{ "success", name, "OK", } - } *resultList = append(*resultList, result) } + content["Data"] = resultList data["Content"] = content data["Title"] = "Health Check" @@ -330,7 +329,6 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { // List Tasks content := make(map[string]interface{}) resultList := new([][]string) - var result = []string{} var fields = []string{ "Task Name", "Task Spec", @@ -339,7 +337,7 @@ func taskStatus(rw http.ResponseWriter, req *http.Request) { "", } for tname, tk := range toolbox.AdminTaskList { - result = []string{ + result := []string{ tname, tk.GetSpec(), tk.GetStatus(), diff --git a/grace/server.go b/grace/server.go index cc985552..c71193ad 100644 --- a/grace/server.go +++ b/grace/server.go @@ -196,7 +196,6 @@ func (srv *Server) signalHooks(ppFlag int, sig os.Signal) { for _, f := range srv.SignalHooks[ppFlag][sig] { f() } - return } // shutdown closes the listener so that no new connections are accepted. it also diff --git a/logs/jianliao.go b/logs/jianliao.go index 16bbdc2e..88ba0f9a 100644 --- a/logs/jianliao.go +++ b/logs/jianliao.go @@ -61,12 +61,10 @@ func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (s *JLWriter) Flush() { - return } // Destroy implementing method. empty. func (s *JLWriter) Destroy() { - return } func init() { diff --git a/logs/slack.go b/logs/slack.go index d8482b3e..1cd2e5ae 100644 --- a/logs/slack.go +++ b/logs/slack.go @@ -49,12 +49,10 @@ func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (s *SLACKWriter) Flush() { - return } // Destroy implementing method. empty. func (s *SLACKWriter) Destroy() { - return } func init() { diff --git a/logs/smtp.go b/logs/smtp.go index 2ec0514d..6208d7b8 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -138,12 +138,10 @@ func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error { // Flush implementing method. empty. func (s *SMTPWriter) Flush() { - return } // Destroy implementing method. empty. func (s *SMTPWriter) Destroy() { - return } func init() { diff --git a/namespace.go b/namespace.go index cfde0111..72f22a72 100644 --- a/namespace.go +++ b/namespace.go @@ -267,13 +267,12 @@ func addPrefix(t *Tree, prefix string) { addPrefix(t.wildcard, prefix) } for _, l := range t.leaves { - if c, ok := l.runObject.(*controllerInfo); ok { + if c, ok := l.runObject.(*ControllerInfo); ok { if !strings.HasPrefix(c.pattern, prefix) { c.pattern = prefix + c.pattern } } } - } // NSCond is Namespace Condition @@ -284,16 +283,16 @@ func NSCond(cond namespaceCond) LinkNamespace { } // NSBefore Namespace BeforeRouter filter -func NSBefore(filiterList ...FilterFunc) LinkNamespace { +func NSBefore(filterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { - ns.Filter("before", filiterList...) + ns.Filter("before", filterList...) } } // NSAfter add Namespace FinishRouter filter -func NSAfter(filiterList ...FilterFunc) LinkNamespace { +func NSAfter(filterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { - ns.Filter("after", filiterList...) + ns.Filter("after", filterList...) } } diff --git a/router.go b/router.go index 9f573f26..c6ec8af6 100644 --- a/router.go +++ b/router.go @@ -109,7 +109,8 @@ func ExceptMethodAppend(action string) { exceptMethod = append(exceptMethod, action) } -type controllerInfo struct { +// ControllerInfo holds information about the controller. +type ControllerInfo struct { pattern string controllerType reflect.Type methods map[string]string @@ -176,7 +177,7 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM } } - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.methods = methods route.routerType = routerTypeBeego @@ -198,7 +199,7 @@ func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingM } } -func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerInfo) { +func (p *ControllerRegister) addToRouter(method, pattern string, r *ControllerInfo) { if !BConfig.RouterCaseSensitive { pattern = strings.ToLower(pattern) } @@ -335,7 +336,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { if _, ok := HTTPMETHOD[method]; method != "*" && !ok { panic("not support http method: " + method) } - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeRESTFul route.runFunction = f @@ -361,7 +362,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { // Handler add user defined Handler func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { - route := &controllerInfo{} + route := &ControllerInfo{} route.pattern = pattern route.routerType = routerTypeHandler route.handler = h @@ -396,7 +397,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) controllerName := strings.TrimSuffix(ct.Name(), "Controller") for i := 0; i < rt.NumMethod(); i++ { if !utils.InSlice(rt.Method(i).Name, exceptMethod) { - route := &controllerInfo{} + route := &ControllerInfo{} route.routerType = routerTypeBeego route.methods = map[string]string{"*": rt.Method(i).Name} route.controllerType = ct @@ -502,7 +503,7 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin } } for _, l := range t.leaves { - if c, ok := l.runObject.(*controllerInfo); ok { + if c, ok := l.runObject.(*ControllerInfo); ok { if c.routerType == routerTypeBeego && strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) { find := false @@ -629,7 +630,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) runRouter reflect.Type findRouter bool runMethod string - routerInfo *controllerInfo + routerInfo *ControllerInfo isRunnable bool ) context := p.pool.Get().(*beecontext.Context) @@ -670,7 +671,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) goto Admin } - if r.Method != "GET" && r.Method != "HEAD" { + if r.Method != http.MethodGet && r.Method != http.MethodHead { if BConfig.CopyRequestBody && !context.Input.IsUpload() { context.Input.CopyBody(BConfig.MaxMemory) } @@ -743,11 +744,11 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) } else { runRouter = routerInfo.controllerType method := r.Method - if r.Method == "POST" && context.Input.Query("_method") == "PUT" { - method = "PUT" + if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodPost { + method = http.MethodPut } - if r.Method == "POST" && context.Input.Query("_method") == "DELETE" { - method = "DELETE" + if r.Method == http.MethodPost && context.Input.Query("_method") == http.MethodDelete { + method = http.MethodDelete } if m, ok := routerInfo.methods[method]; ok { runMethod = m @@ -777,8 +778,8 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if BConfig.WebConfig.EnableXSRF { execController.XSRFToken() - if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || - (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { + if r.Method == http.MethodPost || r.Method == http.MethodDelete || r.Method == http.MethodPut || + (r.Method == http.MethodPost && (context.Input.Query("_method") == http.MethodDelete || context.Input.Query("_method") == http.MethodPut)) { execController.CheckXSRFCookie() } } @@ -788,19 +789,19 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) if !context.ResponseWriter.Started { //exec main logic switch runMethod { - case "GET": + case http.MethodGet: execController.Get() - case "POST": + case http.MethodPost: execController.Post() - case "DELETE": + case http.MethodDelete: execController.Delete() - case "PUT": + case http.MethodPut: execController.Put() - case "HEAD": + case http.MethodHead: execController.Head() - case "PATCH": + case http.MethodPatch: execController.Patch() - case "OPTIONS": + case http.MethodOptions: execController.Options() default: if !execController.HandlerFunc(runMethod) { @@ -887,7 +888,7 @@ Admin: } // FindRouter Find Router info for URL -func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *controllerInfo, isFind bool) { +func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo *ControllerInfo, isFind bool) { var urlPath = context.Input.URL() if !BConfig.RouterCaseSensitive { urlPath = strings.ToLower(urlPath) @@ -895,7 +896,7 @@ func (p *ControllerRegister) FindRouter(context *beecontext.Context) (routerInfo httpMethod := context.Input.Method() if t, ok := p.routers[httpMethod]; ok { runObject := t.Match(urlPath, context) - if r, ok := runObject.(*controllerInfo); ok { + if r, ok := runObject.(*ControllerInfo); ok { return r, true } } diff --git a/router_test.go b/router_test.go index 25804d67..720b4ca8 100644 --- a/router_test.go +++ b/router_test.go @@ -654,32 +654,40 @@ func TestFilterFinishRouterMulti(t *testing.T) { } func beegoFilterNoOutput(ctx *context.Context) { - return } + func beegoBeforeRouter1(ctx *context.Context) { ctx.WriteString("|BeforeRouter1") } + func beegoBeforeRouter2(ctx *context.Context) { ctx.WriteString("|BeforeRouter2") } + func beegoBeforeExec1(ctx *context.Context) { ctx.WriteString("|BeforeExec1") } + func beegoBeforeExec2(ctx *context.Context) { ctx.WriteString("|BeforeExec2") } + func beegoAfterExec1(ctx *context.Context) { ctx.WriteString("|AfterExec1") } + func beegoAfterExec2(ctx *context.Context) { ctx.WriteString("|AfterExec2") } + func beegoFinishRouter1(ctx *context.Context) { ctx.WriteString("|FinishRouter1") } + func beegoFinishRouter2(ctx *context.Context) { ctx.WriteString("|FinishRouter2") } + func beegoResetParams(ctx *context.Context) { ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat")) } diff --git a/session/couchbase/sess_couchbase.go b/session/couchbase/sess_couchbase.go index d5be11d0..791a6cc7 100644 --- a/session/couchbase/sess_couchbase.go +++ b/session/couchbase/sess_couchbase.go @@ -155,10 +155,13 @@ func (cp *Provider) SessionInit(maxlifetime int64, savePath string) error { func (cp *Provider) SessionRead(sid string) (session.Store, error) { cp.b = cp.getBucket() - var doc []byte + var ( + kv map[interface{}]interface{} + err error + doc []byte + ) - err := cp.b.Get(sid, &doc) - var kv map[interface{}]interface{} + err = cp.b.Get(sid, &doc) if doc == nil { kv = make(map[interface{}]interface{}) } else { @@ -230,7 +233,6 @@ func (cp *Provider) SessionDestroy(sid string) error { // SessionGC Recycle func (cp *Provider) SessionGC() { - return } // SessionAll return all active session diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go index 4892a8ab..18b27708 100644 --- a/session/ledis/ledis_session.go +++ b/session/ledis/ledis_session.go @@ -12,8 +12,10 @@ import ( "github.com/siddontang/ledisdb/ledis" ) -var ledispder = &Provider{} -var c *ledis.DB +var ( + ledispder = &Provider{} + c *ledis.DB +) // SessionStore ledis session store type SessionStore struct { @@ -97,27 +99,36 @@ func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error { } cfg := new(config.Config) cfg.DataDir = lp.savePath - nowLedis, err := ledis.Open(cfg) - c, err = nowLedis.Select(lp.db) + + var ledisInstance *ledis.Ledis + ledisInstance, err = ledis.Open(cfg) if err != nil { - println(err) - return nil + return err } - return nil + c, err = ledisInstance.Select(lp.db) + return err } // SessionRead read ledis session by sid func (lp *Provider) SessionRead(sid string) (session.Store, error) { - kvs, err := c.Get([]byte(sid)) - var kv map[interface{}]interface{} + var ( + kv map[interface{}]interface{} + kvs []byte + err error + ) + + if kvs, err = c.Get([]byte(sid)); err != nil { + return nil, err + } + if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob(kvs) - if err != nil { + if kv, err = session.DecodeGob(kvs); err != nil { return nil, err } } + ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} return ls, nil } @@ -142,18 +153,7 @@ func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) c.Set([]byte(sid), data) c.Expire([]byte(sid), lp.maxlifetime) } - kvs, err := c.Get([]byte(sid)) - var kv map[interface{}]interface{} - if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob(kvs) - if err != nil { - return nil, err - } - } - ls := &SessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime} - return ls, nil + return lp.SessionRead(sid) } // SessionDestroy delete ledis session by id @@ -164,7 +164,6 @@ func (lp *Provider) SessionDestroy(sid string) error { // SessionGC Impelment method, no used. func (lp *Provider) SessionGC() { - return } // SessionAll return all active session diff --git a/session/memcache/sess_memcache.go b/session/memcache/sess_memcache.go index cad02258..755979c4 100644 --- a/session/memcache/sess_memcache.go +++ b/session/memcache/sess_memcache.go @@ -215,7 +215,6 @@ func (rp *MemProvider) connectInit() error { // SessionGC Impelment method, no used. func (rp *MemProvider) SessionGC() { - return } // SessionAll return all activeSession diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 7bcf959c..4c9251e7 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -209,7 +209,6 @@ func (mp *Provider) SessionGC() { c := mp.connectInit() c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) c.Close() - return } // SessionAll count values in mysql session diff --git a/session/postgres/sess_postgresql.go b/session/postgres/sess_postgresql.go index f00e0711..ffc27def 100644 --- a/session/postgres/sess_postgresql.go +++ b/session/postgres/sess_postgresql.go @@ -224,7 +224,6 @@ func (mp *Provider) SessionGC() { c := mp.connectInit() c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime) c.Close() - return } // SessionAll count values in postgresql session diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index 14bbd4c1..08efa6e1 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -155,7 +155,7 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { return nil, err } if rp.password != "" { - if _, err := c.Do("AUTH", rp.password); err != nil { + if _, err = c.Do("AUTH", rp.password); err != nil { c.Close() return nil, err } @@ -176,13 +176,20 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) { c := rp.poollist.Get() defer c.Close() - kvs, err := redis.String(c.Do("GET", sid)) - var kv map[interface{}]interface{} + var ( + kv map[interface{}]interface{} + kvs string + err error + ) + + if kvs, err = redis.String(c.Do("GET", sid)); err != nil { + return nil, err + } + if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { - kv, err = session.DecodeGob([]byte(kvs)) - if err != nil { + if kv, err = session.DecodeGob([]byte(kvs)); err != nil { return nil, err } } @@ -216,20 +223,7 @@ func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) c.Do("RENAME", oldsid, sid) c.Do("EXPIRE", sid, rp.maxlifetime) } - - kvs, err := redis.String(c.Do("GET", sid)) - var kv map[interface{}]interface{} - if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) - } else { - kv, err = session.DecodeGob([]byte(kvs)) - if err != nil { - return nil, err - } - } - - rs := &SessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime} - return rs, nil + return rp.SessionRead(sid) } // SessionDestroy delete redis session by id @@ -243,7 +237,6 @@ func (rp *Provider) SessionDestroy(sid string) error { // SessionGC Impelment method, no used. func (rp *Provider) SessionGC() { - return } // SessionAll return all activeSession diff --git a/session/sess_cookie.go b/session/sess_cookie.go index 3fefa360..145e53c9 100644 --- a/session/sess_cookie.go +++ b/session/sess_cookie.go @@ -74,21 +74,16 @@ func (st *CookieSessionStore) SessionID() string { // SessionRelease Write cookie session to http response cookie func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) { - str, err := encodeCookie(cookiepder.block, - cookiepder.config.SecurityKey, - cookiepder.config.SecurityName, - st.values) - if err != nil { - return + encodedCookie, err := encodeCookie(cookiepder.block, cookiepder.config.SecurityKey, cookiepder.config.SecurityName, st.values) + if err == nil { + cookie := &http.Cookie{Name: cookiepder.config.CookieName, + Value: url.QueryEscape(encodedCookie), + Path: "/", + HttpOnly: true, + Secure: cookiepder.config.Secure, + MaxAge: cookiepder.config.Maxage} + http.SetCookie(w, cookie) } - cookie := &http.Cookie{Name: cookiepder.config.CookieName, - Value: url.QueryEscape(str), - Path: "/", - HttpOnly: true, - Secure: cookiepder.config.Secure, - MaxAge: cookiepder.config.Maxage} - http.SetCookie(w, cookie) - return } type cookieConfig struct { @@ -166,7 +161,6 @@ func (pder *CookieProvider) SessionDestroy(sid string) error { // SessionGC Implement method, no used. func (pder *CookieProvider) SessionGC() { - return } // SessionAll Implement method, return 0. diff --git a/session/ssdb/sess_ssdb.go b/session/ssdb/sess_ssdb.go index c846e73a..03b60793 100644 --- a/session/ssdb/sess_ssdb.go +++ b/session/ssdb/sess_ssdb.go @@ -30,13 +30,13 @@ func (p *SsdbProvider) connectInit() error { } func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error { - var e error = nil p.maxLifetime = maxLifetime address := strings.Split(savePath, ":") p.host = address[0] - p.port, e = strconv.Atoi(address[1]) - if e != nil { - return e + + var err error + if p.port, err = strconv.Atoi(address[1]); err != nil { + return err } return p.connectInit() } @@ -78,8 +78,8 @@ func (p *SsdbProvider) SessionExist(sid string) bool { return false } return true - } + func (p *SsdbProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) { //conn.Do("setx", key, v, ttl) if p.client == nil { @@ -123,7 +123,6 @@ func (p *SsdbProvider) SessionDestroy(sid string) error { } func (p *SsdbProvider) SessionGC() { - return } func (p *SsdbProvider) SessionAll() int { diff --git a/staticfile.go b/staticfile.go index 7c270947..bbb2a1fb 100644 --- a/staticfile.go +++ b/staticfile.go @@ -90,8 +90,6 @@ func serverStaticRouter(ctx *context.Context) { } http.ServeContent(ctx.ResponseWriter, ctx.Request, filePath, sch.modTime, sch) - return - } type serveContentHolder struct { From 19f4a6ac0b51cc685fc966b7e8ca12fd89e5bc43 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Sun, 23 Apr 2017 21:37:09 +0300 Subject: [PATCH 074/163] slice support --- param/conv.go | 68 ++++++++++++++++++++++++++++++++++++++++ param/methodparams.go | 72 +++---------------------------------------- param/parsers.go | 29 ++++++++++++++++- router.go | 2 +- 4 files changed, 102 insertions(+), 69 deletions(-) create mode 100644 param/conv.go diff --git a/param/conv.go b/param/conv.go new file mode 100644 index 00000000..e909c4ea --- /dev/null +++ b/param/conv.go @@ -0,0 +1,68 @@ +package param + +import ( + "fmt" + "reflect" + + beecontext "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" +) + +func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { + var strValue string + var reflectValue reflect.Value + switch param.location { + case body: + strValue = string(ctx.Input.RequestBody) + case header: + strValue = ctx.Input.Header(param.name) + default: + strValue = ctx.Input.Query(param.name) + } + + if strValue == "" { + if param.required { + ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) + } else { + strValue = param.defValue + } + } + if strValue == "" { + reflectValue = reflect.Zero(paramType) + } else { + value, err := param.parser.parse(strValue, paramType) + if err != nil { + logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %s, Error: %s", param.name, paramType, strValue, err)) + ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %s to type %s", param.name, strValue, paramType)) + } + + reflectValue, err = safeConvert(reflect.ValueOf(value), paramType) + if err != nil { + panic(err) + } + } + return reflectValue +} + +func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { + result = make([]reflect.Value, 0, len(methodParams)) + for i := 0; i < len(methodParams); i++ { + reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) + result = append(result, reflectValue) + } + return +} + +func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { + defer func() { + if r := recover(); r != nil { + var ok bool + err, ok = r.(error) + if !ok { + err = fmt.Errorf("%v", r) + } + } + }() + result = value.Convert(t) + return +} diff --git a/param/methodparams.go b/param/methodparams.go index 9cd2b1d6..a3e25a55 100644 --- a/param/methodparams.go +++ b/param/methodparams.go @@ -1,13 +1,5 @@ package param -import ( - "fmt" - "reflect" - - beecontext "github.com/astaxie/beego/context" - "github.com/astaxie/beego/logs" -) - //Keeps param information to be auto passed to controller methods type MethodParam struct { name string @@ -51,6 +43,11 @@ func Json(name string, opts ...MethodParamOption) *MethodParam { return newParam(name, jsonParser{}, opts) } +func AsSlice(param *MethodParam) *MethodParam { + param.parser = sliceParser(param.parser) + return param +} + func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { param = &MethodParam{name: name, parser: parser} for _, option := range opts { @@ -58,62 +55,3 @@ func newParam(name string, parser paramParser, opts []MethodParamOption) (param } return } - -func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { - var strValue string - var reflectValue reflect.Value - switch param.location { - case body: - strValue = string(ctx.Input.RequestBody) - case header: - strValue = ctx.Input.Header(param.name) - default: - strValue = ctx.Input.Query(param.name) - } - - if strValue == "" { - if param.required { - ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) - } else { - strValue = param.defValue - } - } - if strValue == "" { - reflectValue = reflect.Zero(paramType) - } else { - value, err := param.parser.parse(strValue, paramType) - if err != nil { - logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %s, Parser: %s, Error: %s", param.name, paramType.Name(), strValue, reflect.TypeOf(param.parser).Name(), err)) - ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %s to type %s", param.name, strValue, paramType.Name())) - } - - reflectValue, err = safeConvert(reflect.ValueOf(value), paramType) - if err != nil { - panic(err) - } - } - return reflectValue -} - -func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { - result = make([]reflect.Value, 0, len(methodParams)) - for i := 0; i < len(methodParams); i++ { - reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) - result = append(result, reflectValue) - } - return -} - -func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { - defer func() { - if r := recover(); r != nil { - var ok bool - err, ok = r.(error) - if !ok { - err = fmt.Errorf("%v", r) - } - } - }() - result = value.Convert(t) - return -} diff --git a/param/parsers.go b/param/parsers.go index e3032e20..cfa1a981 100644 --- a/param/parsers.go +++ b/param/parsers.go @@ -4,6 +4,7 @@ import ( "encoding/json" "reflect" "strconv" + "strings" "time" ) @@ -11,6 +12,12 @@ type paramParser interface { parse(value string, toType reflect.Type) (interface{}, error) } +type parserFunc func(value string, toType reflect.Type) (interface{}, error) + +func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error) { + return f(value, toType) +} + type boolParser struct { } @@ -37,7 +44,11 @@ type floatParser struct { func (p floatParser) parse(value string, toType reflect.Type) (interface{}, error) { if toType.Kind() == reflect.Float32 { - return strconv.ParseFloat(value, 32) + res, err := strconv.ParseFloat(value, 32) + if err != nil { + return nil, err + } + return float32(res), nil } return strconv.ParseFloat(value, 64) } @@ -65,3 +76,19 @@ func (p jsonParser) parse(value string, toType reflect.Type) (interface{}, error } return pResult.Elem().Interface(), nil } + +func sliceParser(elemParser paramParser) paramParser { + return parserFunc(func(value string, toType reflect.Type) (interface{}, error) { + values := strings.Split(value, ",") + result := reflect.MakeSlice(toType, 0, len(values)) + elemType := toType.Elem() + for _, v := range values { + parsedValue, err := elemParser.parse(v, elemType) + if err != nil { + return nil, err + } + result = reflect.Append(result, reflect.ValueOf(parsedValue)) + } + return result.Interface(), nil + }) +} diff --git a/router.go b/router.go index 4937354a..be69419c 100644 --- a/router.go +++ b/router.go @@ -905,7 +905,7 @@ func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, ex //looping in reverse order for the case when both error and value are returned and error sets the response status code for i := len(results) - 1; i >= 0; i-- { result := results[i] - if !result.IsNil() { + if result.Kind() != reflect.Interface || !result.IsNil() { resultValue := result.Interface() response.RenderMethodResult(resultValue, context) } From 864693d2f840374460950e3f393ee48e5b8e868f Mon Sep 17 00:00:00 2001 From: eyalpost Date: Mon, 24 Apr 2017 02:35:04 +0300 Subject: [PATCH 075/163] mall fixes --- cache/conv.go | 2 +- param/options.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cache/conv.go b/cache/conv.go index dbdff1c7..87800586 100644 --- a/cache/conv.go +++ b/cache/conv.go @@ -28,7 +28,7 @@ func GetString(v interface{}) string { return string(result) default: if v != nil { - return fmt.Sprintf("%v", result) + return fmt.Sprint(result) } } return "" diff --git a/param/options.go b/param/options.go index 6fe10108..0692f9d1 100644 --- a/param/options.go +++ b/param/options.go @@ -19,7 +19,7 @@ var InBody MethodParamOption = func(p *MethodParam) { func Default(defValue interface{}) MethodParamOption { return func(p *MethodParam) { if defValue != nil { - p.defValue = fmt.Sprintf("%v", defValue) + p.defValue = fmt.Sprint(defValue) } } } From 52f916a28a8b73e81606ac2b6142e113f64e1e8d Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 24 Apr 2017 21:10:03 +0800 Subject: [PATCH 076/163] support Go1.8 default GOPATH --- router.go | 7 ++----- utils/utils.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 utils/utils.go diff --git a/router.go b/router.go index 9f573f26..55a6553f 100644 --- a/router.go +++ b/router.go @@ -17,7 +17,6 @@ package beego import ( "fmt" "net/http" - "os" "path" "path/filepath" "reflect" @@ -219,13 +218,11 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { for _, c := range cList { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() - gopath := os.Getenv("GOPATH") - if gopath == "" { + wgopath := utils.GetGOPATHs() + if len(wgopath) == 0 { panic("you are in dev mode. So please set gopath") } pkgpath := "" - - wgopath := filepath.SplitList(gopath) for _, wg := range wgopath { wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", t.PkgPath())) if utils.FileExists(wg) { diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 00000000..ed885787 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,30 @@ +package utils + +import ( + "os" + "path/filepath" + "runtime" + "strings" +) + +// GetGOPATHs returns all paths in GOPATH variable. +func GetGOPATHs() []string { + gopath := os.Getenv("GOPATH") + if gopath == "" && strings.Compare(runtime.Version(), "go1.8") >= 0 { + gopath = defaultGOPATH() + } + return filepath.SplitList(gopath) +} + +func defaultGOPATH() string { + env := "HOME" + if runtime.GOOS == "windows" { + env = "USERPROFILE" + } else if runtime.GOOS == "plan9" { + env = "home" + } + if home := os.Getenv(env); home != "" { + return filepath.Join(home, "go") + } + return "" +} From e810f2e93070919cb9afe287f0091d905408e4d6 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 24 Apr 2017 21:36:07 +0800 Subject: [PATCH 077/163] add more oracle alias --- orm/db_alias.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/orm/db_alias.go b/orm/db_alias.go index 8daa4b23..e70db9f0 100644 --- a/orm/db_alias.go +++ b/orm/db_alias.go @@ -60,6 +60,8 @@ var ( "sqlite3": DRSqlite, "tidb": DRTiDB, "oracle": DROracle, + "oci8": DROracle, // github.com/mattn/go-oci8 + "ora": DROracle, //https://github.com/rana/ora } dbBasers = map[DriverType]dbBaser{ DRMySQL: newdbBaseMysql(), From 3742d1178ca9b2c429ed8fe91360002a941d4b89 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 25 Apr 2017 20:42:43 +0800 Subject: [PATCH 078/163] httplib support delete params fix #2593 --- httplib/httplib.go | 2 +- httplib/httplib_test.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 32edc5c4..f232f31b 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -344,7 +344,7 @@ func (b *BeegoHTTPRequest) buildURL(paramBody string) { } // build POST/PUT/PATCH url and body - if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil { + if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH" || b.req.Method == "DELETE") && b.req.Body == nil { // with files if len(b.files) > 0 { pr, pw := io.Pipe() diff --git a/httplib/httplib_test.go b/httplib/httplib_test.go index 05815054..32d3e7f6 100644 --- a/httplib/httplib_test.go +++ b/httplib/httplib_test.go @@ -102,6 +102,14 @@ func TestSimpleDelete(t *testing.T) { t.Log(str) } +func TestSimpleDeleteParam(t *testing.T) { + str, err := Delete("http://httpbin.org/delete").Param("key", "val").String() + if err != nil { + t.Fatal(err) + } + t.Log(str) +} + func TestWithCookie(t *testing.T) { v := "smallfish" str, err := Get("http://httpbin.org/cookies/set?k1=" + v).SetEnableCookie(true).String() From 9b79437778cf2b78f00d71c963cfc4732a01b39e Mon Sep 17 00:00:00 2001 From: eyalpost Date: Tue, 25 Apr 2017 16:00:49 +0300 Subject: [PATCH 079/163] all types working + controller comments generation --- param/conv.go | 58 +++++++----- param/methodparams.go | 72 ++++++++------- param/options.go | 6 ++ param/parsers.go | 57 ++++++++++++ parser.go | 209 +++++++++++++++++++++++++++++++++++------- router.go | 3 + 6 files changed, 317 insertions(+), 88 deletions(-) diff --git a/param/conv.go b/param/conv.go index e909c4ea..4938f45f 100644 --- a/param/conv.go +++ b/param/conv.go @@ -9,39 +9,51 @@ import ( ) func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { - var strValue string - var reflectValue reflect.Value - switch param.location { - case body: - strValue = string(ctx.Input.RequestBody) - case header: - strValue = ctx.Input.Header(param.name) - default: - strValue = ctx.Input.Query(param.name) - } - - if strValue == "" { + paramValue := getParamValue(param, ctx) + if paramValue == "" { if param.required { ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) } else { - strValue = param.defValue + paramValue = param.defValue } } - if strValue == "" { - reflectValue = reflect.Zero(paramType) + + reflectValue, err := parseValue(paramValue, paramType) + if err != nil { + logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %v, Error: %s", param.name, paramType, paramValue, err)) + ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %v to type %s", param.name, paramValue, paramType)) + } + + return reflectValue +} + +func getParamValue(param *MethodParam, ctx *beecontext.Context) string { + switch param.location { + case body: + return string(ctx.Input.RequestBody) + case header: + return ctx.Input.Header(param.name) + // if strValue == "" && strings.Contains(param.name, "_") { //magically handle X-Headers? + // strValue = ctx.Input.Header(strings.Replace(param.name, "_", "-", -1)) + // } + case path: + return ctx.Input.Query(":" + param.name) + default: + return ctx.Input.Query(param.name) + } +} + +func parseValue(paramValue string, paramType reflect.Type) (result reflect.Value, err error) { + if paramValue == "" { + return reflect.Zero(paramType), nil } else { - value, err := param.parser.parse(strValue, paramType) + value, err := parse(paramValue, paramType) if err != nil { - logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %s, Error: %s", param.name, paramType, strValue, err)) - ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %s to type %s", param.name, strValue, paramType)) + return result, err } - reflectValue, err = safeConvert(reflect.ValueOf(value), paramType) - if err != nil { - panic(err) - } + return safeConvert(reflect.ValueOf(value), paramType) } - return reflectValue } func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { diff --git a/param/methodparams.go b/param/methodparams.go index a3e25a55..23fd6661 100644 --- a/param/methodparams.go +++ b/param/methodparams.go @@ -1,9 +1,13 @@ package param +import ( + "fmt" + "strings" +) + //Keeps param information to be auto passed to controller methods type MethodParam struct { name string - parser paramParser location paramLocation required bool defValue string @@ -13,45 +17,51 @@ type paramLocation byte const ( param paramLocation = iota + path body header ) -type MethodParamOption func(*MethodParam) - -func Bool(name string, opts ...MethodParamOption) *MethodParam { - return newParam(name, boolParser{}, opts) -} - -func String(name string, opts ...MethodParamOption) *MethodParam { - return newParam(name, stringParser{}, opts) -} - -func Int(name string, opts ...MethodParamOption) *MethodParam { - return newParam(name, intParser{}, opts) -} - -func Float(name string, opts ...MethodParamOption) *MethodParam { - return newParam(name, floatParser{}, opts) -} - -func Time(name string, opts ...MethodParamOption) *MethodParam { - return newParam(name, timeParser{}, opts) -} - -func Json(name string, opts ...MethodParamOption) *MethodParam { - return newParam(name, jsonParser{}, opts) -} - -func AsSlice(param *MethodParam) *MethodParam { - param.parser = sliceParser(param.parser) - return param +func New(name string, opts ...MethodParamOption) *MethodParam { + return newParam(name, nil, opts) } func newParam(name string, parser paramParser, opts []MethodParamOption) (param *MethodParam) { - param = &MethodParam{name: name, parser: parser} + param = &MethodParam{name: name} for _, option := range opts { option(param) } return } + +func Make(list ...*MethodParam) []*MethodParam { + if len(list) > 0 { + return list + } + return nil +} + +func (mp *MethodParam) String() string { + options := []string{} + result := "param.New(\"" + mp.name + "\"" + if mp.required { + options = append(options, "param.IsRequired") + } + switch mp.location { + case path: + options = append(options, "param.InPath") + case body: + options = append(options, "param.InBody") + case header: + options = append(options, "param.InHeader") + } + if mp.defValue != "" { + options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defValue)) + } + if len(options) > 0 { + result += ", " + } + result += strings.Join(options, ", ") + result += ")" + return result +} diff --git a/param/options.go b/param/options.go index 0692f9d1..0013c31e 100644 --- a/param/options.go +++ b/param/options.go @@ -4,6 +4,8 @@ import ( "fmt" ) +type MethodParamOption func(*MethodParam) + var IsRequired MethodParamOption = func(p *MethodParam) { p.required = true } @@ -12,6 +14,10 @@ var InHeader MethodParamOption = func(p *MethodParam) { p.location = header } +var InPath MethodParamOption = func(p *MethodParam) { + p.location = path +} + var InBody MethodParamOption = func(p *MethodParam) { p.location = body } diff --git a/param/parsers.go b/param/parsers.go index cfa1a981..64cabe49 100644 --- a/param/parsers.go +++ b/param/parsers.go @@ -12,6 +12,45 @@ type paramParser interface { parse(value string, toType reflect.Type) (interface{}, error) } +func parse(value string, t reflect.Type) (interface{}, error) { + parser := getParser(t) + return parser.parse(value, t) +} + +func getParser(t reflect.Type) paramParser { + switch t.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return intParser{} + case reflect.Slice: + if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string + return stringParser{} + } + elemParser := getParser(t.Elem()) + if elemParser == (jsonParser{}) { + return elemParser + } + return sliceParser(elemParser) + case reflect.Bool: + return boolParser{} + case reflect.String: + return stringParser{} + case reflect.Float32, reflect.Float64: + return floatParser{} + case reflect.Ptr: + elemParser := getParser(t.Elem()) + if elemParser == (jsonParser{}) { + return elemParser + } + return ptrParser(elemParser) + default: + if t.PkgPath() == "time" && t.Name() == "Time" { + return timeParser{} + } + return jsonParser{} + } +} + type parserFunc func(value string, toType reflect.Type) (interface{}, error) func (f parserFunc) parse(value string, toType reflect.Type) (interface{}, error) { @@ -92,3 +131,21 @@ func sliceParser(elemParser paramParser) paramParser { return result.Interface(), nil }) } + +func ptrParser(elemParser paramParser) paramParser { + return parserFunc(func(value string, toType reflect.Type) (interface{}, error) { + parsedValue, err := elemParser.parse(value, toType.Elem()) + if err != nil { + return nil, err + } + newValPtr := reflect.New(toType.Elem()) + newVal := reflect.Indirect(newValPtr) + convertedVal, err := safeConvert(reflect.ValueOf(parsedValue), toType.Elem()) + if err != nil { + return nil, err + } + + newVal.Set(convertedVal) + return newValPtr.Interface(), nil + }) +} diff --git a/parser.go b/parser.go index d40ee3ce..43e052bd 100644 --- a/parser.go +++ b/parser.go @@ -24,10 +24,14 @@ import ( "io/ioutil" "os" "path/filepath" + "regexp" "sort" + "strconv" "strings" + "unicode" "github.com/astaxie/beego/logs" + "github.com/astaxie/beego/param" "github.com/astaxie/beego/utils" ) @@ -35,6 +39,7 @@ var globalRouterTemplate = `package routers import ( "github.com/astaxie/beego" + "github.com/astaxie/beego/param" ) func init() { @@ -81,7 +86,7 @@ func parserPkg(pkgRealpath, pkgpath string) error { if specDecl.Recv != nil { exp, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr) // Check that the type is correct first beforing throwing to parser if ok { - parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(exp.X), pkgpath) + parserComments(specDecl, fmt.Sprint(exp.X), pkgpath) } } } @@ -93,44 +98,168 @@ func parserPkg(pkgRealpath, pkgpath string) error { return nil } -func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error { - if comments != nil && comments.List != nil { - for _, c := range comments.List { - t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) - if strings.HasPrefix(t, "@router") { - elements := strings.TrimLeft(t, "@router ") - e1 := strings.SplitN(elements, " ", 2) - if len(e1) < 1 { - return errors.New("you should has router information") - } - key := pkgpath + ":" + controllerName - cc := ControllerComments{} - cc.Method = funcName - cc.Router = e1[0] - if len(e1) == 2 && e1[1] != "" { - e1 = strings.SplitN(e1[1], " ", 2) - if len(e1) >= 1 { - cc.AllowHTTPMethods = strings.Split(strings.Trim(e1[0], "[]"), ",") - } else { - cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get") - } - } else { - cc.AllowHTTPMethods = append(cc.AllowHTTPMethods, "get") - } - if len(e1) == 2 && e1[1] != "" { - keyval := strings.Split(strings.Trim(e1[1], "[]"), " ") - for _, kv := range keyval { - kk := strings.Split(kv, ":") - cc.Params = append(cc.Params, map[string]string{strings.Join(kk[:len(kk)-1], ":"): kk[len(kk)-1]}) - } - } - genInfoList[key] = append(genInfoList[key], cc) - } +type parsedComment struct { + routerPath string + methods []string + params map[string]parsedParam +} + +type parsedParam struct { + name string + datatype string + location string + defValue string + required bool +} + +func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { + if f.Doc != nil { + parsedComment, err := parseComment(f.Doc.List) + if err != nil { + return err } + key := pkgpath + ":" + controllerName + cc := ControllerComments{} + cc.Method = f.Name.String() + cc.Router = parsedComment.routerPath + cc.AllowHTTPMethods = parsedComment.methods + cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment) + genInfoList[key] = append(genInfoList[key], cc) + } return nil } +func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam { + result := make([]*param.MethodParam, 0, len(funcParams)) + for _, fparam := range funcParams { + methodParam := buildMethodParam(fparam, pc) + result = append(result, methodParam) + } + return result +} + +func buildMethodParam(fparam *ast.Field, pc *parsedComment) *param.MethodParam { + options := []param.MethodParamOption{} + name := fparam.Names[0].Name + if cparam, ok := pc.params[name]; ok { + //Build param from comment info + name = cparam.name + if cparam.required { + options = append(options, param.IsRequired) + } + switch cparam.location { + case "body": + options = append(options, param.InBody) + case "header": + options = append(options, param.InHeader) + case "path": + options = append(options, param.InPath) + } + if cparam.defValue != "" { + options = append(options, param.Default(cparam.defValue)) + } + } else { + if paramInPath(name, pc.routerPath) { + options = append(options, param.InPath) + } + } + return param.New(name, options...) +} + +func paramInPath(name, route string) bool { + return strings.HasSuffix(route, ":"+name) || + strings.Contains(route, ":"+name+"/") +} + +var routeRegex = regexp.MustCompile(`@router\s+(\S+)(?:\s+\[(\S+)\])?`) + +func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) { + pc = &parsedComment{} + for _, c := range lines { + t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) + if strings.HasPrefix(t, "@router") { + matches := routeRegex.FindStringSubmatch(t) + if len(matches) == 3 { + pc.routerPath = matches[1] + methods := matches[2] + if methods == "" { + pc.methods = []string{"get"} + //pc.hasGet = true + } else { + pc.methods = strings.Split(methods, ",") + //pc.hasGet = strings.Contains(methods, "get") + } + } else { + return nil, errors.New("Router information is missing") + } + } else if strings.HasPrefix(t, "@Param") { + pv := getparams(strings.TrimSpace(strings.TrimLeft(t, "@Param"))) + if len(pv) < 4 { + logs.Error("Invalid @Param format. Needs at least 4 parameters") + } + p := parsedParam{} + names := strings.Split(pv[0], "=") + funcParamName := names[0] + if len(names) > 1 { + p.name = names[1] + } else { + p.name = funcParamName + } + p.location = pv[1] + p.datatype = pv[2] + switch len(pv) { + case 5: + p.required, _ = strconv.ParseBool(pv[3]) + case 6: + p.defValue = pv[3] + p.required, _ = strconv.ParseBool(pv[4]) + } + if pc.params == nil { + pc.params = map[string]parsedParam{} + } + pc.params[funcParamName] = p + } + } + return +} + +// direct copy from bee\g_docs.go +// analisys params return []string +// @Param query form string true "The email for login" +// [query form string true "The email for login"] +func getparams(str string) []string { + var s []rune + var j int + var start bool + var r []string + var quoted int8 + for _, c := range []rune(str) { + if unicode.IsSpace(c) && quoted == 0 { + if !start { + continue + } else { + start = false + j++ + r = append(r, string(s)) + s = make([]rune, 0) + continue + } + } + + start = true + if c == '"' { + quoted ^= 1 + continue + } + s = append(s, c) + } + if len(s) > 0 { + r = append(r, string(s)) + } + return r +} + func genRouterCode(pkgRealpath string) { os.Mkdir(getRouterDir(pkgRealpath), 0755) logs.Info("generate router from comments") @@ -163,12 +292,24 @@ func genRouterCode(pkgRealpath string) { } params = strings.TrimRight(params, ",") + "}" } + methodParams := "param.Make(" + if len(c.MethodParams) > 0 { + lines := make([]string, 0, len(c.MethodParams)) + for _, m := range c.MethodParams { + lines = append(lines, fmt.Sprint(m)) + } + methodParams += "\n " + + strings.Join(lines, ",\n ") + + ",\n " + } + methodParams += ")" globalinfo = globalinfo + ` beego.GlobalControllerRouter["` + k + `"] = append(beego.GlobalControllerRouter["` + k + `"], beego.ControllerComments{ Method: "` + strings.TrimSpace(c.Method) + `", ` + "Router: `" + c.Router + "`" + `, AllowHTTPMethods: ` + allmethod + `, + MethodParams: ` + methodParams + `, Params: ` + params + `}) ` } diff --git a/router.go b/router.go index 57479248..546db22d 100644 --- a/router.go +++ b/router.go @@ -908,6 +908,9 @@ func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, ex response.RenderMethodResult(resultValue, context) } } + if !context.ResponseWriter.Started && context.Output.Status == 0 { + context.Output.SetStatus(200) + } } // FindRouter Find Router info for URL From 0f554d9b1a498580006ff6140fae2b5e0ab8e328 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 25 Apr 2017 22:21:08 +0800 Subject: [PATCH 080/163] update travis to latest go version --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d4b19015..e3960c70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: go go: - - 1.6 - - 1.7 - - 1.8 + - 1.6.4 + - 1.7.5 + - 1.8.1 services: - redis-server - mysql From cd67f13bf91666ac78363dfd9ed1c7e69ee6c9f8 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 25 Apr 2017 22:21:27 +0800 Subject: [PATCH 081/163] add template layout test #2481 --- template_test.go | 114 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/template_test.go b/template_test.go index 17690965..16967382 100644 --- a/template_test.go +++ b/template_test.go @@ -15,6 +15,7 @@ package beego import ( + "bytes" "os" "path/filepath" "testing" @@ -142,3 +143,116 @@ func TestRelativeTemplate(t *testing.T) { } os.RemoveAll(dir) } + +var add = `{{ template "layout_blog.tpl" . }} +{{ define "css" }} + +{{ end}} + + +{{ define "content" }} +

{{ .Title }}

+

This is SomeVar: {{ .SomeVar }}

+{{ end }} + +{{ define "js" }} + +{{ end}}` + +var layout_blog = ` + + + Lin Li + + + + + {{ block "css" . }}{{ end }} + + + +
+ {{ block "content" . }}{{ end }} +
+ + + {{ block "js" . }}{{ end }} + +` + +var output = ` + + + Lin Li + + + + + + + + + + +
+ +

Hello

+

This is SomeVar: val

+ +
+ + + + + + + + + + + + +` + +func TestTemplateLayout(t *testing.T) { + dir := "_beeTmp" + files := []string{ + "add.tpl", + "layout_blog.tpl", + } + if err := os.MkdirAll(dir, 0777); err != nil { + t.Fatal(err) + } + for k, name := range files { + os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777) + if f, err := os.Create(filepath.Join(dir, name)); err != nil { + t.Fatal(err) + } else { + if k == 0 { + f.WriteString(add) + } else if k == 1 { + f.WriteString(layout_blog) + } + f.Close() + } + } + if err := AddViewPath(dir); err != nil { + t.Fatal(err) + } + beeTemplates := beeViewPathTemplates[dir] + if len(beeTemplates) != 2 { + t.Fatalf("should be 2 but got %v", len(beeTemplates)) + } + out := bytes.NewBufferString("") + if err := beeTemplates["add.tpl"].ExecuteTemplate(out, "add.tpl", map[string]string{"Title": "Hello", "SomeVar": "val"}); err != nil { + t.Fatal(err) + } + if out.String() != output { + t.Log(out.String()) + t.Fatal("Compare failed") + } + for _, name := range files { + os.RemoveAll(filepath.Join(dir, name)) + } + os.RemoveAll(dir) +} From cbd831042a34af663669a2fb4cce2f0ab78e7064 Mon Sep 17 00:00:00 2001 From: eyalpost Date: Tue, 25 Apr 2017 18:39:42 +0300 Subject: [PATCH 082/163] move under context --- {param => context/param}/conv.go | 0 {param => context/param}/methodparams.go | 0 {param => context/param}/options.go | 0 {param => context/param}/parsers.go | 0 {response => context/response}/renderer.go | 0 {response => context/response}/responses.go | 0 controller.go | 2 +- parser.go | 4 ++-- router.go | 4 ++-- 9 files changed, 5 insertions(+), 5 deletions(-) rename {param => context/param}/conv.go (100%) rename {param => context/param}/methodparams.go (100%) rename {param => context/param}/options.go (100%) rename {param => context/param}/parsers.go (100%) rename {response => context/response}/renderer.go (100%) rename {response => context/response}/responses.go (100%) diff --git a/param/conv.go b/context/param/conv.go similarity index 100% rename from param/conv.go rename to context/param/conv.go diff --git a/param/methodparams.go b/context/param/methodparams.go similarity index 100% rename from param/methodparams.go rename to context/param/methodparams.go diff --git a/param/options.go b/context/param/options.go similarity index 100% rename from param/options.go rename to context/param/options.go diff --git a/param/parsers.go b/context/param/parsers.go similarity index 100% rename from param/parsers.go rename to context/param/parsers.go diff --git a/response/renderer.go b/context/response/renderer.go similarity index 100% rename from response/renderer.go rename to context/response/renderer.go diff --git a/response/responses.go b/context/response/responses.go similarity index 100% rename from response/responses.go rename to context/response/responses.go diff --git a/controller.go b/controller.go index 02e45738..510e16b8 100644 --- a/controller.go +++ b/controller.go @@ -28,7 +28,7 @@ import ( "strings" "github.com/astaxie/beego/context" - "github.com/astaxie/beego/param" + "github.com/astaxie/beego/context/param" "github.com/astaxie/beego/session" ) diff --git a/parser.go b/parser.go index 43e052bd..17d9a5d2 100644 --- a/parser.go +++ b/parser.go @@ -30,8 +30,8 @@ import ( "strings" "unicode" + "github.com/astaxie/beego/context/param" "github.com/astaxie/beego/logs" - "github.com/astaxie/beego/param" "github.com/astaxie/beego/utils" ) @@ -39,7 +39,7 @@ var globalRouterTemplate = `package routers import ( "github.com/astaxie/beego" - "github.com/astaxie/beego/param" + "github.com/astaxie/beego/context/param" ) func init() { diff --git a/router.go b/router.go index 546db22d..ebe70e72 100644 --- a/router.go +++ b/router.go @@ -27,9 +27,9 @@ import ( "time" beecontext "github.com/astaxie/beego/context" + "github.com/astaxie/beego/context/param" + "github.com/astaxie/beego/context/response" "github.com/astaxie/beego/logs" - "github.com/astaxie/beego/param" - "github.com/astaxie/beego/response" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" ) From f311ae9ebe799b6a66737b58c1258d2311551643 Mon Sep 17 00:00:00 2001 From: ansiz Date: Wed, 26 Apr 2017 00:12:03 +0800 Subject: [PATCH 083/163] feature: Export function printTree (#2597) This exports PrintTree function which allow to set Role Based Access Control, Create our own requests statistics...etc --- admin.go | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/admin.go b/admin.go index 80615957..875cd0e8 100644 --- a/admin.go +++ b/admin.go @@ -105,29 +105,12 @@ func listConf(rw http.ResponseWriter, r *http.Request) { tmpl.Execute(rw, data) case "router": - var ( - content = map[string]interface{}{ - "Fields": []string{ - "Router Pattern", - "Methods", - "Controller", - }, - } - methods = []string{} - methodsData = make(map[string]interface{}) - ) - for method, t := range BeeApp.Handlers.routers { - - resultList := new([][]string) - - printTree(resultList, t) - - methods = append(methods, method) - methodsData[method] = resultList + content := PrintTree() + content["Fields"] = []string{ + "Router Pattern", + "Methods", + "Controller", } - - content["Data"] = methodsData - content["Methods"] = methods data["Content"] = content data["Title"] = "Routers" execTpl(rw, data, routerAndFilterTpl, defaultScriptsTpl) @@ -200,6 +183,28 @@ func list(root string, p interface{}, m map[string]interface{}) { } } +// PrintTree prints all registered routers. +func PrintTree() map[string]interface{} { + var ( + content = map[string]interface{}{} + methods = []string{} + methodsData = make(map[string]interface{}) + ) + for method, t := range BeeApp.Handlers.routers { + + resultList := new([][]string) + + printTree(resultList, t) + + methods = append(methods, method) + methodsData[method] = resultList + } + + content["Data"] = methodsData + content["Methods"] = methods + return content +} + func printTree(resultList *[][]string, t *Tree) { for _, tr := range t.fixrouters { printTree(resultList, tr) From 4cba78afd948470826c0bed5eda222f9824b8da7 Mon Sep 17 00:00:00 2001 From: eyalpost Date: Tue, 25 Apr 2017 23:42:35 +0300 Subject: [PATCH 084/163] small fixes --- context/param/conv.go | 25 +++++++++++++------------ context/param/parsers.go | 14 ++++++-------- parser.go | 9 ++++----- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/context/param/conv.go b/context/param/conv.go index 4938f45f..8e580c2e 100644 --- a/context/param/conv.go +++ b/context/param/conv.go @@ -8,6 +8,15 @@ import ( "github.com/astaxie/beego/logs" ) +func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { + result = make([]reflect.Value, 0, len(methodParams)) + for i := 0; i < len(methodParams); i++ { + reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) + result = append(result, reflectValue) + } + return +} + func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Context) (result reflect.Value) { paramValue := getParamValue(param, ctx) if paramValue == "" { @@ -18,7 +27,7 @@ func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Co } } - reflectValue, err := parseValue(paramValue, paramType) + reflectValue, err := parseValue(param, paramValue, paramType) if err != nil { logs.Debug(fmt.Sprintf("Error converting param %s to type %s. Value: %v, Error: %s", param.name, paramType, paramValue, err)) ctx.Abort(400, fmt.Sprintf("Invalid parameter %s. Can not convert %v to type %s", param.name, paramValue, paramType)) @@ -43,11 +52,12 @@ func getParamValue(param *MethodParam, ctx *beecontext.Context) string { } } -func parseValue(paramValue string, paramType reflect.Type) (result reflect.Value, err error) { +func parseValue(param *MethodParam, paramValue string, paramType reflect.Type) (result reflect.Value, err error) { if paramValue == "" { return reflect.Zero(paramType), nil } else { - value, err := parse(paramValue, paramType) + parser := getParser(param, paramType) + value, err := parser.parse(paramValue, paramType) if err != nil { return result, err } @@ -56,15 +66,6 @@ func parseValue(paramValue string, paramType reflect.Type) (result reflect.Value } } -func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { - result = make([]reflect.Value, 0, len(methodParams)) - for i := 0; i < len(methodParams); i++ { - reflectValue := convertParam(methodParams[i], methodType.In(i), ctx) - result = append(result, reflectValue) - } - return -} - func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { defer func() { if r := recover(); r != nil { diff --git a/context/param/parsers.go b/context/param/parsers.go index 64cabe49..2b48e878 100644 --- a/context/param/parsers.go +++ b/context/param/parsers.go @@ -12,12 +12,7 @@ type paramParser interface { parse(value string, toType reflect.Type) (interface{}, error) } -func parse(value string, t reflect.Type) (interface{}, error) { - parser := getParser(t) - return parser.parse(value, t) -} - -func getParser(t reflect.Type) paramParser { +func getParser(param *MethodParam, t reflect.Type) paramParser { switch t.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: @@ -26,7 +21,10 @@ func getParser(t reflect.Type) paramParser { if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string return stringParser{} } - elemParser := getParser(t.Elem()) + if param.location == body { + return jsonParser{} + } + elemParser := getParser(param, t.Elem()) if elemParser == (jsonParser{}) { return elemParser } @@ -38,7 +36,7 @@ func getParser(t reflect.Type) paramParser { case reflect.Float32, reflect.Float64: return floatParser{} case reflect.Ptr: - elemParser := getParser(t.Elem()) + elemParser := getParser(param, t.Elem()) if elemParser == (jsonParser{}) { return elemParser } diff --git a/parser.go b/parser.go index 17d9a5d2..66f51d0b 100644 --- a/parser.go +++ b/parser.go @@ -199,12 +199,11 @@ func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) { logs.Error("Invalid @Param format. Needs at least 4 parameters") } p := parsedParam{} - names := strings.Split(pv[0], "=") - funcParamName := names[0] + names := strings.SplitN(pv[0], "=>", 2) + p.name = names[0] + funcParamName := p.name if len(names) > 1 { - p.name = names[1] - } else { - p.name = funcParamName + funcParamName = names[1] } p.location = pv[1] p.datatype = pv[2] From a1bc94e648dbb04b02a06ddfd76aef3006ba52c9 Mon Sep 17 00:00:00 2001 From: eyalpost Date: Wed, 26 Apr 2017 01:00:25 +0300 Subject: [PATCH 085/163] dont generate comment if router not found --- parser.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/parser.go b/parser.go index 66f51d0b..037d5376 100644 --- a/parser.go +++ b/parser.go @@ -118,13 +118,15 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { if err != nil { return err } - key := pkgpath + ":" + controllerName - cc := ControllerComments{} - cc.Method = f.Name.String() - cc.Router = parsedComment.routerPath - cc.AllowHTTPMethods = parsedComment.methods - cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment) - genInfoList[key] = append(genInfoList[key], cc) + if parsedComment.routerPath != "" { + key := pkgpath + ":" + controllerName + cc := ControllerComments{} + cc.Method = f.Name.String() + cc.Router = parsedComment.routerPath + cc.AllowHTTPMethods = parsedComment.methods + cc.MethodParams = buildMethodParams(f.Type.Params.List, parsedComment) + genInfoList[key] = append(genInfoList[key], cc) + } } return nil From 3e29078f68399a18e5a3d3bfac618b38eea8ee18 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 28 Apr 2017 21:38:08 +0800 Subject: [PATCH 086/163] add check ineffect and gofmt --- .travis.yml | 3 +++ httplib/httplib.go | 4 ++-- logs/alils/alils.go | 7 +++---- orm/orm_test.go | 4 +++- session/sess_file.go | 9 ++++++++- templatefunc_test.go | 10 +++++----- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index e3960c70..c5f11b9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,6 +35,7 @@ install: - go get github.com/gogo/protobuf/proto - go get -u honnef.co/go/tools/cmd/gosimple - go get -u github.com/mdempsky/unconvert + - go get -u github.com/gordonklaus/ineffassign before_script: - psql --version - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" @@ -51,5 +52,7 @@ script: - go test -v ./... - gosimple -ignore "$(cat .gosimpleignore)" $(go list ./... | grep -v /vendor/) - unconvert $(go list ./... | grep -v /vendor/) + - ineffassign . + - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s addons: postgresql: "9.4" diff --git a/httplib/httplib.go b/httplib/httplib.go index f232f31b..4fd572d6 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -520,9 +520,9 @@ func (b *BeegoHTTPRequest) Bytes() ([]byte, error) { return nil, err } b.body, err = ioutil.ReadAll(reader) - } else { - b.body, err = ioutil.ReadAll(resp.Body) + return b.body, err } + b.body, err = ioutil.ReadAll(resp.Body) return b.body, err } diff --git a/logs/alils/alils.go b/logs/alils/alils.go index 30a09243..aaee228a 100644 --- a/logs/alils/alils.go +++ b/logs/alils/alils.go @@ -2,11 +2,12 @@ package alils import ( "encoding/json" - "github.com/astaxie/beego/logs" - "github.com/gogo/protobuf/proto" "strings" "sync" "time" + + "github.com/astaxie/beego/logs" + "github.com/gogo/protobuf/proto" ) const ( @@ -124,12 +125,10 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error // 默认发到空Topic if lg == nil { - topic = "" content = msg lg = c.group[0] } } else { - topic = "" content = msg lg = c.group[0] } diff --git a/orm/orm_test.go b/orm/orm_test.go index a8b4fe69..128a86f3 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -135,7 +135,7 @@ func getCaller(skip int) string { if i := strings.LastIndex(funName, "."); i > -1 { funName = funName[i+1:] } - return fmt.Sprintf("%s:%d: \n%s", fn, line, strings.Join(codes, "\n")) + return fmt.Sprintf("%s:%s:%d: \n%s", fn, funName, line, strings.Join(codes, "\n")) } func throwFail(t *testing.T, err error, args ...interface{}) { @@ -1014,6 +1014,8 @@ func TestAll(t *testing.T) { var users3 []*User qs = dORM.QueryTable("user") num, err = qs.Filter("user_name", "nothing").All(&users3) + throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 0)) throwFailNow(t, AssertIs(users3 == nil, false)) } diff --git a/session/sess_file.go b/session/sess_file.go index 12cf1f3f..3ca93d55 100644 --- a/session/sess_file.go +++ b/session/sess_file.go @@ -87,9 +87,16 @@ func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { var f *os.File if err == nil { f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0777) + if err != nil { + SLogger.Println(err) + return + } } else if os.IsNotExist(err) { f, err = os.Create(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid)) - + if err != nil { + SLogger.Println(err) + return + } } else { return } diff --git a/templatefunc_test.go b/templatefunc_test.go index 20d9b850..f40b5985 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -269,28 +269,28 @@ func TestParseFormTag(t *testing.T) { t.Errorf("Form Tag containing only label was not correctly parsed.") } - label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(3)) + label, name, fType, id, class, ignored, _ = parseFormTag(objT.Field(3)) if !(name == "name" && label == "OnlyName: " && fType == "text" && !ignored && id == "name" && class == "form-name") { t.Errorf("Form Tag containing only name was not correctly parsed.") } - label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(4)) + _, _, _, _, _, ignored, _ = parseFormTag(objT.Field(4)) if !ignored { t.Errorf("Form Tag that should be ignored was not correctly parsed.") } - label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(5)) + _, name, _, _, _, _, required = parseFormTag(objT.Field(5)) if !(name == "name" && required) { t.Errorf("Form Tag containing only name and required was not correctly parsed.") } - label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(6)) + _, name, _, _, _, _, required = parseFormTag(objT.Field(6)) if !(name == "name" && !required) { t.Errorf("Form Tag containing only name and ignore required was not correctly parsed.") } - label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(7)) + _, name, _, _, _, _, required = parseFormTag(objT.Field(7)) if !(name == "name" && !required) { t.Errorf("Form Tag containing only name and not required was not correctly parsed.") } From aa8f7bc146da539b90d3f2ce7c3359c7872358e7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 28 Apr 2017 22:36:28 +0800 Subject: [PATCH 087/163] fix ineffectual --- config/ini.go | 5 ++++- logs/color_windows.go | 2 +- logs/file.go | 10 ++++++---- orm/db.go | 2 +- orm/db_mysql.go | 3 +-- orm/orm.go | 2 +- orm/orm_raw.go | 2 +- orm/orm_test.go | 7 +++++++ session/couchbase/sess_couchbase.go | 4 +++- session/sess_test.go | 3 +-- template.go | 3 ++- templatefunc_test.go | 10 +++++----- utils/file.go | 2 +- validation/util_test.go | 2 +- 14 files changed, 35 insertions(+), 22 deletions(-) diff --git a/config/ini.go b/config/ini.go index 3572b955..a681bc1b 100644 --- a/config/ini.go +++ b/config/ini.go @@ -325,7 +325,10 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { // Get section or key comments. Fixed #1607 getCommentStr := func(section, key string) string { - comment, ok := "", false + var ( + comment string + ok bool + ) if len(key) == 0 { comment, ok = c.sectionComment[section] } else { diff --git a/logs/color_windows.go b/logs/color_windows.go index deee4c87..4e28f188 100644 --- a/logs/color_windows.go +++ b/logs/color_windows.go @@ -361,7 +361,7 @@ func isParameterChar(b byte) bool { } func (cw *ansiColorWriter) Write(p []byte) (int, error) { - r, nw, first, last := 0, 0, 0, 0 + var r, nw, first, last int if cw.mode != DiscardNonColorEscSeq { cw.state = outsideCsiCode cw.resetBuffer() diff --git a/logs/file.go b/logs/file.go index 32595ec4..20ef167d 100644 --- a/logs/file.go +++ b/logs/file.go @@ -259,7 +259,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { } // return error if the last file checked still existed if err == nil { - return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename) + return fmt.Errorf("Rotate: Cannot find free log number to rename %s", w.Filename) } // close fileWriter before rename @@ -268,6 +268,9 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { // Rename the file to its new found name // even if occurs error,we MUST guarantee to restart new logger err = os.Rename(w.Filename, fName) + if err != nil { + goto RESTART_LOGGER + } err = os.Chmod(fName, os.FileMode(0440)) // re-start logger RESTART_LOGGER: @@ -276,13 +279,12 @@ RESTART_LOGGER: go w.deleteOldLog() if startLoggerErr != nil { - return fmt.Errorf("Rotate StartLogger: %s\n", startLoggerErr) + return fmt.Errorf("Rotate StartLogger: %s", startLoggerErr) } if err != nil { - return fmt.Errorf("Rotate: %s\n", err) + return fmt.Errorf("Rotate: %s", err) } return nil - } func (w *fileLogWriter) deleteOldLog() { diff --git a/orm/db.go b/orm/db.go index e9508ee0..a3251906 100644 --- a/orm/db.go +++ b/orm/db.go @@ -1110,7 +1110,7 @@ func (d *dbBase) Count(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condition // generate sql with replacing operator string placeholders and replaced values. func (d *dbBase) GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator string, args []interface{}, tz *time.Location) (string, []interface{}) { - sql := "" + var sql string params := getFlatParams(fi, args, tz) if len(params) == 0 { diff --git a/orm/db_mysql.go b/orm/db_mysql.go index 1016de2b..51185563 100644 --- a/orm/db_mysql.go +++ b/orm/db_mysql.go @@ -103,8 +103,7 @@ func (d *dbBaseMysql) IndexExists(db dbQuerier, table string, name string) bool // If no will insert // Add "`" for mysql sql building func (d *dbBaseMysql) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) { - - iouStr := "" + var iouStr string argsMap := map[string]string{} iouStr = "ON DUPLICATE KEY UPDATE" diff --git a/orm/orm.go b/orm/orm.go index d364e621..5db79386 100644 --- a/orm/orm.go +++ b/orm/orm.go @@ -420,7 +420,7 @@ func (o *orm) getRelQs(md interface{}, mi *modelInfo, fi *fieldInfo) *querySet { // table name can be string or struct. // e.g. QueryTable("user"), QueryTable(&user{}) or QueryTable((*User)(nil)), func (o *orm) QueryTable(ptrStructOrTableName interface{}) (qs QuerySeter) { - name := "" + var name string if table, ok := ptrStructOrTableName.(string); ok { name = snakeString(table) if mi, ok := modelCache.get(name); ok { diff --git a/orm/orm_raw.go b/orm/orm_raw.go index a968b1a1..1e86212a 100644 --- a/orm/orm_raw.go +++ b/orm/orm_raw.go @@ -671,7 +671,7 @@ func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (in ind *reflect.Value ) - typ := 0 + var typ int switch container.(type) { case *Params: typ = 1 diff --git a/orm/orm_test.go b/orm/orm_test.go index 128a86f3..fdc6473c 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -1140,6 +1140,7 @@ func TestRelatedSel(t *testing.T) { } err = qs.Filter("user_name", "nobody").RelatedSel("profile").One(&user) + throwFail(t, err) throwFail(t, AssertIs(num, 1)) throwFail(t, AssertIs(user.Profile, nil)) @@ -1248,20 +1249,24 @@ func TestLoadRelated(t *testing.T) { num, err = dORM.LoadRelated(&user, "Posts", true) throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 2)) throwFailNow(t, AssertIs(len(user.Posts), 2)) throwFailNow(t, AssertIs(user.Posts[0].User.UserName, "astaxie")) num, err = dORM.LoadRelated(&user, "Posts", true, 1) throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 1)) throwFailNow(t, AssertIs(len(user.Posts), 1)) num, err = dORM.LoadRelated(&user, "Posts", true, 0, 0, "-Id") throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 2)) throwFailNow(t, AssertIs(len(user.Posts), 2)) throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting")) num, err = dORM.LoadRelated(&user, "Posts", true, 1, 1, "Id") throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 1)) throwFailNow(t, AssertIs(len(user.Posts), 1)) throwFailNow(t, AssertIs(user.Posts[0].Title, "Formatting")) @@ -1978,6 +1983,7 @@ func TestReadOrCreate(t *testing.T) { created, pk, err := dORM.ReadOrCreate(u, "UserName") throwFail(t, err) throwFail(t, AssertIs(created, true)) + throwFail(t, AssertIs(u.ID, pk)) throwFail(t, AssertIs(u.UserName, "Kyle")) throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com")) throwFail(t, AssertIs(u.Password, "other_pass")) @@ -2134,6 +2140,7 @@ func TestUintPk(t *testing.T) { created, pk, err := dORM.ReadOrCreate(u, "ID") throwFail(t, err) + throwFail(t, AssertIs(pk, u.ID)) throwFail(t, AssertIs(created, true)) throwFail(t, AssertIs(u.Name, name)) diff --git a/session/couchbase/sess_couchbase.go b/session/couchbase/sess_couchbase.go index 791a6cc7..707d042c 100644 --- a/session/couchbase/sess_couchbase.go +++ b/session/couchbase/sess_couchbase.go @@ -162,7 +162,9 @@ func (cp *Provider) SessionRead(sid string) (session.Store, error) { ) err = cp.b.Get(sid, &doc) - if doc == nil { + if err != nil { + return nil, err + } else if doc == nil { kv = make(map[interface{}]interface{}) } else { kv, err = session.DecodeGob(doc) diff --git a/session/sess_test.go b/session/sess_test.go index 60b47dd4..906abec2 100644 --- a/session/sess_test.go +++ b/session/sess_test.go @@ -74,8 +74,7 @@ func TestCookieEncodeDecode(t *testing.T) { if err != nil { t.Fatal("encodeCookie:", err) } - dst := make(map[interface{}]interface{}) - dst, err = decodeCookie(block, hashKey, securityName, str, 3600) + dst, err := decodeCookie(block, hashKey, securityName, str, 3600) if err != nil { t.Fatal("decodeCookie", err) } diff --git a/template.go b/template.go index 104383c9..73a14977 100644 --- a/template.go +++ b/template.go @@ -307,8 +307,9 @@ func _getTemplate(t0 *template.Template, root string, subMods [][]string, others } //second check define for _, otherFile := range others { + var data []byte fileAbsPath := filepath.Join(root, otherFile) - data, err := ioutil.ReadFile(fileAbsPath) + data, err = ioutil.ReadFile(fileAbsPath) if err != nil { continue } diff --git a/templatefunc_test.go b/templatefunc_test.go index f40b5985..9df61125 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -254,22 +254,22 @@ func TestParseFormTag(t *testing.T) { objT := reflect.TypeOf(&user{}).Elem() - label, name, fType, id, class, ignored, required := parseFormTag(objT.Field(0)) + label, name, fType, _, _, ignored, _ := parseFormTag(objT.Field(0)) if !(name == "name" && label == "年龄:" && fType == "text" && !ignored) { t.Errorf("Form Tag with name, label and type was not correctly parsed.") } - label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(1)) + label, name, fType, _, _, ignored, _ = parseFormTag(objT.Field(1)) if !(name == "NoName" && label == "年龄:" && fType == "hidden" && !ignored) { t.Errorf("Form Tag with label and type but without name was not correctly parsed.") } - label, name, fType, id, class, ignored, required = parseFormTag(objT.Field(2)) + label, name, fType, _, _, ignored, _ = parseFormTag(objT.Field(2)) if !(name == "OnlyLabel" && label == "年龄:" && fType == "text" && !ignored) { t.Errorf("Form Tag containing only label was not correctly parsed.") } - label, name, fType, id, class, ignored, _ = parseFormTag(objT.Field(3)) + label, name, fType, id, class, ignored, _ := parseFormTag(objT.Field(3)) if !(name == "name" && label == "OnlyName: " && fType == "text" && !ignored && id == "name" && class == "form-name") { t.Errorf("Form Tag containing only name was not correctly parsed.") @@ -280,7 +280,7 @@ func TestParseFormTag(t *testing.T) { t.Errorf("Form Tag that should be ignored was not correctly parsed.") } - _, name, _, _, _, _, required = parseFormTag(objT.Field(5)) + _, name, _, _, _, _, required := parseFormTag(objT.Field(5)) if !(name == "name" && required) { t.Errorf("Form Tag containing only name and required was not correctly parsed.") } diff --git a/utils/file.go b/utils/file.go index db197882..6090eb17 100644 --- a/utils/file.go +++ b/utils/file.go @@ -72,7 +72,7 @@ func GrepFile(patten string, filename string) (lines []string, err error) { lines = make([]string, 0) reader := bufio.NewReader(fd) prefix := "" - isLongLine := false + var isLongLine bool for { byteLine, isPrefix, er := reader.ReadLine() if er != nil && er != io.EOF { diff --git a/validation/util_test.go b/validation/util_test.go index d7e10506..e74d50ed 100644 --- a/validation/util_test.go +++ b/validation/util_test.go @@ -42,7 +42,7 @@ func TestGetValidFuncs(t *testing.T) { } f, _ = tf.FieldByName("Tag") - if vfs, err = getValidFuncs(f); err.Error() != "doesn't exsits Maxx valid function" { + if _, err = getValidFuncs(f); err.Error() != "doesn't exsits Maxx valid function" { t.Fatal(err) } From 64b475d7d640117219d6246ea154282bf3c0c34f Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 28 Apr 2017 22:58:17 +0800 Subject: [PATCH 088/163] fix ReadOrCreate test case --- orm/orm_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/orm/orm_test.go b/orm/orm_test.go index fdc6473c..71b614f3 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -2138,9 +2138,8 @@ func TestUintPk(t *testing.T) { Name: name, } - created, pk, err := dORM.ReadOrCreate(u, "ID") + created, _, err := dORM.ReadOrCreate(u, "ID") throwFail(t, err) - throwFail(t, AssertIs(pk, u.ID)) throwFail(t, AssertIs(created, true)) throwFail(t, AssertIs(u.Name, name)) From 1c32c011a1a9d6ebda2b79e0625f0626104feee3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 28 Apr 2017 23:37:40 +0800 Subject: [PATCH 089/163] fix misspell --- httplib/README.md | 2 +- orm/db.go | 2 +- utils/file_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/httplib/README.md b/httplib/README.md index 6a72cf7c..97df8e6b 100644 --- a/httplib/README.md +++ b/httplib/README.md @@ -32,7 +32,7 @@ The default timeout is `60` seconds, function prototype: SetTimeout(connectTimeout, readWriteTimeout time.Duration) -Exmaple: +Example: // GET httplib.Get("http://beego.me/").SetTimeout(100 * time.Second, 30 * time.Second) diff --git a/orm/db.go b/orm/db.go index a3251906..2bd525c2 100644 --- a/orm/db.go +++ b/orm/db.go @@ -1733,7 +1733,7 @@ func (d *dbBase) TableQuote() string { return "`" } -// replace value placeholer in parametered sql string. +// replace value placeholder in parametered sql string. func (d *dbBase) ReplaceMarks(query *string) { // default use `?` as mark, do nothing } diff --git a/utils/file_test.go b/utils/file_test.go index 86d1a700..b2644157 100644 --- a/utils/file_test.go +++ b/utils/file_test.go @@ -54,7 +54,7 @@ func TestSearchFile(t *testing.T) { _, err = SearchFile(noExistedFile, ".") if err == nil { - t.Errorf("err shouldnot be nil, got path: %s", SelfDir()) + t.Errorf("err shouldnt be nil, got path: %s", SelfDir()) } } From ea3d0690cf5218db7b6154f6225f699b2ded1cf7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 29 Apr 2017 09:13:28 +0800 Subject: [PATCH 090/163] golint --- policy.go | 4 ++-- template.go | 1 + template_test.go | 4 ++-- templatefunc.go | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/policy.go b/policy.go index 2b91fdcc..ab23f927 100644 --- a/policy.go +++ b/policy.go @@ -23,7 +23,7 @@ import ( // PolicyFunc defines a policy function which is invoked before the controller handler is executed. type PolicyFunc func(*context.Context) -// FindRouter Find Router info for URL +// FindPolicy Find Router info for URL func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc { var urlPath = cont.Input.URL() if !BConfig.RouterCaseSensitive { @@ -71,7 +71,7 @@ func (p *ControllerRegister) addToPolicy(method, pattern string, r ...PolicyFunc } } -// Register new policy in beego +// Policy Register new policy in beego func Policy(pattern, method string, policy ...PolicyFunc) { BeeApp.Handlers.addToPolicy(method, pattern, policy...) } diff --git a/template.go b/template.go index 73a14977..d4859cd7 100644 --- a/template.go +++ b/template.go @@ -365,6 +365,7 @@ func DelStaticPath(url string) *App { return BeeApp } +// AddTemplateEngine add a new templatePreProcessor which support extension func AddTemplateEngine(extension string, fn templatePreProcessor) *App { AddTemplateExt(extension) beeTemplateEngines[extension] = fn diff --git a/template_test.go b/template_test.go index 16967382..2153ef72 100644 --- a/template_test.go +++ b/template_test.go @@ -159,7 +159,7 @@ var add = `{{ template "layout_blog.tpl" . }} {{ end}}` -var layout_blog = ` +var layoutBlog = ` Lin Li @@ -231,7 +231,7 @@ func TestTemplateLayout(t *testing.T) { if k == 0 { f.WriteString(add) } else if k == 1 { - f.WriteString(layout_blog) + f.WriteString(layoutBlog) } f.Close() } diff --git a/templatefunc.go b/templatefunc.go index 4fdc936d..a6f9c961 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -507,9 +507,9 @@ func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id str class = fieldT.Tag.Get("class") required = false - required_field := fieldT.Tag.Get("required") - if required_field != "-" && required_field != "" { - required, _ = strconv.ParseBool(required_field) + requiredField := fieldT.Tag.Get("required") + if requiredField != "-" && requiredField != "" { + required, _ = strconv.ParseBool(requiredField) } switch len(tags) { From a91e2e99508e2e3696d6b2644fd61b2b01a1c76d Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 30 Apr 2017 22:41:23 +0800 Subject: [PATCH 091/163] add golint check and fix all golints --- .travis.yml | 2 + config/env/env.go | 2 + context/acceptencoder.go | 1 + grace/server.go | 4 +- hooks.go | 6 +- logs/alils/alils.go | 39 +++---- logs/alils/log.pb.go | 218 ++++++++++++++++++++++-------------- logs/alils/log_config.go | 3 + logs/alils/log_project.go | 9 +- logs/alils/log_store.go | 20 ++-- logs/alils/machine_group.go | 16 ++- logs/alils/request.go | 4 +- logs/file.go | 2 +- logs/log.go | 5 +- logs/logger.go | 17 ++- orm/db.go | 5 +- orm/db_alias.go | 6 +- session/session.go | 45 ++++---- session/ssdb/sess_ssdb.go | 40 +++++-- swagger/swagger.go | 2 +- 20 files changed, 271 insertions(+), 175 deletions(-) diff --git a/.travis.yml b/.travis.yml index c5f11b9f..479d70ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,7 @@ install: - go get -u honnef.co/go/tools/cmd/gosimple - go get -u github.com/mdempsky/unconvert - go get -u github.com/gordonklaus/ineffassign + - go get -u github.com/golang/lint/golint before_script: - psql --version - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" @@ -54,5 +55,6 @@ script: - unconvert $(go list ./... | grep -v /vendor/) - ineffassign . - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s + - golint ./... addons: postgresql: "9.4" diff --git a/config/env/env.go b/config/env/env.go index a819e51a..34f094fe 100644 --- a/config/env/env.go +++ b/config/env/env.go @@ -12,6 +12,8 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +// Package env is used to parse environment. package env import ( diff --git a/context/acceptencoder.go b/context/acceptencoder.go index 350b560d..b4e2492c 100644 --- a/context/acceptencoder.go +++ b/context/acceptencoder.go @@ -39,6 +39,7 @@ var ( getMethodOnly bool ) +// InitGzip init the gzipcompress func InitGzip(minLength, compressLevel int, methods []string) { if minLength >= 0 { gzipMinLength = minLength diff --git a/grace/server.go b/grace/server.go index c71193ad..b8242335 100644 --- a/grace/server.go +++ b/grace/server.go @@ -291,7 +291,7 @@ func (srv *Server) fork() (err error) { // RegisterSignalHook registers a function to be run PreSignal or PostSignal for a given signal. func (srv *Server) RegisterSignalHook(ppFlag int, sig os.Signal, f func()) (err error) { if ppFlag != PreSignal && ppFlag != PostSignal { - err = fmt.Errorf("Invalid ppFlag argument. Must be either grace.PreSignal or grace.PostSignal.") + err = fmt.Errorf("Invalid ppFlag argument. Must be either grace.PreSignal or grace.PostSignal") return } for _, s := range hookableSignals { @@ -300,6 +300,6 @@ func (srv *Server) RegisterSignalHook(ppFlag int, sig os.Signal, f func()) (err return } } - err = fmt.Errorf("Signal '%v' is not supported.", sig) + err = fmt.Errorf("Signal '%v' is not supported", sig) return } diff --git a/hooks.go b/hooks.go index 091ecbc7..0fddc82f 100644 --- a/hooks.go +++ b/hooks.go @@ -55,9 +55,9 @@ func registerSession() error { conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig) conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly conf.Domain = BConfig.WebConfig.Session.SessionDomain - conf.EnableSidInHttpHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader - conf.SessionNameInHttpHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader - conf.EnableSidInUrlQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery + conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader + conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader + conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery } else { if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil { return err diff --git a/logs/alils/alils.go b/logs/alils/alils.go index aaee228a..867ff4cb 100644 --- a/logs/alils/alils.go +++ b/logs/alils/alils.go @@ -11,11 +11,14 @@ import ( ) const ( - CacheSize int = 64 + // CacheSize set the flush size + CacheSize int = 64 + // Delimiter define the topic delimiter Delimiter string = "##" ) -type AliLSConfig struct { +// Config is the Config for Ali Log +type Config struct { Project string `json:"project"` Endpoint string `json:"endpoint"` KeyID string `json:"key_id"` @@ -35,18 +38,17 @@ type aliLSWriter struct { withMap bool groupMap map[string]*LogGroup lock *sync.Mutex - AliLSConfig + Config } -// 创建提供Logger接口的日志服务 +// NewAliLS create a new Logger func NewAliLS() logs.Logger { alils := new(aliLSWriter) alils.Level = logs.LevelTrace return alils } -// 读取配置 -// 初始化必要的数据结构 +// Init parse config and init struct func (c *aliLSWriter) Init(jsonConfig string) (err error) { json.Unmarshal([]byte(jsonConfig), c) @@ -55,28 +57,26 @@ func (c *aliLSWriter) Init(jsonConfig string) (err error) { c.FlushWhen = CacheSize } - // 初始化Project prj := &LogProject{ Name: c.Project, Endpoint: c.Endpoint, - AccessKeyId: c.KeyID, + AccessKeyID: c.KeyID, AccessKeySecret: c.KeySecret, } - // 获取logstore c.store, err = prj.GetLogStore(c.LogStore) if err != nil { return err } - // 创建默认Log Group + // Create default Log Group c.group = append(c.group, &LogGroup{ Topic: proto.String(""), Source: proto.String(c.Source), Logs: make([]*Log, 0, c.FlushWhen), }) - // 创建其它Log Group + // Create other Log Group c.groupMap = make(map[string]*LogGroup) for _, topic := range c.Topics { @@ -114,7 +114,7 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error var lg *LogGroup if c.withMap { - // 解析出Topic,并匹配LogGroup + // Topic,LogGroup strs := strings.SplitN(msg, Delimiter, 2) if len(strs) == 2 { pos := strings.LastIndex(strs[0], " ") @@ -123,7 +123,7 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error lg = c.groupMap[topic] } - // 默认发到空Topic + // send to empty Topic if lg == nil { content = msg lg = c.group[0] @@ -133,15 +133,14 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error lg = c.group[0] } - // 生成日志 - c1 := &Log_Content{ + c1 := &LogContent{ Key: proto.String("msg"), Value: proto.String(content), } l := &Log{ - Time: proto.Uint32(uint32(when.Unix())), // 填写日志时间 - Contents: []*Log_Content{ + Time: proto.Uint32(uint32(when.Unix())), + Contents: []*LogContent{ c1, }, } @@ -150,7 +149,6 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error lg.Logs = append(lg.Logs, l) c.lock.Unlock() - // 满足条件则Flush if len(lg.Logs) >= c.FlushWhen { c.flush(lg) } @@ -161,7 +159,7 @@ func (c *aliLSWriter) WriteMsg(when time.Time, msg string, level int) (err error // Flush implementing method. empty. func (c *aliLSWriter) Flush() { - // flush所有group + // flush all group for _, lg := range c.group { c.flush(lg) } @@ -175,9 +173,6 @@ func (c *aliLSWriter) flush(lg *LogGroup) { c.lock.Lock() defer c.lock.Unlock() - - // 把以上的LogGroup推送到SLS服务器, - // SLS服务器会根据该logstore的shard个数自动进行负载均衡。 err := c.store.PutLogs(lg) if err != nil { return diff --git a/logs/alils/log.pb.go b/logs/alils/log.pb.go index b60ca200..601b0d78 100755 --- a/logs/alils/log.pb.go +++ b/logs/alils/log.pb.go @@ -1,30 +1,43 @@ package alils -import "github.com/gogo/protobuf/proto" -import "fmt" -import "math" +import ( + "fmt" + "io" + "math" -// discarding unused import gogoproto "." - -import github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" - -import "io" + "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_proto "github.com/gogo/protobuf/proto" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +var ( + // ErrInvalidLengthLog invalid proto + ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling") + // ErrIntOverflowLog overflow + ErrIntOverflowLog = fmt.Errorf("proto: integer overflow") +) + +// Log define the proto Log type Log struct { - Time *uint32 `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"` - Contents []*Log_Content `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"` - XXX_unrecognized []byte `json:"-"` + Time *uint32 `protobuf:"varint,1,req,name=Time" json:"Time,omitempty"` + Contents []*LogContent `protobuf:"bytes,2,rep,name=Contents" json:"Contents,omitempty"` + XXXUnrecognized []byte `json:"-"` } -func (m *Log) Reset() { *m = Log{} } -func (m *Log) String() string { return proto.CompactTextString(m) } -func (*Log) ProtoMessage() {} +// Reset the Log +func (m *Log) Reset() { *m = Log{} } +// String return the Compact Log +func (m *Log) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*Log) ProtoMessage() {} + +// GetTime return the Log's Time func (m *Log) GetTime() uint32 { if m != nil && m.Time != nil { return *m.Time @@ -32,49 +45,65 @@ func (m *Log) GetTime() uint32 { return 0 } -func (m *Log) GetContents() []*Log_Content { +// GetContents return the Log's Contents +func (m *Log) GetContents() []*LogContent { if m != nil { return m.Contents } return nil } -type Log_Content struct { - Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"` - Value *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"` - XXX_unrecognized []byte `json:"-"` +// LogContent define the Log content struct +type LogContent struct { + Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"` + Value *string `protobuf:"bytes,2,req,name=Value" json:"Value,omitempty"` + XXXUnrecognized []byte `json:"-"` } -func (m *Log_Content) Reset() { *m = Log_Content{} } -func (m *Log_Content) String() string { return proto.CompactTextString(m) } -func (*Log_Content) ProtoMessage() {} +// Reset LogContent +func (m *LogContent) Reset() { *m = LogContent{} } -func (m *Log_Content) GetKey() string { +// String return the compact text +func (m *LogContent) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*LogContent) ProtoMessage() {} + +// GetKey return the Key +func (m *LogContent) GetKey() string { if m != nil && m.Key != nil { return *m.Key } return "" } -func (m *Log_Content) GetValue() string { +// GetValue return the Value +func (m *LogContent) GetValue() string { if m != nil && m.Value != nil { return *m.Value } return "" } +// LogGroup define the logs struct type LogGroup struct { - Logs []*Log `protobuf:"bytes,1,rep,name=Logs" json:"Logs,omitempty"` - Reserved *string `protobuf:"bytes,2,opt,name=Reserved" json:"Reserved,omitempty"` - Topic *string `protobuf:"bytes,3,opt,name=Topic" json:"Topic,omitempty"` - Source *string `protobuf:"bytes,4,opt,name=Source" json:"Source,omitempty"` - XXX_unrecognized []byte `json:"-"` + Logs []*Log `protobuf:"bytes,1,rep,name=Logs" json:"Logs,omitempty"` + Reserved *string `protobuf:"bytes,2,opt,name=Reserved" json:"Reserved,omitempty"` + Topic *string `protobuf:"bytes,3,opt,name=Topic" json:"Topic,omitempty"` + Source *string `protobuf:"bytes,4,opt,name=Source" json:"Source,omitempty"` + XXXUnrecognized []byte `json:"-"` } -func (m *LogGroup) Reset() { *m = LogGroup{} } -func (m *LogGroup) String() string { return proto.CompactTextString(m) } -func (*LogGroup) ProtoMessage() {} +// Reset LogGroup +func (m *LogGroup) Reset() { *m = LogGroup{} } +// String return the compact text +func (m *LogGroup) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*LogGroup) ProtoMessage() {} + +// GetLogs return the loggroup logs func (m *LogGroup) GetLogs() []*Log { if m != nil { return m.Logs @@ -82,6 +111,7 @@ func (m *LogGroup) GetLogs() []*Log { return nil } +// GetReserved return Reserved func (m *LogGroup) GetReserved() string { if m != nil && m.Reserved != nil { return *m.Reserved @@ -89,6 +119,7 @@ func (m *LogGroup) GetReserved() string { return "" } +// GetTopic return Topic func (m *LogGroup) GetTopic() string { if m != nil && m.Topic != nil { return *m.Topic @@ -96,6 +127,7 @@ func (m *LogGroup) GetTopic() string { return "" } +// GetSource return Source func (m *LogGroup) GetSource() string { if m != nil && m.Source != nil { return *m.Source @@ -103,15 +135,22 @@ func (m *LogGroup) GetSource() string { return "" } +// LogGroupList define the LogGroups type LogGroupList struct { - LogGroups []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"` - XXX_unrecognized []byte `json:"-"` + LogGroups []*LogGroup `protobuf:"bytes,1,rep,name=logGroups" json:"logGroups,omitempty"` + XXXUnrecognized []byte `json:"-"` } -func (m *LogGroupList) Reset() { *m = LogGroupList{} } -func (m *LogGroupList) String() string { return proto.CompactTextString(m) } -func (*LogGroupList) ProtoMessage() {} +// Reset LogGroupList +func (m *LogGroupList) Reset() { *m = LogGroupList{} } +// String return compact text +func (m *LogGroupList) String() string { return proto.CompactTextString(m) } + +// ProtoMessage not implemented +func (*LogGroupList) ProtoMessage() {} + +// GetLogGroups return the LogGroups func (m *LogGroupList) GetLogGroups() []*LogGroup { if m != nil { return m.LogGroups @@ -119,6 +158,7 @@ func (m *LogGroupList) GetLogGroups() []*LogGroup { return nil } +// Marshal the logs to byte slice func (m *Log) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) @@ -129,6 +169,7 @@ func (m *Log) Marshal() (data []byte, err error) { return data[:n], nil } +// MarshalTo data func (m *Log) MarshalTo(data []byte) (int, error) { var i int _ = i @@ -136,11 +177,10 @@ func (m *Log) MarshalTo(data []byte) (int, error) { _ = l if m.Time == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Time") - } else { - data[i] = 0x8 - i++ - i = encodeVarintLog(data, i, uint64(*m.Time)) } + data[i] = 0x8 + i++ + i = encodeVarintLog(data, i, uint64(*m.Time)) if len(m.Contents) > 0 { for _, msg := range m.Contents { data[i] = 0x12 @@ -153,13 +193,14 @@ func (m *Log) MarshalTo(data []byte) (int, error) { i += n } } - if m.XXX_unrecognized != nil { - i += copy(data[i:], m.XXX_unrecognized) + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) } return i, nil } -func (m *Log_Content) Marshal() (data []byte, err error) { +// Marshal LogContent +func (m *LogContent) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) n, err := m.MarshalTo(data) @@ -169,33 +210,34 @@ func (m *Log_Content) Marshal() (data []byte, err error) { return data[:n], nil } -func (m *Log_Content) MarshalTo(data []byte) (int, error) { +// MarshalTo logcontent to data +func (m *LogContent) MarshalTo(data []byte) (int, error) { var i int _ = i var l int _ = l if m.Key == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Key") - } else { - data[i] = 0xa - i++ - i = encodeVarintLog(data, i, uint64(len(*m.Key))) - i += copy(data[i:], *m.Key) } + data[i] = 0xa + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Key))) + i += copy(data[i:], *m.Key) + if m.Value == nil { return 0, github_com_gogo_protobuf_proto.NewRequiredNotSetError("Value") - } else { - data[i] = 0x12 - i++ - i = encodeVarintLog(data, i, uint64(len(*m.Value))) - i += copy(data[i:], *m.Value) } - if m.XXX_unrecognized != nil { - i += copy(data[i:], m.XXX_unrecognized) + data[i] = 0x12 + i++ + i = encodeVarintLog(data, i, uint64(len(*m.Value))) + i += copy(data[i:], *m.Value) + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) } return i, nil } +// Marshal LogGroup func (m *LogGroup) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) @@ -206,6 +248,7 @@ func (m *LogGroup) Marshal() (data []byte, err error) { return data[:n], nil } +// MarshalTo LogGroup to data func (m *LogGroup) MarshalTo(data []byte) (int, error) { var i int _ = i @@ -241,12 +284,13 @@ func (m *LogGroup) MarshalTo(data []byte) (int, error) { i = encodeVarintLog(data, i, uint64(len(*m.Source))) i += copy(data[i:], *m.Source) } - if m.XXX_unrecognized != nil { - i += copy(data[i:], m.XXX_unrecognized) + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) } return i, nil } +// Marshal LogGroupList func (m *LogGroupList) Marshal() (data []byte, err error) { size := m.Size() data = make([]byte, size) @@ -257,6 +301,7 @@ func (m *LogGroupList) Marshal() (data []byte, err error) { return data[:n], nil } +// MarshalTo LogGroupList to data func (m *LogGroupList) MarshalTo(data []byte) (int, error) { var i int _ = i @@ -274,8 +319,8 @@ func (m *LogGroupList) MarshalTo(data []byte) (int, error) { i += n } } - if m.XXX_unrecognized != nil { - i += copy(data[i:], m.XXX_unrecognized) + if m.XXXUnrecognized != nil { + i += copy(data[i:], m.XXXUnrecognized) } return i, nil } @@ -307,6 +352,8 @@ func encodeVarintLog(data []byte, offset int, v uint64) int { data[offset] = uint8(v) return offset + 1 } + +// Size return the log's size func (m *Log) Size() (n int) { var l int _ = l @@ -319,13 +366,14 @@ func (m *Log) Size() (n int) { n += 1 + l + sovLog(uint64(l)) } } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) } return n } -func (m *Log_Content) Size() (n int) { +// Size return LogContent size based on Key and Value +func (m *LogContent) Size() (n int) { var l int _ = l if m.Key != nil { @@ -336,12 +384,13 @@ func (m *Log_Content) Size() (n int) { l = len(*m.Value) n += 1 + l + sovLog(uint64(l)) } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) } return n } +// Size return LogGroup size based on Logs func (m *LogGroup) Size() (n int) { var l int _ = l @@ -363,12 +412,13 @@ func (m *LogGroup) Size() (n int) { l = len(*m.Source) n += 1 + l + sovLog(uint64(l)) } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) } return n } +// Size return LogGroupList size func (m *LogGroupList) Size() (n int) { var l int _ = l @@ -378,8 +428,8 @@ func (m *LogGroupList) Size() (n int) { n += 1 + l + sovLog(uint64(l)) } } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if m.XXXUnrecognized != nil { + n += len(m.XXXUnrecognized) } return n } @@ -397,6 +447,8 @@ func sovLog(x uint64) (n int) { func sozLog(x uint64) (n int) { return sovLog((x << 1) ^ (x >> 63)) } + +// Unmarshal data to log func (m *Log) Unmarshal(data []byte) error { var hasFields [1]uint64 l := len(data) @@ -474,7 +526,7 @@ func (m *Log) Unmarshal(data []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Contents = append(m.Contents, &Log_Content{}) + m.Contents = append(m.Contents, &LogContent{}) if err := m.Contents[len(m.Contents)-1].Unmarshal(data[iNdEx:postIndex]); err != nil { return err } @@ -491,7 +543,7 @@ func (m *Log) Unmarshal(data []byte) error { if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -504,7 +556,9 @@ func (m *Log) Unmarshal(data []byte) error { } return nil } -func (m *Log_Content) Unmarshal(data []byte) error { + +// Unmarshal data to LogContent +func (m *LogContent) Unmarshal(data []byte) error { var hasFields [1]uint64 l := len(data) iNdEx := 0 @@ -608,7 +662,7 @@ func (m *Log_Content) Unmarshal(data []byte) error { if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -624,6 +678,8 @@ func (m *Log_Content) Unmarshal(data []byte) error { } return nil } + +// Unmarshal data to LogGroup func (m *LogGroup) Unmarshal(data []byte) error { l := len(data) iNdEx := 0 @@ -786,7 +842,7 @@ func (m *LogGroup) Unmarshal(data []byte) error { if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -796,6 +852,8 @@ func (m *LogGroup) Unmarshal(data []byte) error { } return nil } + +// Unmarshal data to LogGroupList func (m *LogGroupList) Unmarshal(data []byte) error { l := len(data) iNdEx := 0 @@ -868,7 +926,7 @@ func (m *LogGroupList) Unmarshal(data []byte) error { if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } - m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...) + m.XXXUnrecognized = append(m.XXXUnrecognized, data[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } @@ -878,6 +936,7 @@ func (m *LogGroupList) Unmarshal(data []byte) error { } return nil } + func skipLog(data []byte) (n int, err error) { l := len(data) iNdEx := 0 @@ -940,7 +999,7 @@ func skipLog(data []byte) (n int, err error) { case 3: for { var innerWire uint64 - var start int = iNdEx + var start = iNdEx for shift := uint(0); ; shift += 7 { if shift >= 64 { return 0, ErrIntOverflowLog @@ -977,8 +1036,3 @@ func skipLog(data []byte) (n int, err error) { } panic("unreachable") } - -var ( - ErrInvalidLengthLog = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowLog = fmt.Errorf("proto: integer overflow") -) diff --git a/logs/alils/log_config.go b/logs/alils/log_config.go index 41fa0959..e8564efb 100755 --- a/logs/alils/log_config.go +++ b/logs/alils/log_config.go @@ -1,5 +1,6 @@ package alils +// InputDetail define log detail type InputDetail struct { LogType string `json:"logType"` LogPath string `json:"logPath"` @@ -14,11 +15,13 @@ type InputDetail struct { TopicFormat string `json:"topicFormat"` } +// OutputDetail define the output detail type OutputDetail struct { Endpoint string `json:"endpoint"` LogStoreName string `json:"logstoreName"` } +// LogConfig define Log Config type LogConfig struct { Name string `json:"configName"` InputType string `json:"inputType"` diff --git a/logs/alils/log_project.go b/logs/alils/log_project.go index 63ab07f8..59db8cbf 100755 --- a/logs/alils/log_project.go +++ b/logs/alils/log_project.go @@ -1,5 +1,5 @@ /* -Package sls implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS). +Package alils implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS). For more description about SLS, please read this article: http://gitlab.alibaba-inc.com/sls/doc. @@ -20,19 +20,20 @@ type errorMessage struct { Message string `json:"errorMessage"` } +// LogProject Define the Ali Project detail type LogProject struct { Name string // Project name Endpoint string // IP or hostname of SLS endpoint - AccessKeyId string + AccessKeyID string AccessKeySecret string } // NewLogProject creates a new SLS project. -func NewLogProject(name, endpoint, accessKeyId, accessKeySecret string) (p *LogProject, err error) { +func NewLogProject(name, endpoint, AccessKeyID, accessKeySecret string) (p *LogProject, err error) { p = &LogProject{ Name: name, Endpoint: endpoint, - AccessKeyId: accessKeyId, + AccessKeyID: AccessKeyID, AccessKeySecret: accessKeySecret, } return p, nil diff --git a/logs/alils/log_store.go b/logs/alils/log_store.go index 009e39c4..fa502736 100755 --- a/logs/alils/log_store.go +++ b/logs/alils/log_store.go @@ -12,6 +12,7 @@ import ( "github.com/gogo/protobuf/proto" ) +// LogStore Store the logs type LogStore struct { Name string `json:"logstoreName"` TTL int @@ -23,6 +24,7 @@ type LogStore struct { project *LogProject } +// Shard define the Log Shard type Shard struct { ShardID int `json:"shardID"` } @@ -116,16 +118,16 @@ func (s *LogStore) PutLogs(lg *LogGroup) (err error) { return } -// GetCursor gets log cursor of one shard specified by shardId. +// GetCursor gets log cursor of one shard specified by shardID. // The from can be in three form: a) unix timestamp in seccond, b) "begin", c) "end". // For more detail please read: http://gitlab.alibaba-inc.com/sls/doc/blob/master/api/shard.md#logstore -func (s *LogStore) GetCursor(shardId int, from string) (cursor string, err error) { +func (s *LogStore) GetCursor(shardID int, from string) (cursor string, err error) { h := map[string]string{ "x-sls-bodyrawsize": "0", } uri := fmt.Sprintf("/logstores/%v/shards/%v?type=cursor&from=%v", - s.Name, shardId, from) + s.Name, shardID, from) r, err := request(s.project, "GET", uri, h, nil) if err != nil { @@ -163,10 +165,10 @@ func (s *LogStore) GetCursor(shardId int, from string) (cursor string, err error return } -// GetLogsBytes gets logs binary data from shard specified by shardId according cursor. +// GetLogsBytes gets logs binary data from shard specified by shardID according cursor. // The logGroupMaxCount is the max number of logGroup could be returned. // The nextCursor is the next curosr can be used to read logs at next time. -func (s *LogStore) GetLogsBytes(shardId int, cursor string, +func (s *LogStore) GetLogsBytes(shardID int, cursor string, logGroupMaxCount int) (out []byte, nextCursor string, err error) { h := map[string]string{ @@ -176,7 +178,7 @@ func (s *LogStore) GetLogsBytes(shardId int, cursor string, } uri := fmt.Sprintf("/logstores/%v/shards/%v?type=logs&cursor=%v&count=%v", - s.Name, shardId, cursor, logGroupMaxCount) + s.Name, shardID, cursor, logGroupMaxCount) r, err := request(s.project, "GET", uri, h, nil) if err != nil { @@ -249,13 +251,13 @@ func LogsBytesDecode(data []byte) (gl *LogGroupList, err error) { return } -// GetLogs gets logs from shard specified by shardId according cursor. +// GetLogs gets logs from shard specified by shardID according cursor. // The logGroupMaxCount is the max number of logGroup could be returned. // The nextCursor is the next curosr can be used to read logs at next time. -func (s *LogStore) GetLogs(shardId int, cursor string, +func (s *LogStore) GetLogs(shardID int, cursor string, logGroupMaxCount int) (gl *LogGroupList, nextCursor string, err error) { - out, nextCursor, err := s.GetLogsBytes(shardId, cursor, logGroupMaxCount) + out, nextCursor, err := s.GetLogsBytes(shardID, cursor, logGroupMaxCount) if err != nil { return } diff --git a/logs/alils/machine_group.go b/logs/alils/machine_group.go index 7a0aace1..b6c69a14 100755 --- a/logs/alils/machine_group.go +++ b/logs/alils/machine_group.go @@ -8,18 +8,20 @@ import ( "net/http/httputil" ) -type MachinGroupAttribute struct { +// MachineGroupAttribute define the Attribute +type MachineGroupAttribute struct { ExternalName string `json:"externalName"` TopicName string `json:"groupTopic"` } +// MachineGroup define the machine Group type MachineGroup struct { Name string `json:"groupName"` Type string `json:"groupType"` - MachineIdType string `json:"machineIdentifyType"` - MachineIdList []string `json:"machineList"` + MachineIDType string `json:"machineIdentifyType"` + MachineIDList []string `json:"machineList"` - Attribute MachinGroupAttribute `json:"groupAttribute"` + Attribute MachineGroupAttribute `json:"groupAttribute"` CreateTime uint32 LastModifyTime uint32 @@ -27,12 +29,14 @@ type MachineGroup struct { project *LogProject } +// Machine define the Machine type Machine struct { IP string - UniqueId string `json:"machine-uniqueid"` - UserdefinedId string `json:"userdefined-id"` + UniqueID string `json:"machine-uniqueid"` + UserdefinedID string `json:"userdefined-id"` } +// MachineList define the Machine List type MachineList struct { Total int Machines []*Machine diff --git a/logs/alils/request.go b/logs/alils/request.go index 20df45b4..50d9c43c 100755 --- a/logs/alils/request.go +++ b/logs/alils/request.go @@ -33,12 +33,12 @@ func request(project *LogProject, method, uri string, headers map[string]string, } // Calc Authorization - // Authorization = "SLS :" + // Authorization = "SLS :" digest, err := signature(project, method, uri, headers) if err != nil { return } - auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyId, digest) + auth := fmt.Sprintf("SLS %v:%v", project.AccessKeyID, digest) headers["Authorization"] = auth // Initialize http request diff --git a/logs/file.go b/logs/file.go index 20ef167d..1c2db882 100644 --- a/logs/file.go +++ b/logs/file.go @@ -170,7 +170,7 @@ func (w *fileLogWriter) initFd() error { fd := w.fileWriter fInfo, err := fd.Stat() if err != nil { - return fmt.Errorf("get stat err: %s\n", err) + return fmt.Errorf("get stat err: %s", err) } w.maxSizeCurSize = int(fInfo.Size()) w.dailyOpenTime = time.Now() diff --git a/logs/log.go b/logs/log.go index a5e0a9ea..0e97a70e 100644 --- a/logs/log.go +++ b/logs/log.go @@ -492,9 +492,9 @@ func (bl *BeeLogger) flush() { } // beeLogger references the used application logger. -var beeLogger *BeeLogger = NewLogger() +var beeLogger = NewLogger() -// GetLogger returns the default BeeLogger +// GetBeeLogger returns the default BeeLogger func GetBeeLogger() *BeeLogger { return beeLogger } @@ -534,6 +534,7 @@ func Reset() { beeLogger.Reset() } +// Async set the beelogger with Async mode and hold msglen messages func Async(msgLen ...int64) *BeeLogger { return beeLogger.Async(msgLen...) } diff --git a/logs/logger.go b/logs/logger.go index e0abfdc4..b5d7255f 100644 --- a/logs/logger.go +++ b/logs/logger.go @@ -139,6 +139,11 @@ var ( reset = string([]byte{27, 91, 48, 109}) ) +// ColorByStatus return color by http code +// 2xx return Green +// 3xx return White +// 4xx return Yellow +// 5xx return Red func ColorByStatus(cond bool, code int) string { switch { case code >= 200 && code < 300: @@ -152,6 +157,14 @@ func ColorByStatus(cond bool, code int) string { } } +// ColorByMethod return color by http code +// GET return Blue +// POST return Cyan +// PUT return Yellow +// DELETE return Red +// PATCH return Green +// HEAD return Magenta +// OPTIONS return WHITE func ColorByMethod(cond bool, method string) string { switch method { case "GET": @@ -173,10 +186,10 @@ func ColorByMethod(cond bool, method string) string { } } -// Guard Mutex to guarantee atomicity of W32Debug(string) function +// Guard Mutex to guarantee atomic of W32Debug(string) function var mu sync.Mutex -// Helper method to output colored logs in Windows terminals +// W32Debug Helper method to output colored logs in Windows terminals func W32Debug(msg string) { mu.Lock() defer mu.Unlock() diff --git a/orm/db.go b/orm/db.go index 2bd525c2..2a05797a 100644 --- a/orm/db.go +++ b/orm/db.go @@ -507,10 +507,9 @@ func (d *dbBase) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a case DRPostgres: if len(args) == 0 { return 0, fmt.Errorf("`%s` use InsertOrUpdate must have a conflict column", a.DriverName) - } else { - args0 = strings.ToLower(args[0]) - iouStr = fmt.Sprintf("ON CONFLICT (%s) DO UPDATE SET", args0) } + args0 = strings.ToLower(args[0]) + iouStr = fmt.Sprintf("ON CONFLICT (%s) DO UPDATE SET", args0) default: return 0, fmt.Errorf("`%s` nonsupport InsertOrUpdate in beego", a.DriverName) } diff --git a/orm/db_alias.go b/orm/db_alias.go index e70db9f0..c7089239 100644 --- a/orm/db_alias.go +++ b/orm/db_alias.go @@ -250,7 +250,7 @@ func RegisterDriver(driverName string, typ DriverType) error { drivers[driverName] = typ } else { if t != typ { - return fmt.Errorf("driverName `%s` db driver already registered and is other type\n", driverName) + return fmt.Errorf("driverName `%s` db driver already registered and is other type", driverName) } } return nil @@ -261,7 +261,7 @@ func SetDataBaseTZ(aliasName string, tz *time.Location) error { if al, ok := dataBaseCache.get(aliasName); ok { al.TZ = tz } else { - return fmt.Errorf("DataBase alias name `%s` not registered\n", aliasName) + return fmt.Errorf("DataBase alias name `%s` not registered", aliasName) } return nil } @@ -296,5 +296,5 @@ func GetDB(aliasNames ...string) (*sql.DB, error) { if ok { return al.DB, nil } - return nil, fmt.Errorf("DataBase of alias name `%s` not found\n", name) + return nil, fmt.Errorf("DataBase of alias name `%s` not found", name) } diff --git a/session/session.go b/session/session.go index fb4b2821..cf647521 100644 --- a/session/session.go +++ b/session/session.go @@ -81,6 +81,7 @@ func Register(name string, provide Provider) { provides[name] = provide } +// ManagerConfig define the session config type ManagerConfig struct { CookieName string `json:"cookieName"` EnableSetCookie bool `json:"enableSetCookie,omitempty"` @@ -92,9 +93,9 @@ type ManagerConfig struct { ProviderConfig string `json:"providerConfig"` Domain string `json:"domain"` SessionIDLength int64 `json:"sessionIDLength"` - EnableSidInHttpHeader bool `json:"enableSidInHttpHeader"` - SessionNameInHttpHeader string `json:"sessionNameInHttpHeader"` - EnableSidInUrlQuery bool `json:"enableSidInUrlQuery"` + EnableSidInHTTPHeader bool `json:"EnableSidInHTTPHeader"` + SessionNameInHTTPHeader string `json:"SessionNameInHTTPHeader"` + EnableSidInURLQuery bool `json:"EnableSidInURLQuery"` } // Manager contains Provider and its configuration. @@ -125,14 +126,14 @@ func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) { cf.Maxlifetime = cf.Gclifetime } - if cf.EnableSidInHttpHeader { - if cf.SessionNameInHttpHeader == "" { - panic(errors.New("SessionNameInHttpHeader is empty")) + if cf.EnableSidInHTTPHeader { + if cf.SessionNameInHTTPHeader == "" { + panic(errors.New("SessionNameInHTTPHeader is empty")) } - strMimeHeader := textproto.CanonicalMIMEHeaderKey(cf.SessionNameInHttpHeader) - if cf.SessionNameInHttpHeader != strMimeHeader { - strErrMsg := "SessionNameInHttpHeader (" + cf.SessionNameInHttpHeader + ") has the wrong format, it should be like this : " + strMimeHeader + strMimeHeader := textproto.CanonicalMIMEHeaderKey(cf.SessionNameInHTTPHeader) + if cf.SessionNameInHTTPHeader != strMimeHeader { + strErrMsg := "SessionNameInHTTPHeader (" + cf.SessionNameInHTTPHeader + ") has the wrong format, it should be like this : " + strMimeHeader panic(errors.New(strErrMsg)) } } @@ -163,7 +164,7 @@ func (manager *Manager) getSid(r *http.Request) (string, error) { cookie, errs := r.Cookie(manager.config.CookieName) if errs != nil || cookie.Value == "" { var sid string - if manager.config.EnableSidInUrlQuery { + if manager.config.EnableSidInURLQuery { errs := r.ParseForm() if errs != nil { return "", errs @@ -173,8 +174,8 @@ func (manager *Manager) getSid(r *http.Request) (string, error) { } // if not found in Cookie / param, then read it from request headers - if manager.config.EnableSidInHttpHeader && sid == "" { - sids, isFound := r.Header[manager.config.SessionNameInHttpHeader] + if manager.config.EnableSidInHTTPHeader && sid == "" { + sids, isFound := r.Header[manager.config.SessionNameInHTTPHeader] if isFound && len(sids) != 0 { return sids[0], nil } @@ -226,9 +227,9 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se } r.AddCookie(cookie) - if manager.config.EnableSidInHttpHeader { - r.Header.Set(manager.config.SessionNameInHttpHeader, sid) - w.Header().Set(manager.config.SessionNameInHttpHeader, sid) + if manager.config.EnableSidInHTTPHeader { + r.Header.Set(manager.config.SessionNameInHTTPHeader, sid) + w.Header().Set(manager.config.SessionNameInHTTPHeader, sid) } return @@ -236,9 +237,9 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se // SessionDestroy Destroy session by its id in http request cookie. func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { - if manager.config.EnableSidInHttpHeader { - r.Header.Del(manager.config.SessionNameInHttpHeader) - w.Header().Del(manager.config.SessionNameInHttpHeader) + if manager.config.EnableSidInHTTPHeader { + r.Header.Del(manager.config.SessionNameInHTTPHeader) + w.Header().Del(manager.config.SessionNameInHTTPHeader) } cookie, err := r.Cookie(manager.config.CookieName) @@ -306,9 +307,9 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque } r.AddCookie(cookie) - if manager.config.EnableSidInHttpHeader { - r.Header.Set(manager.config.SessionNameInHttpHeader, sid) - w.Header().Set(manager.config.SessionNameInHttpHeader, sid) + if manager.config.EnableSidInHTTPHeader { + r.Header.Set(manager.config.SessionNameInHTTPHeader, sid) + w.Header().Set(manager.config.SessionNameInHTTPHeader, sid) } return @@ -328,7 +329,7 @@ func (manager *Manager) sessionID() (string, error) { b := make([]byte, manager.config.SessionIDLength) n, err := rand.Read(b) if n != len(b) || err != nil { - return "", fmt.Errorf("Could not successfully read from the system CSPRNG.") + return "", fmt.Errorf("Could not successfully read from the system CSPRNG") } return hex.EncodeToString(b), nil } diff --git a/session/ssdb/sess_ssdb.go b/session/ssdb/sess_ssdb.go index 03b60793..de0c6360 100644 --- a/session/ssdb/sess_ssdb.go +++ b/session/ssdb/sess_ssdb.go @@ -11,16 +11,17 @@ import ( "github.com/ssdb/gossdb/ssdb" ) -var ssdbProvider = &SsdbProvider{} +var ssdbProvider = &Provider{} -type SsdbProvider struct { +// Provider holds ssdb client and configs +type Provider struct { client *ssdb.Client host string port int maxLifetime int64 } -func (p *SsdbProvider) connectInit() error { +func (p *Provider) connectInit() error { var err error if p.host == "" || p.port == 0 { return errors.New("SessionInit First") @@ -29,7 +30,8 @@ func (p *SsdbProvider) connectInit() error { return err } -func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error { +// SessionInit init the ssdb with the config +func (p *Provider) SessionInit(maxLifetime int64, savePath string) error { p.maxLifetime = maxLifetime address := strings.Split(savePath, ":") p.host = address[0] @@ -41,7 +43,8 @@ func (p *SsdbProvider) SessionInit(maxLifetime int64, savePath string) error { return p.connectInit() } -func (p *SsdbProvider) SessionRead(sid string) (session.Store, error) { +// SessionRead return a ssdb client session Store +func (p *Provider) SessionRead(sid string) (session.Store, error) { if p.client == nil { if err := p.connectInit(); err != nil { return nil, err @@ -64,7 +67,8 @@ func (p *SsdbProvider) SessionRead(sid string) (session.Store, error) { return rs, nil } -func (p *SsdbProvider) SessionExist(sid string) bool { +// SessionExist judged whether sid is exist in session +func (p *Provider) SessionExist(sid string) bool { if p.client == nil { if err := p.connectInit(); err != nil { panic(err) @@ -80,7 +84,8 @@ func (p *SsdbProvider) SessionExist(sid string) bool { return true } -func (p *SsdbProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) { +// SessionRegenerate regenerate session with new sid and delete oldsid +func (p *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { //conn.Do("setx", key, v, ttl) if p.client == nil { if err := p.connectInit(); err != nil { @@ -112,7 +117,8 @@ func (p *SsdbProvider) SessionRegenerate(oldsid, sid string) (session.Store, err return rs, nil } -func (p *SsdbProvider) SessionDestroy(sid string) error { +// SessionDestroy destroy the sid +func (p *Provider) SessionDestroy(sid string) error { if p.client == nil { if err := p.connectInit(); err != nil { return err @@ -122,13 +128,16 @@ func (p *SsdbProvider) SessionDestroy(sid string) error { return err } -func (p *SsdbProvider) SessionGC() { +// SessionGC not implemented +func (p *Provider) SessionGC() { } -func (p *SsdbProvider) SessionAll() int { +// SessionAll not implemented +func (p *Provider) SessionAll() int { return 0 } +// SessionStore holds the session information which stored in ssdb type SessionStore struct { sid string lock sync.RWMutex @@ -137,12 +146,15 @@ type SessionStore struct { client *ssdb.Client } +// Set the key and value func (s *SessionStore) Set(key, value interface{}) error { s.lock.Lock() defer s.lock.Unlock() s.values[key] = value return nil } + +// Get return the value by the key func (s *SessionStore) Get(key interface{}) interface{} { s.lock.Lock() defer s.lock.Unlock() @@ -152,30 +164,36 @@ func (s *SessionStore) Get(key interface{}) interface{} { return nil } +// Delete the key in session store func (s *SessionStore) Delete(key interface{}) error { s.lock.Lock() defer s.lock.Unlock() delete(s.values, key) return nil } + +// Flush delete all keys and values func (s *SessionStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() s.values = make(map[interface{}]interface{}) return nil } + +// SessionID return the sessionID func (s *SessionStore) SessionID() string { return s.sid } +// SessionRelease Store the keyvalues into ssdb func (s *SessionStore) SessionRelease(w http.ResponseWriter) { b, err := session.EncodeGob(s.values) if err != nil { return } s.client.Do("setx", s.sid, string(b), s.maxLifetime) - } + func init() { session.Register("ssdb", ssdbProvider) } diff --git a/swagger/swagger.go b/swagger/swagger.go index e0ac5cf5..c687fb8e 100644 --- a/swagger/swagger.go +++ b/swagger/swagger.go @@ -100,7 +100,7 @@ type Parameter struct { Default interface{} `json:"default,omitempty" yaml:"default,omitempty"` } -// A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body". +// ParameterItems A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body". // http://swagger.io/specification/#itemsObject type ParameterItems struct { Type string `json:"type,omitempty" yaml:"type,omitempty"` From 79b66ef05304e0ff5d3fe724b714841f57cf6f4e Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 30 Apr 2017 22:55:39 +0800 Subject: [PATCH 092/163] fix the beego ORM test --- orm/orm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/orm_test.go b/orm/orm_test.go index 71b614f3..c5bfa8b9 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -2144,7 +2144,7 @@ func TestUintPk(t *testing.T) { throwFail(t, AssertIs(u.Name, name)) nu := &UintPk{ID: 8} - created, pk, err = dORM.ReadOrCreate(nu, "ID") + created, pk, err := dORM.ReadOrCreate(nu, "ID") throwFail(t, err) throwFail(t, AssertIs(created, false)) throwFail(t, AssertIs(nu.ID, u.ID)) From 44bdf1df6313496c8bf5da5b3aa58fd4291a78e6 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 30 Apr 2017 23:38:48 +0800 Subject: [PATCH 093/163] ignore NilErr --- session/ledis/ledis_session.go | 5 +---- session/redis/sess_redis.go | 10 +++------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/session/ledis/ledis_session.go b/session/ledis/ledis_session.go index 18b27708..77685d1e 100644 --- a/session/ledis/ledis_session.go +++ b/session/ledis/ledis_session.go @@ -113,13 +113,10 @@ func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error { func (lp *Provider) SessionRead(sid string) (session.Store, error) { var ( kv map[interface{}]interface{} - kvs []byte err error ) - if kvs, err = c.Get([]byte(sid)); err != nil { - return nil, err - } + kvs, _ := c.Get([]byte(sid)) if len(kvs) == 0 { kv = make(map[interface{}]interface{}) diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index 08efa6e1..20dbe030 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -176,16 +176,12 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) { c := rp.poollist.Get() defer c.Close() - var ( - kv map[interface{}]interface{} - kvs string - err error - ) + var kv map[interface{}]interface{} - if kvs, err = redis.String(c.Do("GET", sid)); err != nil { + kvs, err := redis.String(c.Do("GET", sid)) + if err != redis.ErrNil { return nil, err } - if len(kvs) == 0 { kv = make(map[interface{}]interface{}) } else { From 947980b5ebe522e60de6e40d25cc93bef5525498 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 30 Apr 2017 23:43:46 +0800 Subject: [PATCH 094/163] beego 1.8.2 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 7acf4a13..7a8db390 100644 --- a/beego.go +++ b/beego.go @@ -23,7 +23,7 @@ import ( const ( // VERSION represent beego web framework version. - VERSION = "1.8.1" + VERSION = "1.8.2" // DEV is for develop DEV = "dev" From e76423e6dc646c6caa77618282fd7ffebd7a2241 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 30 Apr 2017 23:59:38 +0800 Subject: [PATCH 095/163] revert #2518, fix #2605 --- context/output.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/output.go b/context/output.go index c15f9fe7..564ef96d 100644 --- a/context/output.go +++ b/context/output.go @@ -105,7 +105,7 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface switch { case maxAge > 0: fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(maxAge)*time.Second).UTC().Format(time.RFC1123), maxAge) - case maxAge <= 0: + case maxAge < 0: fmt.Fprintf(&b, "; Max-Age=0") } } From 1b8f05cef15029e184036c081b9d5b9542d1ecbe Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Sun, 30 Apr 2017 19:28:26 +0300 Subject: [PATCH 096/163] golint fixes --- context/param/conv.go | 16 ++++++++-------- context/param/methodparams.go | 4 +++- context/param/options.go | 6 ++++++ context/response/renderer.go | 3 +++ context/response/responses.go | 9 ++++++--- router.go | 2 +- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/context/param/conv.go b/context/param/conv.go index 8e580c2e..e961b4ad 100644 --- a/context/param/conv.go +++ b/context/param/conv.go @@ -8,6 +8,7 @@ import ( "github.com/astaxie/beego/logs" ) +// ConvertParams converts http method params to values that will be passed to the method controller as arguments func ConvertParams(methodParams []*MethodParam, methodType reflect.Type, ctx *beecontext.Context) (result []reflect.Value) { result = make([]reflect.Value, 0, len(methodParams)) for i := 0; i < len(methodParams); i++ { @@ -55,15 +56,14 @@ func getParamValue(param *MethodParam, ctx *beecontext.Context) string { func parseValue(param *MethodParam, paramValue string, paramType reflect.Type) (result reflect.Value, err error) { if paramValue == "" { return reflect.Zero(paramType), nil - } else { - parser := getParser(param, paramType) - value, err := parser.parse(paramValue, paramType) - if err != nil { - return result, err - } - - return safeConvert(reflect.ValueOf(value), paramType) } + parser := getParser(param, paramType) + value, err := parser.parse(paramValue, paramType) + if err != nil { + return result, err + } + + return safeConvert(reflect.ValueOf(value), paramType) } func safeConvert(value reflect.Value, t reflect.Type) (result reflect.Value, err error) { diff --git a/context/param/methodparams.go b/context/param/methodparams.go index 23fd6661..28772504 100644 --- a/context/param/methodparams.go +++ b/context/param/methodparams.go @@ -5,7 +5,7 @@ import ( "strings" ) -//Keeps param information to be auto passed to controller methods +//MethodParam keeps param information to be auto passed to controller methods type MethodParam struct { name string location paramLocation @@ -22,6 +22,7 @@ const ( header ) +//New creates a new MethodParam with name and specific options func New(name string, opts ...MethodParamOption) *MethodParam { return newParam(name, nil, opts) } @@ -34,6 +35,7 @@ func newParam(name string, parser paramParser, opts []MethodParamOption) (param return } +//Make creates an array of MethodParmas or an empty array func Make(list ...*MethodParam) []*MethodParam { if len(list) > 0 { return list diff --git a/context/param/options.go b/context/param/options.go index 0013c31e..846a59f6 100644 --- a/context/param/options.go +++ b/context/param/options.go @@ -4,24 +4,30 @@ import ( "fmt" ) +// MethodParamOption defines a func which apply options on a MethodParam type MethodParamOption func(*MethodParam) +// IsRequired indicates that this param is required and can not be ommited from the http request var IsRequired MethodParamOption = func(p *MethodParam) { p.required = true } +// InHeader indicates that this param is passed via an http header var InHeader MethodParamOption = func(p *MethodParam) { p.location = header } +// InPath indicates that this param is part of the URL path var InPath MethodParamOption = func(p *MethodParam) { p.location = path } +// InBody indicates that this param is passed as an http request body var InBody MethodParamOption = func(p *MethodParam) { p.location = body } +// Default provides a default value for the http param func Default(defValue interface{}) MethodParamOption { return func(p *MethodParam) { if defValue != nil { diff --git a/context/response/renderer.go b/context/response/renderer.go index f5f9a52d..2a4d2797 100644 --- a/context/response/renderer.go +++ b/context/response/renderer.go @@ -6,6 +6,7 @@ import ( beecontext "github.com/astaxie/beego/context" ) +// Renderer defines an http response renderer type Renderer interface { Render(ctx *beecontext.Context) } @@ -16,12 +17,14 @@ func (f rendererFunc) Render(ctx *beecontext.Context) { f(ctx) } +// StatusCode sets the http response status code type StatusCode int func (s StatusCode) Error() string { return strconv.Itoa(int(s)) } +// Render sets the http status code func (s StatusCode) Render(ctx *beecontext.Context) { ctx.Output.SetStatus(int(s)) } diff --git a/context/response/responses.go b/context/response/responses.go index 5fbe4be1..c033edf7 100644 --- a/context/response/responses.go +++ b/context/response/responses.go @@ -4,7 +4,8 @@ import ( beecontext "github.com/astaxie/beego/context" ) -func Json(value interface{}, encoding ...bool) Renderer { +// JSON renders value to the response as JSON +func JSON(value interface{}, encoding ...bool) Renderer { return rendererFunc(func(ctx *beecontext.Context) { var ( hasIndent = true @@ -28,12 +29,14 @@ func errorRenderer(err error) Renderer { }) } -func Redirect(localurl string) statusCodeWithRender { +// Redirect renders http 302 with a URL +func Redirect(localurl string) Renderer { return statusCodeWithRender{302, func(ctx *beecontext.Context) { ctx.Redirect(302, localurl) }} } +// RenderMethodResult renders the return value of a controller method to the output func RenderMethodResult(result interface{}, ctx *beecontext.Context) { if result != nil { renderer, ok := result.(Renderer) @@ -42,7 +45,7 @@ func RenderMethodResult(result interface{}, ctx *beecontext.Context) { if ok { renderer = errorRenderer(err) } else { - renderer = Json(result) + renderer = JSON(result) } } renderer.Render(ctx) diff --git a/router.go b/router.go index ebe70e72..33096039 100644 --- a/router.go +++ b/router.go @@ -813,7 +813,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) default: if !execController.HandlerFunc(runMethod) { method := vc.MethodByName(runMethod) - var in []reflect.Value = param.ConvertParams(methodParams, method.Type(), context) + in := param.ConvertParams(methodParams, method.Type(), context) out := method.Call(in) //For backward compatibility we only handle response if we had incoming methodParams From d3a16dca85d1c4ceae415134718411c7758689fc Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Mon, 1 May 2017 08:57:57 +0300 Subject: [PATCH 097/163] Redirect should returns error --- context/response/responses.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/response/responses.go b/context/response/responses.go index c033edf7..10d1ed3f 100644 --- a/context/response/responses.go +++ b/context/response/responses.go @@ -30,7 +30,7 @@ func errorRenderer(err error) Renderer { } // Redirect renders http 302 with a URL -func Redirect(localurl string) Renderer { +func Redirect(localurl string) error { return statusCodeWithRender{302, func(ctx *beecontext.Context) { ctx.Redirect(302, localurl) }} From 83814a76cc77279d5d979780e82f4464b33677c3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 2 May 2017 12:47:15 +0800 Subject: [PATCH 098/163] hotfix: err nil --- session/redis/sess_redis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index 20dbe030..d0424515 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -179,7 +179,7 @@ func (rp *Provider) SessionRead(sid string) (session.Store, error) { var kv map[interface{}]interface{} kvs, err := redis.String(c.Do("GET", sid)) - if err != redis.ErrNil { + if err != nil && err != redis.ErrNil { return nil, err } if len(kvs) == 0 { From b2e7720fcd26a82d3a00d9846c26ba5931ca75b8 Mon Sep 17 00:00:00 2001 From: Yang Luo Date: Thu, 4 May 2017 14:02:21 +0800 Subject: [PATCH 099/163] Add an authorization plugin that supports ACL, RBAC based on casbin. It requires the built-in HTTP basic authentication by default. --- .travis.yml | 2 + plugins/authz/authz.go | 86 ++++++++++++++++++++++++++ plugins/authz/authz_model.conf | 14 +++++ plugins/authz/authz_policy.csv | 7 +++ plugins/authz/authz_test.go | 107 +++++++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 plugins/authz/authz.go create mode 100644 plugins/authz/authz_model.conf create mode 100644 plugins/authz/authz_policy.csv create mode 100644 plugins/authz/authz_test.go diff --git a/.travis.yml b/.travis.yml index 479d70ca..aa88a44d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,8 @@ install: - go get github.com/ssdb/gossdb/ssdb - go get github.com/cloudflare/golz4 - go get github.com/gogo/protobuf/proto + - go get github.com/Knetic/govaluate + - go get github.com/hsluoyz/casbin - go get -u honnef.co/go/tools/cmd/gosimple - go get -u github.com/mdempsky/unconvert - go get -u github.com/gordonklaus/ineffassign diff --git a/plugins/authz/authz.go b/plugins/authz/authz.go new file mode 100644 index 00000000..709a613a --- /dev/null +++ b/plugins/authz/authz.go @@ -0,0 +1,86 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package authz provides handlers to enable ACL, RBAC, ABAC authorization support. +// Simple Usage: +// import( +// "github.com/astaxie/beego" +// "github.com/astaxie/beego/plugins/authz" +// "github.com/hsluoyz/casbin" +// ) +// +// func main(){ +// // mediate the access for every request +// beego.InsertFilter("*", beego.BeforeRouter, authz.NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv"))) +// beego.Run() +// } +// +// +// Advanced Usage: +// +// func main(){ +// e := casbin.NewEnforcer("authz_model.conf", "") +// e.AddRoleForUser("alice", "admin") +// e.AddPolicy(...) +// +// beego.InsertFilter("*", beego.BeforeRouter, authz.NewAuthorizer(e)) +// beego.Run() +// } +package authz + +import ( + "github.com/astaxie/beego" + "github.com/astaxie/beego/context" + "github.com/hsluoyz/casbin" + "net/http" +) + +// NewAuthorizer returns the authorizer. +// Use a casbin enforcer as input +func NewAuthorizer(e *casbin.Enforcer) beego.FilterFunc { + return func(ctx *context.Context) { + a := &BasicAuthorizer{enforcer: e} + + if !a.CheckPermission(ctx.Request) { + a.RequirePermission(ctx.ResponseWriter) + } + } +} + +// BasicAuthorizer stores the casbin handler +type BasicAuthorizer struct { + enforcer *casbin.Enforcer +} + +// GetUserName gets the user name from the request. +// Currently, only HTTP basic authentication is supported +func (a *BasicAuthorizer) GetUserName(r *http.Request) string { + username, _, _ := r.BasicAuth() + return username +} + +// CheckPermission checks the user/method/path combination from the request. +// Returns true (permission granted) or false (permission forbidden) +func (a *BasicAuthorizer) CheckPermission(r *http.Request) bool { + user := a.GetUserName(r) + method := r.Method + path := r.URL.Path + return a.enforcer.Enforce(user, path, method) +} + +// RequirePermission returns the 403 Forbidden to the client +func (a *BasicAuthorizer) RequirePermission(w http.ResponseWriter) { + w.WriteHeader(403) + w.Write([]byte("403 Forbidden\n")) +} diff --git a/plugins/authz/authz_model.conf b/plugins/authz/authz_model.conf new file mode 100644 index 00000000..d1b3dbd7 --- /dev/null +++ b/plugins/authz/authz_model.conf @@ -0,0 +1,14 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*") \ No newline at end of file diff --git a/plugins/authz/authz_policy.csv b/plugins/authz/authz_policy.csv new file mode 100644 index 00000000..c062dd3e --- /dev/null +++ b/plugins/authz/authz_policy.csv @@ -0,0 +1,7 @@ +p, alice, /dataset1/*, GET +p, alice, /dataset1/resource1, POST +p, bob, /dataset2/resource1, * +p, bob, /dataset2/resource2, GET +p, bob, /dataset2/folder1/*, POST +p, dataset1_admin, /dataset1/*, * +g, cathy, dataset1_admin \ No newline at end of file diff --git a/plugins/authz/authz_test.go b/plugins/authz/authz_test.go new file mode 100644 index 00000000..4003582c --- /dev/null +++ b/plugins/authz/authz_test.go @@ -0,0 +1,107 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package authz + +import ( + "github.com/astaxie/beego" + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/plugins/auth" + "github.com/hsluoyz/casbin" + "net/http" + "net/http/httptest" + "testing" +) + +func testRequest(t *testing.T, handler *beego.ControllerRegister, user string, path string, method string, code int) { + r, _ := http.NewRequest(method, path, nil) + r.SetBasicAuth(user, "123") + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + if w.Code != code { + t.Errorf("%s, %s, %s: %d, supposed to be %d", user, path, method, w.Code, code) + } +} + +func TestBasic(t *testing.T) { + handler := beego.NewControllerRegister() + + handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("alice", "123")) + handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv"))) + + handler.Any("*", func(ctx *context.Context) { + ctx.Output.SetStatus(200) + }) + + testRequest(t, handler, "alice", "/dataset1/resource1", "GET", 200) + testRequest(t, handler, "alice", "/dataset1/resource1", "POST", 200) + testRequest(t, handler, "alice", "/dataset1/resource2", "GET", 200) + testRequest(t, handler, "alice", "/dataset1/resource2", "POST", 403) +} + +func TestPathWildcard(t *testing.T) { + handler := beego.NewControllerRegister() + + handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("bob", "123")) + handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(casbin.NewEnforcer("authz_model.conf", "authz_policy.csv"))) + + handler.Any("*", func(ctx *context.Context) { + ctx.Output.SetStatus(200) + }) + + testRequest(t, handler, "bob", "/dataset2/resource1", "GET", 200) + testRequest(t, handler, "bob", "/dataset2/resource1", "POST", 200) + testRequest(t, handler, "bob", "/dataset2/resource1", "DELETE", 200) + testRequest(t, handler, "bob", "/dataset2/resource2", "GET", 200) + testRequest(t, handler, "bob", "/dataset2/resource2", "POST", 403) + testRequest(t, handler, "bob", "/dataset2/resource2", "DELETE", 403) + + testRequest(t, handler, "bob", "/dataset2/folder1/item1", "GET", 403) + testRequest(t, handler, "bob", "/dataset2/folder1/item1", "POST", 200) + testRequest(t, handler, "bob", "/dataset2/folder1/item1", "DELETE", 403) + testRequest(t, handler, "bob", "/dataset2/folder1/item2", "GET", 403) + testRequest(t, handler, "bob", "/dataset2/folder1/item2", "POST", 200) + testRequest(t, handler, "bob", "/dataset2/folder1/item2", "DELETE", 403) +} + +func TestRBAC(t *testing.T) { + handler := beego.NewControllerRegister() + + handler.InsertFilter("*", beego.BeforeRouter, auth.Basic("cathy", "123")) + e := casbin.NewEnforcer("authz_model.conf", "authz_policy.csv") + handler.InsertFilter("*", beego.BeforeRouter, NewAuthorizer(e)) + + handler.Any("*", func(ctx *context.Context) { + ctx.Output.SetStatus(200) + }) + + // cathy can access all /dataset1/* resources via all methods because it has the dataset1_admin role. + testRequest(t, handler, "cathy", "/dataset1/item", "GET", 200) + testRequest(t, handler, "cathy", "/dataset1/item", "POST", 200) + testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 200) + testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403) + + // delete all roles on user cathy, so cathy cannot access any resources now. + e.DeleteRolesForUser("cathy") + + testRequest(t, handler, "cathy", "/dataset1/item", "GET", 403) + testRequest(t, handler, "cathy", "/dataset1/item", "POST", 403) + testRequest(t, handler, "cathy", "/dataset1/item", "DELETE", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "GET", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "POST", 403) + testRequest(t, handler, "cathy", "/dataset2/item", "DELETE", 403) +} From 9b01b1c63d6f6dfd722d315d29dbaf86c78b2e05 Mon Sep 17 00:00:00 2001 From: guanly Date: Thu, 11 May 2017 14:49:01 +0800 Subject: [PATCH 100/163] =?UTF-8?q?ISSUE2630=20=20=E4=BD=BF=E7=94=A8sqlite?= =?UTF-8?q?=EF=BC=8Corm=E4=B8=AD=E9=80=9A=E8=BF=87filter=E5=90=8E=E7=9A=84?= =?UTF-8?q?delete=E5=88=A0=E9=99=A4=E4=B8=8D=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/astaxie/beego/issues/2630 --- orm/db.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/orm/db.go b/orm/db.go index 2a05797a..21030eef 100644 --- a/orm/db.go +++ b/orm/db.go @@ -833,7 +833,11 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con if err := rs.Scan(&ref); err != nil { return 0, err } - args = append(args, reflect.ValueOf(ref).Interface()) + pkValue, err := d.convertValueFromDB(mi.fields.pk, reflect.ValueOf(ref).Interface(), tz) + if err != nil { + panic(fmt.Errorf("get pk value failed: `%s` ", ref)) + } + args = append(args, pkValue) cnt++ } From 10cd1070f45ceda87c19519e00b11e0ad1227ffd Mon Sep 17 00:00:00 2001 From: guanly Date: Thu, 11 May 2017 21:45:38 +0800 Subject: [PATCH 101/163] =?UTF-8?q?=E4=BD=BF=E7=94=A8sqlite=EF=BC=8Corm?= =?UTF-8?q?=E4=B8=AD=E9=80=9A=E8=BF=87filter=E5=90=8E=E7=9A=84delete?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E6=88=90=E5=8A=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/astaxie/beego/issues/2630 --- orm/db.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/db.go b/orm/db.go index 21030eef..12f0f54d 100644 --- a/orm/db.go +++ b/orm/db.go @@ -835,7 +835,7 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con } pkValue, err := d.convertValueFromDB(mi.fields.pk, reflect.ValueOf(ref).Interface(), tz) if err != nil { - panic(fmt.Errorf("get pk value failed: `%s` ", ref)) + return 0, err } args = append(args, pkValue) cnt++ From cb4f252a06455ac1a4f9e15d7d83e3c33cd188fb Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Thu, 11 May 2017 17:58:25 +0300 Subject: [PATCH 102/163] defValue -> defaultValue --- context/param/conv.go | 5 +---- context/param/methodparams.go | 12 ++++++------ context/param/options.go | 6 +++--- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/context/param/conv.go b/context/param/conv.go index e961b4ad..596d2b1e 100644 --- a/context/param/conv.go +++ b/context/param/conv.go @@ -24,7 +24,7 @@ func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Co if param.required { ctx.Abort(400, fmt.Sprintf("Missing parameter %s", param.name)) } else { - paramValue = param.defValue + paramValue = param.defaultValue } } @@ -43,9 +43,6 @@ func getParamValue(param *MethodParam, ctx *beecontext.Context) string { return string(ctx.Input.RequestBody) case header: return ctx.Input.Header(param.name) - // if strValue == "" && strings.Contains(param.name, "_") { //magically handle X-Headers? - // strValue = ctx.Input.Header(strings.Replace(param.name, "_", "-", -1)) - // } case path: return ctx.Input.Query(":" + param.name) default: diff --git a/context/param/methodparams.go b/context/param/methodparams.go index 28772504..fe4ab421 100644 --- a/context/param/methodparams.go +++ b/context/param/methodparams.go @@ -7,10 +7,10 @@ import ( //MethodParam keeps param information to be auto passed to controller methods type MethodParam struct { - name string - location paramLocation - required bool - defValue string + name string + location paramLocation + required bool + defaultValue string } type paramLocation byte @@ -57,8 +57,8 @@ func (mp *MethodParam) String() string { case header: options = append(options, "param.InHeader") } - if mp.defValue != "" { - options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defValue)) + if mp.defaultValue != "" { + options = append(options, fmt.Sprintf(`param.Default("%s")`, mp.defaultValue)) } if len(options) > 0 { result += ", " diff --git a/context/param/options.go b/context/param/options.go index 846a59f6..32402194 100644 --- a/context/param/options.go +++ b/context/param/options.go @@ -28,10 +28,10 @@ var InBody MethodParamOption = func(p *MethodParam) { } // Default provides a default value for the http param -func Default(defValue interface{}) MethodParamOption { +func Default(defaultValue interface{}) MethodParamOption { return func(p *MethodParam) { - if defValue != nil { - p.defValue = fmt.Sprint(defValue) + if defaultValue != nil { + p.defaultValue = fmt.Sprint(defaultValue) } } } From 74dc3c750028aee134e648f988b2f270e4985645 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Thu, 11 May 2017 19:32:44 +0300 Subject: [PATCH 103/163] tests --- context/param/parsers_test.go | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 context/param/parsers_test.go diff --git a/context/param/parsers_test.go b/context/param/parsers_test.go new file mode 100644 index 00000000..6d305018 --- /dev/null +++ b/context/param/parsers_test.go @@ -0,0 +1,45 @@ +package param + +import "testing" +import "reflect" +import "time" + +type testDefinition struct { + strValue string + expectedValue interface{} + expectedParser paramParser +} + +func Test_Parsers(t *testing.T) { + checkParser(testDefinition{"1", 1, intParser{}}, t) + + checkParser(testDefinition{"1.0", 1.0, floatParser{}}, t) + + checkParser(testDefinition{"1", "1", stringParser{}}, t) + + checkParser(testDefinition{"true", true, boolParser{}}, t) + + checkParser(testDefinition{"2017-05-30T13:54:53Z", time.Date(2017, 5, 30, 13, 54, 53, 0, time.UTC), timeParser{}}, t) + + checkParser(testDefinition{`{"X": 5}`, struct{ X int }{5}, jsonParser{}}, t) + + checkParser(testDefinition{`1,2`, []int{1, 2}, sliceParser(intParser{})}, t) +} + +func checkParser(def testDefinition, t *testing.T) { + toType := reflect.TypeOf(def.expectedValue) + parser := getParser(&MethodParam{}, toType) + + if reflect.TypeOf(parser) != reflect.TypeOf(def.expectedParser) { + t.Errorf("Invalid parser for value %v. Expected: %v, actual: %v", def.strValue, reflect.TypeOf(def.expectedParser).Name(), reflect.TypeOf(parser).Name()) + return + } + result, err := parser.parse(def.strValue, toType) + if err != nil { + t.Errorf("Parsing error for value %v. Expected result: %v, error: %v", def.strValue, def.expectedValue, err) + return + } + if !reflect.DeepEqual(result, def.expectedValue) { + t.Errorf("Parsing error for value %v. Expected result: %v, actual: %v", def.strValue, def.expectedValue, result) + } +} From b6a35a8944ee928dbbcc4fbfb3b1760a6c21d5c2 Mon Sep 17 00:00:00 2001 From: eyalpost Date: Fri, 12 May 2017 09:25:12 +0300 Subject: [PATCH 104/163] more tests --- context/param/parsers_test.go | 51 ++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/context/param/parsers_test.go b/context/param/parsers_test.go index 6d305018..cc97c735 100644 --- a/context/param/parsers_test.go +++ b/context/param/parsers_test.go @@ -11,24 +11,58 @@ type testDefinition struct { } func Test_Parsers(t *testing.T) { + + //ints checkParser(testDefinition{"1", 1, intParser{}}, t) + checkParser(testDefinition{"-1", int64(-1), intParser{}}, t) + checkParser(testDefinition{"1", uint64(1), intParser{}}, t) - checkParser(testDefinition{"1.0", 1.0, floatParser{}}, t) + //floats + checkParser(testDefinition{"1.0", float32(1.0), floatParser{}}, t) + checkParser(testDefinition{"-1.0", float64(-1.0), floatParser{}}, t) - checkParser(testDefinition{"1", "1", stringParser{}}, t) + //strings + checkParser(testDefinition{"AB", "AB", stringParser{}}, t) + checkParser(testDefinition{"AB", []byte{65, 66}, stringParser{}}, t) + //bools checkParser(testDefinition{"true", true, boolParser{}}, t) + checkParser(testDefinition{"0", false, boolParser{}}, t) + //timeParser checkParser(testDefinition{"2017-05-30T13:54:53Z", time.Date(2017, 5, 30, 13, 54, 53, 0, time.UTC), timeParser{}}, t) + checkParser(testDefinition{"2017-05-30", time.Date(2017, 5, 30, 0, 0, 0, 0, time.UTC), timeParser{}}, t) - checkParser(testDefinition{`{"X": 5}`, struct{ X int }{5}, jsonParser{}}, t) + //json + checkParser(testDefinition{`{"X": 5, "Y":"Z"}`, struct { + X int + Y string + }{5, "Z"}, jsonParser{}}, t) + //slice in query is parsed as comma delimited checkParser(testDefinition{`1,2`, []int{1, 2}, sliceParser(intParser{})}, t) + + //slice in body is parsed as json + checkParser(testDefinition{`["a","b"]`, []string{"a", "b"}, jsonParser{}}, t, MethodParam{location: body}) + + //pointers + var someInt = 1 + checkParser(testDefinition{`1`, &someInt, ptrParser(intParser{})}, t) + + var someStruct = struct{ X int }{5} + checkParser(testDefinition{`{"X": 5}`, &someStruct, jsonParser{}}, t) + } -func checkParser(def testDefinition, t *testing.T) { +func checkParser(def testDefinition, t *testing.T, methodParam ...MethodParam) { toType := reflect.TypeOf(def.expectedValue) - parser := getParser(&MethodParam{}, toType) + var mp MethodParam + if len(methodParam) == 0 { + mp = MethodParam{} + } else { + mp = methodParam[0] + } + parser := getParser(&mp, toType) if reflect.TypeOf(parser) != reflect.TypeOf(def.expectedParser) { t.Errorf("Invalid parser for value %v. Expected: %v, actual: %v", def.strValue, reflect.TypeOf(def.expectedParser).Name(), reflect.TypeOf(parser).Name()) @@ -39,7 +73,12 @@ func checkParser(def testDefinition, t *testing.T) { t.Errorf("Parsing error for value %v. Expected result: %v, error: %v", def.strValue, def.expectedValue, err) return } - if !reflect.DeepEqual(result, def.expectedValue) { + convResult, err := safeConvert(reflect.ValueOf(result), toType) + if err != nil { + t.Errorf("Convertion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) + return + } + if !reflect.DeepEqual(convResult.Interface(), def.expectedValue) { t.Errorf("Parsing error for value %v. Expected result: %v, actual: %v", def.strValue, def.expectedValue, result) } } From 0ac2e4716248cd0ec1e77e704f394fe87442a674 Mon Sep 17 00:00:00 2001 From: eyalpost Date: Fri, 12 May 2017 09:28:46 +0300 Subject: [PATCH 105/163] location=>paramType --- context/param/conv.go | 2 +- context/param/methodparams.go | 8 ++++---- context/param/options.go | 6 +++--- context/param/parsers.go | 2 +- context/param/parsers_test.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/context/param/conv.go b/context/param/conv.go index 596d2b1e..c200e008 100644 --- a/context/param/conv.go +++ b/context/param/conv.go @@ -38,7 +38,7 @@ func convertParam(param *MethodParam, paramType reflect.Type, ctx *beecontext.Co } func getParamValue(param *MethodParam, ctx *beecontext.Context) string { - switch param.location { + switch param.in { case body: return string(ctx.Input.RequestBody) case header: diff --git a/context/param/methodparams.go b/context/param/methodparams.go index fe4ab421..cd6708a2 100644 --- a/context/param/methodparams.go +++ b/context/param/methodparams.go @@ -8,15 +8,15 @@ import ( //MethodParam keeps param information to be auto passed to controller methods type MethodParam struct { name string - location paramLocation + in paramType required bool defaultValue string } -type paramLocation byte +type paramType byte const ( - param paramLocation = iota + param paramType = iota path body header @@ -49,7 +49,7 @@ func (mp *MethodParam) String() string { if mp.required { options = append(options, "param.IsRequired") } - switch mp.location { + switch mp.in { case path: options = append(options, "param.InPath") case body: diff --git a/context/param/options.go b/context/param/options.go index 32402194..58bdc3d0 100644 --- a/context/param/options.go +++ b/context/param/options.go @@ -14,17 +14,17 @@ var IsRequired MethodParamOption = func(p *MethodParam) { // InHeader indicates that this param is passed via an http header var InHeader MethodParamOption = func(p *MethodParam) { - p.location = header + p.in = header } // InPath indicates that this param is part of the URL path var InPath MethodParamOption = func(p *MethodParam) { - p.location = path + p.in = path } // InBody indicates that this param is passed as an http request body var InBody MethodParamOption = func(p *MethodParam) { - p.location = body + p.in = body } // Default provides a default value for the http param diff --git a/context/param/parsers.go b/context/param/parsers.go index 2b48e878..421aecf0 100644 --- a/context/param/parsers.go +++ b/context/param/parsers.go @@ -21,7 +21,7 @@ func getParser(param *MethodParam, t reflect.Type) paramParser { if t.Elem().Kind() == reflect.Uint8 { //treat []byte as string return stringParser{} } - if param.location == body { + if param.in == body { return jsonParser{} } elemParser := getParser(param, t.Elem()) diff --git a/context/param/parsers_test.go b/context/param/parsers_test.go index cc97c735..b946ba08 100644 --- a/context/param/parsers_test.go +++ b/context/param/parsers_test.go @@ -43,7 +43,7 @@ func Test_Parsers(t *testing.T) { checkParser(testDefinition{`1,2`, []int{1, 2}, sliceParser(intParser{})}, t) //slice in body is parsed as json - checkParser(testDefinition{`["a","b"]`, []string{"a", "b"}, jsonParser{}}, t, MethodParam{location: body}) + checkParser(testDefinition{`["a","b"]`, []string{"a", "b"}, jsonParser{}}, t, MethodParam{in: body}) //pointers var someInt = 1 From 1004678005135f5d7a6eb940613724ec56a87741 Mon Sep 17 00:00:00 2001 From: eyalpost Date: Fri, 12 May 2017 09:57:56 +0300 Subject: [PATCH 106/163] popular status codes --- context/response/renderer.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/context/response/renderer.go b/context/response/renderer.go index 2a4d2797..38d0cdf6 100644 --- a/context/response/renderer.go +++ b/context/response/renderer.go @@ -3,9 +3,16 @@ package response import ( "strconv" + "net/http" + beecontext "github.com/astaxie/beego/context" ) +const ( + NotFound StatusCode = http.StatusNotFound + BadRequest StatusCode = http.StatusBadRequest +) + // Renderer defines an http response renderer type Renderer interface { Render(ctx *beecontext.Context) From 589f3755f07cbdddeb92f6451bbb60b343eae7c9 Mon Sep 17 00:00:00 2001 From: sunxinle Date: Fri, 12 May 2017 18:11:42 +0800 Subject: [PATCH 107/163] =?UTF-8?q?=E5=85=81=E8=AE=B8o.Raw(sql).QueryRows(?= =?UTF-8?q?&container)=20=E4=BC=A0=E5=85=A5=E7=9A=84container=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E7=BB=93=E6=9E=84=E7=9A=84=E5=B5=8C=E5=A5=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orm/orm_raw.go | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/orm/orm_raw.go b/orm/orm_raw.go index 1e86212a..c8e741ea 100644 --- a/orm/orm_raw.go +++ b/orm/orm_raw.go @@ -493,19 +493,33 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { } } } else { - for i := 0; i < ind.NumField(); i++ { - f := ind.Field(i) - fe := ind.Type().Field(i) - _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName)) - var col string - if col = tags["column"]; col == "" { - col = snakeString(fe.Name) - } - if v, ok := columnsMp[col]; ok { - value := reflect.ValueOf(v).Elem().Interface() - o.setFieldValue(f, value) + // define recursive function + var recursiveSetField func(rv reflect.Value) + recursiveSetField = func(rv reflect.Value) { + for i := 0; i < rv.NumField(); i++ { + f := rv.Field(i) + fe := rv.Type().Field(i) + + // check if the field is a Struct + // recursive the Struct type + if fe.Type.Kind() == reflect.Struct { + recursiveSetField(f) + } + + _, tags := parseStructTag(fe.Tag.Get(defaultStructTagName)) + var col string + if col = tags["column"]; col == "" { + col = snakeString(fe.Name) + } + if v, ok := columnsMp[col]; ok { + value := reflect.ValueOf(v).Elem().Interface() + o.setFieldValue(f, value) + } } } + + // init call the recursive function + recursiveSetField(ind) } if eTyps[0].Kind() == reflect.Ptr { From 40bc52b844c39ce2a306e91d811f8c5c1326d998 Mon Sep 17 00:00:00 2001 From: franzwilhelm Date: Sun, 14 May 2017 00:42:09 +0200 Subject: [PATCH 108/163] fix security struct placement and formatting --- swagger/swagger.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/swagger/swagger.go b/swagger/swagger.go index c687fb8e..fb5da8ad 100644 --- a/swagger/swagger.go +++ b/swagger/swagger.go @@ -32,7 +32,6 @@ type Swagger struct { Paths map[string]*Item `json:"paths" yaml:"paths"` Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"` SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"` - Security map[string][]string `json:"security,omitempty" yaml:"security,omitempty"` Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"` ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` } @@ -75,16 +74,17 @@ type Item struct { // Operation Describes a single API operation on a path. type Operation struct { - Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` - Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` - Description string `json:"description,omitempty" yaml:"description,omitempty"` - OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` - Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` - Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` - Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` - Parameters []Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` - Responses map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"` - Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` + Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` + Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` + Description string `json:"description,omitempty" yaml:"description,omitempty"` + OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"` + Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` + Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` + Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` + Parameters []Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` + Responses map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"` + Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"` + Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` } // Parameter Describes a single operation parameter. From 23250901018690f888de783a117c6858b9d3ca46 Mon Sep 17 00:00:00 2001 From: alexsunxl Date: Sun, 14 May 2017 12:03:34 +0800 Subject: [PATCH 109/163] add test case that used nested struct test QueryRows --- orm/orm_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/orm/orm_test.go b/orm/orm_test.go index c5bfa8b9..f1f2d85e 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -1661,6 +1661,13 @@ func TestRawQueryRow(t *testing.T) { throwFail(t, AssertIs(pid, nil)) } +// user_profile table +type userProfile struct { + User + Age int + Money float64 +} + func TestQueryRows(t *testing.T) { Q := dDbBaser.TableQuote() @@ -1731,6 +1738,19 @@ func TestQueryRows(t *testing.T) { throwFailNow(t, AssertIs(usernames[1], "astaxie")) throwFailNow(t, AssertIs(ids[2], 4)) throwFailNow(t, AssertIs(usernames[2], "nobody")) + + //test query rows by nested struct + var l []userProfile + query = fmt.Sprintf("SELECT * FROM %suser_profile%s LEFT JOIN %suser%s ON %suser_profile%s.%sid%s = %suser%s.%sid%s", Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q) + num, err = dORM.Raw(query).QueryRows(&l) + throwFailNow(t, err) + throwFailNow(t, AssertIs(num, 2)) + throwFailNow(t, AssertIs(len(l), 2)) + throwFailNow(t, AssertIs(l[0].UserName, "slene")) + throwFailNow(t, AssertIs(l[0].Age, 28)) + throwFailNow(t, AssertIs(l[1].UserName, "astaxie")) + throwFailNow(t, AssertIs(l[1].Age, 30)) + } func TestRawValues(t *testing.T) { From c814893d65d10ae5e186d33cd19b7c2fca2ba8a2 Mon Sep 17 00:00:00 2001 From: franzwilhelm Date: Sun, 14 May 2017 12:13:35 +0200 Subject: [PATCH 110/163] add support for global security --- swagger/swagger.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/swagger/swagger.go b/swagger/swagger.go index fb5da8ad..035d5a49 100644 --- a/swagger/swagger.go +++ b/swagger/swagger.go @@ -22,18 +22,19 @@ package swagger // Swagger list the resource type Swagger struct { - SwaggerVersion string `json:"swagger,omitempty" yaml:"swagger,omitempty"` - Infos Information `json:"info" yaml:"info"` - Host string `json:"host,omitempty" yaml:"host,omitempty"` - BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"` - Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` - Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` - Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` - Paths map[string]*Item `json:"paths" yaml:"paths"` - Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"` - SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"` - Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"` - ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` + SwaggerVersion string `json:"swagger,omitempty" yaml:"swagger,omitempty"` + Infos Information `json:"info" yaml:"info"` + Host string `json:"host,omitempty" yaml:"host,omitempty"` + BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"` + Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"` + Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"` + Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"` + Paths map[string]*Item `json:"paths" yaml:"paths"` + Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"` + SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"` + Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"` + Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"` + ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` } // Information Provides metadata about the API. The metadata can be used by the clients if needed. From e1c90bfc09108111a8290ecb7535c9ec60f34b59 Mon Sep 17 00:00:00 2001 From: Robert Wikman Date: Tue, 16 May 2017 00:27:57 +0200 Subject: [PATCH 111/163] Table not found spelling fixes --- orm/orm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/orm.go b/orm/orm.go index 5db79386..fcf82590 100644 --- a/orm/orm.go +++ b/orm/orm.go @@ -107,7 +107,7 @@ func (o *orm) getMiInd(md interface{}, needPtr bool) (mi *modelInfo, ind reflect if mi, ok := modelCache.getByFullName(name); ok { return mi, ind } - panic(fmt.Errorf(" table: `%s` not found, maybe not RegisterModel", name)) + panic(fmt.Errorf(" table: `%s` not found, make sure it was registered with `RegisterModel()`", name)) } // get field info from model info by given field name From b5c6eb54d28efd3a655fa676f11542305106a7c6 Mon Sep 17 00:00:00 2001 From: Robert Wikman Date: Tue, 16 May 2017 00:58:20 +0200 Subject: [PATCH 112/163] Missing PK error spelling fix --- orm/models_boot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/orm/models_boot.go b/orm/models_boot.go index 85d0917f..5327f754 100644 --- a/orm/models_boot.go +++ b/orm/models_boot.go @@ -75,7 +75,7 @@ func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) { } if mi.fields.pk == nil { - fmt.Printf(" `%s` need a primary key field, default use 'id' if not set\n", name) + fmt.Printf(" `%s` needs a primary key field, default is to use 'id' if not set\n", name) os.Exit(2) } From 69f0b947452dc1723e0f693d64fc5ee712b8d1d0 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 16 May 2017 22:21:43 +0800 Subject: [PATCH 113/163] fix gosimple --- templatefunc.go | 10 ++++----- validation/validation_test.go | 8 ++++---- validation/validators.go | 38 +++++++++++++++++------------------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/templatefunc.go b/templatefunc.go index a6f9c961..2591c88e 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -53,21 +53,21 @@ func Substr(s string, start, length int) string { // HTML2str returns escaping text convert from html. func HTML2str(html string) string { - re, _ := regexp.Compile("\\<[\\S\\s]+?\\>") + re, _ := regexp.Compile(`\<[\S\s]+?\>`) html = re.ReplaceAllStringFunc(html, strings.ToLower) //remove STYLE - re, _ = regexp.Compile("\\") + re, _ = regexp.Compile(`\`) html = re.ReplaceAllString(html, "") //remove SCRIPT - re, _ = regexp.Compile("\\") + re, _ = regexp.Compile(`\`) html = re.ReplaceAllString(html, "") - re, _ = regexp.Compile("\\<[\\S\\s]+?\\>") + re, _ = regexp.Compile(`\<[\S\s]+?\>`) html = re.ReplaceAllString(html, "\n") - re, _ = regexp.Compile("\\s{2,}") + re, _ = regexp.Compile(`\s{2,}`) html = re.ReplaceAllString(html, "\n") return strings.TrimSpace(html) diff --git a/validation/validation_test.go b/validation/validation_test.go index 83e881bf..cb7a297e 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -175,10 +175,10 @@ func TestAlphaNumeric(t *testing.T) { func TestMatch(t *testing.T) { valid := Validation{} - if valid.Match("suchuangji@gmail", regexp.MustCompile("^\\w+@\\w+\\.\\w+$"), "match").Ok { + if valid.Match("suchuangji@gmail", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok { t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be false") } - if !valid.Match("suchuangji@gmail.com", regexp.MustCompile("^\\w+@\\w+\\.\\w+$"), "match").Ok { + if !valid.Match("suchuangji@gmail.com", regexp.MustCompile(`^\w+@\w+\.\w+$`), "match").Ok { t.Error("\"suchuangji@gmail\" match \"^\\w+@\\w+\\.\\w+$\" should be true") } } @@ -186,10 +186,10 @@ func TestMatch(t *testing.T) { func TestNoMatch(t *testing.T) { valid := Validation{} - if valid.NoMatch("123@gmail", regexp.MustCompile("[^\\w\\d]"), "nomatch").Ok { + if valid.NoMatch("123@gmail", regexp.MustCompile(`[^\w\d]`), "nomatch").Ok { t.Error("\"123@gmail\" not match \"[^\\w\\d]\" should be false") } - if !valid.NoMatch("123gmail", regexp.MustCompile("[^\\w\\d]"), "match").Ok { + if !valid.NoMatch("123gmail", regexp.MustCompile(`[^\w\d]`), "match").Ok { t.Error("\"123@gmail\" not match \"[^\\w\\d@]\" should be true") } } diff --git a/validation/validators.go b/validation/validators.go index 01aed443..2b5ae38b 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -145,7 +145,7 @@ func (r Required) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default error message func (r Required) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Required"]) + return MessageTmpls["Required"] } // GetKey return the r.Key @@ -364,7 +364,7 @@ func (a Alpha) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Length error message func (a Alpha) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Alpha"]) + return MessageTmpls["Alpha"] } // GetKey return the m.Key @@ -397,7 +397,7 @@ func (n Numeric) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Length error message func (n Numeric) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Numeric"]) + return MessageTmpls["Numeric"] } // GetKey return the n.Key @@ -430,7 +430,7 @@ func (a AlphaNumeric) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Length error message func (a AlphaNumeric) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["AlphaNumeric"]) + return MessageTmpls["AlphaNumeric"] } // GetKey return the a.Key @@ -495,7 +495,7 @@ func (n NoMatch) GetLimitValue() interface{} { return n.Regexp.String() } -var alphaDashPattern = regexp.MustCompile("[^\\d\\w-_]") +var alphaDashPattern = regexp.MustCompile(`[^\d\w-_]`) // AlphaDash check not Alpha type AlphaDash struct { @@ -505,7 +505,7 @@ type AlphaDash struct { // DefaultMessage return the default AlphaDash error message func (a AlphaDash) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["AlphaDash"]) + return MessageTmpls["AlphaDash"] } // GetKey return the n.Key @@ -518,7 +518,7 @@ func (a AlphaDash) GetLimitValue() interface{} { return nil } -var emailPattern = regexp.MustCompile("^[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[a-zA-Z0-9](?:[\\w-]*[\\w])?$") +var emailPattern = regexp.MustCompile(`^[\w!#$%&'*+/=?^_` + "`" + `{|}~-]+(?:\.[\w!#$%&'*+/=?^_` + "`" + `{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[a-zA-Z0-9](?:[\w-]*[\w])?$`) // Email check struct type Email struct { @@ -528,7 +528,7 @@ type Email struct { // DefaultMessage return the default Email error message func (e Email) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Email"]) + return MessageTmpls["Email"] } // GetKey return the n.Key @@ -541,7 +541,7 @@ func (e Email) GetLimitValue() interface{} { return nil } -var ipPattern = regexp.MustCompile("^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$") +var ipPattern = regexp.MustCompile(`^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$`) // IP check struct type IP struct { @@ -551,7 +551,7 @@ type IP struct { // DefaultMessage return the default IP error message func (i IP) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["IP"]) + return MessageTmpls["IP"] } // GetKey return the i.Key @@ -564,7 +564,7 @@ func (i IP) GetLimitValue() interface{} { return nil } -var base64Pattern = regexp.MustCompile("^(?:[A-Za-z0-99+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$") +var base64Pattern = regexp.MustCompile(`^(?:[A-Za-z0-99+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$`) // Base64 check struct type Base64 struct { @@ -574,7 +574,7 @@ type Base64 struct { // DefaultMessage return the default Base64 error message func (b Base64) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Base64"]) + return MessageTmpls["Base64"] } // GetKey return the b.Key @@ -588,7 +588,7 @@ func (b Base64) GetLimitValue() interface{} { } // just for chinese mobile phone number -var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][06789]|[4][579]))\\d{8}$") +var mobilePattern = regexp.MustCompile(`^((\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][06789]|[4][579]))\d{8}$`) // Mobile check struct type Mobile struct { @@ -598,7 +598,7 @@ type Mobile struct { // DefaultMessage return the default Mobile error message func (m Mobile) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Mobile"]) + return MessageTmpls["Mobile"] } // GetKey return the m.Key @@ -612,7 +612,7 @@ func (m Mobile) GetLimitValue() interface{} { } // just for chinese telephone number -var telPattern = regexp.MustCompile("^(0\\d{2,3}(\\-)?)?\\d{7,8}$") +var telPattern = regexp.MustCompile(`^(0\d{2,3}(\-)?)?\d{7,8}$`) // Tel check telephone struct type Tel struct { @@ -622,7 +622,7 @@ type Tel struct { // DefaultMessage return the default Tel error message func (t Tel) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Tel"]) + return MessageTmpls["Tel"] } // GetKey return the t.Key @@ -649,7 +649,7 @@ func (p Phone) IsSatisfied(obj interface{}) bool { // DefaultMessage return the default Phone error message func (p Phone) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["Phone"]) + return MessageTmpls["Phone"] } // GetKey return the p.Key @@ -663,7 +663,7 @@ func (p Phone) GetLimitValue() interface{} { } // just for chinese zipcode -var zipCodePattern = regexp.MustCompile("^[1-9]\\d{5}$") +var zipCodePattern = regexp.MustCompile(`^[1-9]\d{5}$`) // ZipCode check the zip struct type ZipCode struct { @@ -673,7 +673,7 @@ type ZipCode struct { // DefaultMessage return the default Zip error message func (z ZipCode) DefaultMessage() string { - return fmt.Sprint(MessageTmpls["ZipCode"]) + return MessageTmpls["ZipCode"] } // GetKey return the z.Key From 828cbbdf5d265d521bcc12300d9c750e7df505cc Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Wed, 17 May 2017 20:38:59 +0300 Subject: [PATCH 114/163] Refactor a bit to consolidate packages --- context/context.go | 16 +++++++++++ context/output.go | 13 +++++++++ context/renderer.go | 12 ++++++++ context/response/renderer.go | 46 ------------------------------ context/response/responses.go | 53 ----------------------------------- httpResponse/response.go | 52 ++++++++++++++++++++++++++++++++++ router.go | 3 +- 7 files changed, 94 insertions(+), 101 deletions(-) create mode 100644 context/renderer.go delete mode 100644 context/response/renderer.go delete mode 100644 context/response/responses.go create mode 100644 httpResponse/response.go diff --git a/context/context.go b/context/context.go index 03286097..8b32062c 100644 --- a/context/context.go +++ b/context/context.go @@ -171,6 +171,22 @@ func (ctx *Context) CheckXSRFCookie() bool { return true } +// RenderMethodResult renders the return value of a controller method to the output +func (ctx *Context) RenderMethodResult(result interface{}) { + if result != nil { + renderer, ok := result.(Renderer) + if !ok { + err, ok := result.(error) + if ok { + renderer = errorRenderer(err) + } else { + renderer = jsonRenderer(result) + } + } + renderer.Render(ctx) + } +} + //Response is a wrapper for the http.ResponseWriter //started set to true if response was written to then don't execute other handler type Response struct { diff --git a/context/output.go b/context/output.go index 564ef96d..835552b0 100644 --- a/context/output.go +++ b/context/output.go @@ -168,6 +168,19 @@ func sanitizeValue(v string) string { return cookieValueSanitizer.Replace(v) } +func jsonRenderer(value interface{}) Renderer { + return rendererFunc(func(ctx *Context) { + ctx.Output.JSON(value, false, false) + }) +} + +func errorRenderer(err error) Renderer { + return rendererFunc(func(ctx *Context) { + ctx.Output.SetStatus(500) + ctx.WriteString(err.Error()) + }) +} + // JSON writes json to response body. // if coding is true, it converts utf-8 to \u0000 type. func (output *BeegoOutput) JSON(data interface{}, hasIndent bool, coding bool) error { diff --git a/context/renderer.go b/context/renderer.go new file mode 100644 index 00000000..36a7cb53 --- /dev/null +++ b/context/renderer.go @@ -0,0 +1,12 @@ +package context + +// Renderer defines an http response renderer +type Renderer interface { + Render(ctx *Context) +} + +type rendererFunc func(ctx *Context) + +func (f rendererFunc) Render(ctx *Context) { + f(ctx) +} diff --git a/context/response/renderer.go b/context/response/renderer.go deleted file mode 100644 index 38d0cdf6..00000000 --- a/context/response/renderer.go +++ /dev/null @@ -1,46 +0,0 @@ -package response - -import ( - "strconv" - - "net/http" - - beecontext "github.com/astaxie/beego/context" -) - -const ( - NotFound StatusCode = http.StatusNotFound - BadRequest StatusCode = http.StatusBadRequest -) - -// Renderer defines an http response renderer -type Renderer interface { - Render(ctx *beecontext.Context) -} - -type rendererFunc func(ctx *beecontext.Context) - -func (f rendererFunc) Render(ctx *beecontext.Context) { - f(ctx) -} - -// StatusCode sets the http response status code -type StatusCode int - -func (s StatusCode) Error() string { - return strconv.Itoa(int(s)) -} - -// Render sets the http status code -func (s StatusCode) Render(ctx *beecontext.Context) { - ctx.Output.SetStatus(int(s)) -} - -type statusCodeWithRender struct { - statusCode int - rendererFunc -} - -func (s statusCodeWithRender) Error() string { - return strconv.Itoa(s.statusCode) -} diff --git a/context/response/responses.go b/context/response/responses.go deleted file mode 100644 index 10d1ed3f..00000000 --- a/context/response/responses.go +++ /dev/null @@ -1,53 +0,0 @@ -package response - -import ( - beecontext "github.com/astaxie/beego/context" -) - -// JSON renders value to the response as JSON -func JSON(value interface{}, encoding ...bool) Renderer { - return rendererFunc(func(ctx *beecontext.Context) { - var ( - hasIndent = true - hasEncoding = false - ) - //TODO: need access to BConfig :( - // if BConfig.RunMode == PROD { - // hasIndent = false - // } - if len(encoding) > 0 && encoding[0] { - hasEncoding = true - } - ctx.Output.JSON(value, hasIndent, hasEncoding) - }) -} - -func errorRenderer(err error) Renderer { - return rendererFunc(func(ctx *beecontext.Context) { - ctx.Output.SetStatus(500) - ctx.WriteString(err.Error()) - }) -} - -// Redirect renders http 302 with a URL -func Redirect(localurl string) error { - return statusCodeWithRender{302, func(ctx *beecontext.Context) { - ctx.Redirect(302, localurl) - }} -} - -// RenderMethodResult renders the return value of a controller method to the output -func RenderMethodResult(result interface{}, ctx *beecontext.Context) { - if result != nil { - renderer, ok := result.(Renderer) - if !ok { - err, ok := result.(error) - if ok { - renderer = errorRenderer(err) - } else { - renderer = JSON(result) - } - } - renderer.Render(ctx) - } -} diff --git a/httpResponse/response.go b/httpResponse/response.go new file mode 100644 index 00000000..ca74b85c --- /dev/null +++ b/httpResponse/response.go @@ -0,0 +1,52 @@ +package httpResponse + +import ( + "strconv" + + "net/http" + + beecontext "github.com/astaxie/beego/context" +) + +const ( + //BadRequest indicates http error 400 + BadRequest StatusCode = http.StatusBadRequest + + //NotFound indicates http error 404 + NotFound StatusCode = http.StatusNotFound +) + +// Redirect renders http 302 with a URL +func Redirect(localurl string) error { + return statusCodeWithRender{302, func(ctx *beecontext.Context) { + ctx.Redirect(302, localurl) + }} +} + +// StatusCode sets the http response status code +type StatusCode int + +func (s StatusCode) Error() string { + return strconv.Itoa(int(s)) +} + +// Render sets the http status code +func (s StatusCode) Render(ctx *beecontext.Context) { + ctx.Output.SetStatus(int(s)) +} + +type statusCodeWithRender struct { + statusCode int + f func(ctx *beecontext.Context) +} + +//assert that statusCodeWithRender implements Renderer interface +var _r beecontext.Renderer = (*statusCodeWithRender)(nil) + +func (s statusCodeWithRender) Error() string { + return strconv.Itoa(s.statusCode) +} + +func (s statusCodeWithRender) Render(ctx *beecontext.Context) { + s.f(ctx) +} diff --git a/router.go b/router.go index 33096039..72476ae8 100644 --- a/router.go +++ b/router.go @@ -28,7 +28,6 @@ import ( beecontext "github.com/astaxie/beego/context" "github.com/astaxie/beego/context/param" - "github.com/astaxie/beego/context/response" "github.com/astaxie/beego/logs" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" @@ -905,7 +904,7 @@ func (p *ControllerRegister) handleParamResponse(context *beecontext.Context, ex result := results[i] if result.Kind() != reflect.Interface || !result.IsNil() { resultValue := result.Interface() - response.RenderMethodResult(resultValue, context) + context.RenderMethodResult(resultValue) } } if !context.ResponseWriter.Started && context.Output.Status == 0 { From ee1d8bc30ea2d89ecdb70402c7b1213ac8a4e5f1 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Wed, 17 May 2017 20:50:41 +0300 Subject: [PATCH 115/163] fix gosimple --- parser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.go b/parser.go index 037d5376..a9cfd894 100644 --- a/parser.go +++ b/parser.go @@ -235,7 +235,7 @@ func getparams(str string) []string { var start bool var r []string var quoted int8 - for _, c := range []rune(str) { + for _, c := range str { if unicode.IsSpace(c) && quoted == 0 { if !start { continue From e32a18203b95391f627539c440834d65f704492a Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Wed, 17 May 2017 21:27:32 +0300 Subject: [PATCH 116/163] fix gosimple --- context/output.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/context/output.go b/context/output.go index 835552b0..cf9e7a7e 100644 --- a/context/output.go +++ b/context/output.go @@ -343,9 +343,8 @@ func (output *BeegoOutput) IsServerError() bool { } func stringsToJSON(str string) string { - rs := []rune(str) var jsons bytes.Buffer - for _, r := range rs { + for _, r := range str { rint := int(r) if rint < 128 { jsons.WriteRune(r) From 3e51823c0faba9dcf541ec72c90664a30fedceb6 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Thu, 18 May 2017 09:05:49 +0300 Subject: [PATCH 117/163] move response --- {httpResponse => context/httpResponse}/response.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {httpResponse => context/httpResponse}/response.go (100%) diff --git a/httpResponse/response.go b/context/httpResponse/response.go similarity index 100% rename from httpResponse/response.go rename to context/httpResponse/response.go From 2513bcf584f655379bd5195dcaba83649aab9e8f Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Thu, 18 May 2017 10:32:51 +0300 Subject: [PATCH 118/163] remove Redirect to avoid confusion --- context/httpResponse/response.go | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/context/httpResponse/response.go b/context/httpResponse/response.go index ca74b85c..0b013719 100644 --- a/context/httpResponse/response.go +++ b/context/httpResponse/response.go @@ -16,13 +16,6 @@ const ( NotFound StatusCode = http.StatusNotFound ) -// Redirect renders http 302 with a URL -func Redirect(localurl string) error { - return statusCodeWithRender{302, func(ctx *beecontext.Context) { - ctx.Redirect(302, localurl) - }} -} - // StatusCode sets the http response status code type StatusCode int @@ -34,19 +27,3 @@ func (s StatusCode) Error() string { func (s StatusCode) Render(ctx *beecontext.Context) { ctx.Output.SetStatus(int(s)) } - -type statusCodeWithRender struct { - statusCode int - f func(ctx *beecontext.Context) -} - -//assert that statusCodeWithRender implements Renderer interface -var _r beecontext.Renderer = (*statusCodeWithRender)(nil) - -func (s statusCodeWithRender) Error() string { - return strconv.Itoa(s.statusCode) -} - -func (s statusCodeWithRender) Render(ctx *beecontext.Context) { - s.f(ctx) -} From 11b4bf8aaa7ba08f7ca0aba61ebf1cee0c7d83a6 Mon Sep 17 00:00:00 2001 From: Eyal Post Date: Thu, 18 May 2017 10:38:12 +0300 Subject: [PATCH 119/163] move to context --- context/{httpResponse => }/response.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) rename context/{httpResponse => }/response.go (77%) diff --git a/context/httpResponse/response.go b/context/response.go similarity index 77% rename from context/httpResponse/response.go rename to context/response.go index 0b013719..9c3c715a 100644 --- a/context/httpResponse/response.go +++ b/context/response.go @@ -1,11 +1,9 @@ -package httpResponse +package context import ( "strconv" "net/http" - - beecontext "github.com/astaxie/beego/context" ) const ( @@ -24,6 +22,6 @@ func (s StatusCode) Error() string { } // Render sets the http status code -func (s StatusCode) Render(ctx *beecontext.Context) { +func (s StatusCode) Render(ctx *Context) { ctx.Output.SetStatus(int(s)) } From 248beab557b9090d28dc5dfd0806ee3a8113843d Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 18 May 2017 22:55:10 +0800 Subject: [PATCH 120/163] v1.8.3 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 7a8db390..22079a20 100644 --- a/beego.go +++ b/beego.go @@ -23,7 +23,7 @@ import ( const ( // VERSION represent beego web framework version. - VERSION = "1.8.2" + VERSION = "1.8.3" // DEV is for develop DEV = "dev" From 47e351e11db86d120ccb3976d3c4388d07954139 Mon Sep 17 00:00:00 2001 From: Guohua Ouyang Date: Fri, 19 May 2017 09:22:27 +0800 Subject: [PATCH 121/163] Support timeformat "2006-01-02T15:04:05" Fixes #2649 Signed-off-by: Guohua Ouyang --- templatefunc.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/templatefunc.go b/templatefunc.go index 2591c88e..a104fd24 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -27,9 +27,10 @@ import ( ) const ( - formatTime = "15:04:05" - formatDate = "2006-01-02" - formatDateTime = "2006-01-02 15:04:05" + formatTime = "15:04:05" + formatDate = "2006-01-02" + formatDateTime = "2006-01-02 15:04:05" + formatDateTimeT = "2006-01-02T15:04:05" ) // Substr returns the substr from start to length. @@ -360,8 +361,13 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e value = value[:25] t, err = time.ParseInLocation(time.RFC3339, value, time.Local) } else if len(value) >= 19 { - value = value[:19] - t, err = time.ParseInLocation(formatDateTime, value, time.Local) + if strings.Contains(value, "T") { + value = value[:19] + t, err = time.ParseInLocation(formatDateTimeT, value, time.Local) + } else { + value = value[:19] + t, err = time.ParseInLocation(formatDateTime, value, time.Local) + } } else if len(value) >= 10 { if len(value) > 10 { value = value[:10] @@ -373,7 +379,6 @@ func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) e } t, err = time.ParseInLocation(formatTime, value, time.Local) } - if err != nil { return err } From ce677202e557b330e2c06fde64922c609d66e822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=AE=AA=E0=AE=BE=E0=AE=B2=E0=AE=BE=E0=AE=9C=E0=AE=BF?= Date: Tue, 6 Dec 2016 19:25:31 +0530 Subject: [PATCH 122/163] issue no:#2261 fix for xsrf panic error --- error.go | 24 ++++++++++++++++++++++++ hooks.go | 2 ++ 2 files changed, 26 insertions(+) diff --git a/error.go b/error.go index ab626247..b913db39 100644 --- a/error.go +++ b/error.go @@ -252,6 +252,30 @@ func forbidden(rw http.ResponseWriter, r *http.Request) { ) } +// show 422 missing xsrf token +func missingxsrf(rw http.ResponseWriter, r *http.Request) { + responseError(rw, r, + 422, + "
The page you have requested is forbidden."+ + "
Perhaps you are here because:"+ + "

    "+ + "
    '_xsrf' argument missing from POST"+ + "
", + ) +} + +// show 417 invalid xsrf token +func invalidxsrf(rw http.ResponseWriter, r *http.Request) { + responseError(rw, r, + 417, + "
The page you have requested is forbidden."+ + "
Perhaps you are here because:"+ + "

    "+ + "
    expected XSRF not found"+ + "
", + ) +} + // show 404 not found error. func notFound(rw http.ResponseWriter, r *http.Request) { responseError(rw, r, diff --git a/hooks.go b/hooks.go index b5a5e6c5..edf1485a 100644 --- a/hooks.go +++ b/hooks.go @@ -32,6 +32,8 @@ func registerDefaultErrorHandler() error { "502": badGateway, "503": serviceUnavailable, "504": gatewayTimeout, + "417": invalidxsrf, + "422": missingxsrf, } for e, h := range m { if _, ok := ErrorMaps[e]; !ok { From 88d07058a56974878603fe26758fb5690a6a7d00 Mon Sep 17 00:00:00 2001 From: Yang Luo Date: Fri, 19 May 2017 22:19:31 +0800 Subject: [PATCH 123/163] Fix the new repo address for casbin. --- .travis.yml | 2 +- plugins/authz/authz.go | 4 ++-- plugins/authz/authz_test.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index aa88a44d..2937e6e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ install: - go get github.com/cloudflare/golz4 - go get github.com/gogo/protobuf/proto - go get github.com/Knetic/govaluate - - go get github.com/hsluoyz/casbin + - go get github.com/casbin/casbin - go get -u honnef.co/go/tools/cmd/gosimple - go get -u github.com/mdempsky/unconvert - go get -u github.com/gordonklaus/ineffassign diff --git a/plugins/authz/authz.go b/plugins/authz/authz.go index 709a613a..9dc0db76 100644 --- a/plugins/authz/authz.go +++ b/plugins/authz/authz.go @@ -17,7 +17,7 @@ // import( // "github.com/astaxie/beego" // "github.com/astaxie/beego/plugins/authz" -// "github.com/hsluoyz/casbin" +// "github.com/casbin/casbin" // ) // // func main(){ @@ -42,7 +42,7 @@ package authz import ( "github.com/astaxie/beego" "github.com/astaxie/beego/context" - "github.com/hsluoyz/casbin" + "github.com/casbin/casbin" "net/http" ) diff --git a/plugins/authz/authz_test.go b/plugins/authz/authz_test.go index 4003582c..49aed84c 100644 --- a/plugins/authz/authz_test.go +++ b/plugins/authz/authz_test.go @@ -18,7 +18,7 @@ import ( "github.com/astaxie/beego" "github.com/astaxie/beego/context" "github.com/astaxie/beego/plugins/auth" - "github.com/hsluoyz/casbin" + "github.com/casbin/casbin" "net/http" "net/http/httptest" "testing" From 0ea34fff2709a56a0e22fea9b2bd4c249feb4634 Mon Sep 17 00:00:00 2001 From: Jia Li Ong Date: Thu, 1 Jun 2017 17:48:48 +0800 Subject: [PATCH 124/163] Provide permission to access old log files to everyone --- logs/file.go | 2 +- logs/file_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/logs/file.go b/logs/file.go index 1c2db882..33fee1cf 100644 --- a/logs/file.go +++ b/logs/file.go @@ -271,7 +271,7 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { if err != nil { goto RESTART_LOGGER } - err = os.Chmod(fName, os.FileMode(0440)) + err = os.Chmod(fName, os.FileMode(0444)) // re-start logger RESTART_LOGGER: diff --git a/logs/file_test.go b/logs/file_test.go index f345ff20..ba0850b1 100644 --- a/logs/file_test.go +++ b/logs/file_test.go @@ -175,7 +175,7 @@ func TestFileRotate_06(t *testing.T) { //test file mode log.Emergency("emergency") rotateName := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log" s, _ := os.Lstat(rotateName) - if s.Mode() != 0440 { + if s.Mode() != 0444 { os.Remove(rotateName) os.Remove("test3.log") t.Fatal("rotate file mode error") From d15dd2795c962b9fc7c5e55e1c7ff7ebdeddce44 Mon Sep 17 00:00:00 2001 From: Gerson Alexander Pardo Gamez Date: Sat, 3 Jun 2017 15:24:45 -0500 Subject: [PATCH 125/163] added statusCode in FilterMonitorFunc --- admin.go | 6 +++--- router.go | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/admin.go b/admin.go index 875cd0e8..8d28c10c 100644 --- a/admin.go +++ b/admin.go @@ -37,7 +37,7 @@ var beeAdminApp *adminApp // FilterMonitorFunc is default monitor filter when admin module is enable. // if this func returns, admin module records qbs for this request by condition of this function logic. // usage: -// func MyFilterMonitor(method, requestPath string, t time.Duration) bool { +// func MyFilterMonitor(method, requestPath string, t time.Duration, statusCode int) bool { // if method == "POST" { // return false // } @@ -50,7 +50,7 @@ var beeAdminApp *adminApp // return true // } // beego.FilterMonitorFunc = MyFilterMonitor. -var FilterMonitorFunc func(string, string, time.Duration) bool +var FilterMonitorFunc func(string, string, time.Duration, int) bool func init() { beeAdminApp = &adminApp{ @@ -62,7 +62,7 @@ func init() { beeAdminApp.Route("/healthcheck", healthcheck) beeAdminApp.Route("/task", taskStatus) beeAdminApp.Route("/listconf", listConf) - FilterMonitorFunc = func(string, string, time.Duration) bool { return true } + FilterMonitorFunc = func(string, string, time.Duration, int) bool { return true } } // AdminIndex is the default http.Handler for admin module. diff --git a/router.go b/router.go index 72476ae8..c6da4f9b 100644 --- a/router.go +++ b/router.go @@ -849,7 +849,11 @@ Admin: //admin module record QPS if BConfig.Listen.EnableAdmin { timeDur := time.Since(startTime) - if FilterMonitorFunc(r.Method, r.URL.Path, timeDur) { + statusCode := context.ResponseWriter.Status + if statusCode == 0 { + statusCode = 200 + } + if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, statusCode) { if runRouter != nil { go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur) } else { From 7f2e3feb3c485cebd5b8c6ee309884160d6cad37 Mon Sep 17 00:00:00 2001 From: Gerson Alexander Pardo Gamez Date: Mon, 5 Jun 2017 18:21:31 -0500 Subject: [PATCH 126/163] added pattern to FilterMonitorFunc --- admin.go | 4 ++-- router.go | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/admin.go b/admin.go index 8d28c10c..e6f9c3f9 100644 --- a/admin.go +++ b/admin.go @@ -50,7 +50,7 @@ var beeAdminApp *adminApp // return true // } // beego.FilterMonitorFunc = MyFilterMonitor. -var FilterMonitorFunc func(string, string, time.Duration, int) bool +var FilterMonitorFunc func(string, string, time.Duration, string, int) bool func init() { beeAdminApp = &adminApp{ @@ -62,7 +62,7 @@ func init() { beeAdminApp.Route("/healthcheck", healthcheck) beeAdminApp.Route("/task", taskStatus) beeAdminApp.Route("/listconf", listConf) - FilterMonitorFunc = func(string, string, time.Duration, int) bool { return true } + FilterMonitorFunc = func(string, string, time.Duration, string, int) bool { return true } } // AdminIndex is the default http.Handler for admin module. diff --git a/router.go b/router.go index c6da4f9b..874fb109 100644 --- a/router.go +++ b/router.go @@ -849,11 +849,15 @@ Admin: //admin module record QPS if BConfig.Listen.EnableAdmin { timeDur := time.Since(startTime) + pattern := "" + if routerInfo != nil { + pattern = routerInfo.pattern + } statusCode := context.ResponseWriter.Status if statusCode == 0 { statusCode = 200 } - if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, statusCode) { + if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) { if runRouter != nil { go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur) } else { From 0240e182c68574b1d56bff7b053f350a7ce063af Mon Sep 17 00:00:00 2001 From: eyalpost Date: Fri, 9 Jun 2017 10:15:36 +0300 Subject: [PATCH 127/163] correctly handle multiple params with same type --- parser.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/parser.go b/parser.go index a9cfd894..fda83cb7 100644 --- a/parser.go +++ b/parser.go @@ -135,15 +135,16 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { func buildMethodParams(funcParams []*ast.Field, pc *parsedComment) []*param.MethodParam { result := make([]*param.MethodParam, 0, len(funcParams)) for _, fparam := range funcParams { - methodParam := buildMethodParam(fparam, pc) - result = append(result, methodParam) + for _, pName := range fparam.Names { + methodParam := buildMethodParam(fparam, pName.Name, pc) + result = append(result, methodParam) + } } return result } -func buildMethodParam(fparam *ast.Field, pc *parsedComment) *param.MethodParam { +func buildMethodParam(fparam *ast.Field, name string, pc *parsedComment) *param.MethodParam { options := []param.MethodParamOption{} - name := fparam.Names[0].Name if cparam, ok := pc.params[name]; ok { //Build param from comment info name = cparam.name From 8b504e7d51245c987fbc62591c2e53e43a71e3dd Mon Sep 17 00:00:00 2001 From: eyalpost Date: Mon, 12 Jun 2017 21:05:40 +0300 Subject: [PATCH 128/163] incorrect error rendering (wrong status) --- context/output.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/output.go b/context/output.go index cf9e7a7e..61ce8cc7 100644 --- a/context/output.go +++ b/context/output.go @@ -177,7 +177,7 @@ func jsonRenderer(value interface{}) Renderer { func errorRenderer(err error) Renderer { return rendererFunc(func(ctx *Context) { ctx.Output.SetStatus(500) - ctx.WriteString(err.Error()) + ctx.Output.Body([]byte(err.Error())) }) } From 55e6c15073cf127a0a9a3af3fbc68e85c87f7d93 Mon Sep 17 00:00:00 2001 From: huwenbo Date: Tue, 13 Jun 2017 15:19:51 +0800 Subject: [PATCH 129/163] fix panic: sync: negative WaitGroup counter --- grace/conn.go | 13 ++++++++++++- grace/listener.go | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/grace/conn.go b/grace/conn.go index 6807e1ac..f5d2b236 100644 --- a/grace/conn.go +++ b/grace/conn.go @@ -3,11 +3,14 @@ package grace import ( "errors" "net" + "sync" ) type graceConn struct { net.Conn server *Server + m sync.Mutex + closed bool } func (c graceConn) Close() (err error) { @@ -23,6 +26,14 @@ func (c graceConn) Close() (err error) { } } }() + + c.m.Lock() + if c.closed { + c.m.Unlock() + return + } c.server.wg.Done() + c.closed = true + c.m.Unlock() return c.Conn.Close() -} +} \ No newline at end of file diff --git a/grace/listener.go b/grace/listener.go index 823d3cce..7ede63a3 100644 --- a/grace/listener.go +++ b/grace/listener.go @@ -37,7 +37,7 @@ func (gl *graceListener) Accept() (c net.Conn, err error) { tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(3 * time.Minute) - c = graceConn{ + c = &graceConn{ Conn: tc, server: gl.server, } From 3d9286f089c5661641da9651cf56bd4f1666d4db Mon Sep 17 00:00:00 2001 From: huwenbo Date: Tue, 13 Jun 2017 15:34:57 +0800 Subject: [PATCH 130/163] fix panic: sync: negative WaitGroup counter --- grace/conn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grace/conn.go b/grace/conn.go index f5d2b236..4553e182 100644 --- a/grace/conn.go +++ b/grace/conn.go @@ -36,4 +36,4 @@ func (c graceConn) Close() (err error) { c.closed = true c.m.Unlock() return c.Conn.Close() -} \ No newline at end of file +} From 2b00b7d66dd6582405aaae64024bbb9590cc9604 Mon Sep 17 00:00:00 2001 From: huwenbo Date: Tue, 13 Jun 2017 20:15:43 +0800 Subject: [PATCH 131/163] fix panic: sync: negative WaitGroup counter --- grace/conn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grace/conn.go b/grace/conn.go index 4553e182..6f52045f 100644 --- a/grace/conn.go +++ b/grace/conn.go @@ -13,7 +13,7 @@ type graceConn struct { closed bool } -func (c graceConn) Close() (err error) { +func (c *graceConn) Close() (err error) { defer func() { if r := recover(); r != nil { switch x := r.(type) { From a87c1c5e8e8eeb2838e6716b9b73bd2f90314612 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Thu, 15 Jun 2017 17:36:37 +0800 Subject: [PATCH 132/163] AddAPPStartHook func modify --- beego.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/beego.go b/beego.go index 22079a20..218fded8 100644 --- a/beego.go +++ b/beego.go @@ -40,9 +40,9 @@ var ( // AddAPPStartHook is used to register the hookfunc // The hookfuncs will run in beego.Run() -// such as sessionInit, middlerware start, buildtemplate, admin start -func AddAPPStartHook(hf hookfunc) { - hooks = append(hooks, hf) +// such as initiating session , starting middleware , building template, starting admin control and so on. +func AddAPPStartHook(hf ...hookfunc) { + hooks = append(hooks, hf...) } // Run beego application. @@ -69,12 +69,14 @@ func Run(params ...string) { func initBeforeHTTPRun() { //init hooks - AddAPPStartHook(registerMime) - AddAPPStartHook(registerDefaultErrorHandler) - AddAPPStartHook(registerSession) - AddAPPStartHook(registerTemplate) - AddAPPStartHook(registerAdmin) - AddAPPStartHook(registerGzip) + AddAPPStartHook( + registerMime, + registerDefaultErrorHandler, + registerSession, + registerTemplate, + registerAdmin, + registerGzip, + ) for _, hk := range hooks { if err := hk(); err != nil { From 79f60274a0e15ac85483f5a21c9d6ce70e0ff551 Mon Sep 17 00:00:00 2001 From: moqiancong Date: Fri, 16 Jun 2017 01:26:55 +0800 Subject: [PATCH 133/163] fix cache/memory fatal error: concurrent map iteration and map write --- cache/memory.go | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/cache/memory.go b/cache/memory.go index fff2ebbb..57e868cf 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -217,26 +217,31 @@ func (bc *MemoryCache) vaccuum() { if bc.items == nil { return } - for name := range bc.items { - bc.itemExpired(name) + if keys := bc.expiredKeys(); len(keys) != 0 { + bc.clearItems(keys) } } } -// itemExpired returns true if an item is expired. -func (bc *MemoryCache) itemExpired(name string) bool { +// expiredKeys returns key list which are expired. +func (bc *MemoryCache) expiredKeys() (keys []string) { + bc.RLock() + defer bc.RUnlock() + for key, itm := range bc.items { + if itm.isExpire() { + keys = append(keys, key) + } + } + return +} + +// clearItems removes all the items which key in keys. +func (bc *MemoryCache) clearItems(keys []string) { bc.Lock() defer bc.Unlock() - - itm, ok := bc.items[name] - if !ok { - return true + for _, key := range keys { + delete(bc.items, key) } - if itm.isExpire() { - delete(bc.items, name) - return true - } - return false } func init() { From 2231841d74955b92993e034830e18543842d0751 Mon Sep 17 00:00:00 2001 From: miraclesu Date: Mon, 19 Jun 2017 18:10:09 +0800 Subject: [PATCH 134/163] validation: support int64 int32 int16 and int8 type --- validation/util.go | 20 +++++++++++++++ validation/validation.go | 6 ++--- validation/validation_test.go | 8 +++--- validation/validators.go | 46 ++++++++++++++++++++++++++--------- 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/validation/util.go b/validation/util.go index 9e7460a6..c8db0442 100644 --- a/validation/util.go +++ b/validation/util.go @@ -249,6 +249,26 @@ func parseParam(t reflect.Type, s string) (i interface{}, err error) { switch t.Kind() { case reflect.Int: i, err = strconv.Atoi(s) + case reflect.Int64: + i, err = strconv.ParseInt(s, 10, 64) + case reflect.Int32: + var v int64 + v, err = strconv.ParseInt(s, 10, 32) + if err == nil { + i = int32(v) + } + case reflect.Int16: + var v int64 + v, err = strconv.ParseInt(s, 10, 16) + if err == nil { + i = int16(v) + } + case reflect.Int8: + var v int64 + v, err = strconv.ParseInt(s, 10, 8) + if err == nil { + i = int8(v) + } case reflect.String: i = s case reflect.Ptr: diff --git a/validation/validation.go b/validation/validation.go index 9dc51106..9d05befc 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -144,17 +144,17 @@ func (v *Validation) Required(obj interface{}, key string) *Result { } // Min Test that the obj is greater than min if obj's type is int -func (v *Validation) Min(obj interface{}, min int, key string) *Result { +func (v *Validation) Min(obj interface{}, min int64, key string) *Result { return v.apply(Min{min, key}, obj) } // Max Test that the obj is less than max if obj's type is int -func (v *Validation) Max(obj interface{}, max int, key string) *Result { +func (v *Validation) Max(obj interface{}, max int64, key string) *Result { return v.apply(Max{max, key}, obj) } // Range Test that the obj is between mni and max if obj's type is int -func (v *Validation) Range(obj interface{}, min, max int, key string) *Result { +func (v *Validation) Range(obj interface{}, min, max int64, key string) *Result { return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, obj) } diff --git a/validation/validation_test.go b/validation/validation_test.go index 71a8bd17..7a88acad 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -64,10 +64,10 @@ func TestRequired(t *testing.T) { func TestMin(t *testing.T) { valid := Validation{} - if valid.Min(-1, 0, "min0").Ok { + if valid.Min(int64(-1), int64(0), "min0").Ok { t.Error("-1 is less than the minimum value of 0 should be false") } - if !valid.Min(1, 0, "min0").Ok { + if !valid.Min(int64(1), int64(0), "min0").Ok { t.Error("1 is greater or equal than the minimum value of 0 should be true") } } @@ -75,10 +75,10 @@ func TestMin(t *testing.T) { func TestMax(t *testing.T) { valid := Validation{} - if valid.Max(1, 0, "max0").Ok { + if valid.Max(int64(1), int64(0), "max0").Ok { t.Error("1 is greater than the minimum value of 0 should be false") } - if !valid.Max(-1, 0, "max0").Ok { + if !valid.Max(int64(-1), int64(0), "max0").Ok { t.Error("-1 is less or equal than the maximum value of 0 should be true") } } diff --git a/validation/validators.go b/validation/validators.go index 5d489a55..264e61a1 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -161,17 +161,29 @@ func (r Required) GetLimitValue() interface{} { // Min check struct type Min struct { - Min int + Min int64 Key string } // IsSatisfied judge whether obj is valid func (m Min) IsSatisfied(obj interface{}) bool { - num, ok := obj.(int) - if ok { - return num >= m.Min + var v int64 + switch obj.(type) { + case int64: + v = obj.(int64) + case int: + v = int64(obj.(int)) + case int32: + v = int64(obj.(int32)) + case int16: + v = int64(obj.(int16)) + case int8: + v = int64(obj.(int8)) + default: + return false } - return false + + return v >= m.Min } // DefaultMessage return the default min error message @@ -191,17 +203,29 @@ func (m Min) GetLimitValue() interface{} { // Max validate struct type Max struct { - Max int + Max int64 Key string } // IsSatisfied judge whether obj is valid func (m Max) IsSatisfied(obj interface{}) bool { - num, ok := obj.(int) - if ok { - return num <= m.Max + var v int64 + switch obj.(type) { + case int64: + v = obj.(int64) + case int: + v = int64(obj.(int)) + case int32: + v = int64(obj.(int32)) + case int16: + v = int64(obj.(int16)) + case int8: + v = int64(obj.(int8)) + default: + return false } - return false + + return v <= m.Max } // DefaultMessage return the default max error message @@ -243,7 +267,7 @@ func (r Range) GetKey() string { // GetLimitValue return the limit value, Max func (r Range) GetLimitValue() interface{} { - return []int{r.Min.Min, r.Max.Max} + return []int64{r.Min.Min, r.Max.Max} } // MinSize Requires an array or string to be at least a given length. From 5a2eea07cb2cffc7178039b920b76923edb7a722 Mon Sep 17 00:00:00 2001 From: Jia Li Ong Date: Thu, 1 Jun 2017 17:48:48 +0800 Subject: [PATCH 135/163] Provide permission to access old log files to everyone --- logs/file.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/logs/file.go b/logs/file.go index 1c2db882..0b827857 100644 --- a/logs/file.go +++ b/logs/file.go @@ -56,17 +56,20 @@ type fileLogWriter struct { Perm string `json:"perm"` + OtherPerm string `json:"otherperm"` + fileNameOnly, suffix string // like "project.log", project is fileNameOnly and .log is suffix } // newFileWriter create a FileLogWriter returning as LoggerInterface. func newFileWriter() Logger { w := &fileLogWriter{ - Daily: true, - MaxDays: 7, - Rotate: true, - Level: LevelTrace, - Perm: "0660", + Daily: true, + MaxDays: 7, + Rotate: true, + OtherPerm: "0440", + Level: LevelTrace, + Perm: "0660", } return w } @@ -237,8 +240,12 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { // Find the next available number num := 1 fName := "" + otherPerm, err := strconv.ParseInt(w.OtherPerm, 8, 64) + if err != nil { + return err + } - _, err := os.Lstat(w.Filename) + _, err = os.Lstat(w.Filename) if err != nil { //even if the file is not exist or other ,we should RESTART the logger goto RESTART_LOGGER @@ -271,7 +278,9 @@ func (w *fileLogWriter) doRotate(logTime time.Time) error { if err != nil { goto RESTART_LOGGER } - err = os.Chmod(fName, os.FileMode(0440)) + //err = os.Chmod(fName, os.FileMode(0444)) + + err = os.Chmod(fName, os.FileMode(otherPerm)) // re-start logger RESTART_LOGGER: From 6e34f437218b7a9a47a4413e2cd79d3b072c486d Mon Sep 17 00:00:00 2001 From: miraclesu Date: Wed, 28 Jun 2017 16:56:37 +0800 Subject: [PATCH 136/163] Fix break API change support int64 on 64-bit platform --- validation/util.go | 11 ++++++++-- validation/validation.go | 6 +++--- validation/validation_test.go | 8 +++---- validation/validators.go | 39 +++++++++++++++++++++-------------- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/validation/util.go b/validation/util.go index c8db0442..4695b776 100644 --- a/validation/util.go +++ b/validation/util.go @@ -25,6 +25,8 @@ import ( const ( // ValidTag struct tag ValidTag = "valid" + + wordsize = 32 << (^uint(0) >> 32 & 1) ) var ( @@ -43,6 +45,8 @@ var ( "Valid": true, "NoMatch": true, } + + Int64On32Err = fmt.Errorf("not support int64 on 32-bit platform") ) func init() { @@ -250,6 +254,9 @@ func parseParam(t reflect.Type, s string) (i interface{}, err error) { case reflect.Int: i, err = strconv.Atoi(s) case reflect.Int64: + if wordsize == 32 { + return nil, Int64On32Err + } i, err = strconv.ParseInt(s, 10, 64) case reflect.Int32: var v int64 @@ -273,12 +280,12 @@ func parseParam(t reflect.Type, s string) (i interface{}, err error) { i = s case reflect.Ptr: if t.Elem().String() != "regexp.Regexp" { - err = fmt.Errorf("does not support %s", t.Elem().String()) + err = fmt.Errorf("not support %s", t.Elem().String()) return } i, err = regexp.Compile(s) default: - err = fmt.Errorf("does not support %s", t.Kind().String()) + err = fmt.Errorf("not support %s", t.Kind().String()) } return } diff --git a/validation/validation.go b/validation/validation.go index 9d05befc..9dc51106 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -144,17 +144,17 @@ func (v *Validation) Required(obj interface{}, key string) *Result { } // Min Test that the obj is greater than min if obj's type is int -func (v *Validation) Min(obj interface{}, min int64, key string) *Result { +func (v *Validation) Min(obj interface{}, min int, key string) *Result { return v.apply(Min{min, key}, obj) } // Max Test that the obj is less than max if obj's type is int -func (v *Validation) Max(obj interface{}, max int64, key string) *Result { +func (v *Validation) Max(obj interface{}, max int, key string) *Result { return v.apply(Max{max, key}, obj) } // Range Test that the obj is between mni and max if obj's type is int -func (v *Validation) Range(obj interface{}, min, max int64, key string) *Result { +func (v *Validation) Range(obj interface{}, min, max int, key string) *Result { return v.apply(Range{Min{Min: min}, Max{Max: max}, key}, obj) } diff --git a/validation/validation_test.go b/validation/validation_test.go index 7a88acad..71a8bd17 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -64,10 +64,10 @@ func TestRequired(t *testing.T) { func TestMin(t *testing.T) { valid := Validation{} - if valid.Min(int64(-1), int64(0), "min0").Ok { + if valid.Min(-1, 0, "min0").Ok { t.Error("-1 is less than the minimum value of 0 should be false") } - if !valid.Min(int64(1), int64(0), "min0").Ok { + if !valid.Min(1, 0, "min0").Ok { t.Error("1 is greater or equal than the minimum value of 0 should be true") } } @@ -75,10 +75,10 @@ func TestMin(t *testing.T) { func TestMax(t *testing.T) { valid := Validation{} - if valid.Max(int64(1), int64(0), "max0").Ok { + if valid.Max(1, 0, "max0").Ok { t.Error("1 is greater than the minimum value of 0 should be false") } - if !valid.Max(int64(-1), int64(0), "max0").Ok { + if !valid.Max(-1, 0, "max0").Ok { t.Error("-1 is less or equal than the maximum value of 0 should be true") } } diff --git a/validation/validators.go b/validation/validators.go index 264e61a1..5248542a 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -161,24 +161,28 @@ func (r Required) GetLimitValue() interface{} { // Min check struct type Min struct { - Min int64 + Min int Key string } // IsSatisfied judge whether obj is valid +// not support int64 on 32-bit platform func (m Min) IsSatisfied(obj interface{}) bool { - var v int64 + var v int switch obj.(type) { case int64: - v = obj.(int64) + if wordsize == 32 { + return false + } + v = int(obj.(int64)) case int: - v = int64(obj.(int)) + v = obj.(int) case int32: - v = int64(obj.(int32)) + v = int(obj.(int32)) case int16: - v = int64(obj.(int16)) + v = int(obj.(int16)) case int8: - v = int64(obj.(int8)) + v = int(obj.(int8)) default: return false } @@ -203,24 +207,28 @@ func (m Min) GetLimitValue() interface{} { // Max validate struct type Max struct { - Max int64 + Max int Key string } // IsSatisfied judge whether obj is valid +// not support int64 on 32-bit platform func (m Max) IsSatisfied(obj interface{}) bool { - var v int64 + var v int switch obj.(type) { case int64: - v = obj.(int64) + if wordsize == 32 { + return false + } + v = int(obj.(int64)) case int: - v = int64(obj.(int)) + v = obj.(int) case int32: - v = int64(obj.(int32)) + v = int(obj.(int32)) case int16: - v = int64(obj.(int16)) + v = int(obj.(int16)) case int8: - v = int64(obj.(int8)) + v = int(obj.(int8)) default: return false } @@ -251,6 +259,7 @@ type Range struct { } // IsSatisfied judge whether obj is valid +// not support int64 on 32-bit platform func (r Range) IsSatisfied(obj interface{}) bool { return r.Min.IsSatisfied(obj) && r.Max.IsSatisfied(obj) } @@ -267,7 +276,7 @@ func (r Range) GetKey() string { // GetLimitValue return the limit value, Max func (r Range) GetLimitValue() interface{} { - return []int64{r.Min.Min, r.Max.Max} + return []int{r.Min.Min, r.Max.Max} } // MinSize Requires an array or string to be at least a given length. From 234708062a86ae0cb00002c2947443ffe800c550 Mon Sep 17 00:00:00 2001 From: xlwcom Date: Thu, 29 Jun 2017 13:32:40 +0800 Subject: [PATCH 137/163] fix the bug in the "ParseBool" function in the file of config.go --- config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index e8201a24..a9ef7a9a 100644 --- a/config/config.go +++ b/config/config.go @@ -189,16 +189,16 @@ func ParseBool(val interface{}) (value bool, err error) { return false, nil } case int8, int32, int64: - strV := fmt.Sprintf("%s", v) + strV := fmt.Sprintf("%d", v) // w 由 %s 修改为 %d if strV == "1" { return true, nil } else if strV == "0" { return false, nil } case float64: - if v == 1 { + if v == 1.0 { // w 由 1 修改为 1.0 return true, nil - } else if v == 0 { + } else if v == 0.0 { // w 由 0 修改为 0.0 return false, nil } } From e72b02b7cc99493af7286a8966ad3a8f0ed32496 Mon Sep 17 00:00:00 2001 From: miraclesu Date: Mon, 3 Jul 2017 16:14:29 +0800 Subject: [PATCH 138/163] validation: support required option for some struct tag valids --- validation/README.md | 3 +++ validation/validation.go | 17 ++++++++++++ validation/validation_test.go | 51 +++++++++++++++++++++++++++++++++++ validation/validators.go | 10 +++++++ 4 files changed, 81 insertions(+) diff --git a/validation/README.md b/validation/README.md index 5c3212b0..43373e47 100644 --- a/validation/README.md +++ b/validation/README.md @@ -64,6 +64,9 @@ Struct Tag Use: func main() { valid := validation.Validation{} + // ignore empty field valid + // see CanSkipFuncs + // valid := validation.Validation{RequiredFirst:true} u := user{Name: "test", Age: 40} b, err := valid.Valid(u) if err != nil { diff --git a/validation/validation.go b/validation/validation.go index 9dc51106..ef484fb3 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -106,6 +106,11 @@ func (r *Result) Message(message string, args ...interface{}) *Result { // A Validation context manages data validation and error messages. type Validation struct { + // if this field set true, in struct tag valid + // if the struct field vale is empty + // it will skip those valid functions, see CanSkipFuncs + RequiredFirst bool + Errors []*Error ErrorsMap map[string]*Error } @@ -324,7 +329,19 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { if vfs, err = getValidFuncs(objT.Field(i)); err != nil { return } + + var hasReuired bool for _, vf := range vfs { + if vf.Name == "Required" { + hasReuired = true + } + + if !hasReuired && v.RequiredFirst && len(objV.Field(i).String()) == 0 { + if _, ok := CanSkipFuncs[vf.Name]; ok { + continue + } + } + if _, err = funcs.Call(vf.Name, mergeParam(v, objV.Field(i).Interface(), vf.Params)...); err != nil { return diff --git a/validation/validation_test.go b/validation/validation_test.go index 71a8bd17..bf612015 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -391,3 +391,54 @@ func TestRecursiveValid(t *testing.T) { t.Error("validation should not be passed") } } + +func TestSkipValid(t *testing.T) { + type User struct { + ID int + + Email string `valid:"Email"` + ReqEmail string `valid:"Required;Email"` + + IP string `valid:"IP"` + ReqIP string `valid:"Required;IP"` + + Mobile string `valid:"Mobile"` + ReqMobile string `valid:"Required;Mobile"` + + Tel string `valid:"Tel"` + ReqTel string `valid:"Required;Tel"` + + Phone string `valid:"Phone"` + ReqPhone string `valid:"Required;Phone"` + + ZipCode string `valid:"ZipCode"` + ReqZipCode string `valid:"Required;ZipCode"` + } + + u := User{ + ReqEmail: "a@a.com", + ReqIP: "127.0.0.1", + ReqMobile: "18888888888", + ReqTel: "02088888888", + ReqPhone: "02088888888", + ReqZipCode: "510000", + } + + valid := Validation{} + b, err := valid.Valid(u) + if err != nil { + t.Fatal(err) + } + if b { + t.Fatal("validation should not be passed") + } + + valid = Validation{RequiredFirst: true} + b, err = valid.Valid(u) + if err != nil { + t.Fatal(err) + } + if !b { + t.Fatal("validation should be passed") + } +} diff --git a/validation/validators.go b/validation/validators.go index 5248542a..a9c3e6b8 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -23,6 +23,16 @@ import ( "unicode/utf8" ) +// CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty +var CanSkipFuncs = map[string]struct{}{ + "Email": struct{}{}, + "IP": struct{}{}, + "Mobile": struct{}{}, + "Tel": struct{}{}, + "Phone": struct{}{}, + "ZipCode": struct{}{}, +} + // MessageTmpls store commond validate template var MessageTmpls = map[string]string{ "Required": "Can not be empty", From 3c17e2a7e6e9b6e377359e70cb9e361e388ef0dc Mon Sep 17 00:00:00 2001 From: xlwcom Date: Tue, 4 Jul 2017 11:03:49 +0800 Subject: [PATCH 139/163] remove the comments --- config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.go b/config/config.go index a9ef7a9a..c620504a 100644 --- a/config/config.go +++ b/config/config.go @@ -189,16 +189,16 @@ func ParseBool(val interface{}) (value bool, err error) { return false, nil } case int8, int32, int64: - strV := fmt.Sprintf("%d", v) // w 由 %s 修改为 %d + strV := fmt.Sprintf("%d", v) if strV == "1" { return true, nil } else if strV == "0" { return false, nil } case float64: - if v == 1.0 { // w 由 1 修改为 1.0 + if v == 1.0 { return true, nil - } else if v == 0.0 { // w 由 0 修改为 0.0 + } else if v == 0.0 { return false, nil } } From 7ec819deed1788d945d59ed1e400d1f2db0a2bf4 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 4 Jul 2017 21:16:59 +0800 Subject: [PATCH 140/163] fix #2725 big form --- context/input.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/context/input.go b/context/input.go index d9015ce3..aaf97e85 100644 --- a/context/input.go +++ b/context/input.go @@ -19,6 +19,7 @@ import ( "errors" "io" "io/ioutil" + "net/http" "net/url" "reflect" "regexp" @@ -353,7 +354,7 @@ func (input *BeegoInput) CopyBody(MaxMemory int64) []byte { requestbody, _ := ioutil.ReadAll(safe) input.Context.Request.Body.Close() bf := bytes.NewBuffer(requestbody) - input.Context.Request.Body = ioutil.NopCloser(bf) + input.Context.Request.Body = http.MaxBytesReader(input.Context.ResponseWriter, ioutil.NopCloser(bf), MaxMemory) input.RequestBody = requestbody return requestbody } From 6b9c3f48248ec2d85b9264c99ff9a27cc42d0489 Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Thu, 6 Jul 2017 06:40:28 +0530 Subject: [PATCH 141/163] [Proposal] Database Migrations; Summary: The database migrations now can be created using the methods on the migration struct. it does not break any existing migration features. it upgrades the migration struct and adds few more struct types so that the migrations can be efficiently generated for create, alter, reverse, drop. Current Features: * Supports creation of columns * `m.NewCol("name").SetDataType("VARCHAR(10)").SetNullable("true")` * **NOTE** `SetNullable` & `SetDefault` methods should not be called on same column for consistency * Supports addition of primary keys * `m.PriCol("id").SetDataType("INT(10)").SetNullable("true")` * **NOTE** `setAuto(true)` can be only called on Primary keys * Supports addition of unique keys * `m.UniCol("unique_index","column_name").SetDataType("VARCHAR(23)").SetNullable("true")` * **NOTE** `UniCol` can be called again with the same index name to add column to the index * Supports rename of columns * `m.RenameColumn("from_name","to_name")` * Allows standard column methods and methods such that, `SetOldDefault` allows reversibility of renames * TODO: * ForeignKey Signed-off-by: Gnanakeethan Balasubramaniam --- migration/ddl.go | 333 ++++++++++++++++++++++++++++++++++++++--- migration/migration.go | 30 +++- 2 files changed, 333 insertions(+), 30 deletions(-) diff --git a/migration/ddl.go b/migration/ddl.go index 51243337..4389ca8c 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -12,42 +12,327 @@ // See the License for the specific language governing permissions and // limitations under the License. -package migration +package m -// Table store the tablename and Column -type Table struct { - TableName string - Columns []*Column +import ( + "fmt" + + "github.com/astaxie/beego" +) + +// Index struct defines the structure of Index Columns +type Index struct { + Name string } -// Create return the create sql -func (t *Table) Create() string { - return "" +// Foreign struct defines a single foreign relationship +type Foreign struct { + Column *Column + ForeignTable string + ForeignColumn string } -// Drop return the drop sql -func (t *Table) Drop() string { - return "" +// Unique struct defines a single unique key combination +type Unique struct { + Definition string + Columns []*Column } -// Column define the columns name type and Default +//Column struct defines a single column of a table type Column struct { - Name string - Type string - Default interface{} + Name string + Inc string + Null string + Default string + Unsign string + DataType string + remove bool + Modify bool } -// Create return create sql with the provided tbname and columns -func Create(tbname string, columns ...Column) string { - return "" +// RenameColumn struct allows renaming of columns +type RenameColumn struct { + OldName string + OldNull string + OldDefault string + OldUnsign string + OldDataType string + NewName string + Column } -// Drop return the drop sql with the provided tbname and columns -func Drop(tbname string, columns ...Column) string { - return "" +// NewCol creates a new standard column and attaches it to m struct +func (m *Migration) NewCol(name string) *Column { + col := &Column{Name: name} + m.AddColumns(col) + return col } -// TableDDL is still in think -func TableDDL(tbname string, columns ...Column) string { - return "" +//PriCol creates a new primary column and attaches it to m struct +func (m *Migration) PriCol(name string) *Column { + col := &Column{Name: name} + m.AddColumns(col) + m.AddPrimary(col) + return col +} + +//UniCol creates / appends columns to specified unique key and attaches it to m struct +func (m *Migration) UniCol(uni, name string) *Column { + col := &Column{Name: name} + m.AddColumns(col) + + uniqueOriginal := &Unique{} + + for _, unique := range m.Uniques { + if unique.Definition == uni { + unique.AddColumnsToUnique(col) + uniqueOriginal = unique + } + } + if uniqueOriginal.Definition == "" { + unique := &Unique{Definition: uni} + unique.AddColumnsToUnique(col) + m.AddUnique(unique) + } + + return col +} + +//Remove marks the columns to be removed. +//it allows reverse m to create the column. +func (c *Column) Remove() { + c.remove = true +} + +//SetAuto enables auto_increment of column (can be used once) +func (c *Column) SetAuto(inc bool) *Column { + if inc { + c.Inc = "auto_increment" + } + return c +} + +//SetNullable sets the column to be null +func (c *Column) SetNullable(null bool) *Column { + if null { + c.Null = "DEFAULT NULL" + + } else { + c.Null = "NOT NULL" + } + return c +} + +//SetDefault sets the default value, prepend with "DEFAULT " +func (c *Column) SetDefault(def string) *Column { + c.Default = def + return c +} + +//SetUnsigned sets the column to be unsigned int +func (c *Column) SetUnsigned(unsign bool) *Column { + if unsign { + c.Unsign = "UNSIGNED" + } + return c +} + +//SetDataType sets the dataType of the column +func (c *Column) SetDataType(dataType string) *Column { + c.DataType = dataType + return c +} + +//SetOldNullable allows reverting to previous nullable on reverse ms +func (c *RenameColumn) SetOldNullable(null bool) *RenameColumn { + if null { + c.OldNull = "DEFAULT NULL" + + } else { + c.OldNull = "NOT NULL" + } + return c +} + +//SetOldDefault allows reverting to previous default on reverse ms +func (c *RenameColumn) SetOldDefault(def string) *RenameColumn { + c.OldDefault = def + return c +} + +//SetOldUnsigned allows reverting to previous unsgined on reverse ms +func (c *RenameColumn) SetOldUnsigned(unsign bool) *RenameColumn { + if unsign { + c.OldUnsign = "UNSIGNED" + } + return c +} + +//SetOldDataType allows reverting to previous datatype on reverse ms +func (c *RenameColumn) SetOldDataType(dataType string) *RenameColumn { + c.OldDataType = dataType + return c +} + +//SetPrimary adds the columns to the primary key (can only be used any number of times in only one m) +func (c *Column) SetPrimary(m *Migration) *Column { + m.Primary = append(m.Primary, c) + return c +} + +//AddColumnsToUnique adds the columns to Unique Struct +func (unique *Unique) AddColumnsToUnique(columns ...*Column) *Unique { + + unique.Columns = append(unique.Columns, column...) + + return unique +} + +//AddColumns adds columns to m struct +func (m *Migration) AddColumns(columns ...*Column) *Migration { + + m.Columns = append(m.Columns, column...) + + return m +} + +//AddPrimary adds the column to primary in m struct +func (m *Migration) AddPrimary(primary *Column) *Migration { + m.Primary = append(m.Primary, primary) + return m +} + +//AddUnique adds the column to unique in m struct +func (m *Migration) AddUnique(unique *Unique) *Migration { + m.Uniques = append(m.Uniques, unique) + return m +} + +//AddForeign adds the column to foreign in m struct +func (m *Migration) AddForeign(foreign *Foreign) *Migration { + m.Foreigns = append(m.Foreigns, foreign) + return m +} + +//AddIndex adds the column to index in m struct +func (m *Migration) AddIndex(index *Index) *Migration { + m.Indexes = append(m.Indexes, index) + return m +} + +//RenameColumn allows renaming of columns +func (m *Migration) RenameColumn(from, to string) *RenameColumn { + rename := &RenameColumn{OldName: from, NewName: to} + m.Renames = append(m.Renames, rename) + return rename +} + +//GetSQL returns the generated sql depending on ModifyType +func (m *Migration) GetSQL() (sql string) { + sql = "" + switch m.ModifyType { + case "create": + { + sql += fmt.Sprintf("CREATE TABLE `%s` (", m.TableName) + for index, column := range m.Columns { + sql += fmt.Sprintf("\n `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + if len(m.Columns) > index+1 { + sql += "," + } + } + + if len(m.Primary) > 0 { + sql += fmt.Sprintf(",\n PRIMARY KEY( ") + } + for index, column := range m.Primary { + sql += fmt.Sprintf(" `%s`", column.Name) + if len(m.Primary) > index+1 { + sql += "," + } + + } + if len(m.Primary) > 0 { + sql += fmt.Sprintf(")") + } + + for _, unique := range m.Uniques { + sql += fmt.Sprintf(",\n UNIQUE KEY `%s`( ", unique.Definition) + for index, column := range unique.Columns { + sql += fmt.Sprintf(" `%s`", column.Name) + if len(unique.Columns) > index+1 { + sql += "," + } + } + sql += fmt.Sprintf(")") + } + sql += fmt.Sprintf(")ENGINE=%s DEFAULT CHARSET=%s;", m.Engine, m.Charset) + break + } + case "alter": + { + sql += fmt.Sprintf("ALTER TABLE `%s` ", m.TableName) + for index, column := range m.Columns { + if !column.remove { + beego.BeeLogger.Info("col") + sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + } else { + sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name) + } + + if len(m.Columns) > index+1 { + sql += "," + } + } + for index, column := range m.Renames { + sql += fmt.Sprintf(",\n CHANGE COLUMN `%s` `%s` %s %s %s %s %s", column.OldName, column.NewName, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + if len(m.Renames) > index+1 { + sql += "," + } + } + + sql += ";" + + break + } + case "reverse": + { + + sql += fmt.Sprintf("ALTER TABLE `%s`", m.TableName) + for index, column := range m.Columns { + if column.remove { + sql += fmt.Sprintf("\n ADD `%s` %s %s %s %s %s", column.Name, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + } else { + sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name) + } + if len(m.Columns) > index+1 { + sql += "," + } + } + + if len(m.Primary) > 0 { + sql += fmt.Sprintf(",\n DROP PRIMARY KEY") + } + + for index, unique := range m.Uniques { + sql += fmt.Sprintf(",\n DROP KEY `%s`", unique.Definition) + if len(m.Uniques) > index+1 { + sql += "," + } + + } + for index, column := range m.Renames { + sql += fmt.Sprintf(",\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault) + if len(m.Renames) > index+1 { + sql += "," + } + } + sql += ";" + } + case "delete": + { + sql += fmt.Sprintf("DROP TABLE IF EXISTS `%s`;", m.TableName) + } + } + + return } diff --git a/migration/migration.go b/migration/migration.go index c9ca1bc6..2bfa8bd4 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -52,6 +52,25 @@ type Migrationer interface { GetCreated() int64 } +type Migration struct { + sqls []string + Created string + TableName string + Engine string + Charset string + ModifyType string + Columns []*Column + Indexes []*Index + Primary []*Column + Uniques []*Unique + Foreigns []*Foreign + Renames []*RenameColumn + RemoveColumns []*Column + RemoveIndexes []*Index + RemoveUniques []*Unique + RemoveForeigns []*Foreign +} + var ( migrationMap map[string]Migrationer ) @@ -60,12 +79,6 @@ func init() { migrationMap = make(map[string]Migrationer) } -// Migration the basic type which will implement the basic type -type Migration struct { - sqls []string - Created string -} - // Up implement in the Inheritance struct for upgrade func (m *Migration) Up() { @@ -76,6 +89,11 @@ func (m *Migration) Down() { } +func (m *Migration) Migrate(migrationType string) { + m.ModifyType = migrationType + m.sqls = append(m.sqls, m.GetSQL()) +} + // SQL add sql want to execute func (m *Migration) SQL(sql string) { m.sqls = append(m.sqls, sql) From ed558a0e709a0e7b3db45ecc79055b90fd307ad5 Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Thu, 6 Jul 2017 07:45:07 +0530 Subject: [PATCH 142/163] Fix: typo due to find and replace migration renamed to m Signed-off-by: Gnanakeethan Balasubramaniam --- migration/ddl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/ddl.go b/migration/ddl.go index 4389ca8c..f1f19783 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package m +package migration import ( "fmt" From c1ba11f53125e5d3480ed590c1748c6e24e9763e Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Thu, 6 Jul 2017 14:58:40 +0530 Subject: [PATCH 143/163] Fixing typo Signed-off-by: Gnanakeethan Balasubramaniam --- migration/ddl.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/ddl.go b/migration/ddl.go index f1f19783..0702b020 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -183,7 +183,7 @@ func (c *Column) SetPrimary(m *Migration) *Column { //AddColumnsToUnique adds the columns to Unique Struct func (unique *Unique) AddColumnsToUnique(columns ...*Column) *Unique { - unique.Columns = append(unique.Columns, column...) + unique.Columns = append(unique.Columns, columns...) return unique } @@ -191,7 +191,7 @@ func (unique *Unique) AddColumnsToUnique(columns ...*Column) *Unique { //AddColumns adds columns to m struct func (m *Migration) AddColumns(columns ...*Column) *Migration { - m.Columns = append(m.Columns, column...) + m.Columns = append(m.Columns, columns...) return m } From e888fee4e07883b0c50b5c95261a56857707f7e7 Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Thu, 6 Jul 2017 20:26:37 +0530 Subject: [PATCH 144/163] Update: Foreign Key & Comments Summary: Foreign Key functions are now available Signed-off-by: Gnanakeethan Balasubramaniam --- migration/ddl.go | 78 +++++++++++++++++++++++++++++++++--------- migration/migration.go | 1 + 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/migration/ddl.go b/migration/ddl.go index 0702b020..3b405498 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -25,13 +25,6 @@ type Index struct { Name string } -// Foreign struct defines a single foreign relationship -type Foreign struct { - Column *Column - ForeignTable string - ForeignColumn string -} - // Unique struct defines a single unique key combination type Unique struct { Definition string @@ -50,6 +43,15 @@ type Column struct { Modify bool } +// Foreign struct defines a single foreign relationship +type Foreign struct { + ForeignTable string + ForeignColumn string + OnDelete string + OnUpdate string + Column +} + // RenameColumn struct allows renaming of columns type RenameColumn struct { OldName string @@ -98,6 +100,27 @@ func (m *Migration) UniCol(uni, name string) *Column { return col } +//ForeignCol creates a new foreign column and returns the instance of column +func (m *Migration) ForeignCol(colname, foreigncol, foreigntable string) (foreign *Foreign) { + + foreign = &Foreign{ForeignColumn: foreigncol, ForeignTable: foreigntable} + foreign.Name = colname + m.AddForeign(foreign) + return foreign +} + +//SetOnDelete sets the on delete of foreign +func (foreign *Foreign) SetOnDelete(del string) *Foreign { + foreign.OnDelete = "ON DELETE" + del + return foreign +} + +//SetOnUpdate sets the on update of foreign +func (foreign *Foreign) SetOnUpdate(update string) *Foreign { + foreign.OnUpdate = "ON UPDATE" + update + return foreign +} + //Remove marks the columns to be removed. //it allows reverse m to create the column. func (c *Column) Remove() { @@ -125,7 +148,7 @@ func (c *Column) SetNullable(null bool) *Column { //SetDefault sets the default value, prepend with "DEFAULT " func (c *Column) SetDefault(def string) *Column { - c.Default = def + c.Default = "DEFAULT " + def return c } @@ -265,6 +288,15 @@ func (m *Migration) GetSQL() (sql string) { } sql += fmt.Sprintf(")") } + for index, foreign := range m.Foreigns { + sql += fmt.Sprintf(",\n `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default) + sql += fmt.Sprintf(",\n KEY `%s_%s_foreign`(`%s`),", m.TableName, foreign.Column.Name, foreign.Column.Name) + sql += fmt.Sprintf("\n CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate) + + if len(m.Foreigns) > index+1 { + sql += "," + } + } sql += fmt.Sprintf(")ENGINE=%s DEFAULT CHARSET=%s;", m.Engine, m.Charset) break } @@ -279,17 +311,25 @@ func (m *Migration) GetSQL() (sql string) { sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name) } - if len(m.Columns) > index+1 { + if len(m.Columns) > index { sql += "," } } for index, column := range m.Renames { - sql += fmt.Sprintf(",\n CHANGE COLUMN `%s` `%s` %s %s %s %s %s", column.OldName, column.NewName, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) + sql += fmt.Sprintf("CHANGE COLUMN `%s` `%s` %s %s %s %s %s", column.OldName, column.NewName, column.DataType, column.Unsign, column.Null, column.Inc, column.Default) if len(m.Renames) > index+1 { sql += "," } } + for index, foreign := range m.Foreigns { + sql += fmt.Sprintf("ADD `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default) + sql += fmt.Sprintf(",\n ADD KEY `%s_%s_foreign`(`%s`)", m.TableName, foreign.Column.Name, foreign.Column.Name) + sql += fmt.Sprintf(",\n ADD CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate) + if len(m.Foreigns) > index+1 { + sql += "," + } + } sql += ";" break @@ -304,28 +344,34 @@ func (m *Migration) GetSQL() (sql string) { } else { sql += fmt.Sprintf("\n DROP COLUMN `%s`", column.Name) } - if len(m.Columns) > index+1 { + if len(m.Columns) > index { sql += "," } } if len(m.Primary) > 0 { - sql += fmt.Sprintf(",\n DROP PRIMARY KEY") + sql += fmt.Sprintf("\n DROP PRIMARY KEY,") } for index, unique := range m.Uniques { - sql += fmt.Sprintf(",\n DROP KEY `%s`", unique.Definition) - if len(m.Uniques) > index+1 { + sql += fmt.Sprintf("\n DROP KEY `%s`", unique.Definition) + if len(m.Uniques) > index { sql += "," } } for index, column := range m.Renames { - sql += fmt.Sprintf(",\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault) - if len(m.Renames) > index+1 { + sql += fmt.Sprintf("\n CHANGE COLUMN `%s` `%s` %s %s %s %s", column.NewName, column.OldName, column.OldDataType, column.OldUnsign, column.OldNull, column.OldDefault) + if len(m.Renames) > index { sql += "," } } + + for _, foreign := range m.Foreigns { + sql += fmt.Sprintf("\n DROP KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name) + sql += fmt.Sprintf(",\n DROP FOREIGN KEY `%s_%s_foreign`", m.TableName, foreign.Column.Name) + sql += fmt.Sprintf(",\n DROP COLUMN `%s`", foreign.Name) + } sql += ";" } case "delete": diff --git a/migration/migration.go b/migration/migration.go index 2bfa8bd4..d1387ba0 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -89,6 +89,7 @@ func (m *Migration) Down() { } +//Migrate adds the SQL to the execution list func (m *Migration) Migrate(migrationType string) { m.ModifyType = migrationType m.sqls = append(m.sqls, m.GetSQL()) From 83a563c0abdbbd8f74dbf807d4321a1e47bf91f0 Mon Sep 17 00:00:00 2001 From: satng Date: Sun, 9 Jul 2017 12:25:51 +0800 Subject: [PATCH 145/163] =?UTF-8?q?oracle=E6=8F=92=E5=85=A5=E5=8D=A0?= =?UTF-8?q?=E4=BD=8D=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orm/db_oracle.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/orm/db_oracle.go b/orm/db_oracle.go index deca36ad..f5d6aaa2 100644 --- a/orm/db_oracle.go +++ b/orm/db_oracle.go @@ -94,3 +94,43 @@ func (d *dbBaseOracle) IndexExists(db dbQuerier, table string, name string) bool row.Scan(&cnt) return cnt > 0 } + +// execute insert sql with given struct and given values. +// insert the given values, not the field values in struct. +func (d *dbBaseOracle) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []string, values []interface{}) (int64, error) { + Q := d.ins.TableQuote() + + marks := make([]string, len(names)) + for i := range marks { + marks[i] = ":" + names[i] + } + + sep := fmt.Sprintf("%s, %s", Q, Q) + qmarks := strings.Join(marks, ", ") + columns := strings.Join(names, sep) + + multi := len(values) / len(names) + + if isMulti { + qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks + } + + query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s)", Q, mi.table, Q, Q, columns, Q, qmarks) + + d.ins.ReplaceMarks(&query) + + if isMulti || !d.ins.HasReturningID(mi, &query) { + res, err := q.Exec(query, values...) + if err == nil { + if isMulti { + return res.RowsAffected() + } + return res.LastInsertId() + } + return 0, err + } + row := q.QueryRow(query, values...) + var id int64 + err := row.Scan(&id) + return id, err +} From 29bcd31b27c822043b9d1b42f348483975061cd9 Mon Sep 17 00:00:00 2001 From: MiskoLee Date: Mon, 10 Jul 2017 21:27:54 +0800 Subject: [PATCH 146/163] supported gzip for req.Header has `Content-Encoding: gzip` --- context/input.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/context/input.go b/context/input.go index aaf97e85..2c53c601 100644 --- a/context/input.go +++ b/context/input.go @@ -16,6 +16,7 @@ package context import ( "bytes" + "compress/gzip" "errors" "io" "io/ioutil" @@ -350,8 +351,19 @@ func (input *BeegoInput) CopyBody(MaxMemory int64) []byte { if input.Context.Request.Body == nil { return []byte{} } + + var requestbody []byte safe := &io.LimitedReader{R: input.Context.Request.Body, N: MaxMemory} - requestbody, _ := ioutil.ReadAll(safe) + if input.Header("Content-Encoding") == "gzip" { + reader, err := gzip.NewReader(safe) + if err != nil { + return nil + } + requestbody, _ = ioutil.ReadAll(reader) + } else { + requestbody, _ = ioutil.ReadAll(safe) + } + input.Context.Request.Body.Close() bf := bytes.NewBuffer(requestbody) input.Context.Request.Body = http.MaxBytesReader(input.Context.ResponseWriter, ioutil.NopCloser(bf), MaxMemory) From c903de41e404aad3ee5501d2af0c4ce36a0bcee9 Mon Sep 17 00:00:00 2001 From: Gerson Alexander Pardo Gamez Date: Wed, 12 Jul 2017 09:51:37 -0500 Subject: [PATCH 147/163] updated sample for FilterMonitorFunc added pattern to sample --- admin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin.go b/admin.go index e6f9c3f9..0688dcbc 100644 --- a/admin.go +++ b/admin.go @@ -37,7 +37,7 @@ var beeAdminApp *adminApp // FilterMonitorFunc is default monitor filter when admin module is enable. // if this func returns, admin module records qbs for this request by condition of this function logic. // usage: -// func MyFilterMonitor(method, requestPath string, t time.Duration, statusCode int) bool { +// func MyFilterMonitor(method, requestPath string, t time.Duration, pattern string, statusCode int) bool { // if method == "POST" { // return false // } From 7c2ec075a4fb45ca4dd25e7ae7585417d9c99b53 Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Thu, 13 Jul 2017 21:03:30 +0530 Subject: [PATCH 148/163] Update: fixing some methods and adding documentation Signed-off-by: Gnanakeethan Balasubramaniam --- migration/ddl.go | 13 ++++++++++++ migration/doc.go | 48 ++++++++++++++++++++++++++++++++++++++++++ migration/migration.go | 16 +++++++++++++- 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 migration/doc.go diff --git a/migration/ddl.go b/migration/ddl.go index 3b405498..9d4bfc11 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -63,6 +63,19 @@ type RenameColumn struct { Column } +// CreateTable creates the table on system +func (m *Migration) CreateTable(tablename, engine, charset string, p ...func()) { + m.TableName = tablename + m.Engine = engine + m.Charset = charset + m.ModifyType = "create" +} + +func (m *Migration) AlterTable(tablename string) { + m.TableName = tablename + m.ModifyType = "alter" +} + // NewCol creates a new standard column and attaches it to m struct func (m *Migration) NewCol(name string) *Column { col := &Column{Name: name} diff --git a/migration/doc.go b/migration/doc.go new file mode 100644 index 00000000..51050ed2 --- /dev/null +++ b/migration/doc.go @@ -0,0 +1,48 @@ +package migration + +/* Package migration enables you to generate migrations back and forth. It generates both migrations. + + +//Creates a table +m.CreateTable("tablename","InnoDB","utf8"); + + + +//Alter a table +m.AlterTable("tablename") + + + +//Standard Column Methods +* SetDataType +* SetNullable +* SetDefault +* SetUnsigned (use only on integer types unless produces error) + + +//Sets a primary column, multiple calls allowed, standard column methods available +m.PriCol("id").SetAuto(true).SetNullable(false).SetDataType("INT(10)").SetUnsigned(true) + +//UniCol Can be used multiple times, allows standard Column methods. Use same "index" string to add to same index +m.UniCol("index","column") + +//Standard Column Initialisation, can call .Remove() after NewCol("") on alter to remove +m.NewCol("name").SetDataType("VARCHAR(255) COLLATE utf8_unicode_ci").SetNullable(false) +m.NewCol("value").SetDataType("DOUBLE(8,2)").SetNullable(false) + +//Rename Columns , only use with Alter table, doesn't works with Create, prefix standard column methods with "Old" to +//create a true reversible migration eg: SetOldDataType("DOUBLE(12,3)") +m.RenameColumn("from","to")... + + +//Foreign Columns, single columns are only supported, SetOnDelete & SetOnUpdate are available, call appropriately. +//Supports standard column methods, automatic reverse. +m.ForeignCol("local_col","foreign_col","foreign_table") + + + + + + + +*/ diff --git a/migration/migration.go b/migration/migration.go index d1387ba0..ef42c986 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -52,6 +52,7 @@ type Migrationer interface { GetCreated() int64 } +//Migration defines the migrations by either SQL or DDL type Migration struct { sqls []string Created string @@ -82,11 +83,24 @@ func init() { // Up implement in the Inheritance struct for upgrade func (m *Migration) Up() { + switch m.ModifyType { + case "reverse": + m.ModifyType = "alter" + case "delete": + m.ModifyType = "create" + } + m.sqls = append(m.sqls, m.GetSQL()) } // Down implement in the Inheritance struct for down func (m *Migration) Down() { - + switch m.ModifyType { + case "alter": + m.ModifyType = "reverse" + case "create": + m.ModifyType = "delete" + } + m.sqls = append(m.sqls, m.GetSQL()) } //Migrate adds the SQL to the execution list From d453242e4870c06e60baa19ded97470a4d25ef63 Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Fri, 14 Jul 2017 14:42:56 +0530 Subject: [PATCH 149/163] Update: moving package to bottom Signed-off-by: Gnanakeethan Balasubramaniam --- migration/doc.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/migration/doc.go b/migration/doc.go index 51050ed2..2d6ccaf6 100644 --- a/migration/doc.go +++ b/migration/doc.go @@ -1,5 +1,3 @@ -package migration - /* Package migration enables you to generate migrations back and forth. It generates both migrations. @@ -39,10 +37,5 @@ m.RenameColumn("from","to")... //Supports standard column methods, automatic reverse. m.ForeignCol("local_col","foreign_col","foreign_table") - - - - - - */ +package migration From fc86f6422d613b5811b6b07779f506f1eea960da Mon Sep 17 00:00:00 2001 From: "shanfeng.yang" Date: Sat, 15 Jul 2017 17:26:20 +0800 Subject: [PATCH 150/163] sort ControllerComments --- controller.go | 6 ++++++ parser.go | 1 + 2 files changed, 7 insertions(+) diff --git a/controller.go b/controller.go index 510e16b8..da8e8504 100644 --- a/controller.go +++ b/controller.go @@ -55,6 +55,12 @@ type ControllerComments struct { MethodParams []*param.MethodParam } +type ControllerCommentsSlice []ControllerComments + +func (p ControllerCommentsSlice) Len() int { return len(p) } +func (p ControllerCommentsSlice) Less(i, j int) bool { return p[i].Router < p[j].Router } +func (p ControllerCommentsSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + // Controller defines some basic http request handler operations, such as // http context, template and view, session and xsrf. type Controller struct { diff --git a/parser.go b/parser.go index a9cfd894..f9357b99 100644 --- a/parser.go +++ b/parser.go @@ -274,6 +274,7 @@ func genRouterCode(pkgRealpath string) { sort.Strings(sortKey) for _, k := range sortKey { cList := genInfoList[k] + sort.Sort(ControllerCommentsSlice(cList)) for _, c := range cList { allmethod := "nil" if len(c.AllowHTTPMethods) > 0 { From cb38ab4f85bff7c368ea0b85036d14aa9eebf1da Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Sun, 16 Jul 2017 07:10:09 +0530 Subject: [PATCH 151/163] Update: fixing a SQL generation code Signed-off-by: Gnanakeethan Balasubramaniam --- migration/ddl.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/ddl.go b/migration/ddl.go index 9d4bfc11..d7d08f9e 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -151,7 +151,7 @@ func (c *Column) SetAuto(inc bool) *Column { //SetNullable sets the column to be null func (c *Column) SetNullable(null bool) *Column { if null { - c.Null = "DEFAULT NULL" + c.Null = "" } else { c.Null = "NOT NULL" @@ -182,7 +182,7 @@ func (c *Column) SetDataType(dataType string) *Column { //SetOldNullable allows reverting to previous nullable on reverse ms func (c *RenameColumn) SetOldNullable(null bool) *RenameColumn { if null { - c.OldNull = "DEFAULT NULL" + c.OldNull = "" } else { c.OldNull = "NOT NULL" From d58ad2ee3697ff0ccda83d020abc7080d99f0a5e Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Sun, 16 Jul 2017 07:24:58 +0530 Subject: [PATCH 152/163] Update: removing the need to call DDLSpec in the migration file Signed-off-by: Gnanakeethan Balasubramaniam --- migration/migration.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/migration/migration.go b/migration/migration.go index ef42c986..e538eff9 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -80,9 +80,14 @@ func init() { migrationMap = make(map[string]Migrationer) } +//DDLSpec implement in the Inheritance struct for defining structure +func (m *Migration) DDLSpec() { + +} + // Up implement in the Inheritance struct for upgrade func (m *Migration) Up() { - + m.DDLSpec() switch m.ModifyType { case "reverse": m.ModifyType = "alter" From fc55c2b57cc464c2a1e4db2dedcefd65e094f66a Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Sun, 16 Jul 2017 07:30:53 +0530 Subject: [PATCH 153/163] Update: missed to call DDLSpec in Down migration Signed-off-by: Gnanakeethan Balasubramaniam --- migration/migration.go | 1 + 1 file changed, 1 insertion(+) diff --git a/migration/migration.go b/migration/migration.go index e538eff9..c5a9352e 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -99,6 +99,7 @@ func (m *Migration) Up() { // Down implement in the Inheritance struct for down func (m *Migration) Down() { + m.DDLSpec() switch m.ModifyType { case "alter": m.ModifyType = "reverse" From 749a4028b48c87da63c95e7a3b4d57618e82645d Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Sun, 16 Jul 2017 08:11:10 +0530 Subject: [PATCH 154/163] Revert "Update: removing the need to call DDLSpec in the migration file" The odds of getting this perfectly up is not good. This reverts commit d58ad2ee3697ff0ccda83d020abc7080d99f0a5e. --- migration/migration.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/migration/migration.go b/migration/migration.go index c5a9352e..0a140b24 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -80,14 +80,9 @@ func init() { migrationMap = make(map[string]Migrationer) } -//DDLSpec implement in the Inheritance struct for defining structure -func (m *Migration) DDLSpec() { - -} - // Up implement in the Inheritance struct for upgrade func (m *Migration) Up() { - m.DDLSpec() + switch m.ModifyType { case "reverse": m.ModifyType = "alter" From 94e79eddcf0ff616fda140b360033389eda5b809 Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Sun, 16 Jul 2017 08:37:27 +0530 Subject: [PATCH 155/163] Update: removing remnant of revert commit ( a call to function m.DDLSpec() ) Signed-off-by: Gnanakeethan Balasubramaniam --- migration/migration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/migration.go b/migration/migration.go index 0a140b24..51053714 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -94,7 +94,7 @@ func (m *Migration) Up() { // Down implement in the Inheritance struct for down func (m *Migration) Down() { - m.DDLSpec() + switch m.ModifyType { case "alter": m.ModifyType = "reverse" From 8bb0a70847726f36edd1ac8e8ffcd28899e29fb8 Mon Sep 17 00:00:00 2001 From: Gnanakeethan Balasubramaniam Date: Sun, 16 Jul 2017 08:48:44 +0530 Subject: [PATCH 156/163] Update: Fix in SQL Generation Signed-off-by: Gnanakeethan Balasubramaniam --- migration/ddl.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/migration/ddl.go b/migration/ddl.go index d7d08f9e..01cee196 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -301,14 +301,11 @@ func (m *Migration) GetSQL() (sql string) { } sql += fmt.Sprintf(")") } - for index, foreign := range m.Foreigns { + for _, foreign := range m.Foreigns { sql += fmt.Sprintf(",\n `%s` %s %s %s %s %s", foreign.Name, foreign.DataType, foreign.Unsign, foreign.Null, foreign.Inc, foreign.Default) sql += fmt.Sprintf(",\n KEY `%s_%s_foreign`(`%s`),", m.TableName, foreign.Column.Name, foreign.Column.Name) - sql += fmt.Sprintf("\n CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate) + sql += fmt.Sprintf("\n CONSTRAINT `%s_%s_foreign` FOREIGN KEY (`%s`) REFERENCES `%s` (`%s`) %s %s", m.TableName, foreign.Column.Name, foreign.Column.Name, foreign.ForeignTable, foreign.ForeignColumn, foreign.OnDelete, foreign.OnUpdate) - if len(m.Foreigns) > index+1 { - sql += "," - } } sql += fmt.Sprintf(")ENGINE=%s DEFAULT CHARSET=%s;", m.Engine, m.Charset) break From b27ab53017e2682887f8f3815d949eb97725f418 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 18 Jul 2017 23:41:50 +0800 Subject: [PATCH 157/163] fix issue for runMethod and runRouter from context --- router.go | 1 - 1 file changed, 1 deletion(-) diff --git a/router.go b/router.go index 874fb109..e5a4e80d 100644 --- a/router.go +++ b/router.go @@ -704,7 +704,6 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) // User can define RunController and RunMethod in filter if context.Input.RunController != nil && context.Input.RunMethod != "" { findRouter = true - isRunnable = true runMethod = context.Input.RunMethod runRouter = context.Input.RunController } else { From 5ac0cb929c22efa7089ae573bf7764af9acd6ccc Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 18 Jul 2017 23:58:22 +0800 Subject: [PATCH 158/163] v1.9.0 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 218fded8..0512dc60 100644 --- a/beego.go +++ b/beego.go @@ -23,7 +23,7 @@ import ( const ( // VERSION represent beego web framework version. - VERSION = "1.8.3" + VERSION = "1.9.0" // DEV is for develop DEV = "dev" From aa3d6c5363419821f7c175282e8e5476a6fa3cb3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 19 Jul 2017 00:37:42 +0800 Subject: [PATCH 159/163] fix the gosimple --- error_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/error_test.go b/error_test.go index 2fb8f962..378aa953 100644 --- a/error_test.go +++ b/error_test.go @@ -52,7 +52,7 @@ func TestErrorCode_01(t *testing.T) { if w.Code != code { t.Fail() } - if !strings.Contains(string(w.Body.Bytes()), http.StatusText(code)) { + if !strings.Contains(w.Body.String(), http.StatusText(code)) { t.Fail() } } @@ -82,7 +82,7 @@ func TestErrorCode_03(t *testing.T) { if w.Code != 200 { t.Fail() } - if string(w.Body.Bytes()) != parseCodeError { + if w.Body.String() != parseCodeError { t.Fail() } } From 4fc95b0d691f40578766638d54409ad17f15f6d7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 19 Jul 2017 00:52:27 +0800 Subject: [PATCH 160/163] gofmt and golint --- controller.go | 1 + grace/conn.go | 4 +-- migration/ddl.go | 1 + migration/doc.go | 71 ++++++++++++++++++---------------------- validation/util.go | 6 ++-- validation/validators.go | 12 +++---- 6 files changed, 44 insertions(+), 51 deletions(-) diff --git a/controller.go b/controller.go index da8e8504..c104eb2a 100644 --- a/controller.go +++ b/controller.go @@ -55,6 +55,7 @@ type ControllerComments struct { MethodParams []*param.MethodParam } +// ControllerCommentsSlice implements the sort interface type ControllerCommentsSlice []ControllerComments func (p ControllerCommentsSlice) Len() int { return len(p) } diff --git a/grace/conn.go b/grace/conn.go index 6f52045f..e020f850 100644 --- a/grace/conn.go +++ b/grace/conn.go @@ -9,7 +9,7 @@ import ( type graceConn struct { net.Conn server *Server - m sync.Mutex + m sync.Mutex closed bool } @@ -26,7 +26,7 @@ func (c *graceConn) Close() (err error) { } } }() - + c.m.Lock() if c.closed { c.m.Unlock() diff --git a/migration/ddl.go b/migration/ddl.go index 01cee196..cea10355 100644 --- a/migration/ddl.go +++ b/migration/ddl.go @@ -71,6 +71,7 @@ func (m *Migration) CreateTable(tablename, engine, charset string, p ...func()) m.ModifyType = "create" } +// AlterTable set the ModifyType to alter func (m *Migration) AlterTable(tablename string) { m.TableName = tablename m.ModifyType = "alter" diff --git a/migration/doc.go b/migration/doc.go index 2d6ccaf6..0c6564d4 100644 --- a/migration/doc.go +++ b/migration/doc.go @@ -1,41 +1,32 @@ -/* Package migration enables you to generate migrations back and forth. It generates both migrations. - - -//Creates a table -m.CreateTable("tablename","InnoDB","utf8"); - - - -//Alter a table -m.AlterTable("tablename") - - - -//Standard Column Methods -* SetDataType -* SetNullable -* SetDefault -* SetUnsigned (use only on integer types unless produces error) - - -//Sets a primary column, multiple calls allowed, standard column methods available -m.PriCol("id").SetAuto(true).SetNullable(false).SetDataType("INT(10)").SetUnsigned(true) - -//UniCol Can be used multiple times, allows standard Column methods. Use same "index" string to add to same index -m.UniCol("index","column") - -//Standard Column Initialisation, can call .Remove() after NewCol("") on alter to remove -m.NewCol("name").SetDataType("VARCHAR(255) COLLATE utf8_unicode_ci").SetNullable(false) -m.NewCol("value").SetDataType("DOUBLE(8,2)").SetNullable(false) - -//Rename Columns , only use with Alter table, doesn't works with Create, prefix standard column methods with "Old" to -//create a true reversible migration eg: SetOldDataType("DOUBLE(12,3)") -m.RenameColumn("from","to")... - - -//Foreign Columns, single columns are only supported, SetOnDelete & SetOnUpdate are available, call appropriately. -//Supports standard column methods, automatic reverse. -m.ForeignCol("local_col","foreign_col","foreign_table") - -*/ +// Package migration enables you to generate migrations back and forth. It generates both migrations. +// +// //Creates a table +// m.CreateTable("tablename","InnoDB","utf8"); +// +// //Alter a table +// m.AlterTable("tablename") +// +// Standard Column Methods +// * SetDataType +// * SetNullable +// * SetDefault +// * SetUnsigned (use only on integer types unless produces error) +// +// //Sets a primary column, multiple calls allowed, standard column methods available +// m.PriCol("id").SetAuto(true).SetNullable(false).SetDataType("INT(10)").SetUnsigned(true) +// +// //UniCol Can be used multiple times, allows standard Column methods. Use same "index" string to add to same index +// m.UniCol("index","column") +// +// //Standard Column Initialisation, can call .Remove() after NewCol("") on alter to remove +// m.NewCol("name").SetDataType("VARCHAR(255) COLLATE utf8_unicode_ci").SetNullable(false) +// m.NewCol("value").SetDataType("DOUBLE(8,2)").SetNullable(false) +// +// //Rename Columns , only use with Alter table, doesn't works with Create, prefix standard column methods with "Old" to +// //create a true reversible migration eg: SetOldDataType("DOUBLE(12,3)") +// m.RenameColumn("from","to")... +// +// //Foreign Columns, single columns are only supported, SetOnDelete & SetOnUpdate are available, call appropriately. +// //Supports standard column methods, automatic reverse. +// m.ForeignCol("local_col","foreign_col","foreign_table") package migration diff --git a/validation/util.go b/validation/util.go index 4695b776..66fce283 100644 --- a/validation/util.go +++ b/validation/util.go @@ -45,8 +45,8 @@ var ( "Valid": true, "NoMatch": true, } - - Int64On32Err = fmt.Errorf("not support int64 on 32-bit platform") + // ErrInt64On32 show 32 bit platform not support int64 + ErrInt64On32 = fmt.Errorf("not support int64 on 32-bit platform") ) func init() { @@ -255,7 +255,7 @@ func parseParam(t reflect.Type, s string) (i interface{}, err error) { i, err = strconv.Atoi(s) case reflect.Int64: if wordsize == 32 { - return nil, Int64On32Err + return nil, ErrInt64On32 } i, err = strconv.ParseInt(s, 10, 64) case reflect.Int32: diff --git a/validation/validators.go b/validation/validators.go index a9c3e6b8..4dff9c0b 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -25,12 +25,12 @@ import ( // CanSkipFuncs will skip valid if RequiredFirst is true and the struct field's value is empty var CanSkipFuncs = map[string]struct{}{ - "Email": struct{}{}, - "IP": struct{}{}, - "Mobile": struct{}{}, - "Tel": struct{}{}, - "Phone": struct{}{}, - "ZipCode": struct{}{}, + "Email": {}, + "IP": {}, + "Mobile": {}, + "Tel": {}, + "Phone": {}, + "ZipCode": {}, } // MessageTmpls store commond validate template From c8c25549e73c20788ed9fe222cd5fd6b8bf016d7 Mon Sep 17 00:00:00 2001 From: wangguoliang Date: Thu, 7 Sep 2017 19:01:34 +0800 Subject: [PATCH 161/163] should use time.Since instead of time.Now().Sub Signed-off-by: wgliang --- cache/memory.go | 2 +- context/param/options.go | 2 +- context/param/parsers_test.go | 2 +- orm/orm_log.go | 2 +- parser.go | 2 +- toolbox/profile.go | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cache/memory.go b/cache/memory.go index 57e868cf..784dbecb 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -38,7 +38,7 @@ func (mi *MemoryItem) isExpire() bool { if mi.lifespan == 0 { return false } - return time.Now().Sub(mi.createdTime) > mi.lifespan + return time.Since(mi.createdTime) > mi.lifespan } // MemoryCache is Memory cache adapter. diff --git a/context/param/options.go b/context/param/options.go index 58bdc3d0..3d5ba013 100644 --- a/context/param/options.go +++ b/context/param/options.go @@ -7,7 +7,7 @@ import ( // MethodParamOption defines a func which apply options on a MethodParam type MethodParamOption func(*MethodParam) -// IsRequired indicates that this param is required and can not be ommited from the http request +// IsRequired indicates that this param is required and can not be omitted from the http request var IsRequired MethodParamOption = func(p *MethodParam) { p.required = true } diff --git a/context/param/parsers_test.go b/context/param/parsers_test.go index b946ba08..7065a28e 100644 --- a/context/param/parsers_test.go +++ b/context/param/parsers_test.go @@ -75,7 +75,7 @@ func checkParser(def testDefinition, t *testing.T, methodParam ...MethodParam) { } convResult, err := safeConvert(reflect.ValueOf(result), toType) if err != nil { - t.Errorf("Convertion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) + t.Errorf("Conversion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) return } if !reflect.DeepEqual(convResult.Interface(), def.expectedValue) { diff --git a/orm/orm_log.go b/orm/orm_log.go index 26c73f9e..915bfd2d 100644 --- a/orm/orm_log.go +++ b/orm/orm_log.go @@ -36,7 +36,7 @@ func NewLog(out io.Writer) *Log { } func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error, args ...interface{}) { - sub := time.Now().Sub(t) / 1e5 + sub := time.Since(t) / 1e5 elsp := float64(int(sub)) / 10.0 flag := " OK" if err != nil { diff --git a/parser.go b/parser.go index f787fd5c..1933c6c6 100644 --- a/parser.go +++ b/parser.go @@ -227,7 +227,7 @@ func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) { } // direct copy from bee\g_docs.go -// analisys params return []string +// analysis params return []string // @Param query form string true "The email for login" // [query form string true "The email for login"] func getparams(str string) []string { diff --git a/toolbox/profile.go b/toolbox/profile.go index 06e40ede..944ec645 100644 --- a/toolbox/profile.go +++ b/toolbox/profile.go @@ -106,7 +106,7 @@ func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) { if gcstats.NumGC > 0 { lastPause := gcstats.Pause[0] - elapsed := time.Now().Sub(startTime) + elapsed := time.Since(startTime) overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() @@ -123,7 +123,7 @@ func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) { toS(gcstats.PauseQuantiles[99])) } else { // while GC has disabled - elapsed := time.Now().Sub(startTime) + elapsed := time.Since(startTime) allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() fmt.Fprintf(w, "Alloc:%s Sys:%s Alloc(Rate):%s/s\n", From a7354d2d084003e4122d6e69f7e5ab594fd117b2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 9 Sep 2017 06:29:38 +0800 Subject: [PATCH 162/163] Revert "should use time.Since instead of time.Now().Sub" --- cache/memory.go | 2 +- context/param/options.go | 2 +- context/param/parsers_test.go | 2 +- orm/orm_log.go | 2 +- parser.go | 2 +- toolbox/profile.go | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cache/memory.go b/cache/memory.go index 784dbecb..57e868cf 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -38,7 +38,7 @@ func (mi *MemoryItem) isExpire() bool { if mi.lifespan == 0 { return false } - return time.Since(mi.createdTime) > mi.lifespan + return time.Now().Sub(mi.createdTime) > mi.lifespan } // MemoryCache is Memory cache adapter. diff --git a/context/param/options.go b/context/param/options.go index 3d5ba013..58bdc3d0 100644 --- a/context/param/options.go +++ b/context/param/options.go @@ -7,7 +7,7 @@ import ( // MethodParamOption defines a func which apply options on a MethodParam type MethodParamOption func(*MethodParam) -// IsRequired indicates that this param is required and can not be omitted from the http request +// IsRequired indicates that this param is required and can not be ommited from the http request var IsRequired MethodParamOption = func(p *MethodParam) { p.required = true } diff --git a/context/param/parsers_test.go b/context/param/parsers_test.go index 7065a28e..b946ba08 100644 --- a/context/param/parsers_test.go +++ b/context/param/parsers_test.go @@ -75,7 +75,7 @@ func checkParser(def testDefinition, t *testing.T, methodParam ...MethodParam) { } convResult, err := safeConvert(reflect.ValueOf(result), toType) if err != nil { - t.Errorf("Conversion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) + t.Errorf("Convertion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) return } if !reflect.DeepEqual(convResult.Interface(), def.expectedValue) { diff --git a/orm/orm_log.go b/orm/orm_log.go index 915bfd2d..26c73f9e 100644 --- a/orm/orm_log.go +++ b/orm/orm_log.go @@ -36,7 +36,7 @@ func NewLog(out io.Writer) *Log { } func debugLogQueies(alias *alias, operaton, query string, t time.Time, err error, args ...interface{}) { - sub := time.Since(t) / 1e5 + sub := time.Now().Sub(t) / 1e5 elsp := float64(int(sub)) / 10.0 flag := " OK" if err != nil { diff --git a/parser.go b/parser.go index 1933c6c6..f787fd5c 100644 --- a/parser.go +++ b/parser.go @@ -227,7 +227,7 @@ func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) { } // direct copy from bee\g_docs.go -// analysis params return []string +// analisys params return []string // @Param query form string true "The email for login" // [query form string true "The email for login"] func getparams(str string) []string { diff --git a/toolbox/profile.go b/toolbox/profile.go index 944ec645..06e40ede 100644 --- a/toolbox/profile.go +++ b/toolbox/profile.go @@ -106,7 +106,7 @@ func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) { if gcstats.NumGC > 0 { lastPause := gcstats.Pause[0] - elapsed := time.Since(startTime) + elapsed := time.Now().Sub(startTime) overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() @@ -123,7 +123,7 @@ func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats, w io.Writer) { toS(gcstats.PauseQuantiles[99])) } else { // while GC has disabled - elapsed := time.Since(startTime) + elapsed := time.Now().Sub(startTime) allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() fmt.Fprintf(w, "Alloc:%s Sys:%s Alloc(Rate):%s/s\n", From 9b5756696360c2e2f74c1d8b092e236fee6a739a Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Wed, 11 Oct 2017 14:35:31 +0800 Subject: [PATCH 163/163] fix typo --- context/param/options.go | 2 +- context/param/parsers_test.go | 2 +- parser.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/context/param/options.go b/context/param/options.go index 58bdc3d0..3d5ba013 100644 --- a/context/param/options.go +++ b/context/param/options.go @@ -7,7 +7,7 @@ import ( // MethodParamOption defines a func which apply options on a MethodParam type MethodParamOption func(*MethodParam) -// IsRequired indicates that this param is required and can not be ommited from the http request +// IsRequired indicates that this param is required and can not be omitted from the http request var IsRequired MethodParamOption = func(p *MethodParam) { p.required = true } diff --git a/context/param/parsers_test.go b/context/param/parsers_test.go index b946ba08..7065a28e 100644 --- a/context/param/parsers_test.go +++ b/context/param/parsers_test.go @@ -75,7 +75,7 @@ func checkParser(def testDefinition, t *testing.T, methodParam ...MethodParam) { } convResult, err := safeConvert(reflect.ValueOf(result), toType) if err != nil { - t.Errorf("Convertion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) + t.Errorf("Conversion error for %v. from value: %v, toType: %v, error: %v", def.strValue, result, toType, err) return } if !reflect.DeepEqual(convResult.Interface(), def.expectedValue) { diff --git a/parser.go b/parser.go index f787fd5c..1933c6c6 100644 --- a/parser.go +++ b/parser.go @@ -227,7 +227,7 @@ func parseComment(lines []*ast.Comment) (pc *parsedComment, err error) { } // direct copy from bee\g_docs.go -// analisys params return []string +// analysis params return []string // @Param query form string true "The email for login" // [query form string true "The email for login"] func getparams(str string) []string {