mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-25 14:46:02 +03:00
Merge pull request #10 from Neur0toxine/master
updated gin to v1.5.0, several other changes
This commit is contained in:
commit
f42723ab28
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 RetailDriver LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -2,6 +2,8 @@
|
||||
[![Build Status](https://travis-ci.org/retailcrm/mg-transport-core.svg?branch=master)](https://travis-ci.org/retailcrm/mg-transport-core)
|
||||
[![codecov](https://codecov.io/gh/retailcrm/mg-transport-core/branch/master/graph/badge.svg)](https://codecov.io/gh/retailcrm/mg-transport-core)
|
||||
[![GoDoc](https://godoc.org/github.com/retailcrm/mg-transport-core/core?status.svg)](https://godoc.org/github.com/retailcrm/mg-transport-core/core)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/retailcrm/mg-transport-core)](https://goreportcard.com/report/github.com/retailcrm/mg-transport-core)
|
||||
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/retailcrm/mg-transport-core/blob/master/LICENSE.md)
|
||||
This library provides different functions like error-reporting, logging, localization, etc. in order to make it easier to create transports.
|
||||
Usage:
|
||||
```go
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
)
|
||||
|
||||
// Options for tool command
|
||||
type Options struct{}
|
||||
|
||||
var (
|
||||
|
@ -16,7 +16,8 @@ var (
|
||||
"/api/integration-modules/{code}/edit",
|
||||
}
|
||||
markdownSymbols = []string{"*", "_", "`", "["}
|
||||
regCommandName = regexp.MustCompile(`^https://?[\da-z.-]+\.(retailcrm\.(ru|pro|es)|ecomlogic\.com|simlachat\.(com|ru))/?$`)
|
||||
regCommandName = regexp.MustCompile(
|
||||
`^https://?[\da-z.-]+\.(retailcrm\.(ru|pro|es)|ecomlogic\.com|simlachat\.(com|ru))/?$`)
|
||||
slashRegex = regexp.MustCompile(`/+$`)
|
||||
)
|
||||
|
||||
@ -85,7 +86,7 @@ type DatabaseConfig struct {
|
||||
// HTTPClientConfig struct
|
||||
type HTTPClientConfig struct {
|
||||
Timeout time.Duration `yaml:"timeout"`
|
||||
SSLVerification bool `yaml:"ssl_verification"`
|
||||
SSLVerification *bool `yaml:"ssl_verification"`
|
||||
MockAddress string `yaml:"mock_address"`
|
||||
MockedDomains []string `yaml:"mocked_domains"`
|
||||
}
|
||||
@ -198,3 +199,12 @@ func (t Info) GetCode() string {
|
||||
func (t Info) GetLogoPath() string {
|
||||
return t.LogoPath
|
||||
}
|
||||
|
||||
// IsSSLVerificationEnabled returns SSL verification flag (default is true)
|
||||
func (h *HTTPClientConfig) IsSSLVerificationEnabled() bool {
|
||||
if h.SSLVerification == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return *h.SSLVerification
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ log_level: 5
|
||||
debug: true
|
||||
update_interval: 24
|
||||
|
||||
http_client:
|
||||
ssl_verification: false
|
||||
timeout: 30
|
||||
|
||||
config_aws:
|
||||
access_key_id: key
|
||||
secret_access_key: secret
|
||||
|
87
core/csrf.go
87
core/csrf.go
@ -2,6 +2,7 @@ package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
// nolint:gosec
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
@ -14,8 +15,31 @@ import (
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
// CSRFErrorReason is a error reason type
|
||||
type CSRFErrorReason uint8
|
||||
|
||||
// CSRFTokenGetter func type
|
||||
type CSRFTokenGetter func(c *gin.Context) string
|
||||
type CSRFTokenGetter func(*gin.Context) string
|
||||
|
||||
// CSRFAbortFunc is a callback which
|
||||
type CSRFAbortFunc func(*gin.Context, CSRFErrorReason)
|
||||
|
||||
const (
|
||||
// CSRFErrorNoTokenInSession will be returned if token is not present in session
|
||||
CSRFErrorNoTokenInSession CSRFErrorReason = iota
|
||||
|
||||
// CSRFErrorCannotStoreTokenInSession will be returned if middleware cannot store token in session
|
||||
CSRFErrorCannotStoreTokenInSession
|
||||
|
||||
// CSRFErrorIncorrectTokenType will be returned if data type of token in session is not string
|
||||
CSRFErrorIncorrectTokenType
|
||||
|
||||
// CSRFErrorEmptyToken will be returned if token in session is empty
|
||||
CSRFErrorEmptyToken
|
||||
|
||||
// CSRFErrorTokenMismatch will be returned in case of invalid token
|
||||
CSRFErrorTokenMismatch
|
||||
)
|
||||
|
||||
// DefaultCSRFTokenGetter default getter
|
||||
var DefaultCSRFTokenGetter = func(c *gin.Context) string {
|
||||
@ -44,14 +68,14 @@ var DefaultCSRFTokenGetter = func(c *gin.Context) string {
|
||||
// DefaultIgnoredMethods ignored methods for CSRF verifier middleware
|
||||
var DefaultIgnoredMethods = []string{"GET", "HEAD", "OPTIONS"}
|
||||
|
||||
// CSRF struct. Provides CSRF token verification.
|
||||
type CSRF struct {
|
||||
salt string
|
||||
secret string
|
||||
sessionName string
|
||||
abortFunc gin.HandlerFunc
|
||||
abortFunc CSRFAbortFunc
|
||||
csrfTokenGetter CSRFTokenGetter
|
||||
store sessions.Store
|
||||
locale *Localizer
|
||||
}
|
||||
|
||||
// NewCSRF creates CSRF struct with specified configuration and session store.
|
||||
@ -59,7 +83,7 @@ type CSRF struct {
|
||||
// Salt must be different every time (pass empty salt to use random), secret must be provided, sessionName is optional - pass empty to use default,
|
||||
// store will be used to store sessions, abortFunc will be called to return error if token is invalid, csrfTokenGetter will be used to obtain token.
|
||||
// Usage (with random salt):
|
||||
// core.NewCSRF("", "super secret", "csrf_session", store, func (c *gin.Context) {
|
||||
// core.NewCSRF("", "super secret", "csrf_session", store, func (c *gin.Context, reason core.CSRFErrorReason) {
|
||||
// c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid CSRF token"})
|
||||
// }, core.DefaultCSRFTokenGetter)
|
||||
// Note for csrfTokenGetter: if you want to read token from request body (for example, from form field) - don't forget to restore Body data!
|
||||
@ -69,7 +93,8 @@ type CSRF struct {
|
||||
// }
|
||||
// will close body - and all next middlewares won't be able to read body at all!
|
||||
// Use DefaultCSRFTokenGetter as example to implement your own token getter.
|
||||
func NewCSRF(salt, secret, sessionName string, store sessions.Store, abortFunc gin.HandlerFunc, csrfTokenGetter CSRFTokenGetter) *CSRF {
|
||||
// CSRFErrorReason will be passed to abortFunc and can be used for better error messages.
|
||||
func NewCSRF(salt, secret, sessionName string, store sessions.Store, abortFunc CSRFAbortFunc, csrfTokenGetter CSRFTokenGetter) *CSRF {
|
||||
if store == nil {
|
||||
panic("store must not be nil")
|
||||
}
|
||||
@ -115,8 +140,12 @@ func (x *CSRF) strInSlice(slice []string, v string) bool {
|
||||
|
||||
// generateCSRFToken generates new CSRF token
|
||||
func (x *CSRF) generateCSRFToken() string {
|
||||
// nolint:gosec
|
||||
h := sha1.New()
|
||||
io.WriteString(h, x.salt+"#"+x.secret)
|
||||
// Fallback to less secure method - token must be always filled even if we cannot properly generate it
|
||||
if _, err := io.WriteString(h, x.salt+"#"+x.secret); err != nil {
|
||||
return base64.URLEncoding.EncodeToString([]byte(time.Now().String()))
|
||||
}
|
||||
hash := base64.URLEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
return hash
|
||||
@ -155,12 +184,10 @@ func (x *CSRF) CSRFFromContext(c *gin.Context) string {
|
||||
if i, ok := c.Get("csrf_token"); ok {
|
||||
if token, ok := i.(string); ok {
|
||||
return token
|
||||
} else {
|
||||
return x.generateCSRFToken()
|
||||
}
|
||||
} else {
|
||||
return x.generateCSRFToken()
|
||||
}
|
||||
|
||||
return x.generateCSRFToken()
|
||||
}
|
||||
|
||||
// GenerateCSRFMiddleware returns gin.HandlerFunc which will generate CSRF token
|
||||
@ -175,14 +202,14 @@ func (x *CSRF) GenerateCSRFMiddleware() gin.HandlerFunc {
|
||||
if i, ok := session.Values["csrf_token"]; ok {
|
||||
if i, ok := i.(string); !ok || i == "" {
|
||||
if x.fillToken(session, c) != nil {
|
||||
x.abortFunc(c)
|
||||
x.abortFunc(c, CSRFErrorCannotStoreTokenInSession)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if x.fillToken(session, c) != nil {
|
||||
x.abortFunc(c)
|
||||
x.abortFunc(c, CSRFErrorCannotStoreTokenInSession)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
@ -211,23 +238,47 @@ func (x *CSRF) VerifyCSRFMiddleware(ignoredMethods []string) gin.HandlerFunc {
|
||||
session, _ := x.store.Get(c.Request, x.sessionName)
|
||||
|
||||
if i, ok := session.Values["csrf_token"]; ok {
|
||||
if i, ok := i.(string); !ok || i == "" {
|
||||
x.abortFunc(c)
|
||||
var v string
|
||||
if v, ok = i.(string); !ok || v == "" {
|
||||
if !ok {
|
||||
x.abortFunc(c, CSRFErrorIncorrectTokenType)
|
||||
} else if v == "" {
|
||||
x.abortFunc(c, CSRFErrorEmptyToken)
|
||||
}
|
||||
|
||||
c.Abort()
|
||||
return
|
||||
} else {
|
||||
token = i
|
||||
}
|
||||
|
||||
token = v
|
||||
} else {
|
||||
x.abortFunc(c)
|
||||
x.abortFunc(c, CSRFErrorNoTokenInSession)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if x.csrfTokenGetter(c) != token {
|
||||
x.abortFunc(c)
|
||||
x.abortFunc(c, CSRFErrorTokenMismatch)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetCSRFErrorMessage returns generic error message for CSRFErrorReason in English (useful for logs)
|
||||
func GetCSRFErrorMessage(r CSRFErrorReason) string {
|
||||
switch r {
|
||||
case CSRFErrorNoTokenInSession:
|
||||
return "token is not present in session"
|
||||
case CSRFErrorCannotStoreTokenInSession:
|
||||
return "cannot store token in session"
|
||||
case CSRFErrorIncorrectTokenType:
|
||||
return "incorrect token type"
|
||||
case CSRFErrorEmptyToken:
|
||||
return "empty token present in session"
|
||||
case CSRFErrorTokenMismatch:
|
||||
return "token mismatch"
|
||||
default:
|
||||
return "unknown error"
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ func TestCSRF_NewCSRF_NilStore(t *testing.T) {
|
||||
assert.NotNil(t, recover())
|
||||
}()
|
||||
|
||||
NewCSRF("salt", "secret", "csrf", nil, func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
NewCSRF("salt", "secret", "csrf", nil, func(c *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
}
|
||||
|
||||
func TestCSRF_NewCSRF_EmptySecret(t *testing.T) {
|
||||
@ -110,23 +110,38 @@ func TestCSRF_NewCSRF_EmptySecret(t *testing.T) {
|
||||
}()
|
||||
|
||||
store := sessions.NewCookieStore([]byte("keys"))
|
||||
NewCSRF("salt", "", "csrf", store, func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
NewCSRF("salt", "", "csrf", store, func(c *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
}
|
||||
|
||||
func TestCSRF_NewCSRF_SaltAndSessionNotEmpty(t *testing.T) {
|
||||
store := sessions.NewCookieStore([]byte("keys"))
|
||||
csrf := NewCSRF("salt", "secret", "", store, func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
csrf := NewCSRF("salt", "secret", "", store, func(c *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
assert.NotEmpty(t, csrf.salt)
|
||||
assert.NotEmpty(t, csrf.sessionName)
|
||||
}
|
||||
|
||||
func TestCSRF_GetCSRFErrorMessage(t *testing.T) {
|
||||
items := map[CSRFErrorReason]string{
|
||||
CSRFErrorNoTokenInSession: "token is not present in session",
|
||||
CSRFErrorCannotStoreTokenInSession: "cannot store token in session",
|
||||
CSRFErrorIncorrectTokenType: "incorrect token type",
|
||||
CSRFErrorEmptyToken: "empty token present in session",
|
||||
CSRFErrorTokenMismatch: "token mismatch",
|
||||
99: "unknown error",
|
||||
}
|
||||
|
||||
for reason, message := range items {
|
||||
assert.Equal(t, message, GetCSRFErrorMessage(reason))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCSRF_Suite(t *testing.T) {
|
||||
suite.Run(t, new(CSRFTest))
|
||||
}
|
||||
|
||||
func (x *CSRFTest) SetupSuite() {
|
||||
store := sessions.NewCookieStore([]byte("keys"))
|
||||
x.csrf = NewCSRF("salt", "secret", "", store, func(context *gin.Context) {
|
||||
x.csrf = NewCSRF("salt", "secret", "", store, func(context *gin.Context, r CSRFErrorReason) {
|
||||
context.AbortWithStatus(900)
|
||||
}, DefaultCSRFTokenGetter)
|
||||
}
|
||||
|
97
core/doc.go
Normal file
97
core/doc.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2019 RetailDriver LLC
|
||||
// Use of this source code is governed by a MIT
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package core provides different functions like error-reporting, logging, localization, etc. in order to make it easier to create transports.
|
||||
Usage:
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := core.New()
|
||||
app.Config = core.NewConfig("config.yml")
|
||||
app.DefaultError = "unknown_error"
|
||||
app.TranslationsPath = "./translations"
|
||||
|
||||
app.ConfigureRouter(func(engine *gin.Engine) {
|
||||
engine.Static("/static", "./static")
|
||||
engine.HTMLRender = app.CreateRenderer(
|
||||
func(renderer *core.Renderer) {
|
||||
// insert templates here. Example:
|
||||
r.Push("home", "templates/layout.html", "templates/home.html")
|
||||
},
|
||||
template.FuncMap{},
|
||||
)
|
||||
})
|
||||
|
||||
if err := app.Prepare().Run(); err != nil {
|
||||
fmt.Printf("Fatal error: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
Resource embedding
|
||||
|
||||
packr can be used to provide resource embedding, see:
|
||||
https://github.com/gobuffalo/packr/tree/master/v2
|
||||
In order to use packr you must follow instruction, and provide boxes with templates, translations and assets to library.
|
||||
You can find instruction here:
|
||||
https://github.com/gobuffalo/packr/tree/master/v2#library-installation
|
||||
Example of usage:
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"html/template"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
)
|
||||
|
||||
func main() {
|
||||
static := packr.New("assets", "./static")
|
||||
templates := packr.New("templates", "./templates")
|
||||
translations := packr.New("translations", "./translate")
|
||||
|
||||
app := core.New()
|
||||
app.Config = core.NewConfig("config.yml")
|
||||
app.DefaultError = "unknown_error"
|
||||
app.TranslationsBox = translations
|
||||
|
||||
app.ConfigureRouter(func(engine *gin.Engine) {
|
||||
engine.StaticFS("/static", static)
|
||||
engine.HTMLRender = app.CreateRendererFS(
|
||||
templates,
|
||||
func(renderer *core.Renderer) {
|
||||
// insert templates here. Example:
|
||||
r.Push("home", "layout.html", "home.html")
|
||||
},
|
||||
template.FuncMap{},
|
||||
)
|
||||
})
|
||||
|
||||
if err := app.Prepare().Run(); err != nil {
|
||||
fmt.Printf("Fatal error: %s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
Migration generator
|
||||
|
||||
This library contains helper tool for transports. You can install it via go:
|
||||
$ go get -u github.com/retailcrm/mg-transport-core/cmd/transport-core-tool
|
||||
Currently, it only can generate new migrations for your transport.
|
||||
*/
|
||||
package core
|
@ -92,7 +92,7 @@ func (e *Engine) Prepare() *Engine {
|
||||
return e
|
||||
}
|
||||
|
||||
// templateFuncMap combines func map for templates
|
||||
// TemplateFuncMap combines func map for templates
|
||||
func (e *Engine) TemplateFuncMap(functions template.FuncMap) template.FuncMap {
|
||||
funcMap := e.LocalizationFuncMap()
|
||||
|
||||
@ -100,6 +100,10 @@ func (e *Engine) TemplateFuncMap(functions template.FuncMap) template.FuncMap {
|
||||
funcMap[name] = fn
|
||||
}
|
||||
|
||||
funcMap["version"] = func() string {
|
||||
return e.Config.GetVersion()
|
||||
}
|
||||
|
||||
return funcMap
|
||||
}
|
||||
|
||||
@ -133,7 +137,12 @@ func (e *Engine) Router() *gin.Engine {
|
||||
// BuildHTTPClient builds HTTP client with provided configuration
|
||||
func (e *Engine) BuildHTTPClient(replaceDefault ...bool) *Engine {
|
||||
if e.Config.GetHTTPClientConfig() != nil {
|
||||
if client, err := NewHTTPClientBuilder().FromEngine(e).Build(replaceDefault...); err != nil {
|
||||
client, err := NewHTTPClientBuilder().
|
||||
WithLogger(e.Logger).
|
||||
SetLogging(e.Config.IsDebug()).
|
||||
FromEngine(e).Build(replaceDefault...)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
e.httpClient = client
|
||||
@ -156,9 +165,9 @@ func (e *Engine) SetHTTPClient(client *http.Client) *Engine {
|
||||
func (e *Engine) HTTPClient() *http.Client {
|
||||
if e.httpClient == nil {
|
||||
return http.DefaultClient
|
||||
} else {
|
||||
return e.httpClient
|
||||
}
|
||||
|
||||
return e.httpClient
|
||||
}
|
||||
|
||||
// WithCookieSessions generates new CookieStore with optional key length.
|
||||
@ -174,7 +183,7 @@ func (e *Engine) WithCookieSessions(keyLength ...int) *Engine {
|
||||
return e
|
||||
}
|
||||
|
||||
// WithCookieSessions generates new FilesystemStore with optional key length.
|
||||
// WithFilesystemSessions generates new FilesystemStore with optional key length.
|
||||
// Default key length is 32 bytes.
|
||||
func (e *Engine) WithFilesystemSessions(path string, keyLength ...int) *Engine {
|
||||
length := 32
|
||||
@ -190,7 +199,7 @@ func (e *Engine) WithFilesystemSessions(path string, keyLength ...int) *Engine {
|
||||
// InitCSRF initializes CSRF middleware. engine.Sessions must be already initialized,
|
||||
// use engine.WithCookieStore or engine.WithFilesystemStore for that.
|
||||
// Syntax is similar to core.NewCSRF, but you shouldn't pass sessionName, store and salt.
|
||||
func (e *Engine) InitCSRF(secret string, abortFunc gin.HandlerFunc, getter CSRFTokenGetter) *Engine {
|
||||
func (e *Engine) InitCSRF(secret string, abortFunc CSRFAbortFunc, getter CSRFTokenGetter) *Engine {
|
||||
if e.Sessions == nil {
|
||||
panic("engine.Sessions must be initialized first")
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ func (e *EngineTest) Test_BuildHTTPClient() {
|
||||
e.engine.Config = &Config{
|
||||
HTTPClientConfig: &HTTPClientConfig{
|
||||
Timeout: 30,
|
||||
SSLVerification: true,
|
||||
SSLVerification: boolPtr(true),
|
||||
},
|
||||
}
|
||||
e.engine.BuildHTTPClient()
|
||||
@ -204,7 +204,7 @@ func (e *EngineTest) Test_InitCSRF_Fail() {
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.Sessions = nil
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
assert.Nil(e.T(), e.engine.csrf)
|
||||
}
|
||||
|
||||
@ -215,7 +215,7 @@ func (e *EngineTest) Test_InitCSRF() {
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
assert.NotNil(e.T(), e.engine.csrf)
|
||||
}
|
||||
|
||||
@ -235,7 +235,7 @@ func (e *EngineTest) Test_VerifyCSRFMiddleware() {
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.VerifyCSRFMiddleware(DefaultIgnoredMethods)
|
||||
}
|
||||
|
||||
@ -255,7 +255,7 @@ func (e *EngineTest) Test_GenerateCSRFMiddleware() {
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.GenerateCSRFMiddleware()
|
||||
}
|
||||
|
||||
@ -284,7 +284,7 @@ func (e *EngineTest) Test_GetCSRFToken() {
|
||||
|
||||
e.engine.csrf = nil
|
||||
e.engine.WithCookieSessions(4)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context) {}, DefaultCSRFTokenGetter)
|
||||
e.engine.InitCSRF("test", func(context *gin.Context, r CSRFErrorReason) {}, DefaultCSRFTokenGetter)
|
||||
assert.NotEmpty(e.T(), e.engine.GetCSRFToken(c))
|
||||
assert.Equal(e.T(), "token", e.engine.GetCSRFToken(c))
|
||||
}
|
||||
@ -300,3 +300,8 @@ func (e *EngineTest) Test_Run_Fail() {
|
||||
func TestEngine_Suite(t *testing.T) {
|
||||
suite.Run(t, new(EngineTest))
|
||||
}
|
||||
|
||||
func boolPtr(val bool) *bool {
|
||||
b := val
|
||||
return &b
|
||||
}
|
||||
|
@ -8,13 +8,15 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultClient = http.DefaultClient
|
||||
DefaultTransport = http.DefaultTransport
|
||||
)
|
||||
// DefaultClient stores original http.DefaultClient
|
||||
var DefaultClient = http.DefaultClient
|
||||
|
||||
// DefaultTransport stores original http.DefaultTransport
|
||||
var DefaultTransport = http.DefaultTransport
|
||||
|
||||
// HTTPClientBuilder builds http client with mocks (if necessary) and timeout.
|
||||
// Example:
|
||||
@ -44,7 +46,7 @@ type HTTPClientBuilder struct {
|
||||
httpClient *http.Client
|
||||
httpTransport *http.Transport
|
||||
dialer *net.Dialer
|
||||
engine *Engine
|
||||
logger *logging.Logger
|
||||
built bool
|
||||
logging bool
|
||||
timeout time.Duration
|
||||
@ -67,6 +69,15 @@ func NewHTTPClientBuilder() *HTTPClientBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger sets provided logger into HTTPClientBuilder
|
||||
func (b *HTTPClientBuilder) WithLogger(logger *logging.Logger) *HTTPClientBuilder {
|
||||
if logger != nil {
|
||||
b.logger = logger
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// SetTimeout sets timeout for http client
|
||||
func (b *HTTPClientBuilder) SetTimeout(timeout time.Duration) *HTTPClientBuilder {
|
||||
timeout = timeout * time.Second
|
||||
@ -104,9 +115,9 @@ func (b *HTTPClientBuilder) SetSSLVerification(enabled bool) *HTTPClientBuilder
|
||||
return b
|
||||
}
|
||||
|
||||
// EnableLogging enables logging in mocks
|
||||
func (b *HTTPClientBuilder) EnableLogging() *HTTPClientBuilder {
|
||||
b.logging = true
|
||||
// SetLogging enables or disables logging in mocks
|
||||
func (b *HTTPClientBuilder) SetLogging(flag bool) *HTTPClientBuilder {
|
||||
b.logging = flag
|
||||
return b
|
||||
}
|
||||
|
||||
@ -125,15 +136,13 @@ func (b *HTTPClientBuilder) FromConfig(config *HTTPClientConfig) *HTTPClientBuil
|
||||
b.SetTimeout(config.Timeout)
|
||||
}
|
||||
|
||||
b.SetSSLVerification(config.SSLVerification)
|
||||
b.SetSSLVerification(config.IsSSLVerificationEnabled())
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// FromEngine fulfills mock configuration from ConfigInterface inside Engine
|
||||
func (b *HTTPClientBuilder) FromEngine(engine *Engine) *HTTPClientBuilder {
|
||||
b.engine = engine
|
||||
b.logging = engine.Config.IsDebug()
|
||||
return b.FromConfig(engine.Config.GetHTTPClientConfig())
|
||||
}
|
||||
|
||||
@ -156,10 +165,11 @@ func (b *HTTPClientBuilder) parseAddress() error {
|
||||
if host, port, err := net.SplitHostPort(b.mockAddress); err == nil {
|
||||
b.mockHost = host
|
||||
b.mockPort = port
|
||||
return nil
|
||||
} else {
|
||||
return errors.Errorf("cannot split host and port: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildMocks builds mocks for http client
|
||||
@ -177,9 +187,15 @@ func (b *HTTPClientBuilder) buildMocks() error {
|
||||
}
|
||||
|
||||
b.httpTransport.DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) {
|
||||
if host, port, err := net.SplitHostPort(addr); err != nil {
|
||||
var (
|
||||
host string
|
||||
port string
|
||||
err error
|
||||
)
|
||||
if host, port, err = net.SplitHostPort(addr); err != nil {
|
||||
return b.dialer.DialContext(ctx, network, addr)
|
||||
} else {
|
||||
}
|
||||
|
||||
for _, mock := range b.mockedDomains {
|
||||
if mock == host {
|
||||
oldAddr := addr
|
||||
@ -193,7 +209,6 @@ func (b *HTTPClientBuilder) buildMocks() error {
|
||||
b.logf("Mocking \"%s\" with \"%s\"\n", oldAddr, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.dialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
@ -205,8 +220,8 @@ func (b *HTTPClientBuilder) buildMocks() error {
|
||||
// logf prints logs via Engine or via fmt.Printf
|
||||
func (b *HTTPClientBuilder) logf(format string, args ...interface{}) {
|
||||
if b.logging {
|
||||
if b.engine != nil && b.engine.Logger != nil {
|
||||
b.engine.Logger.Infof(format, args...)
|
||||
if b.logger != nil {
|
||||
b.logger.Infof(format, args...)
|
||||
} else {
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
@ -1,11 +1,21 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
@ -25,6 +35,14 @@ func (t *HTTPClientBuilderTest) Test_SetTimeout() {
|
||||
assert.Equal(t.T(), 90*time.Second, t.builder.httpClient.Timeout)
|
||||
}
|
||||
|
||||
func (t *HTTPClientBuilderTest) Test_SetLogging() {
|
||||
t.builder.SetLogging(true)
|
||||
assert.True(t.T(), t.builder.logging)
|
||||
|
||||
t.builder.SetLogging(false)
|
||||
assert.False(t.T(), t.builder.logging)
|
||||
}
|
||||
|
||||
func (t *HTTPClientBuilderTest) Test_SetMockAddress() {
|
||||
addr := "mock.local:3004"
|
||||
t.builder.SetMockAddress(addr)
|
||||
@ -56,16 +74,23 @@ func (t *HTTPClientBuilderTest) Test_SetSSLVerification() {
|
||||
assert.True(t.T(), t.builder.httpTransport.TLSClientConfig.InsecureSkipVerify)
|
||||
}
|
||||
|
||||
func (t *HTTPClientBuilderTest) Test_FromConfigNil() {
|
||||
defer func() {
|
||||
assert.Nil(t.T(), recover())
|
||||
}()
|
||||
t.builder.FromConfig(nil)
|
||||
}
|
||||
|
||||
func (t *HTTPClientBuilderTest) Test_FromConfig() {
|
||||
config := &HTTPClientConfig{
|
||||
SSLVerification: true,
|
||||
SSLVerification: boolPtr(true),
|
||||
MockAddress: "anothermock.local:3004",
|
||||
MockedDomains: []string{"example.gov"},
|
||||
Timeout: 60,
|
||||
}
|
||||
|
||||
t.builder.FromConfig(config)
|
||||
assert.Equal(t.T(), !config.SSLVerification, t.builder.httpTransport.TLSClientConfig.InsecureSkipVerify)
|
||||
assert.Equal(t.T(), !config.IsSSLVerificationEnabled(), t.builder.httpTransport.TLSClientConfig.InsecureSkipVerify)
|
||||
assert.Equal(t.T(), config.MockAddress, t.builder.mockAddress)
|
||||
assert.Equal(t.T(), config.MockedDomains[0], t.builder.mockedDomains[0])
|
||||
assert.Equal(t.T(), config.Timeout*time.Second, t.builder.timeout)
|
||||
@ -76,7 +101,7 @@ func (t *HTTPClientBuilderTest) Test_FromEngine() {
|
||||
engine := &Engine{
|
||||
Config: Config{
|
||||
HTTPClientConfig: &HTTPClientConfig{
|
||||
SSLVerification: true,
|
||||
SSLVerification: boolPtr(true),
|
||||
MockAddress: "anothermock.local:3004",
|
||||
MockedDomains: []string{"example.gov"},
|
||||
},
|
||||
@ -85,7 +110,7 @@ func (t *HTTPClientBuilderTest) Test_FromEngine() {
|
||||
}
|
||||
|
||||
t.builder.FromEngine(engine)
|
||||
assert.NotNil(t.T(), engine, t.builder.engine)
|
||||
assert.Equal(t.T(), engine.Config.GetHTTPClientConfig().MockAddress, t.builder.mockAddress)
|
||||
}
|
||||
|
||||
func (t *HTTPClientBuilderTest) Test_buildDialer() {
|
||||
@ -102,6 +127,18 @@ func (t *HTTPClientBuilderTest) Test_buildMocks() {
|
||||
assert.NoError(t.T(), t.builder.buildMocks())
|
||||
}
|
||||
|
||||
func (t *HTTPClientBuilderTest) Test_WithLogger() {
|
||||
logger := NewLogger("telegram", logging.ERROR, DefaultLogFormatter())
|
||||
builder := NewHTTPClientBuilder()
|
||||
require.Nil(t.T(), builder.logger)
|
||||
|
||||
builder.WithLogger(nil)
|
||||
assert.Nil(t.T(), builder.logger)
|
||||
|
||||
builder.WithLogger(logger)
|
||||
assert.NotNil(t.T(), builder.logger)
|
||||
}
|
||||
|
||||
func (t *HTTPClientBuilderTest) Test_logf() {
|
||||
defer func() {
|
||||
assert.Nil(t.T(), recover())
|
||||
@ -130,6 +167,151 @@ func (t *HTTPClientBuilderTest) Test_RestoreDefault() {
|
||||
assert.Equal(t.T(), http.DefaultTransport, DefaultTransport)
|
||||
}
|
||||
|
||||
// Test_ClientMocksWorking is supposed to test mocking functionality of generated client.
|
||||
// Using real HTTP requests and server doesn't look good obviously, but mocking something to check how
|
||||
// mocks are working doesn't look that good too.
|
||||
// In this case we are trying to test this in just-like-real environment, without mocks or etc.
|
||||
// Fake "real" environment is easiest way to do this.
|
||||
// You know how to make this test better? Let us know.
|
||||
func (t *HTTPClientBuilderTest) Test_ClientMocksWorking() {
|
||||
mockProto := "https://"
|
||||
mockServerAddr := getOutboundIP().String() + ":27717"
|
||||
mockDomainAddr := "example.com"
|
||||
certFileData := `-----BEGIN CERTIFICATE-----
|
||||
MIIC+TCCAeGgAwIBAgIJAPb0Qm9aV+93MA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
|
||||
BAMMCGFwaV9tb2NrMB4XDTE5MTAxNzE0NDMzMloXDTI5MTAxNDE0NDMzMlowEzER
|
||||
MA8GA1UEAwwIYXBpX21vY2swggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQCtxUg6aUBcZLjEYEgzzE+B8wAsyK6dT9fbbDfO/5n9PhHjxQLnjTYrs9xAAl7R
|
||||
Caj8nUg0RzfX38jD1TGwkFrC1u1pjOf74OVMXsw2xa7gmZlnJeeL+QozXQX1rDPk
|
||||
wO5QqomKAIwM3ab+i6k1tLBfDIOHGLhTEFQZ9cmKVuNdTlkeqBh+bKduRIr7DYhQ
|
||||
Dsci/PacGJlt0W+r2YuRmm1KGearbS4HabPOkC0c6KbVD+bUyF+F7DxtJ8vg7O4W
|
||||
SWAXlwoEHonIyJG8H7TQxL2g5w/4UQhp6awAzFNLhqtf/6pw6gPfI9joS+Z/Pxvz
|
||||
Bry41s4LV7KP8v0GqRK3KH2rAgMBAAGjUDBOMB0GA1UdDgQWBBTEiHl+R8N0kLwH
|
||||
1RTsKYe8joAwxzAfBgNVHSMEGDAWgBTEiHl+R8N0kLwH1RTsKYe8joAwxzAMBgNV
|
||||
HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAo/XBUFlrl5yuQ+5eFQjMmbn4T
|
||||
9qVg4NVRy5qnTKzcOR21X3fB394TvfBiGstW0YQCOCtNakv94UAbZozYktQaYOtP
|
||||
x5porosgI2RgOTTwmiYOcYQTS2650jYydHhK16Gu2b3UKernO16mAWXNDWfvS2bk
|
||||
1ufbRWpuUXs0SIR6e/mgSwrBMBvq6fan4EVdEkx4Catjna15DgmBGRL215t5K4aq
|
||||
nAI2GL2ACEdOCyRvgq16AycJJYU7nYQ+t9aveefx0uhbYYIVeYub9NxmCfD3MojI
|
||||
saG/63vo0ng851n90DVoMRWx9n1CjEvss/vvz+jXIl9njaCtizN3WUf1NwUB
|
||||
-----END CERTIFICATE-----`
|
||||
keyFileData := `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEArcVIOmlAXGS4xGBIM8xPgfMALMiunU/X22w3zv+Z/T4R48UC
|
||||
5402K7PcQAJe0Qmo/J1INEc319/Iw9UxsJBawtbtaYzn++DlTF7MNsWu4JmZZyXn
|
||||
i/kKM10F9awz5MDuUKqJigCMDN2m/oupNbSwXwyDhxi4UxBUGfXJilbjXU5ZHqgY
|
||||
fmynbkSK+w2IUA7HIvz2nBiZbdFvq9mLkZptShnmq20uB2mzzpAtHOim1Q/m1Mhf
|
||||
hew8bSfL4OzuFklgF5cKBB6JyMiRvB+00MS9oOcP+FEIaemsAMxTS4arX/+qcOoD
|
||||
3yPY6Evmfz8b8wa8uNbOC1eyj/L9BqkStyh9qwIDAQABAoIBAG/jNUyW9KAJIocf
|
||||
T546kX8kzhoH5ZbZEC3ykkSwg6Bx1OcJtYMOg7DEEK8OV6rVQ3/Ubedra+ON7iFa
|
||||
JrJ/YFFQPnHWDpE6D6qK54bk5mMrw4CNAXg5FH5aCTpUdN139HrwM7st+v9VwO7p
|
||||
UjyIaX/p+M6F9jlVrDFC91Ah/ifW+DzWS27IsQ/396HTvTUNgD/Lj9pKtC6eW3AQ
|
||||
aTpLk/cVrkxxT1qc4rx8NRPe9/670CQDQYMvaNJtC54kRUzH87mn06IEy5fuLeI4
|
||||
WwYgEIkh1YjQsJhg3Z788JGKR5iKuzjXpw7aD6QESISoWPW1Dh/5PJO1zkKxZril
|
||||
xUepcgECgYEA5MLP4oCiuloShhaMc+UMYgH8YuglXegGbF5/iG9d7kgaiz4KiXuD
|
||||
ObwaHegLDFNSxtVSiH6AsXErzd7HHojzTm6B3O5qtBl+QgpW9hiTyJnCxkdzxK7y
|
||||
cVan8Jp3g/ojcYBY+QgZlR81QHQIwSUrAbMC0fjYlrKb5zJfExgFv4ECgYEAwnY7
|
||||
MUoLsHs2eXVOekvb42ReCq7HKVz8TETlSEPN4B5XzOKCnXxCUUvF5iWjWj0XAMSU
|
||||
yF6LJmmfFmOaVHpXDHhF9MmNKxYiIISS+ZM6B2DXP+hS/DXXuy5hGmYO6p4JC08d
|
||||
qulIIR7JSJmHyI+5Ref40WTObJvJQDXi2p5a0ysCgYBjdONG2aBmHrUBARqtZH7e
|
||||
uXhOVBmy2ya3xNnzql+PMl//+8g+/6kM1+AO8oyjHjLV6XcJit5OxyJBTkMJ3obR
|
||||
qa/iKvHPPWosMiyesA7IXzlUVUpaz6juZ7t6Gt4tTfpM5X1JQCFHORtA23HW717k
|
||||
TTzDp0obMqofeUHmnkIZgQKBgFBaGktblUjvIKs/VZYjElD7gABaB+GHkpjRPwyF
|
||||
N+SLpSv7zIzWc3C0Jqnak40OARtIH1JL/qN4sUvHDFYr1xxH9mAXiEVtd9yH61NF
|
||||
Co1R7p9xmBivBt1JZMZLtY4sjwAlSNT+X9ePqQxepESzXpMMLzwWs1UdaiMmIP7E
|
||||
wDLRAoGAM/Cz7B+J7KcKI8VqXAsX5nMklIKvACScKU6oIPwNYXyxUfm7jsNaBqXG
|
||||
weywTxDl/OD5ybNkZIRKsIXciFYG1VCGO2HNGN9qJcV+nJ63kyrIBauwUkuEhiN5
|
||||
uf/TQPpjrGW5nxOf94qn6FzV2WSype9BcM5MD7z7rk202Fs7Zqc=
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
certFile, err := ioutil.TempFile("/tmp", "cert_")
|
||||
require.NoError(t.T(), err, "cannot create temp cert file")
|
||||
keyFile, err := ioutil.TempFile("/tmp", "key_")
|
||||
require.NoError(t.T(), err, "cannot create temp key file")
|
||||
|
||||
_, err = certFile.WriteString(certFileData)
|
||||
require.NoError(t.T(), err, "cannot write temp cert file")
|
||||
_, err = keyFile.WriteString(keyFileData)
|
||||
require.NoError(t.T(), err, "cannot write temp key file")
|
||||
require.NoError(t.T(),
|
||||
ErrorCollector(certFile.Sync(), certFile.Close()), "cannot sync and close temp cert file")
|
||||
require.NoError(t.T(),
|
||||
ErrorCollector(keyFile.Sync(), keyFile.Close()), "cannot sync and close temp key file")
|
||||
|
||||
mux := &http.ServeMux{}
|
||||
srv := &http.Server{Addr: mockServerAddr, Handler: mux}
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
_, _ = io.WriteString(w, "ok")
|
||||
})
|
||||
|
||||
testSkipChan := make(chan error, 1)
|
||||
go func(skip chan error) {
|
||||
if err := srv.ListenAndServeTLS(certFile.Name(), keyFile.Name()); err != nil && err != http.ErrServerClosed {
|
||||
skip <- fmt.Errorf("skipping test because server won't start: %s", err.Error())
|
||||
}
|
||||
}(testSkipChan)
|
||||
|
||||
select {
|
||||
case errStartup := <-testSkipChan:
|
||||
t.T().Skip(errStartup)
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
t.T().Log("test server started")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := srv.Shutdown(context.TODO()); err != nil {
|
||||
t.T().Log("warning > cannot shutdown server gracefully: ", err)
|
||||
} else {
|
||||
t.T().Log("test server stopped")
|
||||
}
|
||||
|
||||
if err := os.Remove(certFile.Name()); err != nil {
|
||||
t.T().Log("warning > cannot remove temp cert file properly: ", err)
|
||||
}
|
||||
|
||||
if err := os.Remove(keyFile.Name()); err != nil {
|
||||
t.T().Log("warning > cannot remove temp key file properly: ", err)
|
||||
}
|
||||
}()
|
||||
|
||||
client, err := NewHTTPClientBuilder().
|
||||
SetLogging(true).
|
||||
SetMockAddress(mockServerAddr).
|
||||
SetMockedDomains([]string{mockDomainAddr}).
|
||||
SetTimeout(time.Second).
|
||||
SetSSLVerification(false).
|
||||
Build()
|
||||
require.NoError(t.T(), err, "cannot build client")
|
||||
|
||||
resp, err := client.Get(mockProto + mockDomainAddr)
|
||||
if err != nil && strings.Contains(err.Error(), "connection refused") {
|
||||
t.T().Skip("connection refused - skipping test: ", err)
|
||||
}
|
||||
require.NoError(t.T(), err, "error while making request")
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t.T(), err, "error while reading body")
|
||||
|
||||
assert.Equal(t.T(), http.StatusCreated, resp.StatusCode, "invalid status code")
|
||||
assert.Equal(t.T(), "ok", string(data), "invalid body contents")
|
||||
}
|
||||
|
||||
// taken from https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go
|
||||
func getOutboundIP() net.IP {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
|
||||
return localAddr.IP
|
||||
}
|
||||
|
||||
func Test_HTTPClientBuilder(t *testing.T) {
|
||||
suite.Run(t, new(HTTPClientBuilderTest))
|
||||
}
|
||||
|
@ -145,9 +145,9 @@ func (l *Localizer) loadFromFS() error {
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLocale will change language for current localizer
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
// NewLogger will create new logger with specified formatter.
|
||||
// Usage:
|
||||
// logger := NewLogger(config, DefaultLogFormatter())
|
||||
// logger := NewLogger("telegram", logging.ERROR, DefaultLogFormatter())
|
||||
func NewLogger(transportCode string, logLevel logging.Level, logFormat logging.Formatter) *logging.Logger {
|
||||
logger := logging.MustGetLogger(transportCode)
|
||||
logBackend := logging.NewLogBackend(os.Stdout, "", 0)
|
||||
|
@ -54,7 +54,7 @@ func (m *Migrate) Add(migration *gormigrate.Migration) {
|
||||
m.migrations[migration.ID] = migration
|
||||
}
|
||||
|
||||
// SetORM to migrate
|
||||
// SetDB to migrate
|
||||
func (m *Migrate) SetDB(db *gorm.DB) *Migrate {
|
||||
m.db = db
|
||||
return m
|
||||
@ -83,15 +83,15 @@ func (m *Migrate) Rollback() error {
|
||||
return errors.New("abnormal termination: first migration is nil")
|
||||
}
|
||||
|
||||
if err := m.GORMigrate.RollbackTo(m.first.ID); err == nil {
|
||||
if err := m.GORMigrate.RollbackMigration(m.first); err == nil {
|
||||
if err := m.GORMigrate.RollbackTo(m.first.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := m.GORMigrate.RollbackMigration(m.first); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// MigrateTo specified version
|
||||
@ -183,12 +183,12 @@ func (m *Migrate) Current() string {
|
||||
return "0"
|
||||
}
|
||||
|
||||
if err := m.db.Last(&migrationInfo).Error; err == nil {
|
||||
return migrationInfo.ID
|
||||
} else {
|
||||
if err := m.db.Last(&migrationInfo).Error; err != nil {
|
||||
fmt.Printf("warning => cannot fetch migration version: %s\n", err.Error())
|
||||
return "0"
|
||||
}
|
||||
|
||||
return migrationInfo.ID
|
||||
}
|
||||
|
||||
// NextFrom returns next version from passed version
|
||||
@ -197,9 +197,9 @@ func (m *Migrate) NextFrom(version string) (string, error) {
|
||||
if ver == version {
|
||||
if key < (len(m.versions) - 1) {
|
||||
return m.versions[key+1], nil
|
||||
} else {
|
||||
return "", errors.New("this is last migration")
|
||||
}
|
||||
|
||||
return "", errors.New("this is last migration")
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,9 +212,9 @@ func (m *Migrate) PreviousFrom(version string) (string, error) {
|
||||
if ver == version {
|
||||
if key > 0 {
|
||||
return m.versions[key-1], nil
|
||||
} else {
|
||||
return "0", nil
|
||||
}
|
||||
|
||||
return "0", nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ func (m *MigrateTest) RefreshMigrate() {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MigrateTest) Migration_TestModelFirst() *gormigrate.Migration {
|
||||
func (m *MigrateTest) MigrationTestModelFirst() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "1",
|
||||
Migrate: func(db *gorm.DB) error {
|
||||
@ -66,7 +66,7 @@ func (m *MigrateTest) Migration_TestModelFirst() *gormigrate.Migration {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MigrateTest) Migration_TestModelSecond() *gormigrate.Migration {
|
||||
func (m *MigrateTest) MigrationTestModelSecond() *gormigrate.Migration {
|
||||
return &gormigrate.Migration{
|
||||
ID: "2",
|
||||
Migrate: func(db *gorm.DB) error {
|
||||
@ -81,7 +81,7 @@ func (m *MigrateTest) Migration_TestModelSecond() *gormigrate.Migration {
|
||||
func (m *MigrateTest) Test_Add() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(nil)
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
|
||||
assert.Equal(m.T(), 1, len(m.Migrate.migrations))
|
||||
i, ok := m.Migrate.migrations["1"]
|
||||
@ -112,7 +112,7 @@ func (m *MigrateTest) Test_prepareMigrations_AlreadyPrepared() {
|
||||
|
||||
func (m *MigrateTest) Test_prepareMigrations_OK() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
err := m.Migrate.prepareMigrations()
|
||||
|
||||
require.NoError(m.T(), err)
|
||||
@ -124,7 +124,7 @@ func (m *MigrateTest) Test_prepareMigrations_OK() {
|
||||
func (m *MigrateTest) Test_Migrate_Fail_NilDB() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.SetDB(nil)
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
|
||||
err := m.Migrate.Migrate()
|
||||
|
||||
@ -134,7 +134,7 @@ func (m *MigrateTest) Test_Migrate_Fail_NilDB() {
|
||||
|
||||
func (m *MigrateTest) Test_Migrate_Success_NoMigrations() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
|
||||
m.mock.ExpectBegin()
|
||||
m.mock.
|
||||
@ -157,7 +157,7 @@ func (m *MigrateTest) Test_Migrate_Success_NoMigrations() {
|
||||
|
||||
func (m *MigrateTest) Test_Migrate_Success() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
|
||||
m.mock.ExpectBegin()
|
||||
m.mock.
|
||||
@ -188,7 +188,7 @@ func (m *MigrateTest) Test_Migrate_Success() {
|
||||
func (m *MigrateTest) Test_Rollback_Fail_NilDB() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.SetDB(nil)
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
|
||||
err := m.Migrate.Rollback()
|
||||
|
||||
@ -198,7 +198,7 @@ func (m *MigrateTest) Test_Rollback_Fail_NilDB() {
|
||||
|
||||
func (m *MigrateTest) Test_Rollback_Fail_NoMigrations() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.first = m.Migration_TestModelFirst()
|
||||
m.Migrate.first = m.MigrationTestModelFirst()
|
||||
|
||||
err := m.Migrate.Rollback()
|
||||
|
||||
@ -208,7 +208,7 @@ func (m *MigrateTest) Test_Rollback_Fail_NoMigrations() {
|
||||
|
||||
func (m *MigrateTest) Test_Rollback_Fail_NoFirstMigration() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
m.Migrate.first = nil
|
||||
|
||||
err := m.Migrate.Rollback()
|
||||
@ -229,7 +229,7 @@ func (m *MigrateTest) Test_MigrateTo_Fail_NilDB() {
|
||||
|
||||
func (m *MigrateTest) Test_MigrateTo_DoNothing() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
|
||||
m.mock.
|
||||
ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)).
|
||||
@ -247,7 +247,7 @@ func (m *MigrateTest) Test_MigrateTo_DoNothing() {
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||
m.mock.ExpectCommit()
|
||||
|
||||
err := m.Migrate.MigrateTo(m.Migration_TestModelFirst().ID)
|
||||
err := m.Migrate.MigrateTo(m.MigrationTestModelFirst().ID)
|
||||
|
||||
assert.NoError(m.T(), err)
|
||||
assert.NoError(m.T(), m.mock.ExpectationsWereMet())
|
||||
@ -255,7 +255,7 @@ func (m *MigrateTest) Test_MigrateTo_DoNothing() {
|
||||
|
||||
func (m *MigrateTest) Test_MigrateTo() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
|
||||
m.mock.
|
||||
ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)).
|
||||
@ -280,7 +280,7 @@ func (m *MigrateTest) Test_MigrateTo() {
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
m.mock.ExpectCommit()
|
||||
|
||||
err := m.Migrate.MigrateTo(m.Migration_TestModelFirst().ID)
|
||||
err := m.Migrate.MigrateTo(m.MigrationTestModelFirst().ID)
|
||||
|
||||
assert.NoError(m.T(), err)
|
||||
assert.NoError(m.T(), m.mock.ExpectationsWereMet())
|
||||
@ -288,13 +288,13 @@ func (m *MigrateTest) Test_MigrateTo() {
|
||||
|
||||
func (m *MigrateTest) Test_RollbackTo() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.Migration_TestModelSecond())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelSecond())
|
||||
|
||||
m.mock.ExpectBegin()
|
||||
m.mock.ExpectCommit()
|
||||
|
||||
err := m.Migrate.RollbackTo(m.Migration_TestModelSecond().ID)
|
||||
err := m.Migrate.RollbackTo(m.MigrationTestModelSecond().ID)
|
||||
|
||||
assert.NoError(m.T(), err)
|
||||
assert.NoError(m.T(), m.mock.ExpectationsWereMet())
|
||||
@ -302,8 +302,8 @@ func (m *MigrateTest) Test_RollbackTo() {
|
||||
|
||||
func (m *MigrateTest) Test_MigrateNextTo() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.Migration_TestModelSecond())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelSecond())
|
||||
|
||||
m.mock.
|
||||
ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)).
|
||||
@ -332,7 +332,7 @@ func (m *MigrateTest) Test_MigrateNextTo() {
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
m.mock.ExpectCommit()
|
||||
|
||||
err := m.Migrate.MigrateNextTo(m.Migration_TestModelFirst().ID)
|
||||
err := m.Migrate.MigrateNextTo(m.MigrationTestModelFirst().ID)
|
||||
|
||||
assert.NoError(m.T(), err)
|
||||
assert.NoError(m.T(), m.mock.ExpectationsWereMet())
|
||||
@ -340,14 +340,14 @@ func (m *MigrateTest) Test_MigrateNextTo() {
|
||||
|
||||
func (m *MigrateTest) Test_MigratePreviousTo() {
|
||||
m.RefreshMigrate()
|
||||
m.Migrate.Add(m.Migration_TestModelFirst())
|
||||
m.Migrate.Add(m.Migration_TestModelSecond())
|
||||
m.Migrate.Add(m.MigrationTestModelFirst())
|
||||
m.Migrate.Add(m.MigrationTestModelSecond())
|
||||
|
||||
m.mock.
|
||||
ExpectExec(regexp.QuoteMeta(`CREATE TABLE "migrations" ("id" varchar(255) , PRIMARY KEY ("id"))`)).
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
|
||||
err := m.Migrate.MigratePreviousTo(m.Migration_TestModelSecond().ID)
|
||||
err := m.Migrate.MigratePreviousTo(m.MigrationTestModelSecond().ID)
|
||||
|
||||
assert.Error(m.T(), err)
|
||||
assert.NoError(m.T(), m.mock.ExpectationsWereMet())
|
||||
|
@ -35,6 +35,7 @@ type NewMigrationCommand struct {
|
||||
Directory string `short:"d" long:"directory" default:"./migrations" description:"Directory where migration will be created"`
|
||||
}
|
||||
|
||||
// FileExists returns true if provided file exist and it's not directory
|
||||
func (x *NewMigrationCommand) FileExists(filename string) bool {
|
||||
info, err := os.Stat(filename)
|
||||
if os.IsNotExist(err) {
|
||||
@ -43,6 +44,7 @@ func (x *NewMigrationCommand) FileExists(filename string) bool {
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
// Execute migration generator command
|
||||
func (x *NewMigrationCommand) Execute(args []string) error {
|
||||
version := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
directory := path.Clean(x.Directory)
|
||||
|
@ -42,7 +42,7 @@ func (s *MigrationGeneratorSuite) Test_Execute() {
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if strings.Index(f.Name(), "_app.go") != -1 {
|
||||
if strings.Contains(f.Name(), "_app.go") {
|
||||
found = true
|
||||
assert.NoError(s.T(), os.Remove(path.Join(s.command.Directory, f.Name())))
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func NewStaticRenderer(funcMap template.FuncMap) Renderer {
|
||||
return newRendererWithMultitemplate(funcMap, multitemplate.New())
|
||||
}
|
||||
|
||||
// NewStaticRenderer is a Renderer constructor with multitemplate.DynamicRender
|
||||
// NewDynamicRenderer is a Renderer constructor with multitemplate.DynamicRender
|
||||
func NewDynamicRenderer(funcMap template.FuncMap) Renderer {
|
||||
return newRendererWithMultitemplate(funcMap, multitemplate.NewDynamic())
|
||||
}
|
||||
@ -47,9 +47,9 @@ func (r *Renderer) Push(name string, files ...string) *template.Template {
|
||||
|
||||
if r.TemplatesBox == nil {
|
||||
return r.storeTemplate(name, r.AddFromFilesFuncs(name, r.FuncMap, files...))
|
||||
} else {
|
||||
return r.storeTemplate(name, r.addFromBox(name, r.FuncMap, files...))
|
||||
}
|
||||
|
||||
return r.storeTemplate(name, r.addFromBox(name, r.FuncMap, files...))
|
||||
}
|
||||
|
||||
// addFromBox adds embedded template
|
||||
@ -67,7 +67,7 @@ func (r *Renderer) addFromBox(name string, funcMap template.FuncMap, files ...st
|
||||
|
||||
// storeTemplate stores built template if multitemplate.DynamicRender is used.
|
||||
// Dynamic render doesn't store templates - it stores builders, that's why we can't just extract them.
|
||||
// It possibly can cause data inconsistency in developer enviroments where return value from Renderer.Push is used.
|
||||
// It possibly can cause data inconsistency in developer environments where return value from Renderer.Push is used.
|
||||
func (r *Renderer) storeTemplate(name string, tpl *template.Template) *template.Template {
|
||||
if _, ok := r.Renderer.(multitemplate.DynamicRender); ok {
|
||||
r.alreadyAdded[name] = tpl
|
||||
|
@ -23,7 +23,7 @@ type TranslationsExtractor struct {
|
||||
TranslationsPath string
|
||||
}
|
||||
|
||||
// TranslationsExtractor constructor. Use "translate.{}.yml" as template if your translations are named like "translate.en.yml"
|
||||
// NewTranslationsExtractor constructor. Use "translate.{}.yml" as template if your translations are named like "translate.en.yml"
|
||||
func NewTranslationsExtractor(fileNameTemplate string) *TranslationsExtractor {
|
||||
return &TranslationsExtractor{fileNameTemplate: fileNameTemplate}
|
||||
}
|
||||
@ -32,46 +32,60 @@ func NewTranslationsExtractor(fileNameTemplate string) *TranslationsExtractor {
|
||||
func (t *TranslationsExtractor) unmarshalToMap(in []byte) (map[string]interface{}, error) {
|
||||
var dataMap map[string]interface{}
|
||||
|
||||
if err := yaml.Unmarshal(in, &dataMap); err == nil {
|
||||
return dataMap, nil
|
||||
} else {
|
||||
if err := yaml.Unmarshal(in, &dataMap); err != nil {
|
||||
return dataMap, err
|
||||
}
|
||||
|
||||
return dataMap, nil
|
||||
}
|
||||
|
||||
// loadYAMLBox loads YAML from box
|
||||
func (t *TranslationsExtractor) loadYAMLBox(fileName string) (map[string]interface{}, error) {
|
||||
var dataMap map[string]interface{}
|
||||
var (
|
||||
dataMap map[string]interface{}
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if data, err := t.TranslationsBox.Find(fileName); err == nil {
|
||||
return t.unmarshalToMap(data)
|
||||
} else {
|
||||
if data, err = t.TranslationsBox.Find(fileName); err != nil {
|
||||
return dataMap, err
|
||||
}
|
||||
|
||||
return t.unmarshalToMap(data)
|
||||
}
|
||||
|
||||
// loadYAMLFile loads YAML from file
|
||||
func (t *TranslationsExtractor) loadYAMLFile(fileName string) (map[string]interface{}, error) {
|
||||
var dataMap map[string]interface{}
|
||||
var (
|
||||
dataMap map[string]interface{}
|
||||
info os.FileInfo
|
||||
err error
|
||||
)
|
||||
|
||||
if info, err := os.Stat(fileName); err == nil {
|
||||
if info, err = os.Stat(fileName); err == nil {
|
||||
if !info.IsDir() {
|
||||
if path, err := filepath.Abs(fileName); err == nil {
|
||||
if source, err := ioutil.ReadFile(path); err == nil {
|
||||
var (
|
||||
path string
|
||||
source []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if path, err = filepath.Abs(fileName); err != nil {
|
||||
return dataMap, err
|
||||
}
|
||||
|
||||
if source, err = ioutil.ReadFile(path); err != nil {
|
||||
return dataMap, err
|
||||
}
|
||||
|
||||
return t.unmarshalToMap(source)
|
||||
} else {
|
||||
return dataMap, err
|
||||
}
|
||||
} else {
|
||||
return dataMap, err
|
||||
}
|
||||
} else {
|
||||
|
||||
return dataMap, errors.New("directory provided instead of file")
|
||||
}
|
||||
} else {
|
||||
|
||||
return dataMap, err
|
||||
}
|
||||
}
|
||||
|
||||
// loadYAML loads YAML from filesystem or from packr box - depends on what was configured. Can return error.
|
||||
func (t *TranslationsExtractor) loadYAML(fileName string) (map[string]interface{}, error) {
|
||||
@ -106,9 +120,13 @@ func (t *TranslationsExtractor) LoadLocale(locale string) (map[string]interface{
|
||||
|
||||
// LoadLocaleKeys returns only sorted keys from translation file
|
||||
func (t *TranslationsExtractor) LoadLocaleKeys(locale string) ([]string, error) {
|
||||
if data, err := t.LoadLocale(locale); err == nil {
|
||||
return t.GetMapKeys(data), nil
|
||||
} else {
|
||||
var (
|
||||
data map[string]interface{}
|
||||
err error
|
||||
)
|
||||
if data, err = t.LoadLocale(locale); err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
return t.GetMapKeys(data), nil
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ func (t *TranslationsExtractorTest) SetupSuite() {
|
||||
"another": "second",
|
||||
}
|
||||
data, _ := yaml.Marshal(translation)
|
||||
// It's not regular temporary file. Little hack in order to test translations extractor.
|
||||
// nolint:gosec
|
||||
errWrite := ioutil.WriteFile("/tmp/translate.en.yml", data, os.ModePerm)
|
||||
require.NoError(t.T(), errWrite)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
// nolint:gosec
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
@ -24,9 +25,9 @@ import (
|
||||
// Utils service object
|
||||
type Utils struct {
|
||||
IsDebug bool
|
||||
TokenCounter uint32
|
||||
ConfigAWS ConfigAWS
|
||||
Logger *logging.Logger
|
||||
TokenCounter uint32
|
||||
slashRegex *regexp.Regexp
|
||||
}
|
||||
|
||||
@ -127,6 +128,7 @@ func (u *Utils) UploadUserAvatar(url string) (picURLs3 string, err error) {
|
||||
s := session.Must(session.NewSession(s3Config))
|
||||
uploader := s3manager.NewUploader(s)
|
||||
|
||||
// nolint:gosec
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return
|
||||
@ -181,6 +183,7 @@ func GetMGItemData(client *v1.MgClient, url string, caption string) (v1.Item, in
|
||||
func GetEntitySHA1(v interface{}) (hash string, err error) {
|
||||
res, _ := json.Marshal(v)
|
||||
|
||||
// nolint:gosec
|
||||
h := sha1.New()
|
||||
_, err = h.Write(res)
|
||||
hash = fmt.Sprintf("%x", h.Sum(nil))
|
||||
|
@ -1,12 +1,11 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"gopkg.in/go-playground/validator.v8"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
// init here will register `validatecrmurl` function for gin validator
|
||||
func init() {
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
if err := v.RegisterValidation("validatecrmurl", validateCrmURL); err != nil {
|
||||
@ -15,9 +14,7 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func validateCrmURL(
|
||||
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
|
||||
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
|
||||
) bool {
|
||||
return regCommandName.Match([]byte(field.Interface().(string)))
|
||||
// validateCrmURL will validate CRM URL
|
||||
func validateCrmURL(fl validator.FieldLevel) bool {
|
||||
return regCommandName.Match([]byte(fl.Field().String()))
|
||||
}
|
||||
|
63
core/validator_test.go
Normal file
63
core/validator_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
type ValidatorSuite struct {
|
||||
suite.Suite
|
||||
engine *validator.Validate
|
||||
}
|
||||
|
||||
func Test_Validator(t *testing.T) {
|
||||
suite.Run(t, new(ValidatorSuite))
|
||||
}
|
||||
|
||||
func (s *ValidatorSuite) SetupSuite() {
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
s.engine = v
|
||||
} else {
|
||||
s.T().Fatal("cannot obtain validation engine")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ValidatorSuite) getError(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
func (s *ValidatorSuite) Test_ValidationInvalidType() {
|
||||
assert.IsType(s.T(), &validator.InvalidValidationError{}, s.engine.Struct(nil))
|
||||
}
|
||||
|
||||
func (s *ValidatorSuite) Test_ValidationFails() {
|
||||
conn := Connection{
|
||||
Key: "key",
|
||||
URL: "url",
|
||||
}
|
||||
err := s.engine.Struct(conn)
|
||||
require.IsType(s.T(), validator.ValidationErrors{}, err)
|
||||
validatorErrors := err.(validator.ValidationErrors)
|
||||
assert.Equal(
|
||||
s.T(),
|
||||
"Key: 'Connection.URL' Error:Field validation for 'URL' failed on the 'validatecrmurl' tag",
|
||||
validatorErrors.Error())
|
||||
}
|
||||
|
||||
func (s *ValidatorSuite) Test_ValidationSuccess() {
|
||||
conn := Connection{
|
||||
Key: "key",
|
||||
URL: "https://test.retailcrm.pro",
|
||||
}
|
||||
err := s.engine.Struct(conn)
|
||||
assert.NoError(s.T(), err, s.getError(err))
|
||||
}
|
17
go.mod
17
go.mod
@ -9,20 +9,20 @@ require (
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190830225923-3302f0226fbd // indirect
|
||||
github.com/getsentry/raven-go v0.2.0
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20190914010127-bba2ccfe37ec
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.4.0
|
||||
github.com/gin-gonic/gin v1.5.0
|
||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||
github.com/gobuffalo/packd v0.3.0
|
||||
github.com/gobuffalo/packr/v2 v2.7.1
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/sessions v1.2.0
|
||||
github.com/h2non/gock v1.0.10
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/jinzhu/gorm v1.9.11
|
||||
github.com/json-iterator/go v1.1.7 // indirect
|
||||
github.com/json-iterator/go v1.1.8 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/lib/pq v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.10 // indirect
|
||||
github.com/mattn/go-isatty v0.0.11 // indirect
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.2
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
|
||||
@ -31,12 +31,11 @@ require (
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.32
|
||||
github.com/rogpeppe/go-internal v1.5.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/ugorji/go v1.1.7 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 // indirect
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 // indirect
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 // indirect
|
||||
golang.org/x/text v0.3.2
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2
|
||||
gopkg.in/go-playground/validator.v9 v9.30.2
|
||||
gopkg.in/gormigrate.v1 v1.6.0
|
||||
gopkg.in/yaml.v2 v2.2.4
|
||||
gopkg.in/yaml.v2 v2.2.7
|
||||
)
|
||||
|
30
go.sum
30
go.sum
@ -48,8 +48,18 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@ -114,6 +124,8 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@ -126,14 +138,21 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
@ -259,9 +278,13 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
|
||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
@ -296,8 +319,11 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.2 h1:icxYLlYflpazIV3ufMoNB9h9SYMQ37DZ8CTwkU4pnOs=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.2/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI=
|
||||
gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
@ -306,6 +332,8 @@ gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
Loading…
Reference in New Issue
Block a user