feat: add a new command - db apikey create (#6)
continuous-integration/drone/push Build is passing Details

Reviewed-on: #6
This commit is contained in:
Dawid Wysokiński 2022-11-20 08:14:41 +00:00
parent 855bcac56e
commit ffcb965590
27 changed files with 710 additions and 18 deletions

View File

@ -9,6 +9,7 @@ steps:
environment:
TESTS_DB_DSN: postgres://postgres:sessions@database:5432/sessions?sslmode=disable
commands:
- make generate
- go test -race -coverprofile=coverage.txt -covermode=atomic ./...
services:
@ -34,6 +35,7 @@ steps:
- name: check go.mod
image: golang:1.19
commands:
- make generate
- go mod tidy
- git diff --exit-code go.mod
@ -53,6 +55,7 @@ steps:
- name: run golangci-lint
image: golangci/golangci-lint:v1.50
commands:
- make generate
- golangci-lint run
trigger:

View File

@ -4,13 +4,23 @@ install-git-hooks:
pre-commit install --hook-type pre-commit
pre-commit install --hook-type commit-msg
.PHONY: install-counterfeiter
install-counterfeiter:
@echo "Installing github.com/maxbrunsfeld/counterfeiter..."
cd ./internal/tools && go install github.com/maxbrunsfeld/counterfeiter/v6
.PHONY: install-golangci-lint
install-golangci-lint:
@echo "Installing github.com/golangci/golangci-lint..."
cd ./internal/tools && go install github.com/golangci/golangci-lint/cmd/golangci-lint
.PHONY: install-tools
install-tools: install-golangci-lint
install-tools: install-golangci-lint install-counterfeiter
.PHONY: install
install: install-tools install-git-hooks
install: install-tools install-git-hooks
.PHONY: generate
generate: install-counterfeiter
@echo "Running go generate..."
go generate ./...

View File

@ -0,0 +1,63 @@
package db
import (
"context"
"fmt"
"time"
"gitea.dwysokinski.me/twhelp/sessions/cmd/sessions/internal"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb"
"gitea.dwysokinski.me/twhelp/sessions/internal/service"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
)
const (
createAPIKeyTimeout = time.Second
)
func newAPIKeyCmd() *cli.Command {
return &cli.Command{
Name: "apikey",
Usage: "Manages api keys",
Subcommands: []*cli.Command{
newCreateAPIKeyCmd(),
},
}
}
func newCreateAPIKeyCmd() *cli.Command {
return &cli.Command{
Name: "create",
Usage: "Creates an API key",
Action: func(c *cli.Context) error {
logger := zap.L()
db, err := internal.NewBunDB()
if err != nil {
return fmt.Errorf("internal.NewBunDB: %w", err)
}
defer func() {
_ = db.Close()
}()
ctx, cancel := context.WithTimeout(c.Context, createAPIKeyTimeout)
defer cancel()
ak, err := service.NewAPIKey(bundb.NewAPIKey(db), service.NewUser(bundb.NewUser(db))).Create(ctx, c.Int64("userId"))
if err != nil {
return fmt.Errorf("APIKeyService.Create: %w", err)
}
logger.Info("API key created", zap.Int64("id", ak.ID), zap.String("key", ak.Key))
return nil
},
Flags: []cli.Flag{
&cli.Int64Flag{
Name: "userId",
Required: true,
},
},
}
}

View File

@ -10,6 +10,7 @@ func New() *cli.Command {
Subcommands: []*cli.Command{
newMigrationCmd(),
newUserCmd(),
newAPIKeyCmd(),
},
}
}

55
internal/bundb/api_key.go Normal file
View File

