From 62903018157c1f98359fe7318d9c54d7c9c643cf Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 12 Dec 2019 09:35:05 +0300 Subject: [PATCH] updated gin, better code --- cmd/transport-core-tool/main.go | 1 + core/config.go | 65 ++++++++++++++++++++++------- core/csrf.go | 22 ++++++---- core/engine.go | 12 ++++-- core/engine_test.go | 7 +++- core/error.go | 2 +- core/http_client_builder.go | 53 +++++++++++++---------- core/http_client_builder_test.go | 6 +-- core/localizer.go | 4 +- core/migrate.go | 20 ++++----- core/migrate_test.go | 46 ++++++++++---------- core/migration_generator.go | 2 + core/migration_generator_test.go | 2 +- core/template.go | 9 ++-- core/translations_extractor.go | 20 ++++----- core/translations_extractor_test.go | 2 + core/utils.go | 3 ++ core/validator.go | 13 +++--- core/validator_test.go | 63 ++++++++++++++++++++++++++++ go.mod | 17 ++++---- go.sum | 30 ++++++++++++- 21 files changed, 275 insertions(+), 124 deletions(-) create mode 100644 core/validator_test.go diff --git a/cmd/transport-core-tool/main.go b/cmd/transport-core-tool/main.go index 0c848a3..8a45880 100644 --- a/cmd/transport-core-tool/main.go +++ b/cmd/transport-core-tool/main.go @@ -7,6 +7,7 @@ import ( "github.com/retailcrm/mg-transport-core/core" ) +// Options for tool command type Options struct{} var ( diff --git a/core/config.go b/core/config.go index 08c8bd6..fd64441 100644 --- a/core/config.go +++ b/core/config.go @@ -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(`/+$`) ) @@ -29,7 +30,7 @@ type ConfigInterface interface { GetDBConfig() DatabaseConfig GetAWSConfig() ConfigAWS GetTransportInfo() InfoInterface - GetHTTPClientConfig() *HTTPClientConfig + GetHTTPClientConfig() HTTPClientConfigInterface GetUpdateInterval() int IsDebug() bool } @@ -41,18 +42,26 @@ type InfoInterface interface { GetLogoPath() string } +// HTTPClientConfigInterface can be used to provide alternative way for configuring HTTP server +type HTTPClientConfigInterface interface { + GetTimeout() time.Duration + IsSSLVerificationEnabled() bool + GetMockAddress() string + GetMockedDomains() []string +} + // Config struct type Config struct { - Version string `yaml:"version"` - LogLevel logging.Level `yaml:"log_level"` - Database DatabaseConfig `yaml:"database"` - SentryDSN string `yaml:"sentry_dsn"` - HTTPServer HTTPServerConfig `yaml:"http_server"` - Debug bool `yaml:"debug"` - UpdateInterval int `yaml:"update_interval"` - ConfigAWS ConfigAWS `yaml:"config_aws"` - TransportInfo Info `yaml:"transport_info"` - HTTPClientConfig *HTTPClientConfig `yaml:"http_client"` + Version string `yaml:"version"` + LogLevel logging.Level `yaml:"log_level"` + Database DatabaseConfig `yaml:"database"` + SentryDSN string `yaml:"sentry_dsn"` + HTTPServer HTTPServerConfig `yaml:"http_server"` + Debug bool `yaml:"debug"` + UpdateInterval int `yaml:"update_interval"` + ConfigAWS ConfigAWS `yaml:"config_aws"` + TransportInfo Info `yaml:"transport_info"` + HTTPClientConfig HTTPClientConfigInterface `yaml:"http_client"` } // Info struct @@ -85,7 +94,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"` } @@ -180,7 +189,7 @@ func (c Config) GetUpdateInterval() int { } // GetHTTPClientConfig returns http client config -func (c Config) GetHTTPClientConfig() *HTTPClientConfig { +func (c Config) GetHTTPClientConfig() HTTPClientConfigInterface { return c.HTTPClientConfig } @@ -198,3 +207,31 @@ func (t Info) GetCode() string { func (t Info) GetLogoPath() string { return t.LogoPath } + +// GetTimeout returns timeout for HTTP client (default is 30 seconds) +func (h *HTTPClientConfig) GetTimeout() time.Duration { + if h.Timeout <= 0 { + h.Timeout = 30 * time.Second + } + + return h.Timeout +} + +// IsSSLVerificationEnabled returns SSL verification flag (default is true) +func (h *HTTPClientConfig) IsSSLVerificationEnabled() bool { + if h.SSLVerification == nil { + return true + } + + return *h.SSLVerification +} + +// GetMockAddress returns mock address +func (h *HTTPClientConfig) GetMockAddress() string { + return h.MockAddress +} + +// GetMockedDomains returns mocked domains list +func (h *HTTPClientConfig) GetMockedDomains() []string { + return h.MockedDomains +} diff --git a/core/csrf.go b/core/csrf.go index 9781689..0f423d6 100644 --- a/core/csrf.go +++ b/core/csrf.go @@ -2,6 +2,7 @@ package core import ( "bytes" + // nolint:gosec "crypto/sha1" "encoding/base64" "io" @@ -44,6 +45,7 @@ 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 @@ -51,7 +53,6 @@ type CSRF struct { abortFunc gin.HandlerFunc csrfTokenGetter CSRFTokenGetter store sessions.Store - locale *Localizer } // NewCSRF creates CSRF struct with specified configuration and session store. @@ -115,8 +116,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 +160,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 @@ -211,13 +214,14 @@ 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 == "" { + var v string + if v, ok = i.(string); !ok || v == "" { x.abortFunc(c) c.Abort() return - } else { - token = i } + + token = v } else { x.abortFunc(c) c.Abort() diff --git a/core/engine.go b/core/engine.go index f13d34f..10b6a0a 100644 --- a/core/engine.go +++ b/core/engine.go @@ -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 } @@ -156,9 +160,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 +178,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 diff --git a/core/engine_test.go b/core/engine_test.go index ae2cc57..e8de080 100644 --- a/core/engine_test.go +++ b/core/engine_test.go @@ -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() @@ -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 +} diff --git a/core/error.go b/core/error.go index 011f5d6..5539ad2 100644 --- a/core/error.go +++ b/core/error.go @@ -28,4 +28,4 @@ func BadRequest(error string) (int, interface{}) { // context.JSON(BadRequest("invalid data")) func InternalServerError(error string) (int, interface{}) { return GetErrorResponse(http.StatusInternalServerError, error) -} \ No newline at end of file +} diff --git a/core/http_client_builder.go b/core/http_client_builder.go index 9836ba1..f4dc325 100644 --- a/core/http_client_builder.go +++ b/core/http_client_builder.go @@ -11,10 +11,11 @@ import ( "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: @@ -111,21 +112,21 @@ func (b *HTTPClientBuilder) EnableLogging() *HTTPClientBuilder { } // FromConfig fulfills mock configuration from HTTPClientConfig -func (b *HTTPClientBuilder) FromConfig(config *HTTPClientConfig) *HTTPClientBuilder { +func (b *HTTPClientBuilder) FromConfig(config HTTPClientConfigInterface) *HTTPClientBuilder { if config == nil { return b } - if config.MockAddress != "" { - b.SetMockAddress(config.MockAddress) - b.SetMockedDomains(config.MockedDomains) + if config.GetMockAddress() != "" { + b.SetMockAddress(config.GetMockAddress()) + b.SetMockedDomains(config.GetMockedDomains()) } - if config.Timeout > 0 { - b.SetTimeout(config.Timeout) + if config.GetTimeout() > 0 { + b.SetTimeout(config.GetTimeout()) } - b.SetSSLVerification(config.SSLVerification) + b.SetSSLVerification(config.IsSSLVerificationEnabled()) return b } @@ -156,10 +157,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,21 +179,26 @@ 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 + } - if b.mockPort == "0" { - addr = net.JoinHostPort(b.mockHost, port) - } else { - addr = net.JoinHostPort(b.mockHost, b.mockPort) - } + for _, mock := range b.mockedDomains { + if mock == host { + oldAddr := addr - b.logf("Mocking \"%s\" with \"%s\"\n", oldAddr, addr) + if b.mockPort == "0" { + addr = net.JoinHostPort(b.mockHost, port) + } else { + addr = net.JoinHostPort(b.mockHost, b.mockPort) } + + b.logf("Mocking \"%s\" with \"%s\"\n", oldAddr, addr) } } diff --git a/core/http_client_builder_test.go b/core/http_client_builder_test.go index efbebc0..874a37d 100644 --- a/core/http_client_builder_test.go +++ b/core/http_client_builder_test.go @@ -58,14 +58,14 @@ func (t *HTTPClientBuilderTest) Test_SetSSLVerification() { 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 +76,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"}, }, diff --git a/core/localizer.go b/core/localizer.go index 28b81b9..321f492 100644 --- a/core/localizer.go +++ b/core/localizer.go @@ -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 diff --git a/core/migrate.go b/core/migrate.go index 5c7938e..37b5eac 100644 --- a/core/migrate.go +++ b/core/migrate.go @@ -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 @@ -86,9 +86,9 @@ func (m *Migrate) Rollback() error { if err := m.GORMigrate.RollbackTo(m.first.ID); err == nil { if err := m.GORMigrate.RollbackMigration(m.first); err == nil { return nil - } else { - return err } + + return err } else { return err } @@ -185,10 +185,10 @@ func (m *Migrate) Current() string { if err := m.db.Last(&migrationInfo).Error; err == nil { return migrationInfo.ID - } else { - fmt.Printf("warning => cannot fetch migration version: %s\n", err.Error()) - return "0" } + + fmt.Printf("warning => cannot fetch migration version: %s\n", err.Error()) + return "0" } // 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 } } diff --git a/core/migrate_test.go b/core/migrate_test.go index 8ce67c9..1533cb8 100644 --- a/core/migrate_test.go +++ b/core/migrate_test.go @@ -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()) diff --git a/core/migration_generator.go b/core/migration_generator.go index a48651b..b8bc0a0 100644 --- a/core/migration_generator.go +++ b/core/migration_generator.go @@ -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) diff --git a/core/migration_generator_test.go b/core/migration_generator_test.go index 8ff5a6a..ea28944 100644 --- a/core/migration_generator_test.go +++ b/core/migration_generator_test.go @@ -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()))) } diff --git a/core/template.go b/core/template.go index 6e43619..20e4365 100644 --- a/core/template.go +++ b/core/template.go @@ -4,7 +4,6 @@ import ( "html/template" "github.com/gin-contrib/multitemplate" - "github.com/gobuffalo/packr/v2" ) // Renderer wraps multitemplate.Renderer in order to make it easier to use @@ -25,7 +24,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 +46,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 +66,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 diff --git a/core/translations_extractor.go b/core/translations_extractor.go index 1697b50..3352307 100644 --- a/core/translations_extractor.go +++ b/core/translations_extractor.go @@ -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} } @@ -43,10 +43,10 @@ func (t *TranslationsExtractor) unmarshalToMap(in []byte) (map[string]interface{ func (t *TranslationsExtractor) loadYAMLBox(fileName string) (map[string]interface{}, error) { var dataMap map[string]interface{} - 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 + } else { + return t.unmarshalToMap(data) } } @@ -57,10 +57,10 @@ func (t *TranslationsExtractor) loadYAMLFile(fileName string) (map[string]interf 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 { - return t.unmarshalToMap(source) - } else { + if source, err := ioutil.ReadFile(path); err != nil { return dataMap, err + } else { + return t.unmarshalToMap(source) } } else { return dataMap, err @@ -106,9 +106,9 @@ 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 { + if data, err := t.LoadLocale(locale); err != nil { return []string{}, err } + + return t.GetMapKeys(data), nil } diff --git a/core/translations_extractor_test.go b/core/translations_extractor_test.go index ddb3b43..796b6fb 100644 --- a/core/translations_extractor_test.go +++ b/core/translations_extractor_test.go @@ -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) diff --git a/core/utils.go b/core/utils.go index ea96712..aa25a5b 100644 --- a/core/utils.go +++ b/core/utils.go @@ -1,6 +1,7 @@ package core import ( + // nolint:gosec "crypto/sha1" "crypto/sha256" "encoding/json" @@ -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)) diff --git a/core/validator.go b/core/validator.go index ea5d88f..0d3b712 100644 --- a/core/validator.go +++ b/core/validator.go @@ -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())) } diff --git a/core/validator_test.go b/core/validator_test.go new file mode 100644 index 0000000..bf5fd49 --- /dev/null +++ b/core/validator_test.go @@ -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)) +} diff --git a/go.mod b/go.mod index 305b211..7e5bbfe 100644 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index c48871c..b03e9da 100644 --- a/go.sum +++ b/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=