@ -0,0 +1,55 @@
package bundb
import (
"context"
"errors"
"fmt"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb/internal/model"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"github.com/jackc/pgerrcode"
"github.com/uptrace/bun"
"github.com/uptrace/bun/driver/pgdriver"
)
type APIKey struct {
db *bun.DB
}
func NewAPIKey(db *bun.DB) *APIKey {
return &APIKey{db: db}
}
func (a *APIKey) Create(ctx context.Context, params domain.CreateAPIKeyParams) (domain.APIKey, error) {
apiKey := model.NewAPIKey(params)
if _, err := a.db.NewInsert().
Model(&apiKey).
Returning("*").
Exec(ctx); err != nil {
return domain.APIKey{}, fmt.Errorf(
"something went wrong while inserting api key into the db: %w",
mapCreateAPIKeyError(err, params),
)
}
return apiKey.ToDomain(), nil
}
func mapCreateAPIKeyError(err error, params domain.CreateAPIKeyParams) error {
var pgError pgdriver.Error
if !errors.As(err, &pgError) {
return err
}
code := pgError.Field('C')
constraint := pgError.Field('n')
switch {
case code == pgerrcode.ForeignKeyViolation && constraint == "api_keys_user_id_fkey":
return domain.UserDoesNotExistError{
ID: params.UserID(),
}
default:
return err
}
}

View File

@ -0,0 +1,68 @@
package bundb_test
import (
"context"
"testing"
"time"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"github.com/google/uuid"
"github.com/jackc/pgerrcode"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/uptrace/bun/driver/pgdriver"
)
func TestAPIKey_Create(t *testing.T) {
t.Parallel()
db := newDB(t)
fixture := loadFixtures(t, db)
repo := bundb.NewAPIKey(db)
t.Run("OK", func(t *testing.T) {
t.Parallel()
params, err := domain.NewCreateAPIKeyParams(uuid.NewString(), fixture.user(t, "user-1").ID)
require.Nil(t, err)
apiKey, err := repo.Create(context.Background(), params)
assert.NoError(t, err)
assert.Greater(t, apiKey.ID, int64(0))
assert.Equal(t, params.Key(), apiKey.Key)
assert.Equal(t, params.UserID(), apiKey.UserID)
assert.WithinDuration(t, time.Now(), apiKey.CreatedAt, time.Second)
assert.Zero(t, apiKey.LastUsedAt)
})
t.Run("ERR: player doesn't exist", func(t *testing.T) {
t.Parallel()
params, err := domain.NewCreateAPIKeyParams(uuid.NewString(), fixture.user(t, "user-1").ID+1)
require.Nil(t, err)
apiKey, err := repo.Create(context.Background(), params)
assert.ErrorIs(t, err, domain.UserDoesNotExistError{
ID: params.UserID(),
})
assert.Zero(t, apiKey)
})
t.Run("ERR: key must be unique", func(t *testing.T) {
t.Parallel()
params, err := domain.NewCreateAPIKeyParams(
fixture.apiKey(t, "user-1-api-key-1").Key,
fixture.user(t, "user-1").ID,
)
require.Nil(t, err)
apiKey, err := repo.Create(context.Background(), params)
var pgError pgdriver.Error
assert.ErrorAs(t, err, &pgError)
assert.Equal(t, pgerrcode.UniqueViolation, pgError.Field('C'))
assert.Equal(t, "api_keys_key_key", pgError.Field('n'))
assert.Zero(t, apiKey)
})
}

View File

@ -147,9 +147,19 @@ func (f *bunfixture) user(tb testing.TB, id string) domain.User {
row, err := f.Row("User." + id)
require.NoError(tb, err)
g, ok := row.(*model.User)
u, ok := row.(*model.User)
require.True(tb, ok)
return g.ToDomain()
return u.ToDomain()
}
func (f *bunfixture) apiKey(tb testing.TB, id string) domain.APIKey {
tb.Helper()
row, err := f.Row("APIKey." + id)
require.NoError(tb, err)
ak, ok := row.(*model.APIKey)
require.True(tb, ok)
return ak.ToDomain()
}
func generateSchema() string {

View File

@ -3,14 +3,34 @@ package model
import (
"time"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"github.com/uptrace/bun"
)
type ApiKey struct {
type APIKey struct {
bun.BaseModel `bun:"base_model,table:api_keys,alias:ak"`
ID int64 `bun:"id,pk,autoincrement,identity"`
Key string `bun:"key,nullzero,type:varchar(255),notnull,unique"`
UserID int64 `bun:"user_id,nullzero,notnull"`
CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"`
ID int64 `bun:"id,pk,autoincrement,identity"`
Key string `bun:"key,nullzero,type:varchar(255),notnull,unique"`
UserID int64 `bun:"user_id,nullzero,notnull"`
CreatedAt time.Time `bun:"created_at,nullzero,notnull,default:current_timestamp"`
LastUsedAt time.Time `bun:"last_used_at,nullzero"`
}
func NewAPIKey(p domain.CreateAPIKeyParams) APIKey {
return APIKey{
Key: p.Key(),
UserID: p.UserID(),
CreatedAt: time.Now(),
}
}
func (a *APIKey) ToDomain() domain.APIKey {
return domain.APIKey{
ID: a.ID,
Key: a.Key,
UserID: a.UserID,
CreatedAt: a.CreatedAt,
LastUsedAt: a.LastUsedAt,
}
}

View File

@ -0,0 +1,31 @@
package model_test
import (
"testing"
"time"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb/internal/model"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestAPIKey(t *testing.T) {
t.Parallel()
var id int64 = 123
lastUsedAt := time.Now().Add(-5 * time.Minute)
params, err := domain.NewCreateAPIKeyParams(uuid.NewString(), 1)
assert.NoError(t, err)
result := model.NewAPIKey(params)
result.ID = id
result.LastUsedAt = lastUsedAt
apiKey := result.ToDomain()
assert.Equal(t, id, apiKey.ID)
assert.Equal(t, params.Key(), apiKey.Key)
assert.Equal(t, params.UserID(), apiKey.UserID)
assert.WithinDuration(t, time.Now(), apiKey.CreatedAt, 10*time.Millisecond)
assert.Equal(t, lastUsedAt, apiKey.LastUsedAt)
}

View File

@ -0,0 +1,31 @@
package migrations
import (
"context"
"fmt"
"gitea.dwysokinski.me/twhelp/sessions/internal/bundb/internal/model"
"github.com/uptrace/bun"
)
func init() {
Migrations.MustRegister(func(ctx context.Context, db *bun.DB) error {
if _, err := db.NewCreateTable().
Model(&model.APIKey{}).
Varchar(defaultVarcharLength).
ForeignKey(`(user_id) REFERENCES users (id) ON DELETE CASCADE`).
Exec(ctx); err != nil {
return fmt.Errorf("couldn't create the 'api_keys' table: %w", err)
}
return nil
}, func(ctx context.Context, db *bun.DB) error {
if _, err := db.NewDropTable().
Model(&model.APIKey{}).
IfExists().
Cascade().
Exec(ctx); err != nil {
return fmt.Errorf("couldn't drop the 'api_keys' table: %w", err)
}
return nil
})
}

View File

@ -4,4 +4,11 @@
id: 11111
name: User-1
name_lower: user-1
created_at: 2022-03-15T15:00:10.000Z
created_at: 2022-03-15T15:00:10.000Z
- model: APIKey
rows:
- _id: user-1-api-key-1
id: 11111
key: 4c0d2d63-4ef7-4c23-bb4d-f3646cc9658f
user_id: 11111
created_at: 2022-03-15T15:15:10.000Z

View File

@ -2,6 +2,7 @@ package bundb
import (
"context"
"database/sql"
"errors"
"fmt"
@ -36,6 +37,26 @@ func (u *User) Create(ctx context.Context, params domain.CreateUserParams) (doma
return user.ToDomain(), nil
}
func (u *User) Get(ctx context.Context, id int64) (domain.User, error) {
var user model.User
if err := u.db.NewSelect().
Model(&user).
Where("id = ?", id).
Scan(ctx); err != nil {
if errors.Is(err, sql.ErrNoRows) {
return domain.User{}, domain.UserNotFoundError{ID: id}
}
return domain.User{}, fmt.Errorf(
"something went wrong while selecting user (id=%d) from the db: %w",
id,
err,
)
}
return user.ToDomain(), nil
}
func mapCreateUserError(err error, params domain.CreateUserParams) error {
var pgError pgdriver.Error
if !errors.As(err, &pgError) {

View File

@ -60,3 +60,30 @@ func TestUser_Create(t *testing.T) {
}
})
}
func TestUser_Get(t *testing.T) {
t.Parallel()
db := newDB(t)
fixture := loadFixtures(t, db)
repo := bundb.NewUser(db)
userFromFixture := fixture.user(t, "user-1")
t.Run("OK", func(t *testing.T) {
t.Parallel()
user, err := repo.Get(context.Background(), userFromFixture.ID)
assert.NoError(t, err)
assert.Equal(t, userFromFixture, user)
})
t.Run("ERR: user not found", func(t *testing.T) {
t.Parallel()
user, err := repo.Get(context.Background(), userFromFixture.ID+1)
assert.ErrorIs(t, err, domain.UserNotFoundError{
ID: userFromFixture.ID + 1,
})
assert.Zero(t, user)
})
}

View File

@ -0,0 +1,50 @@
package domain
import (
"time"
)
const (
apiKeyUserIDMin = 1
)
type APIKey struct {
ID int64
Key string
UserID int64
CreatedAt time.Time
LastUsedAt time.Time
}
type CreateAPIKeyParams struct {
key string
userID int64
}
func NewCreateAPIKeyParams(key string, userID int64) (CreateAPIKeyParams, error) {
if key == "" {
return CreateAPIKeyParams{}, ValidationError{
Field: "Key",
Err: ErrRequired,
}
}
if userID < apiKeyUserIDMin {
return CreateAPIKeyParams{}, ValidationError{
Field: "UserID",
Err: MinError{
Min: apiKeyUserIDMin,
},
}
}
return CreateAPIKeyParams{key: key, userID: userID}, nil
}
func (c CreateAPIKeyParams) Key() string {
return c.key
}
func (c CreateAPIKeyParams) UserID() int64 {
return c.userID
}

View File

@ -0,0 +1,64 @@
package domain_test
import (
"testing"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestNewCreateAPIKeyParams(t *testing.T) {
t.Parallel()
tests := []struct {
name string
key string
userID int64
expectedErr error
}{
{
name: "OK",
key: uuid.NewString(),
userID: 1,
expectedErr: nil,
},
{
name: "ERR: username is required",
key: "",
expectedErr: domain.ValidationError{
Field: "Key",
Err: domain.ErrRequired,
},
},
{
name: "ERR: userID < 1",
key: uuid.NewString(),
userID: 0,
expectedErr: domain.ValidationError{
Field: "UserID",
Err: domain.MinError{
Min: 1,
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
params, err := domain.NewCreateAPIKeyParams(tt.key, tt.userID)
if tt.expectedErr != nil {
assert.Equal(t, tt.expectedErr, err)
assert.Zero(t, params)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.key, params.Key())
assert.Equal(t, tt.userID, params.UserID())
})
}
}

View File

@ -91,3 +91,19 @@ func (e MaxLengthError) UserError() string {
func (e MaxLengthError) Code() ErrorCode {
return ErrorCodeValidationError
}
type MinError struct {
Min int
}
func (e MinError) Error() string {
return fmt.Sprintf("must be no less than %d", e.Min)
}
func (e MinError) UserError() string {
return e.Error()
}
func (e MinError) Code() ErrorCode {
return ErrorCodeValidationError
}

View File

@ -14,6 +14,7 @@ func TestErrorCode_String(t *testing.T) {
assert.Equal(t, domain.ErrorCodeUnknown.String(), "internal-server-error")
assert.Equal(t, domain.ErrorCodeEntityNotFound.String(), "entity-not-found")
assert.Equal(t, domain.ErrorCodeValidationError.String(), "validation-error")
assert.Equal(t, domain.ErrorCodeAlreadyExists.String(), "already-exists")
}
func TestValidationError(t *testing.T) {
@ -53,3 +54,15 @@ func TestMaxLengthError(t *testing.T) {
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}
func TestMinError(t *testing.T) {
t.Parallel()
err := domain.MinError{
Min: 25,
}
var _ domain.Error = err
assert.Equal(t, "must be no less than 25", err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}

View File

@ -61,9 +61,7 @@ func NewCreateUserParams(name string) (CreateUserParams, error) {
}
}
return CreateUserParams{
name: name,
}, nil
return CreateUserParams{name: name}, nil
}
func (c CreateUserParams) Name() string {
@ -85,3 +83,35 @@ func (e UsernameAlreadyTakenError) UserError() string {
func (e UsernameAlreadyTakenError) Code() ErrorCode {
return ErrorCodeAlreadyExists
}
type UserDoesNotExistError struct {
ID int64
}
func (e UserDoesNotExistError) Error() string {
return fmt.Sprintf("user (ID=%d) doesn't exist", e.ID)
}
func (e UserDoesNotExistError) UserError() string {
return e.Error()
}
func (e UserDoesNotExistError) Code() ErrorCode {
return ErrorCodeValidationError
}
type UserNotFoundError struct {
ID int64
}
func (e UserNotFoundError) Error() string {
return fmt.Sprintf("user (ID=%d) not found", e.ID)
}
func (e UserNotFoundError) UserError() string {
return e.Error()
}
func (e UserNotFoundError) Code() ErrorCode {
return ErrorCodeEntityNotFound
}

View File

@ -2,6 +2,7 @@ package domain_test
import (
"fmt"
"math/rand"
"testing"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
@ -14,7 +15,6 @@ func TestNewCreateUserParams(t *testing.T) {
tests := []struct {
name string
username string
apiKey string
expectedErr error
}{
{
@ -40,6 +40,16 @@ func TestNewCreateUserParams(t *testing.T) {
},
},
},
{
name: "ERR: len(username) > 40",
username: randString(41),
expectedErr: domain.ValidationError{
Field: "Name",
Err: domain.MaxLengthError{
Max: 40,
},
},
},
{
name: "ERR: username - hyphen at the end",
username: "u-",
@ -103,3 +113,36 @@ func TestUsernameAlreadyTakenError(t *testing.T) {
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeAlreadyExists, err.Code())
}
func TestUserDoesNotExistError(t *testing.T) {
t.Parallel()
err := domain.UserDoesNotExistError{
ID: 12345,
}
var _ domain.Error = err
assert.Equal(t, fmt.Sprintf("user (ID=%d) doesn't exist", err.ID), err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}
func TestUserNotFoundError(t *testing.T) {
t.Parallel()
err := domain.UserNotFoundError{
ID: 12345,
}
var _ domain.Error = err
assert.Equal(t, fmt.Sprintf("user (ID=%d) not found", err.ID), err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeEntityNotFound, err.Code())
}
func randString(length int) string {
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}

View File

@ -0,0 +1,56 @@
package service
import (
"context"
"errors"
"fmt"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"github.com/google/uuid"
)
//counterfeiter:generate -o internal/mock/api_key_repository.gen.go . APIKeyRepository
type APIKeyRepository interface {
Create(ctx context.Context, params domain.CreateAPIKeyParams) (domain.APIKey, error)
}
//counterfeiter:generate -o internal/mock/user_getter.gen.go . UserGetter
type UserGetter interface {
Get(ctx context.Context, id int64) (domain.User, error)
}
type APIKey struct {
repo APIKeyRepository
userSvc UserGetter
}
func NewAPIKey(repo APIKeyRepository, userSvc UserGetter) *APIKey {
return &APIKey{repo: repo, userSvc: userSvc}
}
func (a *APIKey) Create(ctx context.Context, userID int64) (domain.APIKey, error) {
user, err := a.userSvc.Get(ctx, userID)
if err != nil {
if errors.Is(err, domain.UserNotFoundError{ID: userID}) {
return domain.APIKey{}, domain.UserDoesNotExistError{ID: userID}
}
return domain.APIKey{}, fmt.Errorf("UserService.Get: %w", err)
}
key, err := uuid.NewRandom()
if err != nil {
return domain.APIKey{}, fmt.Errorf("couldn't generate api key: %w", err)
}
params, err := domain.NewCreateAPIKeyParams(key.String(), user.ID)
if err != nil {
return domain.APIKey{}, err
}
apiKey, err := a.repo.Create(ctx, params)
if err != nil {
return domain.APIKey{}, fmt.Errorf("APIKeyRepository.Create: %w", err)
}
return apiKey, nil
}

View File

@ -0,0 +1,55 @@
package service_test
import (
"context"
"testing"
"time"
"gitea.dwysokinski.me/twhelp/sessions/internal/domain"
"gitea.dwysokinski.me/twhelp/sessions/internal/service"
"gitea.dwysokinski.me/twhelp/sessions/internal/service/internal/mock"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func TestAPIKey_Create(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
apiKeyRepo := &mock.FakeAPIKeyRepository{}
apiKeyRepo.CreateCalls(func(ctx context.Context, params domain.CreateAPIKeyParams) (domain.APIKey, error) {
return domain.APIKey{
ID: 1,
Key: params.Key(),
UserID: params.UserID(),
CreatedAt: time.Now(),
}, nil
})
userSvc := &mock.FakeUserGetter{}
user := domain.User{ID: 1234}
userSvc.GetReturns(user, nil)
ak, err := service.NewAPIKey(apiKeyRepo, userSvc).Create(context.Background(), user.ID)
assert.NoError(t, err)
assert.Greater(t, ak.ID, int64(0))
_, err = uuid.Parse(ak.Key)
assert.NoError(t, err)
assert.Equal(t, user.ID, ak.UserID)
assert.WithinDuration(t, time.Now(), ak.CreatedAt, 100*time.Millisecond)
})
t.Run("ERR: user doesnt exist", func(t *testing.T) {
t.Parallel()
userSvc := &mock.FakeUserGetter{}
var userID int64 = 1234
userSvc.GetReturns(domain.User{}, domain.UserNotFoundError{ID: userID})
ak, err := service.NewAPIKey(nil, userSvc).Create(context.Background(), userID)
assert.ErrorIs(t, err, domain.UserDoesNotExistError{ID: userID})
assert.Zero(t, ak)
})
}

View File

@ -0,0 +1 @@
*.go

View File

@ -0,0 +1,3 @@
package service
//go:generate counterfeiter -generate

View File

@ -9,6 +9,7 @@ import (
type UserRepository interface {
Create(ctx context.Context, params domain.CreateUserParams) (domain.User, error)
Get(ctx context.Context, id int64) (domain.User, error)
}
type User struct {
@ -24,6 +25,13 @@ func (u *User) Create(ctx context.Context, params domain.CreateUserParams) (doma
if err != nil {
return domain.User{}, fmt.Errorf("UserRepository.Create: %w", err)
}
return user, nil
}
func (u *User) Get(ctx context.Context, id int64) (domain.User, error) {
user, err := u.repo.Get(ctx, id)
if err != nil {
return domain.User{}, fmt.Errorf("UserRepository.Get: %w", err)
}
return user, nil
}

View File

@ -2,7 +2,10 @@ module gitea.dwysokinski.me/twhelp/sessions/internal/tools
go 1.19
require github.com/golangci/golangci-lint v1.50.1
require (
github.com/golangci/golangci-lint v1.50.1
github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0
)
require (
4d63.com/gochecknoglobals v0.1.0 // indirect

View File

@ -358,6 +358,8 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0 h1:rBhB9Rls+yb8kA4x5a/cWxOufWfXt24E+kq4YlbGj3g=
github.com/maxbrunsfeld/counterfeiter/v6 v6.5.0/go.mod h1:fJ0UAZc1fx3xZhU4eSHQDJ1ApFmTVhp5VTpV9tm2ogg=
github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo=
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
github.com/mgechev/revive v1.2.4 h1:+2Hd/S8oO2H0Ikq2+egtNwQsVhAeELHjxjIUFX5ajLI=
@ -379,7 +381,6 @@ github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nishanths/exhaustive v0.8.3 h1:pw5O09vwg8ZaditDp/nQRqVnrMczSJDxRDJMowvhsrM=
github.com/nishanths/exhaustive v0.8.3/go.mod h1:qj+zJJUgJ76tR92+25+03oYUhzF4R7/2Wk7fGTfCHmg=
@ -458,6 +459,7 @@ github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tM
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
github.com/sashamelentyev/usestdlibvars v1.20.0 h1:K6CXjqqtSYSsuyRDDC7Sjn6vTMLiSJa4ZmDkiokoqtw=
github.com/sashamelentyev/usestdlibvars v1.20.0/go.mod h1:0GaP+ecfZMXShS0A94CJn6aEuPRILv8h/VuWI9n1ygg=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/securego/gosec/v2 v2.13.1 h1:7mU32qn2dyC81MH9L2kefnQyRMUarfDER3iQyMHcjYM=
github.com/securego/gosec/v2 v2.13.1/go.mod h1:EO1sImBMBWFjOTFzMWfTRrZW6M15gm60ljzrmy/wtHo=
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
@ -926,8 +928,8 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@ -5,4 +5,5 @@ package tools
import (
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
_ "github.com/maxbrunsfeld/counterfeiter/v6"
)