From cf0b53e52d5dc7b57f4f5365af7cde7270fd6b33 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 27 Sep 2021 02:13:38 +0700 Subject: [PATCH 01/21] refactor: better dependency injection method --- api/app/v1/app.go | 54 ++- api/app/v1/core/joke_getter.go | 26 +- api/app/v1/core/joke_setter.go | 4 +- api/app/v1/core/schema.go | 26 + api/app/v1/core/submit_setter.go | 3 +- api/app/v1/handler/builder.go | 16 - api/app/v1/handler/health/health.go | 19 +- api/app/v1/handler/health/health_test.go | 45 +- api/app/v1/handler/health/schema.go | 5 + api/app/v1/handler/joke/dependencies.go | 17 + api/app/v1/handler/joke/dependencies_test.go | 78 +++ api/app/v1/handler/joke/joke_add.go | 20 +- api/app/v1/handler/joke/joke_add_test.go | 18 +- api/app/v1/handler/joke/joke_delete.go | 20 +- api/app/v1/handler/joke/joke_delete_test.go | 21 +- api/app/v1/handler/joke/joke_get.go | 38 +- api/app/v1/handler/joke/joke_get_test.go | 94 ++-- api/app/v1/handler/joke/joke_total.go | 18 +- api/app/v1/handler/joke/joke_total_test.go | 11 +- api/app/v1/handler/joke/joke_update.go | 29 +- api/app/v1/handler/joke/joke_update_test.go | 23 +- .../joke.go => handler/joke/schema.go} | 14 +- api/app/v1/handler/submit/dependencies.go | 17 + .../v1/handler/submit/dependencies_test.go | 58 +++ .../submit.go => handler/submit/schema.go} | 16 +- api/app/v1/handler/submit/submit_add.go | 26 +- api/app/v1/handler/submit/submit_get.go | 10 +- api/app/v1/handler/submit/submit_get_test.go | 55 +-- api/app/v1/middleware/auth.go | 17 +- .../general.go => middleware/schema.go} | 6 +- api/app/v1/middleware/validation.go | 3 +- api/app/v1/models/errors.go | 14 - api/app/v1/platform/cache/cache.go | 16 - api/app/v1/platform/cache/redis.go | 19 - api/app/v1/platform/database/create.go | 7 +- api/app/v1/platform/database/postgres.go | 27 -- api/app/v1/routes/dependencies.go | 19 + api/app/v1/routes/health.go | 9 +- api/app/v1/routes/joke.go | 25 +- api/app/v1/routes/submit.go | 17 +- api/go.mod | 46 +- api/go.sum | 455 ++++++++++++++++++ 42 files changed, 1089 insertions(+), 372 deletions(-) create mode 100644 api/app/v1/core/schema.go delete mode 100644 api/app/v1/handler/builder.go create mode 100644 api/app/v1/handler/health/schema.go create mode 100644 api/app/v1/handler/joke/dependencies.go create mode 100644 api/app/v1/handler/joke/dependencies_test.go rename api/app/v1/{models/joke.go => handler/joke/schema.go} (58%) create mode 100644 api/app/v1/handler/submit/dependencies.go create mode 100644 api/app/v1/handler/submit/dependencies_test.go rename api/app/v1/{models/submit.go => handler/submit/schema.go} (65%) rename api/app/v1/{models/general.go => middleware/schema.go} (77%) delete mode 100644 api/app/v1/models/errors.go delete mode 100644 api/app/v1/platform/cache/cache.go delete mode 100644 api/app/v1/platform/cache/redis.go delete mode 100644 api/app/v1/platform/database/postgres.go create mode 100644 api/app/v1/routes/dependencies.go diff --git a/api/app/v1/app.go b/api/app/v1/app.go index bccbe98..6442858 100644 --- a/api/app/v1/app.go +++ b/api/app/v1/app.go @@ -1,31 +1,59 @@ package v1 import ( + "context" "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/platform/cache" "jokes-bapak2-api/app/v1/platform/database" "jokes-bapak2-api/app/v1/routes" "log" "os" "time" + "github.com/Masterminds/squirrel" + "github.com/allegro/bigcache/v3" "github.com/getsentry/sentry-go" + "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/etag" + "github.com/gojek/heimdall/v7/httpclient" + "github.com/jackc/pgx/v4/pgxpool" ) -var memory = cache.InMemory() -var db = database.New() - func New() *fiber.App { + // Setup PostgreSQL + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatalln("Unable to create pool config", err) + } + poolConfig.MaxConns = 15 + poolConfig.MinConns = 2 + + db, err := pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + + // Setup Redis + opt, err := redis.ParseURL(os.Getenv("REDIS_URL")) + if err != nil { + log.Fatalln(err) + } + rdb := redis.NewClient(opt) + + // Setup In Memory + memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) + if err != nil { + log.Fatalln(err) + } + app := fiber.New(fiber.Config{ DisableKeepalive: true, CaseSensitive: true, ErrorHandler: errorHandler, }) - err := sentry.Init(sentry.ClientOptions{ + err = sentry.Init(sentry.ClientOptions{ Dsn: os.Getenv("SENTRY_DSN"), Environment: os.Getenv("ENV"), AttachStacktrace: true, @@ -39,7 +67,7 @@ func New() *fiber.App { defer sentry.Flush(2 * time.Second) - err = database.Setup() + err = database.Setup(db) if err != nil { sentry.CaptureException(err) log.Fatal(err) @@ -57,9 +85,17 @@ func New() *fiber.App { app.Use(cors.New()) app.Use(etag.New()) - routes.Health(app) - routes.Joke(app) - routes.Submit(app) + route := routes.Dependencies{ + DB: db, + Redis: rdb, + Memory: memory, + HTTP: httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)), + Query: squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar), + App: app, + } + route.Health() + route.Joke() + route.Submit() return app } diff --git a/api/app/v1/core/joke_getter.go b/api/app/v1/core/joke_getter.go index 6297293..b2890ad 100644 --- a/api/app/v1/core/joke_getter.go +++ b/api/app/v1/core/joke_getter.go @@ -2,7 +2,7 @@ package core import ( "context" - "jokes-bapak2-api/app/v1/models" + "errors" "math/rand" "github.com/allegro/bigcache/v3" @@ -14,7 +14,7 @@ import ( // GetAllJSONJokes fetch the database for all the jokes then output it as a JSON []byte. // Keep in mind, you will need to store it to memory yourself. func GetAllJSONJokes(db *pgxpool.Pool) ([]byte, error) { - var jokes []models.Joke + var jokes []Joke results, err := db.Query(context.Background(), "SELECT \"id\",\"link\" FROM \"jokesbapak2\" ORDER BY \"id\"") if err != nil { return nil, err @@ -39,13 +39,13 @@ func GetAllJSONJokes(db *pgxpool.Pool) ([]byte, error) { func GetRandomJokeFromCache(memory *bigcache.BigCache) (string, error) { jokes, err := memory.Get("jokes") if err != nil { - if err.Error() == "Entry not found" { - return "", models.ErrNotFound + if errors.Is(err, bigcache.ErrEntryNotFound) { + return "", ErrNotFound } return "", err } - var data []models.Joke + var data []Joke err = ffjson.Unmarshal(jokes, &data) if err != nil { return "", nil @@ -54,7 +54,7 @@ func GetRandomJokeFromCache(memory *bigcache.BigCache) (string, error) { // Return an error if the database is empty dataLength := len(data) if dataLength == 0 { - return "", models.ErrEmpty + return "", ErrEmpty } random := rand.Intn(dataLength) @@ -67,7 +67,7 @@ func GetRandomJokeFromCache(memory *bigcache.BigCache) (string, error) { func CheckJokesCache(memory *bigcache.BigCache) (bool, error) { _, err := memory.Get("jokes") if err != nil { - if err.Error() == "Entry not found" { + if errors.Is(err, bigcache.ErrEntryNotFound) { return false, nil } return false, err @@ -80,7 +80,7 @@ func CheckJokesCache(memory *bigcache.BigCache) (bool, error) { func CheckTotalJokesCache(memory *bigcache.BigCache) (bool, error) { _, err := memory.Get("total") if err != nil { - if err.Error() == "Entry not found" { + if errors.Is(err, bigcache.ErrEntryNotFound) { return false, nil } return false, err @@ -93,13 +93,13 @@ func CheckTotalJokesCache(memory *bigcache.BigCache) (bool, error) { func GetCachedJokeByID(memory *bigcache.BigCache, id int) (string, error) { jokes, err := memory.Get("jokes") if err != nil { - if err.Error() == "Entry not found" { - return "", models.ErrNotFound + if errors.Is(err, bigcache.ErrEntryNotFound) { + return "", ErrNotFound } return "", err } - var data []models.Joke + var data []Joke err = ffjson.Unmarshal(jokes, &data) if err != nil { return "", nil @@ -119,8 +119,8 @@ func GetCachedJokeByID(memory *bigcache.BigCache, id int) (string, error) { func GetCachedTotalJokes(memory *bigcache.BigCache) (int, error) { total, err := memory.Get("total") if err != nil { - if err.Error() == "Entry not found" { - return 0, models.ErrNotFound + if errors.Is(err, bigcache.ErrEntryNotFound) { + return 0, ErrNotFound } return 0, err } diff --git a/api/app/v1/core/joke_setter.go b/api/app/v1/core/joke_setter.go index 2412822..d77c611 100644 --- a/api/app/v1/core/joke_setter.go +++ b/api/app/v1/core/joke_setter.go @@ -1,8 +1,6 @@ package core import ( - "jokes-bapak2-api/app/v1/models" - "github.com/allegro/bigcache/v3" "github.com/jackc/pgx/v4/pgxpool" "github.com/pquerna/ffjson/ffjson" @@ -39,7 +37,7 @@ func SetTotalJoke(db *pgxpool.Pool, memory *bigcache.BigCache) error { return err } - var data []models.Joke + var data []Joke err = ffjson.Unmarshal(jokes, &data) if err != nil { return err diff --git a/api/app/v1/core/schema.go b/api/app/v1/core/schema.go new file mode 100644 index 0000000..b66d86b --- /dev/null +++ b/api/app/v1/core/schema.go @@ -0,0 +1,26 @@ +package core + +import "errors" + +type Joke struct { + ID int `json:"id" form:"id" db:"id"` + Link string `json:"link" form:"link" db:"link"` + Creator int `json:"creator" form:"creator" db:"creator"` +} + +type ImageAPI struct { + Data ImageAPIData `json:"data"` + Success bool `json:"success"` + Status int `json:"status"` +} + +type ImageAPIData struct { + ID string `json:"id"` + Title string `json:"title"` + URLViewer string `json:"url_viewer"` + URL string `json:"url"` + DisplayURL string `json:"display_url"` +} + +var ErrNotFound = errors.New("record not found") +var ErrEmpty = errors.New("record is empty") diff --git a/api/app/v1/core/submit_setter.go b/api/app/v1/core/submit_setter.go index 231c704..4fb1119 100644 --- a/api/app/v1/core/submit_setter.go +++ b/api/app/v1/core/submit_setter.go @@ -4,7 +4,6 @@ import ( "bytes" "io" "io/ioutil" - "jokes-bapak2-api/app/v1/models" "jokes-bapak2-api/app/v1/utils" "mime/multipart" "net/http" @@ -71,7 +70,7 @@ func UploadImage(client *httpclient.Client, image io.Reader) (string, error) { return "", err } - var data models.ImageAPI + var data ImageAPI err = ffjson.Unmarshal(responseBody, &data) if err != nil { return "", err diff --git a/api/app/v1/handler/builder.go b/api/app/v1/handler/builder.go deleted file mode 100644 index 6121335..0000000 --- a/api/app/v1/handler/builder.go +++ /dev/null @@ -1,16 +0,0 @@ -package handler - -import ( - "jokes-bapak2-api/app/v1/platform/cache" - "jokes-bapak2-api/app/v1/platform/database" - "time" - - "github.com/Masterminds/squirrel" - "github.com/gojek/heimdall/v7/httpclient" -) - -var Psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) -var Db = database.New() -var Redis = cache.New() -var Memory = cache.InMemory() -var Client = httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)) diff --git a/api/app/v1/handler/health/health.go b/api/app/v1/handler/health/health.go index 128cda8..a2b43f4 100644 --- a/api/app/v1/handler/health/health.go +++ b/api/app/v1/handler/health/health.go @@ -2,28 +2,33 @@ package health import ( "context" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" + "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4/pgxpool" ) -func Health(c *fiber.Ctx) error { +type Dependencies struct { + DB *pgxpool.Pool + Redis *redis.Client +} + +func (d *Dependencies) Health(c *fiber.Ctx) error { // Ping REDIS database - err := handler.Redis.Ping(context.Background()).Err() + err := d.Redis.Ping(context.Background()).Err() if err != nil { return c. Status(fiber.StatusServiceUnavailable). - JSON(models.Error{ + JSON(Error{ Error: "REDIS: " + err.Error(), }) } - _, err = handler.Db.Query(context.Background(), "SELECT \"id\" FROM \"jokesbapak2\" LIMIT 1") + _, err = d.DB.Query(context.Background(), "SELECT \"id\" FROM \"jokesbapak2\" LIMIT 1") if err != nil { return c. Status(fiber.StatusServiceUnavailable). - JSON(models.Error{ + JSON(Error{ Error: "POSTGRESQL: " + err.Error(), }) } diff --git a/api/app/v1/handler/health/health_test.go b/api/app/v1/handler/health/health_test.go index cf55b70..d4f1899 100644 --- a/api/app/v1/handler/health/health_test.go +++ b/api/app/v1/handler/health/health_test.go @@ -4,8 +4,9 @@ import ( "context" "io/ioutil" v1 "jokes-bapak2-api/app/v1" - "jokes-bapak2-api/app/v1/platform/database" + "log" "net/http" + "os" "testing" "time" @@ -15,39 +16,61 @@ import ( "github.com/stretchr/testify/assert" ) -var db *pgxpool.Pool = database.New() var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} var app *fiber.App = v1.New() +var db *pgxpool.Pool func cleanup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatalln("Unable to create pool config", err) + } + poolConfig.MaxConns = 5 + poolConfig.MinConns = 2 + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + j, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"") if err != nil { panic(err) } + defer j.Close() + a, err := db.Query(context.Background(), "DROP TABLE \"administrators\"") if err != nil { panic(err) } - defer j.Close() defer a.Close() } func setup() error { - err := database.Setup() + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { - return err + log.Fatalln("Unable to create pool config", err) } + poolConfig.MaxConns = 5 + poolConfig.MinConns = 2 + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + a, err := db.Query(context.Background(), "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4);", 1, "very secure", "not the real one", time.Now().Format(time.RFC3339)) if err != nil { return err } + defer a.Close() + j, err := db.Query(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) if err != nil { return err } - defer a.Close() defer j.Close() return nil @@ -62,11 +85,19 @@ func TestHealth(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/health", nil) - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "health") assert.Equalf(t, 200, res.StatusCode, "health") assert.NotEqualf(t, 0, res.ContentLength, "health") _, err = ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "health") } diff --git a/api/app/v1/handler/health/schema.go b/api/app/v1/handler/health/schema.go new file mode 100644 index 0000000..cb99e1f --- /dev/null +++ b/api/app/v1/handler/health/schema.go @@ -0,0 +1,5 @@ +package health + +type Error struct { + Error string `json:"error"` +} diff --git a/api/app/v1/handler/joke/dependencies.go b/api/app/v1/handler/joke/dependencies.go new file mode 100644 index 0000000..75d0b4c --- /dev/null +++ b/api/app/v1/handler/joke/dependencies.go @@ -0,0 +1,17 @@ +package joke + +import ( + "github.com/Masterminds/squirrel" + "github.com/allegro/bigcache/v3" + "github.com/go-redis/redis/v8" + "github.com/gojek/heimdall/v7/httpclient" + "github.com/jackc/pgx/v4/pgxpool" +) + +type Dependencies struct { + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType +} diff --git a/api/app/v1/handler/joke/dependencies_test.go b/api/app/v1/handler/joke/dependencies_test.go new file mode 100644 index 0000000..963acae --- /dev/null +++ b/api/app/v1/handler/joke/dependencies_test.go @@ -0,0 +1,78 @@ +package joke_test + +import ( + "context" + v1 "jokes-bapak2-api/app/v1" + "jokes-bapak2-api/app/v1/platform/database" + "log" + "os" + + "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4/pgxpool" +) + +var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} +var app *fiber.App = v1.New() +var db *pgxpool.Pool + +func cleanup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatalln("Unable to create pool config", err) + } + poolConfig.MaxConns = 5 + poolConfig.MinConns = 2 + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + + j, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"") + if err != nil { + panic(err) + } + defer j.Close() + + a, err := db.Query(context.Background(), "DROP TABLE \"administrators\"") + if err != nil { + panic(err) + } + + defer a.Close() +} + +func setup() error { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatalln("Unable to create pool config", err) + } + poolConfig.MaxConns = 15 + poolConfig.MinConns = 2 + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + + err = database.Setup(db) + if err != nil { + return err + } + + a, err := db.Query(context.Background(), "INSERT INTO \"administrators\" (\"id\", \"key\", \"token\", \"last_used\") VALUES (1, 'test', '$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106', '');") + if err != nil { + return err + } + + defer a.Close() + + j, err := db.Query(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) + if err != nil { + return err + } + + defer j.Close() + + return nil +} diff --git a/api/app/v1/handler/joke/joke_add.go b/api/app/v1/handler/joke/joke_add.go index 30c0989..9d0935d 100644 --- a/api/app/v1/handler/joke/joke_add.go +++ b/api/app/v1/handler/joke/joke_add.go @@ -4,21 +4,19 @@ import ( "context" "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" "github.com/gofiber/fiber/v2" ) -func AddNewJoke(c *fiber.Ctx) error { - var body models.Joke +func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { + var body core.Joke err := c.BodyParser(&body) if err != nil { return err } // Check link validity - valid, err := core.CheckImageValidity(handler.Client, body.Link) + valid, err := core.CheckImageValidity(d.HTTP, body.Link) if err != nil { return err } @@ -26,12 +24,12 @@ func AddNewJoke(c *fiber.Ctx) error { if !valid { return c. Status(fiber.StatusBadRequest). - JSON(models.Error{ + JSON(Error{ Error: "URL provided is not a valid image", }) } - sql, args, err := handler.Psql. + sql, args, err := d.Query. Insert("jokesbapak2"). Columns("link", "creator"). Values(body.Link, c.Locals("userID")). @@ -41,25 +39,25 @@ func AddNewJoke(c *fiber.Ctx) error { } // TODO: Implement solution if the link provided already exists. - r, err := handler.Db.Query(context.Background(), sql, args...) + r, err := d.DB.Query(context.Background(), sql, args...) if err != nil { return err } defer r.Close() - err = core.SetAllJSONJoke(handler.Db, handler.Memory) + err = core.SetAllJSONJoke(d.DB, d.Memory) if err != nil { return err } - err = core.SetTotalJoke(handler.Db, handler.Memory) + err = core.SetTotalJoke(d.DB, d.Memory) if err != nil { return err } return c. Status(fiber.StatusCreated). - JSON(models.ResponseJoke{ + JSON(ResponseJoke{ Link: body.Link, }) } diff --git a/api/app/v1/handler/joke/joke_add_test.go b/api/app/v1/handler/joke/joke_add_test.go index ada5cd0..942f906 100644 --- a/api/app/v1/handler/joke/joke_add_test.go +++ b/api/app/v1/handler/joke/joke_add_test.go @@ -5,6 +5,7 @@ import ( "net/http" "strings" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -23,8 +24,11 @@ func TestAddNewJoke_201(t *testing.T) { req, _ := http.NewRequest("PUT", "/", reqBody) req.Header.Set("content-type", "application/json") req.Header.Add("accept", "application/json") - res, err := app.Test(req, -1) - + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } + assert.Equalf(t, false, err != nil, "joke add") assert.Equalf(t, 201, res.StatusCode, "joke add") assert.NotEqualf(t, 0, res.ContentLength, "joke add") @@ -47,11 +51,19 @@ func TestAddNewJoke_NotValidImage(t *testing.T) { req, _ := http.NewRequest("PUT", "/", reqBody) req.Header.Set("content-type", "application/json") req.Header.Add("accept", "application/json") - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "joke add") assert.Equalf(t, 400, res.StatusCode, "joke add") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke add") assert.Equalf(t, "{\"error\":\"URL provided is not a valid image\"}", string(body), "joke add") } diff --git a/api/app/v1/handler/joke/joke_delete.go b/api/app/v1/handler/joke/joke_delete.go index d3b7c69..765c55f 100644 --- a/api/app/v1/handler/joke/joke_delete.go +++ b/api/app/v1/handler/joke/joke_delete.go @@ -5,21 +5,19 @@ import ( "strconv" "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" ) -func DeleteJoke(c *fiber.Ctx) error { +func (d *Dependencies) DeleteJoke(c *fiber.Ctx) error { id, err := strconv.Atoi(c.Params("id")) if err != nil { return err } // Check if the joke exists - sql, args, err := handler.Psql. + sql, args, err := d.Query. Select("id"). From("jokesbapak2"). Where(squirrel.Eq{"id": id}). @@ -29,13 +27,13 @@ func DeleteJoke(c *fiber.Ctx) error { } var jokeID int - err = handler.Db.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + err = d.DB.QueryRow(context.Background(), sql, args...).Scan(&jokeID) if err != nil { return err } if jokeID == id { - sql, args, err = handler.Psql. + sql, args, err = d.Query. Delete("jokesbapak2"). Where(squirrel.Eq{"id": id}). ToSql() @@ -43,31 +41,31 @@ func DeleteJoke(c *fiber.Ctx) error { return err } - r, err := handler.Db.Query(context.Background(), sql, args...) + r, err := d.DB.Query(context.Background(), sql, args...) if err != nil { return err } defer r.Close() - err = core.SetAllJSONJoke(handler.Db, handler.Memory) + err = core.SetAllJSONJoke(d.DB, d.Memory) if err != nil { return err } - err = core.SetTotalJoke(handler.Db, handler.Memory) + err = core.SetTotalJoke(d.DB, d.Memory) if err != nil { return err } return c. Status(fiber.StatusOK). - JSON(models.ResponseJoke{ + JSON(ResponseJoke{ Message: "specified joke id has been deleted", }) } return c. Status(fiber.StatusNotAcceptable). - JSON(models.Error{ + JSON(Error{ Error: "specified joke id does not exists", }) } diff --git a/api/app/v1/handler/joke/joke_delete_test.go b/api/app/v1/handler/joke/joke_delete_test.go index 3a9531f..edce32c 100644 --- a/api/app/v1/handler/joke/joke_delete_test.go +++ b/api/app/v1/handler/joke/joke_delete_test.go @@ -6,6 +6,7 @@ import ( "net/http" "strings" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -30,12 +31,20 @@ func TestDeleteJoke_200(t *testing.T) { req, _ := http.NewRequest("DELETE", "/id/100", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "joke delete") assert.Equalf(t, 200, res.StatusCode, "joke delete") assert.NotEqualf(t, 0, res.ContentLength, "joke delete") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke delete") assert.Equalf(t, "{\"message\":\"specified joke id has been deleted\"}", string(body), "joke delete") } @@ -53,12 +62,20 @@ func TestDeleteJoke_NotExists(t *testing.T) { req, _ := http.NewRequest("DELETE", "/id/100", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "joke delete") assert.Equalf(t, 406, res.StatusCode, "joke delete") assert.NotEqualf(t, 0, res.ContentLength, "joke delete") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke delete") assert.Equalf(t, "{\"message\":\"specified joke id does not exists\"}", string(body), "joke delete") } diff --git a/api/app/v1/handler/joke/joke_get.go b/api/app/v1/handler/joke/joke_get.go index cab33a8..690350f 100644 --- a/api/app/v1/handler/joke/joke_get.go +++ b/api/app/v1/handler/joke/joke_get.go @@ -7,19 +7,17 @@ import ( "time" "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" "jokes-bapak2-api/app/v1/utils" "github.com/gofiber/fiber/v2" ) -func TodayJoke(c *fiber.Ctx) error { +func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { // check from handler.Redis if today's joke already exists // send the joke if exists // get a new joke if it's not, then send it. - var joke models.Today - err := handler.Redis.MGet(context.Background(), "today:link", "today:date", "today:image", "today:contentType").Scan(&joke) + var joke Today + err := d.Redis.MGet(context.Background(), "today:link", "today:date", "today:image", "today:contentType").Scan(&joke) if err != nil { return err } @@ -34,12 +32,12 @@ func TodayJoke(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).Send([]byte(joke.Image)) } else { var link string - err := handler.Db.QueryRow(context.Background(), "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) + err := d.DB.QueryRow(context.Background(), "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) if err != nil { return err } - response, err := handler.Client.Get(link, nil) + response, err := d.HTTP.Get(link, nil) if err != nil { return err } @@ -50,7 +48,7 @@ func TodayJoke(c *fiber.Ctx) error { } now := time.Now().UTC().Format(time.RFC3339) - err = handler.Redis.MSet(context.Background(), map[string]interface{}{ + err = d.Redis.MSet(context.Background(), map[string]interface{}{ "today:link": link, "today:date": now, "today:image": string(data), @@ -66,30 +64,30 @@ func TodayJoke(c *fiber.Ctx) error { } -func SingleJoke(c *fiber.Ctx) error { - checkCache, err := core.CheckJokesCache(handler.Memory) +func (d *Dependencies) SingleJoke(c *fiber.Ctx) error { + checkCache, err := core.CheckJokesCache(d.Memory) if err != nil { return err } if !checkCache { - jokes, err := core.GetAllJSONJokes(handler.Db) + jokes, err := core.GetAllJSONJokes(d.DB) if err != nil { return err } - err = handler.Memory.Set("jokes", jokes) + err = d.Memory.Set("jokes", jokes) if err != nil { return err } } - link, err := core.GetRandomJokeFromCache(handler.Memory) + link, err := core.GetRandomJokeFromCache(d.Memory) if err != nil { return err } // Get image data - response, err := handler.Client.Get(link, nil) + response, err := d.HTTP.Get(link, nil) if err != nil { return err } @@ -104,18 +102,18 @@ func SingleJoke(c *fiber.Ctx) error { } -func JokeByID(c *fiber.Ctx) error { - checkCache, err := core.CheckJokesCache(handler.Memory) +func (d *Dependencies) JokeByID(c *fiber.Ctx) error { + checkCache, err := core.CheckJokesCache(d.Memory) if err != nil { return err } if !checkCache { - jokes, err := core.GetAllJSONJokes(handler.Db) + jokes, err := core.GetAllJSONJokes(d.DB) if err != nil { return err } - err = handler.Memory.Set("jokes", jokes) + err = d.Memory.Set("jokes", jokes) if err != nil { return err } @@ -126,7 +124,7 @@ func JokeByID(c *fiber.Ctx) error { return err } - link, err := core.GetCachedJokeByID(handler.Memory, id) + link, err := core.GetCachedJokeByID(d.Memory, id) if err != nil { return err } @@ -138,7 +136,7 @@ func JokeByID(c *fiber.Ctx) error { } // Get image data - response, err := handler.Client.Get(link, nil) + response, err := d.HTTP.Get(link, nil) if err != nil { return err } diff --git a/api/app/v1/handler/joke/joke_get_test.go b/api/app/v1/handler/joke/joke_get_test.go index fe136bf..c7d3abe 100644 --- a/api/app/v1/handler/joke/joke_get_test.go +++ b/api/app/v1/handler/joke/joke_get_test.go @@ -1,61 +1,15 @@ package joke_test import ( - "context" "io/ioutil" "net/http" "testing" + "time" - v1 "jokes-bapak2-api/app/v1" - "jokes-bapak2-api/app/v1/platform/database" - - "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4/pgxpool" _ "github.com/joho/godotenv/autoload" "github.com/stretchr/testify/assert" ) -var db *pgxpool.Pool = database.New() -var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} -var app *fiber.App = v1.New() - -func cleanup() { - j, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"") - if err != nil { - panic(err) - } - a, err := db.Query(context.Background(), "DROP TABLE \"administrators\"") - if err != nil { - panic(err) - } - - defer j.Close() - defer a.Close() -} - -func setup() error { - err := database.Setup() - if err != nil { - return err - } - - a, err := db.Query(context.Background(), "INSERT INTO \"administrators\" (\"id\", \"key\", \"token\", \"last_used\") VALUES (1, 'test', '$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106', '');") - if err != nil { - return err - } - - defer a.Close() - - j, err := db.Query(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) - if err != nil { - return err - } - - defer j.Close() - - return nil -} - /// Need to find some workaround for this test func TestTodayJoke(t *testing.T) { err := setup() @@ -66,12 +20,20 @@ func TestTodayJoke(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/today", nil) - res, err := app.Test(req, -1) - + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } + assert.Equalf(t, false, err != nil, "today joke") assert.Equalf(t, 200, res.StatusCode, "today joke") assert.NotEqualf(t, 0, res.ContentLength, "today joke") _, err = ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "today joke") } @@ -84,12 +46,20 @@ func TestSingleJoke(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/", nil) - res, err := app.Test(req, -1) - + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } + assert.Equalf(t, false, err != nil, "single joke") assert.Equalf(t, 200, res.StatusCode, "single joke") assert.NotEqualf(t, 0, res.ContentLength, "single joke") _, err = ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "single joke") } @@ -102,12 +72,20 @@ func TestJokeByID_200(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/id/1", nil) - res, err := app.Test(req, -1) - + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } + assert.Equalf(t, false, err != nil, "joke by id") assert.Equalf(t, 200, res.StatusCode, "joke by id") assert.NotEqualf(t, 0, res.ContentLength, "joke by id") _, err = ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke by id") } @@ -120,12 +98,20 @@ func TestJokeByID_404(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/id/300", nil) - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "joke by id") assert.Equalf(t, 404, res.StatusCode, "joke by id") assert.NotEqualf(t, 0, res.ContentLength, "joke by id") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke by id") assert.Equalf(t, "Requested ID was not found.", string(body), "joke by id") } diff --git a/api/app/v1/handler/joke/joke_total.go b/api/app/v1/handler/joke/joke_total.go index 6fc1874..f17f961 100644 --- a/api/app/v1/handler/joke/joke_total.go +++ b/api/app/v1/handler/joke/joke_total.go @@ -1,34 +1,34 @@ package joke import ( + "errors" "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" "strconv" + "github.com/allegro/bigcache/v3" "github.com/gofiber/fiber/v2" ) -func TotalJokes(c *fiber.Ctx) error { - checkTotal, err := core.CheckTotalJokesCache(handler.Memory) +func (d *Dependencies) TotalJokes(c *fiber.Ctx) error { + checkTotal, err := core.CheckTotalJokesCache(d.Memory) if err != nil { return err } if !checkTotal { - err = core.SetTotalJoke(handler.Db, handler.Memory) + err = core.SetTotalJoke(d.DB, d.Memory) if err != nil { return err } } - total, err := handler.Memory.Get("total") + total, err := d.Memory.Get("total") if err != nil { - if err.Error() == "Entry not found" { + if errors.Is(err, bigcache.ErrEntryNotFound) { return c. Status(fiber.StatusInternalServerError). - JSON(models.Error{ + JSON(Error{ Error: "no data found", }) } @@ -37,7 +37,7 @@ func TotalJokes(c *fiber.Ctx) error { return c. Status(fiber.StatusOK). - JSON(models.ResponseJoke{ + JSON(ResponseJoke{ Message: strconv.Itoa(int(total[0])), }) } diff --git a/api/app/v1/handler/joke/joke_total_test.go b/api/app/v1/handler/joke/joke_total_test.go index ea907c9..bcff035 100644 --- a/api/app/v1/handler/joke/joke_total_test.go +++ b/api/app/v1/handler/joke/joke_total_test.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "net/http" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -17,12 +18,20 @@ func TestTotalJokes(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/total", nil) - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "joke total") assert.Equalf(t, 200, res.StatusCode, "joke total") assert.NotEqualf(t, 0, res.ContentLength, "joke total") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke total") // FIXME: This should be "message": "3", not one. I don't know what's wrong as it's 1 AM. assert.Equalf(t, "{\"message\":\"3\"}", string(body), "joke total") diff --git a/api/app/v1/handler/joke/joke_update.go b/api/app/v1/handler/joke/joke_update.go index 7a5ee83..a3b4ad4 100644 --- a/api/app/v1/handler/joke/joke_update.go +++ b/api/app/v1/handler/joke/joke_update.go @@ -4,17 +4,16 @@ import ( "context" "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4" ) -func UpdateJoke(c *fiber.Ctx) error { +func (d *Dependencies) UpdateJoke(c *fiber.Ctx) error { id := c.Params("id") // Check if the joke exists - sql, args, err := handler.Psql. + sql, args, err := d.Query. Select("id"). From("jokesbapak2"). Where(squirrel.Eq{"id": id}). @@ -24,20 +23,20 @@ func UpdateJoke(c *fiber.Ctx) error { } var jokeID string - err = handler.Db.QueryRow(context.Background(), sql, args...).Scan(&jokeID) - if err != nil && err != models.ErrNoRows { + err = d.DB.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + if err != nil && err != pgx.ErrNoRows { return err } if jokeID == id { - body := new(models.Joke) + body := new(core.Joke) err = c.BodyParser(&body) if err != nil { return err } // Check link validity - valid, err := core.CheckImageValidity(handler.Client, body.Link) + valid, err := core.CheckImageValidity(d.HTTP, body.Link) if err != nil { return err } @@ -45,12 +44,12 @@ func UpdateJoke(c *fiber.Ctx) error { if !valid { return c. Status(fiber.StatusBadRequest). - JSON(models.Error{ + JSON(Error{ Error: "URL provided is not a valid image", }) } - sql, args, err = handler.Psql. + sql, args, err = d.Query. Update("jokesbapak2"). Set("link", body.Link). Set("creator", c.Locals("userID")). @@ -59,25 +58,25 @@ func UpdateJoke(c *fiber.Ctx) error { return err } - r, err := handler.Db.Query(context.Background(), sql, args...) + r, err := d.DB.Query(context.Background(), sql, args...) if err != nil { return err } defer r.Close() - err = core.SetAllJSONJoke(handler.Db, handler.Memory) + err = core.SetAllJSONJoke(d.DB, d.Memory) if err != nil { return err } - err = core.SetTotalJoke(handler.Db, handler.Memory) + err = core.SetTotalJoke(d.DB, d.Memory) if err != nil { return err } return c. Status(fiber.StatusOK). - JSON(models.ResponseJoke{ + JSON(ResponseJoke{ Message: "specified joke id has been updated", Link: body.Link, }) @@ -85,7 +84,7 @@ func UpdateJoke(c *fiber.Ctx) error { return c. Status(fiber.StatusNotAcceptable). - JSON(models.Error{ + JSON(Error{ Error: "specified joke id does not exists", }) } diff --git a/api/app/v1/handler/joke/joke_update_test.go b/api/app/v1/handler/joke/joke_update_test.go index 34518cf..d316f67 100644 --- a/api/app/v1/handler/joke/joke_update_test.go +++ b/api/app/v1/handler/joke/joke_update_test.go @@ -5,6 +5,7 @@ import ( "net/http" "strings" "testing" + "time" "github.com/stretchr/testify/assert" ) @@ -21,12 +22,20 @@ func TestUpdateJoke_200(t *testing.T) { req, _ := http.NewRequest("PATCH", "/id/1", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "joke update") assert.Equalf(t, 200, res.StatusCode, "joke update") assert.NotEqualf(t, 0, res.ContentLength, "joke update") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke update") assert.Equalf(t, "{\"message\":\"specified joke id has been deleted\"}", string(body), "joke update") } @@ -44,12 +53,20 @@ func TestUpdateJoke_NotExists(t *testing.T) { req, _ := http.NewRequest("PATCH", "/id/100", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, -1) - + res, err := app.Test(req, int(time.Minute * 2)) +if err != nil { + t.Fatal(err) + } + assert.Equalf(t, false, err != nil, "joke update") assert.Equalf(t, 406, res.StatusCode, "joke update") assert.NotEqualf(t, 0, res.ContentLength, "joke update") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "joke update") assert.Equalf(t, "{\"message\":\"specified joke id does not exists\"}", string(body), "joke update") } diff --git a/api/app/v1/models/joke.go b/api/app/v1/handler/joke/schema.go similarity index 58% rename from api/app/v1/models/joke.go rename to api/app/v1/handler/joke/schema.go index f2b66c4..b0180c1 100644 --- a/api/app/v1/models/joke.go +++ b/api/app/v1/handler/joke/schema.go @@ -1,9 +1,8 @@ -package models +package joke -type Joke struct { - ID int `json:"id" form:"id" db:"id"` - Link string `json:"link" form:"link" db:"link"` - Creator int `json:"creator" form:"creator" db:"creator"` +type ResponseJoke struct { + Link string `json:"link,omitempty"` + Message string `json:"message,omitempty"` } type Today struct { @@ -12,7 +11,6 @@ type Today struct { ContentType string `redis:"today:contentType"` } -type ResponseJoke struct { - Link string `json:"link,omitempty"` - Message string `json:"message,omitempty"` +type Error struct { + Error string `json:"error"` } diff --git a/api/app/v1/handler/submit/dependencies.go b/api/app/v1/handler/submit/dependencies.go new file mode 100644 index 0000000..5f73c1b --- /dev/null +++ b/api/app/v1/handler/submit/dependencies.go @@ -0,0 +1,17 @@ +package submit + +import ( + "github.com/Masterminds/squirrel" + "github.com/allegro/bigcache/v3" + "github.com/go-redis/redis/v8" + "github.com/gojek/heimdall/v7/httpclient" + "github.com/jackc/pgx/v4/pgxpool" +) + +type Dependencies struct { + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType +} diff --git a/api/app/v1/handler/submit/dependencies_test.go b/api/app/v1/handler/submit/dependencies_test.go new file mode 100644 index 0000000..7e19ff7 --- /dev/null +++ b/api/app/v1/handler/submit/dependencies_test.go @@ -0,0 +1,58 @@ +package submit_test + +import ( + "context" + v1 "jokes-bapak2-api/app/v1" + "log" + "os" + + "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4/pgxpool" +) + +var submissionData = []interface{}{1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1} +var app *fiber.App = v1.New() +var db *pgxpool.Pool + +func cleanup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatalln("Unable to create pool config", err) + } + poolConfig.MaxConns = 5 + poolConfig.MinConns = 2 + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + + s, err := db.Query(context.Background(), "DROP TABLE \"submission\"") + if err != nil { + panic(err) + } + defer s.Close() +} + +func setup() error { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatalln("Unable to create pool config", err) + } + poolConfig.MaxConns = 15 + poolConfig.MinConns = 2 + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + + s, err := db.Query(context.Background(), "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) + if err != nil { + return err + } + + defer s.Close() + + return nil +} diff --git a/api/app/v1/models/submit.go b/api/app/v1/handler/submit/schema.go similarity index 65% rename from api/app/v1/models/submit.go rename to api/app/v1/handler/submit/schema.go index ded4eef..3dd0b51 100644 --- a/api/app/v1/models/submit.go +++ b/api/app/v1/handler/submit/schema.go @@ -1,4 +1,4 @@ -package models +package submit type Submission struct { ID int `json:"id,omitempty" db:"id"` @@ -22,16 +22,6 @@ type ResponseSubmission struct { Data Submission `json:"data,omitempty"` } -type ImageAPI struct { - Data ImageAPIData `json:"data"` - Success bool `json:"success"` - Status int `json:"status"` -} - -type ImageAPIData struct { - ID string `json:"id"` - Title string `json:"title"` - URLViewer string `json:"url_viewer"` - URL string `json:"url"` - DisplayURL string `json:"display_url"` +type Error struct { + Error string `json:"error"` } diff --git a/api/app/v1/handler/submit/submit_add.go b/api/app/v1/handler/submit/submit_add.go index 65be308..7af06ca 100644 --- a/api/app/v1/handler/submit/submit_add.go +++ b/api/app/v1/handler/submit/submit_add.go @@ -3,8 +3,6 @@ package submit import ( "context" "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" "strings" "time" @@ -12,8 +10,8 @@ import ( "github.com/gofiber/fiber/v2" ) -func SubmitJoke(c *fiber.Ctx) error { - var body models.Submission +func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { + var body Submission err := c.BodyParser(&body) if err != nil { return err @@ -21,21 +19,21 @@ func SubmitJoke(c *fiber.Ctx) error { // Image and/or Link should not be empty if body.Image == "" && body.Link == "" { - return c.Status(fiber.StatusBadRequest).JSON(models.Error{ + return c.Status(fiber.StatusBadRequest).JSON(Error{ Error: "a link or an image should be supplied in a form of multipart/form-data", }) } // Author should be supplied if body.Author == "" { - return c.Status(fiber.StatusBadRequest).JSON(models.Error{ + return c.Status(fiber.StatusBadRequest).JSON(Error{ Error: "an author key consisting on the format \"yourname \" must be supplied", }) } else { // Validate format valid := core.ValidateAuthor(body.Author) if !valid { - return c.Status(fiber.StatusBadRequest).JSON(models.Error{ + return c.Status(fiber.StatusBadRequest).JSON(Error{ Error: "please stick to the format of \"yourname \" and within 200 characters", }) } @@ -45,12 +43,12 @@ func SubmitJoke(c *fiber.Ctx) error { // Check link validity if link was provided if body.Link != "" { - valid, err := core.CheckImageValidity(handler.Client, body.Link) + valid, err := core.CheckImageValidity(d.HTTP, body.Link) if err != nil { return err } if !valid { - return c.Status(fiber.StatusBadRequest).JSON(models.Error{ + return c.Status(fiber.StatusBadRequest).JSON(Error{ Error: "URL provided is not a valid image", }) } @@ -62,7 +60,7 @@ func SubmitJoke(c *fiber.Ctx) error { if body.Image != "" { image := strings.NewReader(body.Image) - url, err = core.UploadImage(handler.Client, image) + url, err = core.UploadImage(d.HTTP, image) if err != nil { return err } @@ -70,7 +68,7 @@ func SubmitJoke(c *fiber.Ctx) error { now := time.Now().UTC().Format(time.RFC3339) - sql, args, err := handler.Psql. + sql, args, err := d.Query. Insert("submission"). Columns("link", "created_at", "author"). Values(url, now, body.Author). @@ -80,8 +78,8 @@ func SubmitJoke(c *fiber.Ctx) error { return err } - var submission []models.Submission - result, err := handler.Db.Query(context.Background(), sql, args...) + var submission []Submission + result, err := d.DB.Query(context.Background(), sql, args...) if err != nil { return err } @@ -94,7 +92,7 @@ func SubmitJoke(c *fiber.Ctx) error { return c. Status(fiber.StatusCreated). - JSON(models.ResponseSubmission{ + JSON(ResponseSubmission{ Message: "Joke submitted. Please wait for a few days for admin to approve your submission.", Data: submission[0], }) diff --git a/api/app/v1/handler/submit/submit_get.go b/api/app/v1/handler/submit/submit_get.go index f3a3fa7..8ab1ae3 100644 --- a/api/app/v1/handler/submit/submit_get.go +++ b/api/app/v1/handler/submit/submit_get.go @@ -3,8 +3,6 @@ package submit import ( "bytes" "context" - "jokes-bapak2-api/app/v1/handler" - "jokes-bapak2-api/app/v1/models" "log" "strconv" @@ -13,8 +11,8 @@ import ( "github.com/gofiber/fiber/v2" ) -func GetSubmission(c *fiber.Ctx) error { - query := new(models.SubmissionQuery) +func (d *Dependencies) GetSubmission(c *fiber.Ctx) error { + query := new(SubmissionQuery) err := c.QueryParser(query) if err != nil { return err @@ -81,8 +79,8 @@ func GetSubmission(c *fiber.Ctx) error { sql = bob.ReplacePlaceholder(sqlQuery.String(), bob.Dollar) - var submissions []models.Submission - results, err := handler.Db.Query(context.Background(), sql, args...) + var submissions []Submission + results, err := d.DB.Query(context.Background(), sql, args...) if err != nil { log.Println(err) return err diff --git a/api/app/v1/handler/submit/submit_get_test.go b/api/app/v1/handler/submit/submit_get_test.go index 2d6eb47..37c9427 100644 --- a/api/app/v1/handler/submit/submit_get_test.go +++ b/api/app/v1/handler/submit/submit_get_test.go @@ -1,47 +1,16 @@ package submit_test import ( - "context" "io/ioutil" - v1 "jokes-bapak2-api/app/v1" - "jokes-bapak2-api/app/v1/platform/database" "net/http" "testing" + "time" - "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4/pgxpool" _ "github.com/joho/godotenv/autoload" "github.com/stretchr/testify/assert" ) -var db *pgxpool.Pool = database.New() -var submissionData = []interface{}{1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1} -var app *fiber.App = v1.New() - -func cleanup() { - s, err := db.Query(context.Background(), "DROP TABLE \"submission\"") - if err != nil { - panic(err) - } - defer s.Close() -} - -func setup() error { - err := database.Setup() - if err != nil { - return err - } - - s, err := db.Query(context.Background(), "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) - if err != nil { - return err - } - - defer s.Close() - - return nil -} func TestGetSubmission_200(t *testing.T) { err := setup() if err != nil { @@ -51,11 +20,19 @@ func TestGetSubmission_200(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/submit", nil) - res, err := app.Test(req, -1) + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } assert.Equalf(t, false, err != nil, "get submission") assert.Equalf(t, 200, res.StatusCode, "get submission") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "get submission") assert.Equalf(t, "{\"count\":2,\"jokes\":[{\"id\":1,\"link\":\"https://via.placeholder.com/300/01f/fff.png\",\"created_at\":\"2021-08-03T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":0},{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") } @@ -69,11 +46,19 @@ func TestGetSubmission_Params(t *testing.T) { defer cleanup() req, _ := http.NewRequest("GET", "/submit?page=1&limit=5&approved=true", nil) - res, err := app.Test(req, -1) - + res, err := app.Test(req, int(time.Minute * 2)) + if err != nil { + t.Fatal(err) + } + assert.Equalf(t, false, err != nil, "get submission") assert.Equalf(t, 200, res.StatusCode, "get submission") body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + assert.Nilf(t, err, "get submission") assert.Equalf(t, "{\"count\":1,\"jokes\":[{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") } diff --git a/api/app/v1/middleware/auth.go b/api/app/v1/middleware/auth.go index 7ef7768..2e3e0fa 100644 --- a/api/app/v1/middleware/auth.go +++ b/api/app/v1/middleware/auth.go @@ -2,22 +2,21 @@ package middleware import ( "context" + "errors" "time" - "jokes-bapak2-api/app/v1/models" - "jokes-bapak2-api/app/v1/platform/database" - "github.com/Masterminds/squirrel" phccrypto "github.com/aldy505/phc-crypto" "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" ) var psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) -var db = database.New() -func RequireAuth() fiber.Handler { +func RequireAuth(db *pgxpool.Pool) fiber.Handler { return func(c *fiber.Ctx) error { - var auth models.Auth + var auth Auth err := c.BodyParser(&auth) if err != nil { return err @@ -36,10 +35,10 @@ func RequireAuth() fiber.Handler { var token string err = db.QueryRow(context.Background(), sql, args...).Scan(&token) if err != nil { - if err.Error() == "no rows in result set" { + if errors.Is(err, pgx.ErrNoRows) { return c. Status(fiber.StatusForbidden). - JSON(models.Error{ + JSON(Error{ Error: "Invalid key", }) } @@ -90,7 +89,7 @@ func RequireAuth() fiber.Handler { return c. Status(fiber.StatusForbidden). - JSON(models.Error{ + JSON(Error{ Error: "Invalid key", }) } diff --git a/api/app/v1/models/general.go b/api/app/v1/middleware/schema.go similarity index 77% rename from api/app/v1/models/general.go rename to api/app/v1/middleware/schema.go index 11eae1e..9659dce 100644 --- a/api/app/v1/models/general.go +++ b/api/app/v1/middleware/schema.go @@ -1,4 +1,4 @@ -package models +package middleware type Auth struct { ID int `json:"id" form:"id" db:"id"` @@ -6,3 +6,7 @@ type Auth struct { Token string `json:"token" form:"token" db:"token"` LastUsed string `json:"last_used" form:"last_used" db:"last_used"` } + +type Error struct { + Error string `json:"error"` +} diff --git a/api/app/v1/middleware/validation.go b/api/app/v1/middleware/validation.go index 0156d81..8e12c7c 100644 --- a/api/app/v1/middleware/validation.go +++ b/api/app/v1/middleware/validation.go @@ -1,7 +1,6 @@ package middleware import ( - "jokes-bapak2-api/app/v1/models" "regexp" "github.com/gofiber/fiber/v2" @@ -21,7 +20,7 @@ func OnlyIntegerAsID() fiber.Handler { return c. Status(fiber.StatusBadRequest). - JSON(models.Error{ + JSON(Error{ Error: "only numbers are allowed as ID", }) } diff --git a/api/app/v1/models/errors.go b/api/app/v1/models/errors.go deleted file mode 100644 index f7060e0..0000000 --- a/api/app/v1/models/errors.go +++ /dev/null @@ -1,14 +0,0 @@ -package models - -import "errors" - -var ErrNoRows = errors.New("no rows in result set") -var ErrConnDone = errors.New("connection is already closed") -var ErrTxDone = errors.New("transaction has already been committed or rolled back") - -var ErrNotFound = errors.New("record not found") -var ErrEmpty = errors.New("record is empty") - -type Error struct { - Error string `json:"error"` -} diff --git a/api/app/v1/platform/cache/cache.go b/api/app/v1/platform/cache/cache.go deleted file mode 100644 index d2493e1..0000000 --- a/api/app/v1/platform/cache/cache.go +++ /dev/null @@ -1,16 +0,0 @@ -package cache - -import ( - "log" - "time" - - "github.com/allegro/bigcache/v3" -) - -func InMemory() *bigcache.BigCache { - cache, err := bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) - if err != nil { - log.Fatalln(err) - } - return cache -} diff --git a/api/app/v1/platform/cache/redis.go b/api/app/v1/platform/cache/redis.go deleted file mode 100644 index 2a6eab8..0000000 --- a/api/app/v1/platform/cache/redis.go +++ /dev/null @@ -1,19 +0,0 @@ -package cache - -import ( - "log" - "os" - - "github.com/go-redis/redis/v8" - _ "github.com/joho/godotenv/autoload" -) - -// Connect to the database -func New() *redis.Client { - opt, err := redis.ParseURL(os.Getenv("REDIS_URL")) - if err != nil { - log.Fatalln(err) - } - rdb := redis.NewClient(opt) - return rdb -} diff --git a/api/app/v1/platform/database/create.go b/api/app/v1/platform/database/create.go index 0611f39..b556fc0 100644 --- a/api/app/v1/platform/database/create.go +++ b/api/app/v1/platform/database/create.go @@ -5,12 +5,11 @@ import ( "log" "github.com/aldy505/bob" + "github.com/jackc/pgx/v4/pgxpool" ) // Setup the table connection, create table if not exists -func Setup() error { - db := New() - +func Setup(db *pgxpool.Pool) error { // administrators table var tableAuthExists bool err := db.QueryRow(context.Background(), `SELECT EXISTS ( @@ -27,7 +26,7 @@ func Setup() error { sql, _, err := bob. CreateTable("administrators"). AddColumn(bob.ColumnDef{Name: "id", Type: "SERIAL", Extras: []string{"PRIMARY KEY"}}). - StringColumn("key", "UNIQUE"). + StringColumn("key", "NOT NULL", "UNIQUE"). TextColumn("token"). StringColumn("last_used"). ToSql() diff --git a/api/app/v1/platform/database/postgres.go b/api/app/v1/platform/database/postgres.go deleted file mode 100644 index eb7b926..0000000 --- a/api/app/v1/platform/database/postgres.go +++ /dev/null @@ -1,27 +0,0 @@ -package database - -import ( - "context" - "log" - "os" - - "github.com/jackc/pgx/v4/pgxpool" - _ "github.com/joho/godotenv/autoload" -) - -// Connect to the database -func New() *pgxpool.Pool { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 18 - poolConfig.MinConns = 2 - - conn, err := pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - - return conn -} diff --git a/api/app/v1/routes/dependencies.go b/api/app/v1/routes/dependencies.go new file mode 100644 index 0000000..65f6bb5 --- /dev/null +++ b/api/app/v1/routes/dependencies.go @@ -0,0 +1,19 @@ +package routes + +import ( + "github.com/Masterminds/squirrel" + "github.com/allegro/bigcache/v3" + "github.com/go-redis/redis/v8" + "github.com/gofiber/fiber/v2" + "github.com/gojek/heimdall/v7/httpclient" + "github.com/jackc/pgx/v4/pgxpool" +) + +type Dependencies struct { + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType + App *fiber.App +} diff --git a/api/app/v1/routes/health.go b/api/app/v1/routes/health.go index c1dc52b..d0796a0 100644 --- a/api/app/v1/routes/health.go +++ b/api/app/v1/routes/health.go @@ -8,9 +8,12 @@ import ( "github.com/gofiber/fiber/v2/middleware/cache" ) -func Health(app *fiber.App) *fiber.App { +func (d *Dependencies) Health() *fiber.App { // Health check - app.Get("/health", cache.New(cache.Config{Expiration: 30 * time.Minute}), health.Health) + deps := health.Dependencies{ + Redis: d.Redis, + } + d.App.Get("/health", cache.New(cache.Config{Expiration: 30 * time.Minute}), deps.Health) - return app + return d.App } diff --git a/api/app/v1/routes/joke.go b/api/app/v1/routes/joke.go index 1bddc1d..86f8b67 100644 --- a/api/app/v1/routes/joke.go +++ b/api/app/v1/routes/joke.go @@ -9,27 +9,34 @@ import ( "github.com/gofiber/fiber/v2/middleware/cache" ) -func Joke(app *fiber.App) *fiber.App { +func (d *Dependencies) Joke() *fiber.App { + deps := joke.Dependencies{ + DB: d.DB, + Redis: d.Redis, + Memory: d.Memory, + HTTP: d.HTTP, + Query: d.Query, + } // Single route - app.Get("/", joke.SingleJoke) + d.App.Get("/", deps.SingleJoke) // Today's joke - app.Get("/today", cache.New(cache.Config{Expiration: 6 * time.Hour}), joke.TodayJoke) + d.App.Get("/today", cache.New(cache.Config{Expiration: 6 * time.Hour}), deps.TodayJoke) // Joke by ID - app.Get("/id/:id", middleware.OnlyIntegerAsID(), joke.JokeByID) + d.App.Get("/id/:id", middleware.OnlyIntegerAsID(), deps.JokeByID) // Count total jokes - app.Get("/total", cache.New(cache.Config{Expiration: 15 * time.Minute}), joke.TotalJokes) + d.App.Get("/total", cache.New(cache.Config{Expiration: 15 * time.Minute}), deps.TotalJokes) // Add new joke - app.Put("/", middleware.RequireAuth(), joke.AddNewJoke) + d.App.Put("/", middleware.RequireAuth(d.DB), deps.AddNewJoke) // Update a joke - app.Patch("/id/:id", middleware.RequireAuth(), middleware.OnlyIntegerAsID(), joke.UpdateJoke) + d.App.Patch("/id/:id", middleware.RequireAuth(d.DB), middleware.OnlyIntegerAsID(), deps.UpdateJoke) // Delete a joke - app.Delete("/id/:id", middleware.RequireAuth(), middleware.OnlyIntegerAsID(), joke.DeleteJoke) + d.App.Delete("/id/:id", middleware.RequireAuth(d.DB), middleware.OnlyIntegerAsID(), deps.DeleteJoke) - return app + return d.App } diff --git a/api/app/v1/routes/submit.go b/api/app/v1/routes/submit.go index 24b9274..1311e79 100644 --- a/api/app/v1/routes/submit.go +++ b/api/app/v1/routes/submit.go @@ -8,9 +8,16 @@ import ( "github.com/gofiber/fiber/v2/middleware/cache" ) -func Submit(app *fiber.App) *fiber.App { +func (d *Dependencies) Submit() *fiber.App { + deps := submit.Dependencies{ + DB: d.DB, + Redis: d.Redis, + Memory: d.Memory, + HTTP: d.HTTP, + Query: d.Query, + } // Get pending submitted joke - app.Get( + d.App.Get( "/submit", cache.New(cache.Config{ Expiration: 5 * time.Minute, @@ -18,10 +25,10 @@ func Submit(app *fiber.App) *fiber.App { return c.OriginalURL() }, }), - submit.GetSubmission) + deps.GetSubmission) // Add a joke - app.Post("/submit", submit.SubmitJoke) + d.App.Post("/submit", deps.SubmitJoke) - return app + return d.App } diff --git a/api/go.mod b/api/go.mod index 49da1b8..90de283 100644 --- a/api/go.mod +++ b/api/go.mod @@ -1,6 +1,6 @@ module jokes-bapak2-api -go 1.16 +go 1.17 require ( github.com/Masterminds/squirrel v1.5.0 @@ -24,3 +24,47 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) + +require ( + github.com/andybalholm/brotli v1.0.2 // indirect + github.com/atotto/clipboard v0.1.4 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/charmbracelet/bubbles v0.9.0 // indirect + github.com/charmbracelet/bubbletea v0.15.0 // indirect + github.com/charmbracelet/lipgloss v0.4.0 // indirect + github.com/containerd/console v1.0.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/gofiber/cli v0.0.9 // indirect + github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.9.0 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.1.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.8.0 // indirect + github.com/jackc/puddle v1.1.3 // indirect + github.com/klauspost/compress v1.12.2 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/muesli/reflow v0.3.0 // indirect + github.com/muesli/termenv v0.9.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/spf13/cobra v1.2.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.3.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.26.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect + golang.org/x/term v0.0.0-20210422114643-f5beecf764ed // indirect + golang.org/x/text v0.3.6 // indirect +) diff --git a/api/go.sum b/api/go.sum index baaf628..ed4cd0f 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,7 +1,45 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -10,6 +48,7 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -30,6 +69,7 @@ github.com/allegro/bigcache/v3 v3.0.0 h1:5Hxq+GTy8gHEeQccCZZDCfZRTydUfErdUf0iVDc github.com/allegro/bigcache/v3 v3.0.0/go.mod h1:t5TAJn1B9qvf/VlJrSM1r6NlFAYoFDubYUsCuIO9nUQ= github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -37,6 +77,9 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= @@ -45,14 +88,36 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.8.0/go.mod h1:5WX1sSSjNCgCrzvRMN/z23HxvWaa+AI16Ch0KPZPeDs= +github.com/charmbracelet/bubbles v0.9.0 h1:lqJ8FXwoLceQF2J0A+dWo1Cuu1dNyjbW4Opgdi2vkhw= +github.com/charmbracelet/bubbles v0.9.0/go.mod h1:NWT/c+0rYEnYChz5qCyX4Lj6fDw9gGToh9EFJPajghU= +github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg= +github.com/charmbracelet/bubbletea v0.14.0/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE= +github.com/charmbracelet/bubbletea v0.14.1/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE= +github.com/charmbracelet/bubbletea v0.15.0 h1:7++QPke7CsjBs+tZl49x7KXTHsof+NUMhreAtwBXygE= +github.com/charmbracelet/bubbletea v0.15.0/go.mod h1:YTZSs2p3odhwYZdhqJheYHVUjU37c9OLgS85kw6NGQY= +github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= +github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk= +github.com/charmbracelet/lipgloss v0.4.0 h1:768h64EFkGUr8V5yAKV7/Ta0NiVceiPaV+PphaW1K9g= +github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go/v2 v2.0.3 h1:ZA346ACHIZctef6trOTwBAEvPVm1k0uLm/bb2Atc+S8= @@ -60,15 +125,25 @@ github.com/cockroachdb/cockroach-go/v2 v2.0.3/go.mod h1:hAuDgiVgDVkfirP9JnhXEfcX github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -80,6 +155,7 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -88,7 +164,12 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= @@ -100,6 +181,8 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/georgysavva/scany v0.2.9 h1:Xt6rjYpHnMClTm/g+oZTnoSxUwiln5GqMNU+QeLNHQU= github.com/georgysavva/scany v0.2.9/go.mod h1:yeOeC1BdIdl6hOwy8uefL2WNSlseFzbhlG/frrh65SA= @@ -111,6 +194,9 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -126,6 +212,9 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofiber/cli v0.0.9 h1:W29t34Y3xnkQ98B+1OV480d8Ehw5hwqPYrwGDiZ4ZVY= +github.com/gofiber/cli v0.0.9/go.mod h1:L+Zjl5jYfCKKcLI+b9LmhO5CQOg5iE8POwdM0r6Mack= github.com/gofiber/fiber/v2 v2.15.0 h1:yd+o1t6/hjkmjZxz4FJlgHAKBIu1w1PnRL3VB67KMHM= github.com/gofiber/fiber/v2 v2.15.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -135,6 +224,7 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gojek/heimdall/v7 v7.0.2 h1:+YutGXZ8oEWbCJIwjRnkKmoTl+Oxt1Urs3hc/FR0sxU= github.com/gojek/heimdall/v7 v7.0.2/go.mod h1:Z43HtMid7ysSjmsedPTXAki6jcdcNVnjn5pmsTyiMic= github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf h1:5xRGbUdOmZKoDXkGx5evVLehuCMpuO1hl701bEQqXOM= @@ -142,17 +232,35 @@ github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf/go.mod h1:QzhUKaYKJ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -162,23 +270,53 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -201,7 +339,10 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= @@ -279,6 +420,7 @@ github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -291,6 +433,9 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= @@ -300,6 +445,7 @@ github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2 github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -308,6 +454,7 @@ github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -315,6 +462,7 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= @@ -331,8 +479,13 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -344,7 +497,15 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= @@ -360,11 +521,19 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= +github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= +github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= +github.com/muesli/termenv v0.7.2/go.mod h1:ct2L5N2lmix82RaY3bMWwVu/jUFc9Ule0KGDCiKYPh8= +github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0= +github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8= +github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -379,6 +548,7 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -404,6 +574,7 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -414,6 +585,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -421,6 +593,7 @@ github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74 github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -428,16 +601,24 @@ github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= @@ -446,6 +627,7 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= @@ -464,14 +646,25 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -484,9 +677,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -512,24 +708,41 @@ github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmv github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -537,6 +750,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -545,20 +759,48 @@ golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -572,28 +814,64 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -605,39 +883,78 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210422114643-f5beecf764ed h1:Ei4bQjjpYUsS4efOUz+5Nz++IVkHk87n2zBA0NxBWc0= +golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -646,18 +963,60 @@ golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -666,30 +1025,115 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= @@ -702,7 +1146,9 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 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/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -713,16 +1159,25 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From 685b316ce925c884e0b7da17b44182bf8e0481dc Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 27 Sep 2021 02:14:03 +0700 Subject: [PATCH 02/21] ci: limiting actions to 15 minutes --- .github/workflows/api.yml | 1 + .github/workflows/client.yml | 2 +- .github/workflows/pr.yml | 3 ++- README.md | 25 +++++++++++++------------ 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index fec57c1..1a37d3d 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -9,6 +9,7 @@ jobs: name: Build runs-on: ubuntu-latest container: golang:1.16.6 + timeout-minutes: 15 services: postgres: image: postgres:13-alpine diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index 1377cfb..2f001ce 100644 --- a/.github/workflows/client.yml +++ b/.github/workflows/client.yml @@ -8,7 +8,7 @@ jobs: client-build: name: Build runs-on: ubuntu-latest - + timeout-minutes: 15 defaults: run: working-directory: ./client diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a72aa0b..84c12f2 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -8,7 +8,7 @@ jobs: client-build: name: Client runs-on: ubuntu-latest - + timeout-minutes: 15 defaults: run: working-directory: ./client @@ -63,6 +63,7 @@ jobs: name: API runs-on: ubuntu-latest container: golang:1.16.6 + timeout-minutes: 15 services: postgres: image: postgres:13-alpine diff --git a/README.md b/README.md index 0219129..a6caff8 100644 --- a/README.md +++ b/README.md @@ -54,25 +54,26 @@ See [CONTRIBUTING](./CONTRIBUTING.md) or README files on each project directory * [Ronny Gunawan](https://github.com/ronnygunawan) for the caching concept & ideas * [artileda](https://github.com/artileda) for the jokes submission * [elianiva](https://github.com/elianiva) for solving my SvelteKit problems +* [kokizzu](https://github.com/kokizzu) for the dependency injection concept & ideas ## License Jokes Bapak2 API is licensed under [GNU GENERAL PUBLIC LICENSE v3 license](./LICENSE) ``` - Jokes Bapak2 API is a free-to-use image API of Indonesian dad jokes. - Copyright (C) 2021-present Jokes Bapak2 Contributors +Jokes Bapak2 API is a free-to-use image API of Indonesian dad jokes. +Copyright (C) 2021-present Jokes Bapak2 Contributors - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . +You should have received a copy of the GNU General Public License +along with this program. If not, see . ``` \ No newline at end of file From cf28bf4213e523952b83736d999884994bdaeae1 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 27 Sep 2021 17:10:19 +0700 Subject: [PATCH 03/21] fix: adjusting database transactions --- api/app/{v1 => }/app.go | 64 ++- api/app/{v1 => }/core/joke_getter.go | 11 +- api/app/{v1 => }/core/joke_setter.go | 10 +- api/app/{v1 => }/core/joke_validation.go | 2 +- api/app/{v1 => }/core/schema.go | 0 api/app/{v1 => }/core/submit_setter.go | 2 +- api/app/{v1 => }/core/submit_validation.go | 0 api/{ => app}/favicon.png | Bin api/app/{v1 => }/handler/health/health.go | 15 +- api/app/handler/health/health_test.go | 34 ++ api/app/{v1 => }/handler/health/schema.go | 0 api/app/{v1 => }/handler/joke/dependencies.go | 13 +- api/app/handler/joke/dependencies_test.go | 9 + api/app/{v1 => }/handler/joke/joke_add.go | 31 +- .../{v1 => }/handler/joke/joke_add_test.go | 20 +- api/app/{v1 => }/handler/joke/joke_delete.go | 18 +- .../{v1 => }/handler/joke/joke_delete_test.go | 23 +- api/app/{v1 => }/handler/joke/joke_get.go | 21 +- .../{v1 => }/handler/joke/joke_get_test.go | 42 +- api/app/{v1 => }/handler/joke/joke_total.go | 4 +- .../{v1 => }/handler/joke/joke_total_test.go | 11 +- api/app/{v1 => }/handler/joke/joke_update.go | 19 +- .../{v1 => }/handler/joke/joke_update_test.go | 18 +- api/app/{v1 => }/handler/joke/schema.go | 0 .../{v1 => }/handler/submit/dependencies.go | 13 +- api/app/handler/submit/dependencies_test.go | 9 + api/app/{v1 => }/handler/submit/schema.go | 0 api/app/{v1 => }/handler/submit/submit_add.go | 5 +- api/app/{v1 => }/handler/submit/submit_get.go | 3 +- .../handler/submit/submit_get_test.go | 20 +- api/app/{v1 => }/middleware/auth.go | 8 +- api/app/{v1 => }/middleware/schema.go | 0 api/app/{v1 => }/middleware/validation.go | 0 api/app/{v1 => }/platform/database/create.go | 31 +- .../platform/database/placeholder.sql | 0 api/app/{v1 => }/routes/dependencies.go | 16 +- api/app/{v1 => }/routes/health.go | 12 +- api/app/routes/joke.go | 44 ++ api/app/{v1 => }/routes/submit.go | 6 +- api/app/{v1 => }/utils/array.go | 0 api/app/{v1 => }/utils/array_test.go | 2 +- api/app/{v1 => }/utils/date.go | 0 api/app/{v1 => }/utils/date_test.go | 3 +- api/app/{v1 => }/utils/parse.go | 0 api/app/{v1 => }/utils/parse_test.go | 3 +- api/app/{v1 => }/utils/random.go | 0 api/app/{v1 => }/utils/random_test.go | 2 +- api/app/{v1 => }/utils/request.go | 0 api/app/{v1 => }/utils/request_test.go | 3 +- api/app/v1/handler/health/health_test.go | 103 ---- api/app/v1/handler/joke/dependencies_test.go | 78 --- .../v1/handler/submit/dependencies_test.go | 58 --- api/app/v1/routes/joke.go | 42 -- api/{app/v1 => }/documentation.json | 4 + api/{app/v1 => }/documentation.yaml | 2 + api/go.mod | 16 - api/go.sum | 454 ------------------ api/main.go | 34 +- api/main_test.go | 130 +++++ 59 files changed, 457 insertions(+), 1011 deletions(-) rename api/app/{v1 => }/app.go (63%) rename api/app/{v1 => }/core/joke_getter.go (90%) rename api/app/{v1 => }/core/joke_setter.go (72%) rename api/app/{v1 => }/core/joke_validation.go (95%) rename api/app/{v1 => }/core/schema.go (100%) rename api/app/{v1 => }/core/submit_setter.go (97%) rename api/app/{v1 => }/core/submit_validation.go (100%) rename api/{ => app}/favicon.png (100%) rename api/app/{v1 => }/handler/health/health.go (66%) create mode 100644 api/app/handler/health/health_test.go rename api/app/{v1 => }/handler/health/schema.go (100%) rename api/app/{v1 => }/handler/joke/dependencies.go (56%) create mode 100644 api/app/handler/joke/dependencies_test.go rename api/app/{v1 => }/handler/joke/joke_add.go (64%) rename api/app/{v1 => }/handler/joke/joke_add_test.go (87%) rename api/app/{v1 => }/handler/joke/joke_delete.go (74%) rename api/app/{v1 => }/handler/joke/joke_delete_test.go (78%) rename api/app/{v1 => }/handler/joke/joke_get.go (82%) rename api/app/{v1 => }/handler/joke/joke_get_test.go (80%) rename api/app/{v1 => }/handler/joke/joke_total.go (88%) rename api/app/{v1 => }/handler/joke/joke_total_test.go (85%) rename api/app/{v1 => }/handler/joke/joke_update.go (80%) rename api/app/{v1 => }/handler/joke/joke_update_test.go (88%) rename api/app/{v1 => }/handler/joke/schema.go (100%) rename api/app/{v1 => }/handler/submit/dependencies.go (56%) create mode 100644 api/app/handler/submit/dependencies_test.go rename api/app/{v1 => }/handler/submit/schema.go (100%) rename api/app/{v1 => }/handler/submit/submit_add.go (95%) rename api/app/{v1 => }/handler/submit/submit_get.go (95%) rename api/app/{v1 => }/handler/submit/submit_get_test.go (87%) rename api/app/{v1 => }/middleware/auth.go (86%) rename api/app/{v1 => }/middleware/schema.go (100%) rename api/app/{v1 => }/middleware/validation.go (100%) rename api/app/{v1 => }/platform/database/create.go (83%) rename api/app/{v1 => }/platform/database/placeholder.sql (100%) rename api/app/{v1 => }/routes/dependencies.go (53%) rename api/app/{v1 => }/routes/health.go (50%) create mode 100644 api/app/routes/joke.go rename api/app/{v1 => }/routes/submit.go (83%) rename api/app/{v1 => }/utils/array.go (100%) rename api/app/{v1 => }/utils/array_test.go (92%) rename api/app/{v1 => }/utils/date.go (100%) rename api/app/{v1 => }/utils/date_test.go (96%) rename api/app/{v1 => }/utils/parse.go (100%) rename api/app/{v1 => }/utils/parse_test.go (96%) rename api/app/{v1 => }/utils/random.go (100%) rename api/app/{v1 => }/utils/random_test.go (93%) rename api/app/{v1 => }/utils/request.go (100%) rename api/app/{v1 => }/utils/request_test.go (93%) delete mode 100644 api/app/v1/handler/health/health_test.go delete mode 100644 api/app/v1/handler/joke/dependencies_test.go delete mode 100644 api/app/v1/handler/submit/dependencies_test.go delete mode 100644 api/app/v1/routes/joke.go rename api/{app/v1 => }/documentation.json (99%) rename api/{app/v1 => }/documentation.yaml (98%) create mode 100644 api/main_test.go diff --git a/api/app/v1/app.go b/api/app/app.go similarity index 63% rename from api/app/v1/app.go rename to api/app/app.go index 6442858..1c8dd9c 100644 --- a/api/app/v1/app.go +++ b/api/app/app.go @@ -1,10 +1,10 @@ -package v1 +package app import ( "context" - "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/platform/database" - "jokes-bapak2-api/app/v1/routes" + "jokes-bapak2-api/app/core" + "jokes-bapak2-api/app/platform/database" + "jokes-bapak2-api/app/routes" "log" "os" "time" @@ -16,11 +16,16 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/etag" + "github.com/gofiber/fiber/v2/middleware/limiter" "github.com/gojek/heimdall/v7/httpclient" "github.com/jackc/pgx/v4/pgxpool" + _ "github.com/joho/godotenv/autoload" ) func New() *fiber.App { + // Setup Context + ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1) + // Setup PostgreSQL poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { @@ -29,7 +34,7 @@ func New() *fiber.App { poolConfig.MaxConns = 15 poolConfig.MinConns = 2 - db, err := pgxpool.ConnectConfig(context.Background(), poolConfig) + db, err := pgxpool.ConnectConfig(ctx, poolConfig) if err != nil { log.Fatalln("Unable to create connection", err) } @@ -47,12 +52,7 @@ func New() *fiber.App { log.Fatalln(err) } - app := fiber.New(fiber.Config{ - DisableKeepalive: true, - CaseSensitive: true, - ErrorHandler: errorHandler, - }) - + // Setup Sentry err = sentry.Init(sentry.ClientOptions{ Dsn: os.Getenv("SENTRY_DSN"), Environment: os.Getenv("ENV"), @@ -67,31 +67,49 @@ func New() *fiber.App { defer sentry.Flush(2 * time.Second) - err = database.Setup(db) + err = database.Setup(db, &ctx) if err != nil { sentry.CaptureException(err) log.Fatal(err) } - err = core.SetAllJSONJoke(db, memory) + err = core.SetAllJSONJoke(db, memory, &ctx) if err != nil { log.Fatalln(err) } - err = core.SetTotalJoke(db, memory) + err = core.SetTotalJoke(db, memory, &ctx) if err != nil { log.Fatalln(err) } + timeoutDefault := time.Minute * 1 + + app := fiber.New(fiber.Config{ + ReadTimeout: timeoutDefault, + WriteTimeout: timeoutDefault, + CaseSensitive: true, + DisableKeepalive: true, + ErrorHandler: errorHandler, + }) + + app.Use(limiter.New(limiter.Config{ + Max: 30, + Expiration: 1 * time.Minute, + LimitReached: limitHandler, + })) + app.Use(cors.New()) app.Use(etag.New()) route := routes.Dependencies{ - DB: db, - Redis: rdb, - Memory: memory, - HTTP: httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)), - Query: squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar), - App: app, + DB: db, + Redis: rdb, + Memory: memory, + HTTP: httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)), + Query: squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar), + App: app, + Context: &ctx, + Cancel: &cancel, } route.Health() route.Joke() @@ -100,6 +118,12 @@ func New() *fiber.App { return app } +func limitHandler(c *fiber.Ctx) error { + return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ + "message": "we only allow up to 15 request per minute", + }) +} + func errorHandler(c *fiber.Ctx, err error) error { log.Println(err) sentry.CaptureException(err) diff --git a/api/app/v1/core/joke_getter.go b/api/app/core/joke_getter.go similarity index 90% rename from api/app/v1/core/joke_getter.go rename to api/app/core/joke_getter.go index b2890ad..cdb913a 100644 --- a/api/app/v1/core/joke_getter.go +++ b/api/app/core/joke_getter.go @@ -13,13 +13,18 @@ import ( // GetAllJSONJokes fetch the database for all the jokes then output it as a JSON []byte. // Keep in mind, you will need to store it to memory yourself. -func GetAllJSONJokes(db *pgxpool.Pool) ([]byte, error) { - var jokes []Joke - results, err := db.Query(context.Background(), "SELECT \"id\",\"link\" FROM \"jokesbapak2\" ORDER BY \"id\"") +func GetAllJSONJokes(db *pgxpool.Pool, ctx *context.Context) ([]byte, error) { + conn, err := db.Acquire(*ctx) if err != nil { return nil, err } + defer conn.Release() + var jokes []Joke + results, err := conn.Query(*ctx, "SELECT \"id\",\"link\" FROM \"jokesbapak2\" ORDER BY \"id\"") + if err != nil { + return nil, err + } defer results.Close() err = pgxscan.ScanAll(&jokes, results) diff --git a/api/app/v1/core/joke_setter.go b/api/app/core/joke_setter.go similarity index 72% rename from api/app/v1/core/joke_setter.go rename to api/app/core/joke_setter.go index d77c611..cc4ac25 100644 --- a/api/app/v1/core/joke_setter.go +++ b/api/app/core/joke_setter.go @@ -1,14 +1,16 @@ package core import ( + "context" + "github.com/allegro/bigcache/v3" "github.com/jackc/pgx/v4/pgxpool" "github.com/pquerna/ffjson/ffjson" ) // SetAllJSONJoke fetches jokes data from GetAllJSONJokes then set it to memory cache. -func SetAllJSONJoke(db *pgxpool.Pool, memory *bigcache.BigCache) error { - jokes, err := GetAllJSONJokes(db) +func SetAllJSONJoke(db *pgxpool.Pool, memory *bigcache.BigCache, ctx *context.Context) error { + jokes, err := GetAllJSONJokes(db, ctx) if err != nil { return err } @@ -19,14 +21,14 @@ func SetAllJSONJoke(db *pgxpool.Pool, memory *bigcache.BigCache) error { return nil } -func SetTotalJoke(db *pgxpool.Pool, memory *bigcache.BigCache) error { +func SetTotalJoke(db *pgxpool.Pool, memory *bigcache.BigCache, ctx *context.Context) error { check, err := CheckJokesCache(memory) if err != nil { return err } if !check { - err = SetAllJSONJoke(db, memory) + err = SetAllJSONJoke(db, memory, ctx) if err != nil { return err } diff --git a/api/app/v1/core/joke_validation.go b/api/app/core/joke_validation.go similarity index 95% rename from api/app/v1/core/joke_validation.go rename to api/app/core/joke_validation.go index 9d47236..b0ff300 100644 --- a/api/app/v1/core/joke_validation.go +++ b/api/app/core/joke_validation.go @@ -2,7 +2,7 @@ package core import ( "errors" - "jokes-bapak2-api/app/v1/utils" + "jokes-bapak2-api/app/utils" "net/http" "strings" diff --git a/api/app/v1/core/schema.go b/api/app/core/schema.go similarity index 100% rename from api/app/v1/core/schema.go rename to api/app/core/schema.go diff --git a/api/app/v1/core/submit_setter.go b/api/app/core/submit_setter.go similarity index 97% rename from api/app/v1/core/submit_setter.go rename to api/app/core/submit_setter.go index 4fb1119..e490d83 100644 --- a/api/app/v1/core/submit_setter.go +++ b/api/app/core/submit_setter.go @@ -4,7 +4,7 @@ import ( "bytes" "io" "io/ioutil" - "jokes-bapak2-api/app/v1/utils" + "jokes-bapak2-api/app/utils" "mime/multipart" "net/http" "net/url" diff --git a/api/app/v1/core/submit_validation.go b/api/app/core/submit_validation.go similarity index 100% rename from api/app/v1/core/submit_validation.go rename to api/app/core/submit_validation.go diff --git a/api/favicon.png b/api/app/favicon.png similarity index 100% rename from api/favicon.png rename to api/app/favicon.png diff --git a/api/app/v1/handler/health/health.go b/api/app/handler/health/health.go similarity index 66% rename from api/app/v1/handler/health/health.go rename to api/app/handler/health/health.go index a2b43f4..48c1b08 100644 --- a/api/app/v1/handler/health/health.go +++ b/api/app/handler/health/health.go @@ -9,13 +9,20 @@ import ( ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client + DB *pgxpool.Pool + Redis *redis.Client + Context *context.Context } func (d *Dependencies) Health(c *fiber.Ctx) error { + conn, err := d.DB.Acquire(*d.Context) + if err != nil { + return err + } + defer conn.Release() + // Ping REDIS database - err := d.Redis.Ping(context.Background()).Err() + err = d.Redis.Ping(*d.Context).Err() if err != nil { return c. Status(fiber.StatusServiceUnavailable). @@ -24,7 +31,7 @@ func (d *Dependencies) Health(c *fiber.Ctx) error { }) } - _, err = d.DB.Query(context.Background(), "SELECT \"id\" FROM \"jokesbapak2\" LIMIT 1") + _, err = conn.Query(*d.Context, "SELECT \"id\" FROM \"jokesbapak2\" LIMIT 1") if err != nil { return c. Status(fiber.StatusServiceUnavailable). diff --git a/api/app/handler/health/health_test.go b/api/app/handler/health/health_test.go new file mode 100644 index 0000000..26c919d --- /dev/null +++ b/api/app/handler/health/health_test.go @@ -0,0 +1,34 @@ +package health_test + +import ( + "io/ioutil" + v1 "jokes-bapak2-api/app" + "net/http" + "testing" + "time" + + "github.com/gofiber/fiber/v2" + _ "github.com/joho/godotenv/autoload" + "github.com/stretchr/testify/assert" +) + +var app *fiber.App = v1.New() + +func TestHealth(t *testing.T) { + req, _ := http.NewRequest("GET", "/health", nil) + res, err := app.Test(req, int(time.Minute*2)) + if err != nil { + t.Fatal(err) + } + + assert.Equalf(t, false, err != nil, "health") + assert.Equalf(t, 200, res.StatusCode, "health") + assert.NotEqualf(t, 0, res.ContentLength, "health") + _, err = ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + + assert.Nilf(t, err, "health") +} diff --git a/api/app/v1/handler/health/schema.go b/api/app/handler/health/schema.go similarity index 100% rename from api/app/v1/handler/health/schema.go rename to api/app/handler/health/schema.go diff --git a/api/app/v1/handler/joke/dependencies.go b/api/app/handler/joke/dependencies.go similarity index 56% rename from api/app/v1/handler/joke/dependencies.go rename to api/app/handler/joke/dependencies.go index 75d0b4c..013f3a7 100644 --- a/api/app/v1/handler/joke/dependencies.go +++ b/api/app/handler/joke/dependencies.go @@ -1,6 +1,8 @@ package joke import ( + "context" + "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" "github.com/go-redis/redis/v8" @@ -9,9 +11,10 @@ import ( ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client - Memory *bigcache.BigCache - HTTP *httpclient.Client - Query squirrel.StatementBuilderType + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType + Context *context.Context } diff --git a/api/app/handler/joke/dependencies_test.go b/api/app/handler/joke/dependencies_test.go new file mode 100644 index 0000000..2d98a9b --- /dev/null +++ b/api/app/handler/joke/dependencies_test.go @@ -0,0 +1,9 @@ +package joke_test + +import ( + v1 "jokes-bapak2-api/app" + + "github.com/gofiber/fiber/v2" +) + +var app *fiber.App = v1.New() diff --git a/api/app/v1/handler/joke/joke_add.go b/api/app/handler/joke/joke_add.go similarity index 64% rename from api/app/v1/handler/joke/joke_add.go rename to api/app/handler/joke/joke_add.go index 9d0935d..6dec1a8 100644 --- a/api/app/v1/handler/joke/joke_add.go +++ b/api/app/handler/joke/joke_add.go @@ -1,16 +1,26 @@ package joke import ( - "context" - - "jokes-bapak2-api/app/v1/core" + "jokes-bapak2-api/app/core" "github.com/gofiber/fiber/v2" ) func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { + conn, err := d.DB.Acquire(*d.Context) + if err != nil { + return err + } + defer conn.Release() + + tx, err := conn.Begin(*d.Context) + if err != nil { + return err + } + defer tx.Rollback(*d.Context) + var body core.Joke - err := c.BodyParser(&body) + err = c.BodyParser(&body) if err != nil { return err } @@ -39,18 +49,21 @@ func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { } // TODO: Implement solution if the link provided already exists. - r, err := d.DB.Query(context.Background(), sql, args...) + _, err = tx.Exec(*d.Context, sql, args...) if err != nil { return err } - defer r.Close() - - err = core.SetAllJSONJoke(d.DB, d.Memory) + err = tx.Commit(*d.Context) if err != nil { return err } - err = core.SetTotalJoke(d.DB, d.Memory) + + err = core.SetAllJSONJoke(d.DB, d.Memory, d.Context) + if err != nil { + return err + } + err = core.SetTotalJoke(d.DB, d.Memory, d.Context) if err != nil { return err } diff --git a/api/app/v1/handler/joke/joke_add_test.go b/api/app/handler/joke/joke_add_test.go similarity index 87% rename from api/app/v1/handler/joke/joke_add_test.go rename to api/app/handler/joke/joke_add_test.go index 942f906..e4bd308 100644 --- a/api/app/v1/handler/joke/joke_add_test.go +++ b/api/app/handler/joke/joke_add_test.go @@ -13,22 +13,16 @@ import ( func TestAddNewJoke_201(t *testing.T) { // TODO: Remove this line below, make this test works t.SkipNow() - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() reqBody := strings.NewReader("{\"link\":\"https://via.placeholder.com/300/07f/ff0000.png\",\"key\":\"test\",\"token\":\"password\"}") req, _ := http.NewRequest("PUT", "/", reqBody) req.Header.Set("content-type", "application/json") req.Header.Add("accept", "application/json") - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } - + assert.Equalf(t, false, err != nil, "joke add") assert.Equalf(t, 201, res.StatusCode, "joke add") assert.NotEqualf(t, 0, res.ContentLength, "joke add") @@ -40,18 +34,12 @@ func TestAddNewJoke_201(t *testing.T) { func TestAddNewJoke_NotValidImage(t *testing.T) { // TODO: Remove this line below, make this test works t.SkipNow() - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() reqBody := strings.NewReader("{\"link\":\"https://google.com/\",\"key\":\"test\",\"token\":\"password\"}") req, _ := http.NewRequest("PUT", "/", reqBody) req.Header.Set("content-type", "application/json") req.Header.Add("accept", "application/json") - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } @@ -63,7 +51,7 @@ func TestAddNewJoke_NotValidImage(t *testing.T) { t.Fatal(err) } defer res.Body.Close() - + assert.Nilf(t, err, "joke add") assert.Equalf(t, "{\"error\":\"URL provided is not a valid image\"}", string(body), "joke add") } diff --git a/api/app/v1/handler/joke/joke_delete.go b/api/app/handler/joke/joke_delete.go similarity index 74% rename from api/app/v1/handler/joke/joke_delete.go rename to api/app/handler/joke/joke_delete.go index 765c55f..64b3b0f 100644 --- a/api/app/v1/handler/joke/joke_delete.go +++ b/api/app/handler/joke/joke_delete.go @@ -1,16 +1,20 @@ package joke import ( - "context" + "jokes-bapak2-api/app/core" "strconv" - "jokes-bapak2-api/app/v1/core" - "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" ) func (d *Dependencies) DeleteJoke(c *fiber.Ctx) error { + conn, err := d.DB.Acquire(*d.Context) + if err != nil { + return err + } + defer conn.Release() + id, err := strconv.Atoi(c.Params("id")) if err != nil { return err @@ -27,7 +31,7 @@ func (d *Dependencies) DeleteJoke(c *fiber.Ctx) error { } var jokeID int - err = d.DB.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + err = conn.QueryRow(*d.Context, sql, args...).Scan(&jokeID) if err != nil { return err } @@ -41,18 +45,18 @@ func (d *Dependencies) DeleteJoke(c *fiber.Ctx) error { return err } - r, err := d.DB.Query(context.Background(), sql, args...) + r, err := conn.Query(*d.Context, sql, args...) if err != nil { return err } defer r.Close() - err = core.SetAllJSONJoke(d.DB, d.Memory) + err = core.SetAllJSONJoke(d.DB, d.Memory, d.Context) if err != nil { return err } - err = core.SetTotalJoke(d.DB, d.Memory) + err = core.SetTotalJoke(d.DB, d.Memory, d.Context) if err != nil { return err } diff --git a/api/app/v1/handler/joke/joke_delete_test.go b/api/app/handler/joke/joke_delete_test.go similarity index 78% rename from api/app/v1/handler/joke/joke_delete_test.go rename to api/app/handler/joke/joke_delete_test.go index edce32c..d2defe4 100644 --- a/api/app/v1/handler/joke/joke_delete_test.go +++ b/api/app/handler/joke/joke_delete_test.go @@ -1,7 +1,6 @@ package joke_test import ( - "context" "io/ioutil" "net/http" "strings" @@ -14,24 +13,12 @@ import ( func TestDeleteJoke_200(t *testing.T) { // TODO: Remove this line below, make this test works t.SkipNow() - err := setup() - if err != nil { - t.Fatal(err) - } - - j, err := db.Query(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3);", 100, "https://via.placeholder.com/300/01f/fff.png", 1) - if err != nil { - t.Fatal(err) - } - - defer j.Close() - defer cleanup() reqBody := strings.NewReader("{\"key\":\"very secure\",\"token\":\"password\"}") req, _ := http.NewRequest("DELETE", "/id/100", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } @@ -51,18 +38,12 @@ func TestDeleteJoke_200(t *testing.T) { func TestDeleteJoke_NotExists(t *testing.T) { // TODO: Remove this line below, make this test works t.SkipNow() - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() reqBody := strings.NewReader("{\"key\":\"very secure\",\"token\":\"password\"}") req, _ := http.NewRequest("DELETE", "/id/100", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } diff --git a/api/app/v1/handler/joke/joke_get.go b/api/app/handler/joke/joke_get.go similarity index 82% rename from api/app/v1/handler/joke/joke_get.go rename to api/app/handler/joke/joke_get.go index 690350f..c9ee9cd 100644 --- a/api/app/v1/handler/joke/joke_get.go +++ b/api/app/handler/joke/joke_get.go @@ -1,14 +1,12 @@ package joke import ( - "context" "io/ioutil" + "jokes-bapak2-api/app/core" + "jokes-bapak2-api/app/utils" "strconv" "time" - "jokes-bapak2-api/app/v1/core" - "jokes-bapak2-api/app/v1/utils" - "github.com/gofiber/fiber/v2" ) @@ -17,7 +15,7 @@ func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { // send the joke if exists // get a new joke if it's not, then send it. var joke Today - err := d.Redis.MGet(context.Background(), "today:link", "today:date", "today:image", "today:contentType").Scan(&joke) + err := d.Redis.MGet(*d.Context, "today:link", "today:date", "today:image", "today:contentType").Scan(&joke) if err != nil { return err } @@ -31,8 +29,13 @@ func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { c.Set("Content-Type", joke.ContentType) return c.Status(fiber.StatusOK).Send([]byte(joke.Image)) } else { + conn, err := d.DB.Acquire(*d.Context) + if err != nil { + return err + } + defer conn.Release() var link string - err := d.DB.QueryRow(context.Background(), "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) + err = conn.QueryRow(*d.Context, "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) if err != nil { return err } @@ -48,7 +51,7 @@ func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { } now := time.Now().UTC().Format(time.RFC3339) - err = d.Redis.MSet(context.Background(), map[string]interface{}{ + err = d.Redis.MSet(*d.Context, map[string]interface{}{ "today:link": link, "today:date": now, "today:image": string(data), @@ -71,7 +74,7 @@ func (d *Dependencies) SingleJoke(c *fiber.Ctx) error { } if !checkCache { - jokes, err := core.GetAllJSONJokes(d.DB) + jokes, err := core.GetAllJSONJokes(d.DB, d.Context) if err != nil { return err } @@ -109,7 +112,7 @@ func (d *Dependencies) JokeByID(c *fiber.Ctx) error { } if !checkCache { - jokes, err := core.GetAllJSONJokes(d.DB) + jokes, err := core.GetAllJSONJokes(d.DB, d.Context) if err != nil { return err } diff --git a/api/app/v1/handler/joke/joke_get_test.go b/api/app/handler/joke/joke_get_test.go similarity index 80% rename from api/app/v1/handler/joke/joke_get_test.go rename to api/app/handler/joke/joke_get_test.go index c7d3abe..6d374f3 100644 --- a/api/app/v1/handler/joke/joke_get_test.go +++ b/api/app/handler/joke/joke_get_test.go @@ -12,19 +12,12 @@ import ( /// Need to find some workaround for this test func TestTodayJoke(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - req, _ := http.NewRequest("GET", "/today", nil) - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } - + assert.Equalf(t, false, err != nil, "today joke") assert.Equalf(t, 200, res.StatusCode, "today joke") assert.NotEqualf(t, 0, res.ContentLength, "today joke") @@ -38,19 +31,12 @@ func TestTodayJoke(t *testing.T) { } func TestSingleJoke(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - req, _ := http.NewRequest("GET", "/", nil) - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } - + assert.Equalf(t, false, err != nil, "single joke") assert.Equalf(t, 200, res.StatusCode, "single joke") assert.NotEqualf(t, 0, res.ContentLength, "single joke") @@ -64,19 +50,12 @@ func TestSingleJoke(t *testing.T) { } func TestJokeByID_200(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - req, _ := http.NewRequest("GET", "/id/1", nil) - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } - + assert.Equalf(t, false, err != nil, "joke by id") assert.Equalf(t, 200, res.StatusCode, "joke by id") assert.NotEqualf(t, 0, res.ContentLength, "joke by id") @@ -90,15 +69,8 @@ func TestJokeByID_200(t *testing.T) { } func TestJokeByID_404(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - req, _ := http.NewRequest("GET", "/id/300", nil) - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } diff --git a/api/app/v1/handler/joke/joke_total.go b/api/app/handler/joke/joke_total.go similarity index 88% rename from api/app/v1/handler/joke/joke_total.go rename to api/app/handler/joke/joke_total.go index f17f961..be24a32 100644 --- a/api/app/v1/handler/joke/joke_total.go +++ b/api/app/handler/joke/joke_total.go @@ -2,7 +2,7 @@ package joke import ( "errors" - "jokes-bapak2-api/app/v1/core" + "jokes-bapak2-api/app/core" "strconv" "github.com/allegro/bigcache/v3" @@ -16,7 +16,7 @@ func (d *Dependencies) TotalJokes(c *fiber.Ctx) error { } if !checkTotal { - err = core.SetTotalJoke(d.DB, d.Memory) + err = core.SetTotalJoke(d.DB, d.Memory, d.Context) if err != nil { return err } diff --git a/api/app/v1/handler/joke/joke_total_test.go b/api/app/handler/joke/joke_total_test.go similarity index 85% rename from api/app/v1/handler/joke/joke_total_test.go rename to api/app/handler/joke/joke_total_test.go index bcff035..e7cd8f9 100644 --- a/api/app/v1/handler/joke/joke_total_test.go +++ b/api/app/handler/joke/joke_total_test.go @@ -10,15 +10,8 @@ import ( ) func TestTotalJokes(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - req, _ := http.NewRequest("GET", "/total", nil) - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } @@ -31,7 +24,7 @@ func TestTotalJokes(t *testing.T) { t.Fatal(err) } defer res.Body.Close() - + assert.Nilf(t, err, "joke total") // FIXME: This should be "message": "3", not one. I don't know what's wrong as it's 1 AM. assert.Equalf(t, "{\"message\":\"3\"}", string(body), "joke total") diff --git a/api/app/v1/handler/joke/joke_update.go b/api/app/handler/joke/joke_update.go similarity index 80% rename from api/app/v1/handler/joke/joke_update.go rename to api/app/handler/joke/joke_update.go index a3b4ad4..ffc426d 100644 --- a/api/app/v1/handler/joke/joke_update.go +++ b/api/app/handler/joke/joke_update.go @@ -1,9 +1,7 @@ package joke import ( - "context" - - "jokes-bapak2-api/app/v1/core" + "jokes-bapak2-api/app/core" "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" @@ -11,6 +9,12 @@ import ( ) func (d *Dependencies) UpdateJoke(c *fiber.Ctx) error { + conn, err := d.DB.Acquire(*d.Context) + if err != nil { + return err + } + defer conn.Release() + id := c.Params("id") // Check if the joke exists sql, args, err := d.Query. @@ -23,7 +27,7 @@ func (d *Dependencies) UpdateJoke(c *fiber.Ctx) error { } var jokeID string - err = d.DB.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + err = conn.QueryRow(*d.Context, sql, args...).Scan(&jokeID) if err != nil && err != pgx.ErrNoRows { return err } @@ -58,18 +62,17 @@ func (d *Dependencies) UpdateJoke(c *fiber.Ctx) error { return err } - r, err := d.DB.Query(context.Background(), sql, args...) + r, err := conn.Query(*d.Context, sql, args...) if err != nil { return err } - defer r.Close() - err = core.SetAllJSONJoke(d.DB, d.Memory) + err = core.SetAllJSONJoke(d.DB, d.Memory, d.Context) if err != nil { return err } - err = core.SetTotalJoke(d.DB, d.Memory) + err = core.SetTotalJoke(d.DB, d.Memory, d.Context) if err != nil { return err } diff --git a/api/app/v1/handler/joke/joke_update_test.go b/api/app/handler/joke/joke_update_test.go similarity index 88% rename from api/app/v1/handler/joke/joke_update_test.go rename to api/app/handler/joke/joke_update_test.go index d316f67..f1a8898 100644 --- a/api/app/v1/handler/joke/joke_update_test.go +++ b/api/app/handler/joke/joke_update_test.go @@ -12,17 +12,12 @@ import ( func TestUpdateJoke_200(t *testing.T) { t.SkipNow() - err := setup() - if err != nil { - t.Fatal(err) - } - defer cleanup() reqBody := strings.NewReader("{\"link\":\"https://picsum.photos/id/9/200/300\",\"key\":\"test\",\"token\":\"password\"}") req, _ := http.NewRequest("PATCH", "/id/1", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } @@ -43,21 +38,16 @@ func TestUpdateJoke_200(t *testing.T) { func TestUpdateJoke_NotExists(t *testing.T) { // TODO: Remove this line below, make this test works t.SkipNow() - err := setup() - if err != nil { - t.Fatal(err) - } - defer cleanup() reqBody := strings.NewReader("{\"link\":\"https://picsum.photos/id/9/200/300\",\"key\":\"test\",\"token\":\"password\"}") req, _ := http.NewRequest("PATCH", "/id/100", reqBody) req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute * 2)) -if err != nil { + res, err := app.Test(req, int(time.Minute*2)) + if err != nil { t.Fatal(err) } - + assert.Equalf(t, false, err != nil, "joke update") assert.Equalf(t, 406, res.StatusCode, "joke update") assert.NotEqualf(t, 0, res.ContentLength, "joke update") diff --git a/api/app/v1/handler/joke/schema.go b/api/app/handler/joke/schema.go similarity index 100% rename from api/app/v1/handler/joke/schema.go rename to api/app/handler/joke/schema.go diff --git a/api/app/v1/handler/submit/dependencies.go b/api/app/handler/submit/dependencies.go similarity index 56% rename from api/app/v1/handler/submit/dependencies.go rename to api/app/handler/submit/dependencies.go index 5f73c1b..370f616 100644 --- a/api/app/v1/handler/submit/dependencies.go +++ b/api/app/handler/submit/dependencies.go @@ -1,6 +1,8 @@ package submit import ( + "context" + "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" "github.com/go-redis/redis/v8" @@ -9,9 +11,10 @@ import ( ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client - Memory *bigcache.BigCache - HTTP *httpclient.Client - Query squirrel.StatementBuilderType + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType + Context *context.Context } diff --git a/api/app/handler/submit/dependencies_test.go b/api/app/handler/submit/dependencies_test.go new file mode 100644 index 0000000..1733fac --- /dev/null +++ b/api/app/handler/submit/dependencies_test.go @@ -0,0 +1,9 @@ +package submit_test + +import ( + v1 "jokes-bapak2-api/app" + + "github.com/gofiber/fiber/v2" +) + +var app *fiber.App = v1.New() diff --git a/api/app/v1/handler/submit/schema.go b/api/app/handler/submit/schema.go similarity index 100% rename from api/app/v1/handler/submit/schema.go rename to api/app/handler/submit/schema.go diff --git a/api/app/v1/handler/submit/submit_add.go b/api/app/handler/submit/submit_add.go similarity index 95% rename from api/app/v1/handler/submit/submit_add.go rename to api/app/handler/submit/submit_add.go index 7af06ca..983528b 100644 --- a/api/app/v1/handler/submit/submit_add.go +++ b/api/app/handler/submit/submit_add.go @@ -1,8 +1,7 @@ package submit import ( - "context" - "jokes-bapak2-api/app/v1/core" + "jokes-bapak2-api/app/core" "strings" "time" @@ -79,7 +78,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { } var submission []Submission - result, err := d.DB.Query(context.Background(), sql, args...) + result, err := d.DB.Query(*d.Context, sql, args...) if err != nil { return err } diff --git a/api/app/v1/handler/submit/submit_get.go b/api/app/handler/submit/submit_get.go similarity index 95% rename from api/app/v1/handler/submit/submit_get.go rename to api/app/handler/submit/submit_get.go index 8ab1ae3..cb26198 100644 --- a/api/app/v1/handler/submit/submit_get.go +++ b/api/app/handler/submit/submit_get.go @@ -2,7 +2,6 @@ package submit import ( "bytes" - "context" "log" "strconv" @@ -80,7 +79,7 @@ func (d *Dependencies) GetSubmission(c *fiber.Ctx) error { sql = bob.ReplacePlaceholder(sqlQuery.String(), bob.Dollar) var submissions []Submission - results, err := d.DB.Query(context.Background(), sql, args...) + results, err := d.DB.Query(*d.Context, sql, args...) if err != nil { log.Println(err) return err diff --git a/api/app/v1/handler/submit/submit_get_test.go b/api/app/handler/submit/submit_get_test.go similarity index 87% rename from api/app/v1/handler/submit/submit_get_test.go rename to api/app/handler/submit/submit_get_test.go index 37c9427..864ba97 100644 --- a/api/app/v1/handler/submit/submit_get_test.go +++ b/api/app/handler/submit/submit_get_test.go @@ -12,15 +12,8 @@ import ( ) func TestGetSubmission_200(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - req, _ := http.NewRequest("GET", "/submit", nil) - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } @@ -38,19 +31,12 @@ func TestGetSubmission_200(t *testing.T) { } func TestGetSubmission_Params(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - req, _ := http.NewRequest("GET", "/submit?page=1&limit=5&approved=true", nil) - res, err := app.Test(req, int(time.Minute * 2)) + res, err := app.Test(req, int(time.Minute*2)) if err != nil { t.Fatal(err) } - + assert.Equalf(t, false, err != nil, "get submission") assert.Equalf(t, 200, res.StatusCode, "get submission") body, err := ioutil.ReadAll(res.Body) diff --git a/api/app/v1/middleware/auth.go b/api/app/middleware/auth.go similarity index 86% rename from api/app/v1/middleware/auth.go rename to api/app/middleware/auth.go index 2e3e0fa..96ddb09 100644 --- a/api/app/v1/middleware/auth.go +++ b/api/app/middleware/auth.go @@ -14,7 +14,7 @@ import ( var psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) -func RequireAuth(db *pgxpool.Pool) fiber.Handler { +func RequireAuth(db *pgxpool.Pool, ctx *context.Context) fiber.Handler { return func(c *fiber.Ctx) error { var auth Auth err := c.BodyParser(&auth) @@ -33,7 +33,7 @@ func RequireAuth(db *pgxpool.Pool) fiber.Handler { } var token string - err = db.QueryRow(context.Background(), sql, args...).Scan(&token) + err = db.QueryRow(*ctx, sql, args...).Scan(&token) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return c. @@ -64,7 +64,7 @@ func RequireAuth(db *pgxpool.Pool) fiber.Handler { return err } - _, err = db.Query(context.Background(), sql, args...) + _, err = db.Query(*ctx, sql, args...) if err != nil { return err } @@ -79,7 +79,7 @@ func RequireAuth(db *pgxpool.Pool) fiber.Handler { } var id int - err = db.QueryRow(context.Background(), sql, args...).Scan(&id) + err = db.QueryRow(*ctx, sql, args...).Scan(&id) if err != nil { return err } diff --git a/api/app/v1/middleware/schema.go b/api/app/middleware/schema.go similarity index 100% rename from api/app/v1/middleware/schema.go rename to api/app/middleware/schema.go diff --git a/api/app/v1/middleware/validation.go b/api/app/middleware/validation.go similarity index 100% rename from api/app/v1/middleware/validation.go rename to api/app/middleware/validation.go diff --git a/api/app/v1/platform/database/create.go b/api/app/platform/database/create.go similarity index 83% rename from api/app/v1/platform/database/create.go rename to api/app/platform/database/create.go index b556fc0..9037326 100644 --- a/api/app/v1/platform/database/create.go +++ b/api/app/platform/database/create.go @@ -9,10 +9,22 @@ import ( ) // Setup the table connection, create table if not exists -func Setup(db *pgxpool.Pool) error { +func Setup(db *pgxpool.Pool, ctx *context.Context) error { + conn, err := db.Acquire(*ctx) + if err != nil { + return err + } + defer conn.Release() + + tx, err := conn.Begin(*ctx) + if err != nil { + return err + } + defer tx.Rollback(*ctx) + // administrators table var tableAuthExists bool - err := db.QueryRow(context.Background(), `SELECT EXISTS ( + err = db.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'administrators' @@ -35,7 +47,7 @@ func Setup(db *pgxpool.Pool) error { return err } - _, err = db.Query(context.Background(), sql) + _, err = tx.Exec(*ctx, sql) if err != nil { log.Fatalln("18 - failed on table creation: ", err) return err @@ -46,7 +58,7 @@ func Setup(db *pgxpool.Pool) error { // Check if table exists var tableJokesExists bool - err = db.QueryRow(context.Background(), `SELECT EXISTS ( + err = db.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jokesbapak2' @@ -68,7 +80,7 @@ func Setup(db *pgxpool.Pool) error { return err } - _, err = db.Query(context.Background(), sql) + _, err = tx.Exec(*ctx, sql) if err != nil { log.Fatalln("12 - failed on table creation: ", err) return err @@ -79,7 +91,7 @@ func Setup(db *pgxpool.Pool) error { //Check if table exists var tableSubmissionExists bool - err = db.QueryRow(context.Background(), `SELECT EXISTS ( + err = db.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'submission' @@ -102,11 +114,16 @@ func Setup(db *pgxpool.Pool) error { log.Fatalln("14 - failed on table creation: ", err) } - _, err = db.Query(context.Background(), sql) + _, err = tx.Query(*ctx, sql) if err != nil { log.Fatalln("15 - failed on table creation: ", err) } } + err = tx.Commit(*ctx) + if err != nil { + return err + } + return nil } diff --git a/api/app/v1/platform/database/placeholder.sql b/api/app/platform/database/placeholder.sql similarity index 100% rename from api/app/v1/platform/database/placeholder.sql rename to api/app/platform/database/placeholder.sql diff --git a/api/app/v1/routes/dependencies.go b/api/app/routes/dependencies.go similarity index 53% rename from api/app/v1/routes/dependencies.go rename to api/app/routes/dependencies.go index 65f6bb5..59eefdd 100644 --- a/api/app/v1/routes/dependencies.go +++ b/api/app/routes/dependencies.go @@ -1,6 +1,8 @@ package routes import ( + "context" + "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" "github.com/go-redis/redis/v8" @@ -10,10 +12,12 @@ import ( ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client - Memory *bigcache.BigCache - HTTP *httpclient.Client - Query squirrel.StatementBuilderType - App *fiber.App + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType + App *fiber.App + Context *context.Context + Cancel *context.CancelFunc } diff --git a/api/app/v1/routes/health.go b/api/app/routes/health.go similarity index 50% rename from api/app/v1/routes/health.go rename to api/app/routes/health.go index d0796a0..28cbcca 100644 --- a/api/app/v1/routes/health.go +++ b/api/app/routes/health.go @@ -1,19 +1,19 @@ package routes import ( - "jokes-bapak2-api/app/v1/handler/health" + "jokes-bapak2-api/app/handler/health" "time" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cache" ) -func (d *Dependencies) Health() *fiber.App { +func (d *Dependencies) Health() { // Health check deps := health.Dependencies{ - Redis: d.Redis, + DB: d.DB, + Redis: d.Redis, + Context: d.Context, } d.App.Get("/health", cache.New(cache.Config{Expiration: 30 * time.Minute}), deps.Health) - - return d.App + d.App.Get("/v1/health", cache.New(cache.Config{Expiration: 30 * time.Minute}), deps.Health) } diff --git a/api/app/routes/joke.go b/api/app/routes/joke.go new file mode 100644 index 0000000..8fa701b --- /dev/null +++ b/api/app/routes/joke.go @@ -0,0 +1,44 @@ +package routes + +import ( + "jokes-bapak2-api/app/handler/joke" + "jokes-bapak2-api/app/middleware" + "time" + + "github.com/gofiber/fiber/v2/middleware/cache" +) + +func (d *Dependencies) Joke() { + deps := joke.Dependencies{ + DB: d.DB, + Redis: d.Redis, + Memory: d.Memory, + HTTP: d.HTTP, + Query: d.Query, + Context: d.Context, + } + // Single route + d.App.Get("/", deps.SingleJoke) + d.App.Get("/v1", deps.SingleJoke) + + // Today's joke + d.App.Get("/today", cache.New(cache.Config{Expiration: 6 * time.Hour}), deps.TodayJoke) + d.App.Get("/v1/today", cache.New(cache.Config{Expiration: 6 * time.Hour}), deps.TodayJoke) + + // Joke by ID + d.App.Get("/id/:id", middleware.OnlyIntegerAsID(), deps.JokeByID) + d.App.Get("/v1/id/:id", middleware.OnlyIntegerAsID(), deps.JokeByID) + + // Count total jokes + d.App.Get("/total", cache.New(cache.Config{Expiration: 15 * time.Minute}), deps.TotalJokes) + d.App.Get("/v1/total", cache.New(cache.Config{Expiration: 15 * time.Minute}), deps.TotalJokes) + + // Add new joke + d.App.Put("/", middleware.RequireAuth(d.DB, d.Context), deps.AddNewJoke) + + // Update a joke + d.App.Patch("/id/:id", middleware.RequireAuth(d.DB, d.Context), middleware.OnlyIntegerAsID(), deps.UpdateJoke) + + // Delete a joke + d.App.Delete("/id/:id", middleware.RequireAuth(d.DB, d.Context), middleware.OnlyIntegerAsID(), deps.DeleteJoke) +} diff --git a/api/app/v1/routes/submit.go b/api/app/routes/submit.go similarity index 83% rename from api/app/v1/routes/submit.go rename to api/app/routes/submit.go index 1311e79..c677e45 100644 --- a/api/app/v1/routes/submit.go +++ b/api/app/routes/submit.go @@ -1,14 +1,14 @@ package routes import ( - "jokes-bapak2-api/app/v1/handler/submit" + "jokes-bapak2-api/app/handler/submit" "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/cache" ) -func (d *Dependencies) Submit() *fiber.App { +func (d *Dependencies) Submit() { deps := submit.Dependencies{ DB: d.DB, Redis: d.Redis, @@ -29,6 +29,4 @@ func (d *Dependencies) Submit() *fiber.App { // Add a joke d.App.Post("/submit", deps.SubmitJoke) - - return d.App } diff --git a/api/app/v1/utils/array.go b/api/app/utils/array.go similarity index 100% rename from api/app/v1/utils/array.go rename to api/app/utils/array.go diff --git a/api/app/v1/utils/array_test.go b/api/app/utils/array_test.go similarity index 92% rename from api/app/v1/utils/array_test.go rename to api/app/utils/array_test.go index 3f57c03..8be25de 100644 --- a/api/app/v1/utils/array_test.go +++ b/api/app/utils/array_test.go @@ -1,7 +1,7 @@ package utils_test import ( - "jokes-bapak2-api/app/v1/utils" + "jokes-bapak2-api/app/utils" "testing" ) diff --git a/api/app/v1/utils/date.go b/api/app/utils/date.go similarity index 100% rename from api/app/v1/utils/date.go rename to api/app/utils/date.go diff --git a/api/app/v1/utils/date_test.go b/api/app/utils/date_test.go similarity index 96% rename from api/app/v1/utils/date_test.go rename to api/app/utils/date_test.go index f96f077..a3c6c27 100644 --- a/api/app/v1/utils/date_test.go +++ b/api/app/utils/date_test.go @@ -1,10 +1,9 @@ package utils_test import ( + "jokes-bapak2-api/app/utils" "testing" "time" - - "jokes-bapak2-api/app/v1/utils" ) func TestIsToday_Today(t *testing.T) { diff --git a/api/app/v1/utils/parse.go b/api/app/utils/parse.go similarity index 100% rename from api/app/v1/utils/parse.go rename to api/app/utils/parse.go diff --git a/api/app/v1/utils/parse_test.go b/api/app/utils/parse_test.go similarity index 96% rename from api/app/v1/utils/parse_test.go rename to api/app/utils/parse_test.go index db1dd0c..7c59a8b 100644 --- a/api/app/v1/utils/parse_test.go +++ b/api/app/utils/parse_test.go @@ -1,10 +1,9 @@ package utils_test import ( + "jokes-bapak2-api/app/utils" "strings" "testing" - - "jokes-bapak2-api/app/v1/utils" ) func TestParseToJSONBody(t *testing.T) { diff --git a/api/app/v1/utils/random.go b/api/app/utils/random.go similarity index 100% rename from api/app/v1/utils/random.go rename to api/app/utils/random.go diff --git a/api/app/v1/utils/random_test.go b/api/app/utils/random_test.go similarity index 93% rename from api/app/v1/utils/random_test.go rename to api/app/utils/random_test.go index 9b1b642..8f36d66 100644 --- a/api/app/v1/utils/random_test.go +++ b/api/app/utils/random_test.go @@ -1,7 +1,7 @@ package utils_test import ( - "jokes-bapak2-api/app/v1/utils" + "jokes-bapak2-api/app/utils" "testing" ) diff --git a/api/app/v1/utils/request.go b/api/app/utils/request.go similarity index 100% rename from api/app/v1/utils/request.go rename to api/app/utils/request.go diff --git a/api/app/v1/utils/request_test.go b/api/app/utils/request_test.go similarity index 93% rename from api/app/v1/utils/request_test.go rename to api/app/utils/request_test.go index 6e51489..d47bed7 100644 --- a/api/app/v1/utils/request_test.go +++ b/api/app/utils/request_test.go @@ -1,10 +1,9 @@ package utils_test import ( + "jokes-bapak2-api/app/utils" "net/http" "testing" - - "jokes-bapak2-api/app/v1/utils" ) func TestRequest_Get(t *testing.T) { diff --git a/api/app/v1/handler/health/health_test.go b/api/app/v1/handler/health/health_test.go deleted file mode 100644 index d4f1899..0000000 --- a/api/app/v1/handler/health/health_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package health_test - -import ( - "context" - "io/ioutil" - v1 "jokes-bapak2-api/app/v1" - "log" - "net/http" - "os" - "testing" - "time" - - "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4/pgxpool" - _ "github.com/joho/godotenv/autoload" - "github.com/stretchr/testify/assert" -) - -var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} -var app *fiber.App = v1.New() -var db *pgxpool.Pool - -func cleanup() { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 5 - poolConfig.MinConns = 2 - - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - - j, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"") - if err != nil { - panic(err) - } - defer j.Close() - - a, err := db.Query(context.Background(), "DROP TABLE \"administrators\"") - if err != nil { - panic(err) - } - - defer a.Close() -} - -func setup() error { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 5 - poolConfig.MinConns = 2 - - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - - a, err := db.Query(context.Background(), "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4);", 1, "very secure", "not the real one", time.Now().Format(time.RFC3339)) - if err != nil { - return err - } - defer a.Close() - - j, err := db.Query(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) - if err != nil { - return err - } - - defer j.Close() - - return nil -} - -func TestHealth(t *testing.T) { - err := setup() - if err != nil { - t.Fatal(err) - } - - defer cleanup() - - req, _ := http.NewRequest("GET", "/health", nil) - res, err := app.Test(req, int(time.Minute * 2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "health") - assert.Equalf(t, 200, res.StatusCode, "health") - assert.NotEqualf(t, 0, res.ContentLength, "health") - _, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "health") -} diff --git a/api/app/v1/handler/joke/dependencies_test.go b/api/app/v1/handler/joke/dependencies_test.go deleted file mode 100644 index 963acae..0000000 --- a/api/app/v1/handler/joke/dependencies_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package joke_test - -import ( - "context" - v1 "jokes-bapak2-api/app/v1" - "jokes-bapak2-api/app/v1/platform/database" - "log" - "os" - - "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4/pgxpool" -) - -var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} -var app *fiber.App = v1.New() -var db *pgxpool.Pool - -func cleanup() { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 5 - poolConfig.MinConns = 2 - - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - - j, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"") - if err != nil { - panic(err) - } - defer j.Close() - - a, err := db.Query(context.Background(), "DROP TABLE \"administrators\"") - if err != nil { - panic(err) - } - - defer a.Close() -} - -func setup() error { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 15 - poolConfig.MinConns = 2 - - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - - err = database.Setup(db) - if err != nil { - return err - } - - a, err := db.Query(context.Background(), "INSERT INTO \"administrators\" (\"id\", \"key\", \"token\", \"last_used\") VALUES (1, 'test', '$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106', '');") - if err != nil { - return err - } - - defer a.Close() - - j, err := db.Query(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) - if err != nil { - return err - } - - defer j.Close() - - return nil -} diff --git a/api/app/v1/handler/submit/dependencies_test.go b/api/app/v1/handler/submit/dependencies_test.go deleted file mode 100644 index 7e19ff7..0000000 --- a/api/app/v1/handler/submit/dependencies_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package submit_test - -import ( - "context" - v1 "jokes-bapak2-api/app/v1" - "log" - "os" - - "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4/pgxpool" -) - -var submissionData = []interface{}{1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1} -var app *fiber.App = v1.New() -var db *pgxpool.Pool - -func cleanup() { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 5 - poolConfig.MinConns = 2 - - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - - s, err := db.Query(context.Background(), "DROP TABLE \"submission\"") - if err != nil { - panic(err) - } - defer s.Close() -} - -func setup() error { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 15 - poolConfig.MinConns = 2 - - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - - s, err := db.Query(context.Background(), "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) - if err != nil { - return err - } - - defer s.Close() - - return nil -} diff --git a/api/app/v1/routes/joke.go b/api/app/v1/routes/joke.go deleted file mode 100644 index 86f8b67..0000000 --- a/api/app/v1/routes/joke.go +++ /dev/null @@ -1,42 +0,0 @@ -package routes - -import ( - "jokes-bapak2-api/app/v1/handler/joke" - "jokes-bapak2-api/app/v1/middleware" - "time" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cache" -) - -func (d *Dependencies) Joke() *fiber.App { - deps := joke.Dependencies{ - DB: d.DB, - Redis: d.Redis, - Memory: d.Memory, - HTTP: d.HTTP, - Query: d.Query, - } - // Single route - d.App.Get("/", deps.SingleJoke) - - // Today's joke - d.App.Get("/today", cache.New(cache.Config{Expiration: 6 * time.Hour}), deps.TodayJoke) - - // Joke by ID - d.App.Get("/id/:id", middleware.OnlyIntegerAsID(), deps.JokeByID) - - // Count total jokes - d.App.Get("/total", cache.New(cache.Config{Expiration: 15 * time.Minute}), deps.TotalJokes) - - // Add new joke - d.App.Put("/", middleware.RequireAuth(d.DB), deps.AddNewJoke) - - // Update a joke - d.App.Patch("/id/:id", middleware.RequireAuth(d.DB), middleware.OnlyIntegerAsID(), deps.UpdateJoke) - - // Delete a joke - d.App.Delete("/id/:id", middleware.RequireAuth(d.DB), middleware.OnlyIntegerAsID(), deps.DeleteJoke) - - return d.App -} diff --git a/api/app/v1/documentation.json b/api/documentation.json similarity index 99% rename from api/app/v1/documentation.json rename to api/documentation.json index 827aded..958db5c 100644 --- a/api/app/v1/documentation.json +++ b/api/documentation.json @@ -19,6 +19,10 @@ "url": "https://jokesbapak2.herokuapp.com/v1", "description": "Production" }, + { + "url": "https://jokesbapak2.herokuapp.com", + "description": "Production" + }, { "url": "http://localhost:5000", "description": "Development" diff --git a/api/app/v1/documentation.yaml b/api/documentation.yaml similarity index 98% rename from api/app/v1/documentation.yaml rename to api/documentation.yaml index d15d255..4d67392 100644 --- a/api/app/v1/documentation.yaml +++ b/api/documentation.yaml @@ -16,6 +16,8 @@ info: servers: - url: "https://jokesbapak2.herokuapp.com/v1" description: Production + - url: "https://jokesbapak2.herokuapp.com" + description: Production - url: "http://localhost:5000" description: Development paths: diff --git a/api/go.mod b/api/go.mod index 90de283..7dae60f 100644 --- a/api/go.mod +++ b/api/go.mod @@ -27,18 +27,11 @@ require ( require ( github.com/andybalholm/brotli v1.0.2 // indirect - github.com/atotto/clipboard v0.1.4 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect - github.com/charmbracelet/bubbles v0.9.0 // indirect - github.com/charmbracelet/bubbletea v0.15.0 // indirect - github.com/charmbracelet/lipgloss v0.4.0 // indirect - github.com/containerd/console v1.0.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/gofiber/cli v0.0.9 // indirect github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.9.0 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -50,21 +43,12 @@ require ( github.com/klauspost/compress v1.12.2 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.9.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/spf13/cobra v1.2.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.3.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.26.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect - golang.org/x/term v0.0.0-20210422114643-f5beecf764ed // indirect golang.org/x/text v0.3.6 // indirect ) diff --git a/api/go.sum b/api/go.sum index ed4cd0f..04e5f52 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,45 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= github.com/DataDog/datadog-go v3.7.1+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -48,7 +10,6 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -69,7 +30,6 @@ github.com/allegro/bigcache/v3 v3.0.0 h1:5Hxq+GTy8gHEeQccCZZDCfZRTydUfErdUf0iVDc github.com/allegro/bigcache/v3 v3.0.0/go.mod h1:t5TAJn1B9qvf/VlJrSM1r6NlFAYoFDubYUsCuIO9nUQ= github.com/andybalholm/brotli v1.0.2 h1:JKnhI/XQ75uFBTiuzXpzFrUriDPiZjlOSzh6wXogP0E= github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -77,9 +37,6 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= @@ -88,36 +45,14 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/cactus/go-statsd-client/statsd v0.0.0-20200423205355-cb0885a1018c/go.mod h1:l/bIBLeOl9eX+wxJAzxS4TveKRtAqlyDpHjhkfO0MEI= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.8.0/go.mod h1:5WX1sSSjNCgCrzvRMN/z23HxvWaa+AI16Ch0KPZPeDs= -github.com/charmbracelet/bubbles v0.9.0 h1:lqJ8FXwoLceQF2J0A+dWo1Cuu1dNyjbW4Opgdi2vkhw= -github.com/charmbracelet/bubbles v0.9.0/go.mod h1:NWT/c+0rYEnYChz5qCyX4Lj6fDw9gGToh9EFJPajghU= -github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg= -github.com/charmbracelet/bubbletea v0.14.0/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE= -github.com/charmbracelet/bubbletea v0.14.1/go.mod h1:b5lOf5mLjMg1tRn1HVla54guZB+jvsyV0yYAQja95zE= -github.com/charmbracelet/bubbletea v0.15.0 h1:7++QPke7CsjBs+tZl49x7KXTHsof+NUMhreAtwBXygE= -github.com/charmbracelet/bubbletea v0.15.0/go.mod h1:YTZSs2p3odhwYZdhqJheYHVUjU37c9OLgS85kw6NGQY= -github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= -github.com/charmbracelet/lipgloss v0.3.0/go.mod h1:VkhdBS2eNAmRkTwRKLJCFhCOVkjntMusBDxv7TXahuk= -github.com/charmbracelet/lipgloss v0.4.0 h1:768h64EFkGUr8V5yAKV7/Ta0NiVceiPaV+PphaW1K9g= -github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go/v2 v2.0.3 h1:ZA346ACHIZctef6trOTwBAEvPVm1k0uLm/bb2Atc+S8= @@ -125,25 +60,15 @@ github.com/cockroachdb/cockroach-go/v2 v2.0.3/go.mod h1:hAuDgiVgDVkfirP9JnhXEfcX github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -155,7 +80,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -164,12 +88,7 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= @@ -179,7 +98,6 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -194,9 +112,6 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -212,9 +127,6 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofiber/cli v0.0.9 h1:W29t34Y3xnkQ98B+1OV480d8Ehw5hwqPYrwGDiZ4ZVY= -github.com/gofiber/cli v0.0.9/go.mod h1:L+Zjl5jYfCKKcLI+b9LmhO5CQOg5iE8POwdM0r6Mack= github.com/gofiber/fiber/v2 v2.15.0 h1:yd+o1t6/hjkmjZxz4FJlgHAKBIu1w1PnRL3VB67KMHM= github.com/gofiber/fiber/v2 v2.15.0/go.mod h1:iftruuHGkRYGEXVISmdD7HTYWyfS2Bh+Dkfq4n/1Owg= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -224,7 +136,6 @@ github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFG github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gojek/heimdall/v7 v7.0.2 h1:+YutGXZ8oEWbCJIwjRnkKmoTl+Oxt1Urs3hc/FR0sxU= github.com/gojek/heimdall/v7 v7.0.2/go.mod h1:Z43HtMid7ysSjmsedPTXAki6jcdcNVnjn5pmsTyiMic= github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf h1:5xRGbUdOmZKoDXkGx5evVLehuCMpuO1hl701bEQqXOM= @@ -232,35 +143,17 @@ github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf/go.mod h1:QzhUKaYKJ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= @@ -270,53 +163,23 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -339,10 +202,7 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= @@ -420,7 +280,6 @@ github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -433,9 +292,6 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= @@ -445,7 +301,6 @@ github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2 github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -454,7 +309,6 @@ github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8 github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -462,7 +316,6 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= @@ -479,13 +332,8 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -497,15 +345,7 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.8/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.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= @@ -521,19 +361,11 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4 github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= -github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.7.2/go.mod h1:ct2L5N2lmix82RaY3bMWwVu/jUFc9Ule0KGDCiKYPh8= -github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0= -github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8= -github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -548,7 +380,6 @@ github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -574,7 +405,6 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -585,7 +415,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -593,7 +422,6 @@ github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74 github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -601,24 +429,16 @@ github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= @@ -627,7 +447,6 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= @@ -646,25 +465,14 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= -github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= @@ -677,12 +485,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -708,41 +513,24 @@ github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmv github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -750,7 +538,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -759,48 +546,20 @@ golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -814,64 +573,28 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210510120150-4163338589ed h1:p9UgmWI9wKpfYmgaV/IZKGdXc5qEK45tDwwwDyjS26I= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -883,78 +606,39 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210422114643-f5beecf764ed h1:Ei4bQjjpYUsS4efOUz+5Nz++IVkHk87n2zBA0NxBWc0= -golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -963,60 +647,18 @@ golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1025,115 +667,30 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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= @@ -1146,9 +703,7 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 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/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -1159,25 +714,16 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20191120175047-4206685974f2/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/api/main.go b/api/main.go index 08b959b..d4cb1b5 100644 --- a/api/main.go +++ b/api/main.go @@ -1,52 +1,26 @@ package main import ( + "jokes-bapak2-api/app" "log" "os" "os/signal" - "time" - - v1 "jokes-bapak2-api/app/v1" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/favicon" - "github.com/gofiber/fiber/v2/middleware/limiter" _ "github.com/joho/godotenv/autoload" ) func main() { - timeoutDefault, _ := time.ParseDuration("1m") - - app := fiber.New(fiber.Config{ - ReadTimeout: timeoutDefault, - WriteTimeout: timeoutDefault, - }) - - app.Use(limiter.New(limiter.Config{ - Max: 30, - Expiration: 1 * time.Minute, - LimitReached: limitHandler, - })) - app.Use(favicon.New(favicon.Config{ - File: "./favicon.png", - })) - - app.Mount("/v1", v1.New()) + a := app.New() // Start server (with or without graceful shutdown). if os.Getenv("ENV") == "development" { - StartServer(app) + StartServer(a) } else { - StartServerWithGracefulShutdown(app) + StartServerWithGracefulShutdown(a) } } -func limitHandler(c *fiber.Ctx) error { - return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ - "message": "we only allow up to 15 request per minute", - }) -} - // StartServerWithGracefulShutdown function for starting server with a graceful shutdown. func StartServerWithGracefulShutdown(a *fiber.App) { // Create channel for idle connections. diff --git a/api/main_test.go b/api/main_test.go new file mode 100644 index 0000000..f1116ec --- /dev/null +++ b/api/main_test.go @@ -0,0 +1,130 @@ +package main_test + +import ( + "context" + "errors" + "flag" + "jokes-bapak2-api/app/platform/database" + "log" + "os" + "testing" + "time" + + "github.com/jackc/pgx/v4/pgxpool" +) + +var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} +var submissionData = []interface{}{1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1} +var administratorsData = []interface{}{1, "very secure", "not the real one", time.Now().Format(time.RFC3339), 2, "test", "$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106", ""} + +var db *pgxpool.Pool +var ctx context.Context = context.Background() + +func TestMain(m *testing.M) { + flag.Parse() + err := setup() + if err != nil { + log.Fatalln(err) + } + + code := m.Run() + os.Exit(code) +} + +func cleanup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Fatalln("Unable to create pool config", err) + } + poolConfig.MaxConns = 3 + poolConfig.MinConns = 1 + + db, err = pgxpool.ConnectConfig(ctx, poolConfig) + if err != nil { + log.Fatalln("Unable to create connection", err) + } + defer db.Close() + + d, err := db.Query(ctx, "BEGIN; DROP TABLE \"jokesbapak2\"; DROP TABLE \"administrators\"; DROP TABLE \"submission\"; COMMIT;") + if err != nil { + panic(err) + } + defer d.Close() +} + +func setup() error { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + return errors.New("Unable to create pool config: " + err.Error()) + } + poolConfig.MaxConns = 3 + poolConfig.MinConns = 1 + + db, err = pgxpool.ConnectConfig(ctx, poolConfig) + if err != nil { + return errors.New("Unable to create connection: " + err.Error()) + } + defer db.Close() + + conn, err := db.Acquire(ctx) + if err != nil { + return err + } + defer conn.Release() + + tx, err := conn.Begin(ctx) + if err != nil { + return err + } + defer tx.Rollback(ctx) + + _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS \"jokesbapak2\"") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS \"administrators\"") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS \"submission\"") + if err != nil { + return err + } + err = tx.Commit(ctx) + if err != nil { + return err + } + + err = database.Setup(db, &ctx) + if err != nil { + return err + } + + tx, err = conn.Begin(ctx) + if err != nil { + return err + } + defer tx.Rollback(ctx) + + _, err = tx.Exec(ctx, "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) + if err != nil { + return err + } + + _, err = tx.Exec(ctx, "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) + if err != nil { + return err + } + + err = tx.Commit(ctx) + if err != nil { + return err + } + + return nil +} From a5c665fe4f0201dd982adce8ce6eb3ee9ea36ce7 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 27 Sep 2021 20:02:58 +0700 Subject: [PATCH 04/21] fix: sql transactions --- api/app/app.go | 7 +- api/app/handler/joke/joke_add.go | 26 ++++++- api/app/handler/submit/schema.go | 7 +- api/app/handler/submit/submit_add.go | 55 +++++++++++--- api/app/platform/database/create.go | 23 ++---- api/app/platform/database/placeholder.sql | 22 +++--- api/app/routes/health.go | 1 + api/app/routes/submit.go | 12 +-- api/main_test.go | 93 ++++++++--------------- 9 files changed, 131 insertions(+), 115 deletions(-) diff --git a/api/app/app.go b/api/app/app.go index 1c8dd9c..9b4972b 100644 --- a/api/app/app.go +++ b/api/app/app.go @@ -24,7 +24,7 @@ import ( func New() *fiber.App { // Setup Context - ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1) + ctx, cancel := context.WithCancel(context.Background()) // Setup PostgreSQL poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) @@ -62,15 +62,14 @@ func New() *fiber.App { Debug: true, }) if err != nil { - log.Fatal(err) + log.Fatalln(err) } - defer sentry.Flush(2 * time.Second) err = database.Setup(db, &ctx) if err != nil { sentry.CaptureException(err) - log.Fatal(err) + log.Fatalln(err) } err = core.SetAllJSONJoke(db, memory, &ctx) diff --git a/api/app/handler/joke/joke_add.go b/api/app/handler/joke/joke_add.go index 6dec1a8..f5ae547 100644 --- a/api/app/handler/joke/joke_add.go +++ b/api/app/handler/joke/joke_add.go @@ -3,7 +3,9 @@ package joke import ( "jokes-bapak2-api/app/core" + "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4" ) func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { @@ -38,8 +40,29 @@ func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { Error: "URL provided is not a valid image", }) } - + // Validate if link already exists sql, args, err := d.Query. + Select("link"). + From("jokesbapak2"). + Where(squirrel.Eq{"link": body.Link}). + ToSql() + if err != nil { + return err + } + + v, err := conn.Query(*d.Context, sql, args...) + if err != nil && err != pgx.ErrNoRows { + return err + } + defer v.Close() + + if err == nil { + return c.Status(fiber.StatusConflict).JSON(Error{ + Error: "Given link is already on the jokesbapak2 database", + }) + } + + sql, args, err = d.Query. Insert("jokesbapak2"). Columns("link", "creator"). Values(body.Link, c.Locals("userID")). @@ -48,7 +71,6 @@ func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { return err } - // TODO: Implement solution if the link provided already exists. _, err = tx.Exec(*d.Context, sql, args...) if err != nil { return err diff --git a/api/app/handler/submit/schema.go b/api/app/handler/submit/schema.go index 3dd0b51..1a8584b 100644 --- a/api/app/handler/submit/schema.go +++ b/api/app/handler/submit/schema.go @@ -17,9 +17,10 @@ type SubmissionQuery struct { } type ResponseSubmission struct { - ID string `json:"id,omitempty"` - Message string `json:"message,omitempty"` - Data Submission `json:"data,omitempty"` + ID string `json:"id,omitempty"` + Message string `json:"message,omitempty"` + Submission Submission `json:"submission,omitempty"` + AuthorPage string `json:"author_page,omitempty"` } type Error struct { diff --git a/api/app/handler/submit/submit_add.go b/api/app/handler/submit/submit_add.go index 983528b..66ea9f0 100644 --- a/api/app/handler/submit/submit_add.go +++ b/api/app/handler/submit/submit_add.go @@ -2,16 +2,25 @@ package submit import ( "jokes-bapak2-api/app/core" + "net/url" "strings" "time" + "github.com/Masterminds/squirrel" "github.com/georgysavva/scany/pgxscan" "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4" ) func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { + conn, err := d.DB.Acquire(*d.Context) + if err != nil { + return err + } + defer conn.Release() + var body Submission - err := c.BodyParser(&body) + err = c.BodyParser(&body) if err != nil { return err } @@ -19,26 +28,26 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { // Image and/or Link should not be empty if body.Image == "" && body.Link == "" { return c.Status(fiber.StatusBadRequest).JSON(Error{ - Error: "a link or an image should be supplied in a form of multipart/form-data", + Error: "A link or an image should be supplied in a form of multipart/form-data", }) } // Author should be supplied if body.Author == "" { return c.Status(fiber.StatusBadRequest).JSON(Error{ - Error: "an author key consisting on the format \"yourname \" must be supplied", + Error: "An author key consisting on the format \"yourname \" must be supplied", }) } else { // Validate format valid := core.ValidateAuthor(body.Author) if !valid { return c.Status(fiber.StatusBadRequest).JSON(Error{ - Error: "please stick to the format of \"yourname \" and within 200 characters", + Error: "Please stick to the format of \"yourname \" and within 200 characters", }) } } - var url string + var link string // Check link validity if link was provided if body.Link != "" { @@ -52,25 +61,46 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { }) } - url = body.Link + link = body.Link } // If image was provided if body.Image != "" { image := strings.NewReader(body.Image) - url, err = core.UploadImage(d.HTTP, image) + link, err = core.UploadImage(d.HTTP, image) if err != nil { return err } } + // Validate if link already exists + sql, args, err := d.Query. + Select("link"). + From("submission"). + Where(squirrel.Eq{"link": link}). + ToSql() + if err != nil { + return err + } + + v, err := conn.Query(*d.Context, sql, args...) + if err != nil && err != pgx.ErrNoRows { + return err + } + defer v.Close() + + if err == nil { + return c.Status(fiber.StatusConflict).JSON(Error{ + Error: "Given link is already on the submission queue.", + }) + } now := time.Now().UTC().Format(time.RFC3339) - sql, args, err := d.Query. + sql, args, err = d.Query. Insert("submission"). Columns("link", "created_at", "author"). - Values(url, now, body.Author). + Values(link, now, body.Author). Suffix("RETURNING id,created_at,link,author,status"). ToSql() if err != nil { @@ -78,7 +108,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { } var submission []Submission - result, err := d.DB.Query(*d.Context, sql, args...) + result, err := conn.Query(*d.Context, sql, args...) if err != nil { return err } @@ -92,7 +122,8 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { return c. Status(fiber.StatusCreated). JSON(ResponseSubmission{ - Message: "Joke submitted. Please wait for a few days for admin to approve your submission.", - Data: submission[0], + Message: "Joke submitted. Please wait for a few days for admin to approve your submission.", + Submission: submission[0], + AuthorPage: "/submit?author=" + url.QueryEscape(body.Author), }) } diff --git a/api/app/platform/database/create.go b/api/app/platform/database/create.go index 9037326..44b2cfa 100644 --- a/api/app/platform/database/create.go +++ b/api/app/platform/database/create.go @@ -16,15 +16,9 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { } defer conn.Release() - tx, err := conn.Begin(*ctx) - if err != nil { - return err - } - defer tx.Rollback(*ctx) - // administrators table var tableAuthExists bool - err = db.QueryRow(*ctx, `SELECT EXISTS ( + err = conn.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'administrators' @@ -47,7 +41,7 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { return err } - _, err = tx.Exec(*ctx, sql) + _, err = conn.Query(*ctx, sql) if err != nil { log.Fatalln("18 - failed on table creation: ", err) return err @@ -58,7 +52,7 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { // Check if table exists var tableJokesExists bool - err = db.QueryRow(*ctx, `SELECT EXISTS ( + err = conn.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jokesbapak2' @@ -80,7 +74,7 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { return err } - _, err = tx.Exec(*ctx, sql) + _, err = conn.Query(*ctx, sql) if err != nil { log.Fatalln("12 - failed on table creation: ", err) return err @@ -91,7 +85,7 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { //Check if table exists var tableSubmissionExists bool - err = db.QueryRow(*ctx, `SELECT EXISTS ( + err = conn.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'submission' @@ -114,16 +108,11 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { log.Fatalln("14 - failed on table creation: ", err) } - _, err = tx.Query(*ctx, sql) + _, err = conn.Query(*ctx, sql) if err != nil { log.Fatalln("15 - failed on table creation: ", err) } } - err = tx.Commit(*ctx) - if err != nil { - return err - } - return nil } diff --git a/api/app/platform/database/placeholder.sql b/api/app/platform/database/placeholder.sql index 71023e5..07866bf 100644 --- a/api/app/platform/database/placeholder.sql +++ b/api/app/platform/database/placeholder.sql @@ -8,14 +8,14 @@ INSERT INTO "administrators" ("id", "key", "token", "last_used") VALUES -- 10 jokes is enough right? -INSERT INTO "jokesbapak2" ("id", "link", "creator") VALUES -(1, 'https://picsum.photos/id/1000/500/500', 1), -(2, 'https://picsum.photos/id/1001/500/500', 1), -(3, 'https://picsum.photos/id/1002/500/500', 1), -(4, 'https://picsum.photos/id/1003/500/500', 1), -(5, 'https://picsum.photos/id/1004/500/500', 1), -(6, 'https://picsum.photos/id/1005/500/500', 1), -(7, 'https://picsum.photos/id/1006/500/500', 1), -(8, 'https://picsum.photos/id/1010/500/500', 1), -(9, 'https://picsum.photos/id/1008/500/500', 1), -(10, 'https://picsum.photos/id/1009/500/500', 1); \ No newline at end of file +INSERT INTO "jokesbapak2" ("link", "creator") VALUES +('https://picsum.photos/id/1000/500/500', 1), +('https://picsum.photos/id/1001/500/500', 1), +('https://picsum.photos/id/1002/500/500', 1), +('https://picsum.photos/id/1003/500/500', 1), +('https://picsum.photos/id/1004/500/500', 1), +('https://picsum.photos/id/1005/500/500', 1), +('https://picsum.photos/id/1006/500/500', 1), +('https://picsum.photos/id/1010/500/500', 1), +('https://picsum.photos/id/1008/500/500', 1), +('https://picsum.photos/id/1009/500/500', 1); \ No newline at end of file diff --git a/api/app/routes/health.go b/api/app/routes/health.go index 28cbcca..0cce80f 100644 --- a/api/app/routes/health.go +++ b/api/app/routes/health.go @@ -14,6 +14,7 @@ func (d *Dependencies) Health() { Redis: d.Redis, Context: d.Context, } + d.App.Get("/health", cache.New(cache.Config{Expiration: 30 * time.Minute}), deps.Health) d.App.Get("/v1/health", cache.New(cache.Config{Expiration: 30 * time.Minute}), deps.Health) } diff --git a/api/app/routes/submit.go b/api/app/routes/submit.go index c677e45..bdd13af 100644 --- a/api/app/routes/submit.go +++ b/api/app/routes/submit.go @@ -10,12 +10,14 @@ import ( func (d *Dependencies) Submit() { deps := submit.Dependencies{ - DB: d.DB, - Redis: d.Redis, - Memory: d.Memory, - HTTP: d.HTTP, - Query: d.Query, + DB: d.DB, + Redis: d.Redis, + Memory: d.Memory, + HTTP: d.HTTP, + Query: d.Query, + Context: d.Context, } + // Get pending submitted joke d.App.Get( "/submit", diff --git a/api/main_test.go b/api/main_test.go index f1116ec..75df18b 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -16,51 +16,31 @@ import ( var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} var submissionData = []interface{}{1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1} var administratorsData = []interface{}{1, "very secure", "not the real one", time.Now().Format(time.RFC3339), 2, "test", "$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106", ""} - -var db *pgxpool.Pool var ctx context.Context = context.Background() func TestMain(m *testing.M) { flag.Parse() + log.Println("---- Preparing for integration test") err := setup() if err != nil { log.Fatalln(err) } + log.Println("---- Preparation complete") + log.Print("\n") code := m.Run() os.Exit(code) } -func cleanup() { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Fatalln("Unable to create pool config", err) - } - poolConfig.MaxConns = 3 - poolConfig.MinConns = 1 - - db, err = pgxpool.ConnectConfig(ctx, poolConfig) - if err != nil { - log.Fatalln("Unable to create connection", err) - } - defer db.Close() - - d, err := db.Query(ctx, "BEGIN; DROP TABLE \"jokesbapak2\"; DROP TABLE \"administrators\"; DROP TABLE \"submission\"; COMMIT;") - if err != nil { - panic(err) - } - defer d.Close() -} - func setup() error { poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { return errors.New("Unable to create pool config: " + err.Error()) } - poolConfig.MaxConns = 3 - poolConfig.MinConns = 1 + poolConfig.MaxConns = 15 + poolConfig.MinConns = 2 - db, err = pgxpool.ConnectConfig(ctx, poolConfig) + db, err := pgxpool.ConnectConfig(ctx, poolConfig) if err != nil { return errors.New("Unable to create connection: " + err.Error()) } @@ -72,59 +52,50 @@ func setup() error { } defer conn.Release() - tx, err := conn.Begin(ctx) - if err != nil { - return err - } - defer tx.Rollback(ctx) + // dj, err := conn.Query(ctx, "DROP TABLE \"jokesbapak2\"") + // if err != nil { + // log.Println("busy here - 57") + // return err + // } + // defer dj.Close() - _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS \"jokesbapak2\"") - if err != nil { - return err - } - _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS \"administrators\"") - if err != nil { - return err - } - _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS \"submission\"") - if err != nil { - return err - } - err = tx.Commit(ctx) - if err != nil { - return err - } + // ds, err := conn.Query(ctx, "DROP TABLE \"submission\"") + // if err != nil { + // log.Println("busy here - 67") + // return err + // } + // defer ds.Close() + + // da, err := conn.Query(ctx, "DROP TABLE \"administrators\"") + // if err != nil { + // log.Println("busy here - 62") + // return err + // } + // defer da.Close() err = database.Setup(db, &ctx) if err != nil { + log.Println("busy here - 73") return err } - tx, err = conn.Begin(ctx) + ia, err := conn.Query(ctx, "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) if err != nil { return err } - defer tx.Rollback(ctx) + defer ia.Close() - _, err = tx.Exec(ctx, "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) + ij, err := conn.Query(ctx, "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) if err != nil { return err } + defer ij.Close() - _, err = tx.Exec(ctx, "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) - if err != nil { - return err - } - - _, err = tx.Exec(ctx, "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) - if err != nil { - return err - } - - err = tx.Commit(ctx) + is, err := conn.Query(ctx, "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) if err != nil { return err } + defer is.Close() return nil } From ae11ee7ed8d61bd81643d577adbfa6152daee373 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Tue, 28 Sep 2021 11:29:39 +0700 Subject: [PATCH 05/21] fix: verification cleanup --- api/Dockerfile | 2 +- api/app/app.go | 18 +++++----- api/app/handler/joke/joke_add.go | 9 +++-- api/app/handler/joke/joke_update.go | 5 +-- api/app/handler/submit/submit_add.go | 6 ++-- api/app/handler/submit/submit_get.go | 9 +++-- api/app/platform/database/create.go | 28 +++++++++++---- api/main_test.go | 51 ++++++++++++---------------- 8 files changed, 71 insertions(+), 57 deletions(-) diff --git a/api/Dockerfile b/api/Dockerfile index 6599306..b0c07de 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.16.6-buster +FROM golang:1.17-buster WORKDIR /app diff --git a/api/app/app.go b/api/app/app.go index 9b4972b..79aef00 100644 --- a/api/app/app.go +++ b/api/app/app.go @@ -29,14 +29,16 @@ func New() *fiber.App { // Setup PostgreSQL poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { - log.Fatalln("Unable to create pool config", err) + log.Panicln("Unable to create pool config", err) } + poolConfig.MaxConnIdleTime = time.Minute * 3 + poolConfig.MaxConnLifetime = time.Minute * 5 poolConfig.MaxConns = 15 - poolConfig.MinConns = 2 + poolConfig.MinConns = 4 db, err := pgxpool.ConnectConfig(ctx, poolConfig) if err != nil { - log.Fatalln("Unable to create connection", err) + log.Panicln("Unable to create connection", err) } // Setup Redis @@ -49,7 +51,7 @@ func New() *fiber.App { // Setup In Memory memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) if err != nil { - log.Fatalln(err) + log.Panicln(err) } // Setup Sentry @@ -62,23 +64,23 @@ func New() *fiber.App { Debug: true, }) if err != nil { - log.Fatalln(err) + log.Panicln(err) } defer sentry.Flush(2 * time.Second) err = database.Setup(db, &ctx) if err != nil { sentry.CaptureException(err) - log.Fatalln(err) + log.Panicln(err) } err = core.SetAllJSONJoke(db, memory, &ctx) if err != nil { - log.Fatalln(err) + log.Panicln(err) } err = core.SetTotalJoke(db, memory, &ctx) if err != nil { - log.Fatalln(err) + log.Panicln(err) } timeoutDefault := time.Minute * 1 diff --git a/api/app/handler/joke/joke_add.go b/api/app/handler/joke/joke_add.go index f5ae547..baa6ec8 100644 --- a/api/app/handler/joke/joke_add.go +++ b/api/app/handler/joke/joke_add.go @@ -49,14 +49,13 @@ func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { if err != nil { return err } - - v, err := conn.Query(*d.Context, sql, args...) + var validateLink string + err = conn.QueryRow(*d.Context, sql, args...).Scan(&validateLink) if err != nil && err != pgx.ErrNoRows { return err } - defer v.Close() - - if err == nil { + + if err == nil && validateLink != "" { return c.Status(fiber.StatusConflict).JSON(Error{ Error: "Given link is already on the jokesbapak2 database", }) diff --git a/api/app/handler/joke/joke_update.go b/api/app/handler/joke/joke_update.go index ffc426d..6ab0cd3 100644 --- a/api/app/handler/joke/joke_update.go +++ b/api/app/handler/joke/joke_update.go @@ -2,6 +2,7 @@ package joke import ( "jokes-bapak2-api/app/core" + "strconv" "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" @@ -26,13 +27,13 @@ func (d *Dependencies) UpdateJoke(c *fiber.Ctx) error { return err } - var jokeID string + var jokeID int err = conn.QueryRow(*d.Context, sql, args...).Scan(&jokeID) if err != nil && err != pgx.ErrNoRows { return err } - if jokeID == id { + if strconv.Itoa(jokeID) == id { body := new(core.Joke) err = c.BodyParser(&body) if err != nil { diff --git a/api/app/handler/submit/submit_add.go b/api/app/handler/submit/submit_add.go index 66ea9f0..6afd4e5 100644 --- a/api/app/handler/submit/submit_add.go +++ b/api/app/handler/submit/submit_add.go @@ -83,13 +83,13 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { return err } - v, err := conn.Query(*d.Context, sql, args...) + var validateLink string + err = conn.QueryRow(*d.Context, sql, args...).Scan(&validateLink) if err != nil && err != pgx.ErrNoRows { return err } - defer v.Close() - if err == nil { + if err == nil && validateLink != "" { return c.Status(fiber.StatusConflict).JSON(Error{ Error: "Given link is already on the submission queue.", }) diff --git a/api/app/handler/submit/submit_get.go b/api/app/handler/submit/submit_get.go index cb26198..2b16163 100644 --- a/api/app/handler/submit/submit_get.go +++ b/api/app/handler/submit/submit_get.go @@ -2,7 +2,7 @@ package submit import ( "bytes" - "log" + "net/url" "strconv" "github.com/aldy505/bob" @@ -58,7 +58,11 @@ func (d *Dependencies) GetSubmission(c *fiber.Ctx) error { if query.Author != "" { sqlQuery.WriteString(" AND author = ?") - args = append(args, query.Author) + escapedAuthor, err := url.QueryUnescape(query.Author) + if err != nil { + return err + } + args = append(args, escapedAuthor) } if query.Approved != "" { @@ -81,7 +85,6 @@ func (d *Dependencies) GetSubmission(c *fiber.Ctx) error { var submissions []Submission results, err := d.DB.Query(*d.Context, sql, args...) if err != nil { - log.Println(err) return err } diff --git a/api/app/platform/database/create.go b/api/app/platform/database/create.go index 44b2cfa..1b4ea70 100644 --- a/api/app/platform/database/create.go +++ b/api/app/platform/database/create.go @@ -12,6 +12,7 @@ import ( func Setup(db *pgxpool.Pool, ctx *context.Context) error { conn, err := db.Acquire(*ctx) if err != nil { + log.Fatalln("30 - err here") return err } defer conn.Release() @@ -41,18 +42,26 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { return err } - _, err = conn.Query(*ctx, sql) + q, err := conn.Query(*ctx, sql) if err != nil { log.Fatalln("18 - failed on table creation: ", err) return err } + defer q.Close() } + conn2, err := db.Acquire(*ctx) + if err != nil { + log.Fatalln("32 - err here") + return err + } + defer conn2.Release() + // Jokesbapak2 table // Check if table exists var tableJokesExists bool - err = conn.QueryRow(*ctx, `SELECT EXISTS ( + err = conn2.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jokesbapak2' @@ -74,22 +83,28 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { return err } - _, err = conn.Query(*ctx, sql) + q, err := conn2.Query(*ctx, sql) if err != nil { log.Fatalln("12 - failed on table creation: ", err) return err } + defer q.Close() } // Submission table + conn3, err := db.Acquire(*ctx) + if err != nil { + return err + } + defer conn3.Release() //Check if table exists var tableSubmissionExists bool - err = conn.QueryRow(*ctx, `SELECT EXISTS ( + err = conn3.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'submission' - );`).Scan(&tableJokesExists) + );`).Scan(&tableSubmissionExists) if err != nil { log.Fatalln("13 - failed on checking table: ", err) return err @@ -108,10 +123,11 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { log.Fatalln("14 - failed on table creation: ", err) } - _, err = conn.Query(*ctx, sql) + q, err := conn3.Query(*ctx, sql) if err != nil { log.Fatalln("15 - failed on table creation: ", err) } + defer q.Close() } return nil diff --git a/api/main_test.go b/api/main_test.go index 75df18b..6d43f5d 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -23,7 +23,7 @@ func TestMain(m *testing.M) { log.Println("---- Preparing for integration test") err := setup() if err != nil { - log.Fatalln(err) + log.Panicln(err) } log.Println("---- Preparation complete") log.Print("\n") @@ -37,8 +37,6 @@ func setup() error { if err != nil { return errors.New("Unable to create pool config: " + err.Error()) } - poolConfig.MaxConns = 15 - poolConfig.MinConns = 2 db, err := pgxpool.ConnectConfig(ctx, poolConfig) if err != nil { @@ -46,32 +44,27 @@ func setup() error { } defer db.Close() - conn, err := db.Acquire(ctx) + + dj, err := db.Query(ctx, "DROP TABLE \"jokesbapak2\"") + if err != nil { + log.Println("busy here - 57") + return err + } + defer dj.Close() + + ds, err := db.Query(ctx, "DROP TABLE \"submission\"") + if err != nil { + log.Println("busy here - 67") + return err + } + defer ds.Close() + + da, err := db.Query(ctx, "DROP TABLE \"administrators\"") if err != nil { + log.Println("busy here - 62") return err } - defer conn.Release() - - // dj, err := conn.Query(ctx, "DROP TABLE \"jokesbapak2\"") - // if err != nil { - // log.Println("busy here - 57") - // return err - // } - // defer dj.Close() - - // ds, err := conn.Query(ctx, "DROP TABLE \"submission\"") - // if err != nil { - // log.Println("busy here - 67") - // return err - // } - // defer ds.Close() - - // da, err := conn.Query(ctx, "DROP TABLE \"administrators\"") - // if err != nil { - // log.Println("busy here - 62") - // return err - // } - // defer da.Close() + defer da.Close() err = database.Setup(db, &ctx) if err != nil { @@ -79,19 +72,19 @@ func setup() error { return err } - ia, err := conn.Query(ctx, "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) + ia, err := db.Query(ctx, "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) if err != nil { return err } defer ia.Close() - ij, err := conn.Query(ctx, "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) + ij, err := db.Query(ctx, "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) if err != nil { return err } defer ij.Close() - is, err := conn.Query(ctx, "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) + is, err := db.Query(ctx, "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) if err != nil { return err } From fad708e08d5f265664cf6eaf87a7d4bafdc97384 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Tue, 28 Sep 2021 11:29:54 +0700 Subject: [PATCH 06/21] chore: running formatter --- api/app/handler/joke/joke_add.go | 2 +- api/main_test.go | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/api/app/handler/joke/joke_add.go b/api/app/handler/joke/joke_add.go index baa6ec8..a5dbb62 100644 --- a/api/app/handler/joke/joke_add.go +++ b/api/app/handler/joke/joke_add.go @@ -54,7 +54,7 @@ func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { if err != nil && err != pgx.ErrNoRows { return err } - + if err == nil && validateLink != "" { return c.Status(fiber.StatusConflict).JSON(Error{ Error: "Given link is already on the jokesbapak2 database", diff --git a/api/main_test.go b/api/main_test.go index 6d43f5d..12c34ec 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -44,20 +44,19 @@ func setup() error { } defer db.Close() + dj, err := db.Query(ctx, "DROP TABLE \"jokesbapak2\"") + if err != nil { + log.Println("busy here - 57") + return err + } + defer dj.Close() - dj, err := db.Query(ctx, "DROP TABLE \"jokesbapak2\"") - if err != nil { - log.Println("busy here - 57") - return err - } - defer dj.Close() - - ds, err := db.Query(ctx, "DROP TABLE \"submission\"") - if err != nil { - log.Println("busy here - 67") - return err - } - defer ds.Close() + ds, err := db.Query(ctx, "DROP TABLE \"submission\"") + if err != nil { + log.Println("busy here - 67") + return err + } + defer ds.Close() da, err := db.Query(ctx, "DROP TABLE \"administrators\"") if err != nil { From 9c497b81aa4fde739ab2796efaf8aaff6e570dcb Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Tue, 28 Sep 2021 13:34:02 +0700 Subject: [PATCH 07/21] fix: method complexity --- README.md | 8 +- api/app/handler/joke/dependencies_test.go | 9 --- api/app/handler/joke/joke_add_test.go | 57 ------------- api/app/handler/joke/joke_delete_test.go | 62 -------------- api/app/handler/joke/joke_get.go | 69 ++++++++-------- api/app/handler/joke/joke_get_test.go | 89 --------------------- api/app/handler/joke/joke_total_test.go | 32 -------- api/app/handler/joke/joke_update_test.go | 62 -------------- api/app/handler/submit/dependencies_test.go | 9 --- api/app/handler/submit/submit_add.go | 42 +++++++--- api/app/handler/submit/submit_get_test.go | 50 ------------ api/app/platform/database/create.go | 85 ++++++++++++-------- 12 files changed, 117 insertions(+), 457 deletions(-) delete mode 100644 api/app/handler/joke/dependencies_test.go delete mode 100644 api/app/handler/joke/joke_add_test.go delete mode 100644 api/app/handler/joke/joke_delete_test.go delete mode 100644 api/app/handler/joke/joke_get_test.go delete mode 100644 api/app/handler/joke/joke_total_test.go delete mode 100644 api/app/handler/joke/joke_update_test.go delete mode 100644 api/app/handler/submit/dependencies_test.go delete mode 100644 api/app/handler/submit/submit_get_test.go diff --git a/README.md b/README.md index a6caff8..04783e9 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,10 @@ This is some kind of [icanhazdadjokes](https://icanhazdadjoke.com/) but it's Ind You can consume this API via a website (linked in the front facing web) with a few endpoints: - * `/v1/` - Random jokes bapak2 - * `/v1/id/{number}` - Jokes bapak2 based on ID - * `/v1/today` - Jokes bapak2 of the day - * `/v1/total` - Total available jokes bapak2 + * `/` - Random jokes bapak2 + * `/id/{number}` - Jokes bapak2 based on ID + * `/today` - Jokes bapak2 of the day + * `/total` - Total available jokes bapak2 Currently I'm (still) searching for an alternative for AWS S3 that I can use for free. diff --git a/api/app/handler/joke/dependencies_test.go b/api/app/handler/joke/dependencies_test.go deleted file mode 100644 index 2d98a9b..0000000 --- a/api/app/handler/joke/dependencies_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package joke_test - -import ( - v1 "jokes-bapak2-api/app" - - "github.com/gofiber/fiber/v2" -) - -var app *fiber.App = v1.New() diff --git a/api/app/handler/joke/joke_add_test.go b/api/app/handler/joke/joke_add_test.go deleted file mode 100644 index e4bd308..0000000 --- a/api/app/handler/joke/joke_add_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package joke_test - -import ( - "io/ioutil" - "net/http" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestAddNewJoke_201(t *testing.T) { - // TODO: Remove this line below, make this test works - t.SkipNow() - - reqBody := strings.NewReader("{\"link\":\"https://via.placeholder.com/300/07f/ff0000.png\",\"key\":\"test\",\"token\":\"password\"}") - req, _ := http.NewRequest("PUT", "/", reqBody) - req.Header.Set("content-type", "application/json") - req.Header.Add("accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke add") - assert.Equalf(t, 201, res.StatusCode, "joke add") - assert.NotEqualf(t, 0, res.ContentLength, "joke add") - body, err := ioutil.ReadAll(res.Body) - assert.Nilf(t, err, "joke add") - assert.Equalf(t, "{\"link\":\"https://via.placeholder.com/300/07f/ff0000.png\"}", string(body), "joke add") -} - -func TestAddNewJoke_NotValidImage(t *testing.T) { - // TODO: Remove this line below, make this test works - t.SkipNow() - - reqBody := strings.NewReader("{\"link\":\"https://google.com/\",\"key\":\"test\",\"token\":\"password\"}") - req, _ := http.NewRequest("PUT", "/", reqBody) - req.Header.Set("content-type", "application/json") - req.Header.Add("accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke add") - assert.Equalf(t, 400, res.StatusCode, "joke add") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke add") - assert.Equalf(t, "{\"error\":\"URL provided is not a valid image\"}", string(body), "joke add") -} diff --git a/api/app/handler/joke/joke_delete_test.go b/api/app/handler/joke/joke_delete_test.go deleted file mode 100644 index d2defe4..0000000 --- a/api/app/handler/joke/joke_delete_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package joke_test - -import ( - "io/ioutil" - "net/http" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestDeleteJoke_200(t *testing.T) { - // TODO: Remove this line below, make this test works - t.SkipNow() - - reqBody := strings.NewReader("{\"key\":\"very secure\",\"token\":\"password\"}") - req, _ := http.NewRequest("DELETE", "/id/100", reqBody) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke delete") - assert.Equalf(t, 200, res.StatusCode, "joke delete") - assert.NotEqualf(t, 0, res.ContentLength, "joke delete") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke delete") - assert.Equalf(t, "{\"message\":\"specified joke id has been deleted\"}", string(body), "joke delete") -} -func TestDeleteJoke_NotExists(t *testing.T) { - // TODO: Remove this line below, make this test works - t.SkipNow() - - reqBody := strings.NewReader("{\"key\":\"very secure\",\"token\":\"password\"}") - req, _ := http.NewRequest("DELETE", "/id/100", reqBody) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke delete") - assert.Equalf(t, 406, res.StatusCode, "joke delete") - assert.NotEqualf(t, 0, res.ContentLength, "joke delete") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke delete") - assert.Equalf(t, "{\"message\":\"specified joke id does not exists\"}", string(body), "joke delete") -} diff --git a/api/app/handler/joke/joke_get.go b/api/app/handler/joke/joke_get.go index c9ee9cd..0e57e68 100644 --- a/api/app/handler/joke/joke_get.go +++ b/api/app/handler/joke/joke_get.go @@ -28,43 +28,42 @@ func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { if eq { c.Set("Content-Type", joke.ContentType) return c.Status(fiber.StatusOK).Send([]byte(joke.Image)) - } else { - conn, err := d.DB.Acquire(*d.Context) - if err != nil { - return err - } - defer conn.Release() - var link string - err = conn.QueryRow(*d.Context, "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) - if err != nil { - return err - } - - response, err := d.HTTP.Get(link, nil) - if err != nil { - return err - } - - data, err := ioutil.ReadAll(response.Body) - if err != nil { - return err - } - - now := time.Now().UTC().Format(time.RFC3339) - err = d.Redis.MSet(*d.Context, map[string]interface{}{ - "today:link": link, - "today:date": now, - "today:image": string(data), - "today:contentType": response.Header.Get("content-type"), - }).Err() - if err != nil { - return err - } - - c.Set("Content-Type", response.Header.Get("content-type")) - return c.Status(fiber.StatusOK).Send(data) } + conn, err := d.DB.Acquire(*d.Context) + if err != nil { + return err + } + defer conn.Release() + var link string + err = conn.QueryRow(*d.Context, "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) + if err != nil { + return err + } + + response, err := d.HTTP.Get(link, nil) + if err != nil { + return err + } + + data, err := ioutil.ReadAll(response.Body) + if err != nil { + return err + } + + now := time.Now().UTC().Format(time.RFC3339) + err = d.Redis.MSet(*d.Context, map[string]interface{}{ + "today:link": link, + "today:date": now, + "today:image": string(data), + "today:contentType": response.Header.Get("content-type"), + }).Err() + if err != nil { + return err + } + + c.Set("Content-Type", response.Header.Get("content-type")) + return c.Status(fiber.StatusOK).Send(data) } func (d *Dependencies) SingleJoke(c *fiber.Ctx) error { diff --git a/api/app/handler/joke/joke_get_test.go b/api/app/handler/joke/joke_get_test.go deleted file mode 100644 index 6d374f3..0000000 --- a/api/app/handler/joke/joke_get_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package joke_test - -import ( - "io/ioutil" - "net/http" - "testing" - "time" - - _ "github.com/joho/godotenv/autoload" - "github.com/stretchr/testify/assert" -) - -/// Need to find some workaround for this test -func TestTodayJoke(t *testing.T) { - req, _ := http.NewRequest("GET", "/today", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "today joke") - assert.Equalf(t, 200, res.StatusCode, "today joke") - assert.NotEqualf(t, 0, res.ContentLength, "today joke") - _, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "today joke") -} - -func TestSingleJoke(t *testing.T) { - req, _ := http.NewRequest("GET", "/", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "single joke") - assert.Equalf(t, 200, res.StatusCode, "single joke") - assert.NotEqualf(t, 0, res.ContentLength, "single joke") - _, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "single joke") -} - -func TestJokeByID_200(t *testing.T) { - req, _ := http.NewRequest("GET", "/id/1", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke by id") - assert.Equalf(t, 200, res.StatusCode, "joke by id") - assert.NotEqualf(t, 0, res.ContentLength, "joke by id") - _, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke by id") -} - -func TestJokeByID_404(t *testing.T) { - req, _ := http.NewRequest("GET", "/id/300", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke by id") - assert.Equalf(t, 404, res.StatusCode, "joke by id") - assert.NotEqualf(t, 0, res.ContentLength, "joke by id") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke by id") - assert.Equalf(t, "Requested ID was not found.", string(body), "joke by id") -} diff --git a/api/app/handler/joke/joke_total_test.go b/api/app/handler/joke/joke_total_test.go deleted file mode 100644 index e7cd8f9..0000000 --- a/api/app/handler/joke/joke_total_test.go +++ /dev/null @@ -1,32 +0,0 @@ -package joke_test - -import ( - "io/ioutil" - "net/http" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestTotalJokes(t *testing.T) { - req, _ := http.NewRequest("GET", "/total", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke total") - assert.Equalf(t, 200, res.StatusCode, "joke total") - assert.NotEqualf(t, 0, res.ContentLength, "joke total") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke total") - // FIXME: This should be "message": "3", not one. I don't know what's wrong as it's 1 AM. - assert.Equalf(t, "{\"message\":\"3\"}", string(body), "joke total") - -} diff --git a/api/app/handler/joke/joke_update_test.go b/api/app/handler/joke/joke_update_test.go deleted file mode 100644 index f1a8898..0000000 --- a/api/app/handler/joke/joke_update_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package joke_test - -import ( - "io/ioutil" - "net/http" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestUpdateJoke_200(t *testing.T) { - t.SkipNow() - - reqBody := strings.NewReader("{\"link\":\"https://picsum.photos/id/9/200/300\",\"key\":\"test\",\"token\":\"password\"}") - req, _ := http.NewRequest("PATCH", "/id/1", reqBody) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke update") - assert.Equalf(t, 200, res.StatusCode, "joke update") - assert.NotEqualf(t, 0, res.ContentLength, "joke update") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke update") - assert.Equalf(t, "{\"message\":\"specified joke id has been deleted\"}", string(body), "joke update") -} - -func TestUpdateJoke_NotExists(t *testing.T) { - // TODO: Remove this line below, make this test works - t.SkipNow() - - reqBody := strings.NewReader("{\"link\":\"https://picsum.photos/id/9/200/300\",\"key\":\"test\",\"token\":\"password\"}") - req, _ := http.NewRequest("PATCH", "/id/100", reqBody) - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "joke update") - assert.Equalf(t, 406, res.StatusCode, "joke update") - assert.NotEqualf(t, 0, res.ContentLength, "joke update") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "joke update") - assert.Equalf(t, "{\"message\":\"specified joke id does not exists\"}", string(body), "joke update") -} diff --git a/api/app/handler/submit/dependencies_test.go b/api/app/handler/submit/dependencies_test.go deleted file mode 100644 index 1733fac..0000000 --- a/api/app/handler/submit/dependencies_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package submit_test - -import ( - v1 "jokes-bapak2-api/app" - - "github.com/gofiber/fiber/v2" -) - -var app *fiber.App = v1.New() diff --git a/api/app/handler/submit/submit_add.go b/api/app/handler/submit/submit_add.go index 6afd4e5..234a5cf 100644 --- a/api/app/handler/submit/submit_add.go +++ b/api/app/handler/submit/submit_add.go @@ -1,6 +1,7 @@ package submit import ( + "context" "jokes-bapak2-api/app/core" "net/url" "strings" @@ -10,6 +11,7 @@ import ( "github.com/georgysavva/scany/pgxscan" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" ) func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { @@ -73,23 +75,14 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { return err } } + // Validate if link already exists - sql, args, err := d.Query. - Select("link"). - From("submission"). - Where(squirrel.Eq{"link": link}). - ToSql() + validateLink, err := validateIfLinkExists(conn, d.Context, d.Query, link) if err != nil { return err } - var validateLink string - err = conn.QueryRow(*d.Context, sql, args...).Scan(&validateLink) - if err != nil && err != pgx.ErrNoRows { - return err - } - - if err == nil && validateLink != "" { + if validateLink { return c.Status(fiber.StatusConflict).JSON(Error{ Error: "Given link is already on the submission queue.", }) @@ -97,7 +90,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { now := time.Now().UTC().Format(time.RFC3339) - sql, args, err = d.Query. + sql, args, err := d.Query. Insert("submission"). Columns("link", "created_at", "author"). Values(link, now, body.Author). @@ -127,3 +120,26 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { AuthorPage: "/submit?author=" + url.QueryEscape(body.Author), }) } + +func validateIfLinkExists(conn *pgxpool.Conn, ctx *context.Context, query squirrel.StatementBuilderType, link string) (bool, error) { + sql, args, err := query. + Select("link"). + From("submission"). + Where(squirrel.Eq{"link": link}). + ToSql() + if err != nil { + return false, err + } + + var validateLink string + err = conn.QueryRow(*ctx, sql, args...).Scan(&validateLink) + if err != nil && err != pgx.ErrNoRows { + return false, err + } + + if err == nil && validateLink != "" { + return true, nil + } + + return false, nil +} diff --git a/api/app/handler/submit/submit_get_test.go b/api/app/handler/submit/submit_get_test.go deleted file mode 100644 index 864ba97..0000000 --- a/api/app/handler/submit/submit_get_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package submit_test - -import ( - "io/ioutil" - "net/http" - "testing" - "time" - - _ "github.com/joho/godotenv/autoload" - - "github.com/stretchr/testify/assert" -) - -func TestGetSubmission_200(t *testing.T) { - req, _ := http.NewRequest("GET", "/submit", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "get submission") - assert.Equalf(t, 200, res.StatusCode, "get submission") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "get submission") - assert.Equalf(t, "{\"count\":2,\"jokes\":[{\"id\":1,\"link\":\"https://via.placeholder.com/300/01f/fff.png\",\"created_at\":\"2021-08-03T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":0},{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") -} - -func TestGetSubmission_Params(t *testing.T) { - req, _ := http.NewRequest("GET", "/submit?page=1&limit=5&approved=true", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "get submission") - assert.Equalf(t, 200, res.StatusCode, "get submission") - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "get submission") - assert.Equalf(t, "{\"count\":1,\"jokes\":[{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") -} diff --git a/api/app/platform/database/create.go b/api/app/platform/database/create.go index 1b4ea70..c570f02 100644 --- a/api/app/platform/database/create.go +++ b/api/app/platform/database/create.go @@ -17,15 +17,46 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { } defer conn.Release() - // administrators table + err = setupAuthTable(conn, ctx) + if err != nil { + return err + } + + conn2, err := db.Acquire(*ctx) + if err != nil { + log.Fatalln("32 - err here") + return err + } + defer conn2.Release() + + err = setupJokesTable(conn2, ctx) + if err != nil { + return err + } + + conn3, err := db.Acquire(*ctx) + if err != nil { + return err + } + defer conn3.Release() + + err = setupSubmissionTable(conn3, ctx) + if err != nil { + return err + } + + return nil +} + +func setupAuthTable(conn *pgxpool.Conn, ctx *context.Context) error { + // Check if table exists var tableAuthExists bool - err = conn.QueryRow(*ctx, `SELECT EXISTS ( + err := conn.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'administrators' );`).Scan(&tableAuthExists) if err != nil { - log.Fatalln("16 - failed on checking table: ", err) return err } @@ -38,36 +69,27 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { StringColumn("last_used"). ToSql() if err != nil { - log.Fatalln("17 - failed on table creation: ", err) return err } q, err := conn.Query(*ctx, sql) if err != nil { - log.Fatalln("18 - failed on table creation: ", err) return err } defer q.Close() } + return nil +} - conn2, err := db.Acquire(*ctx) - if err != nil { - log.Fatalln("32 - err here") - return err - } - defer conn2.Release() - - // Jokesbapak2 table - +func setupJokesTable(conn *pgxpool.Conn, ctx *context.Context) error { // Check if table exists var tableJokesExists bool - err = conn2.QueryRow(*ctx, `SELECT EXISTS ( + err := conn.QueryRow(*ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jokesbapak2' );`).Scan(&tableJokesExists) if err != nil { - log.Fatalln("10 - failed on checking table: ", err) return err } @@ -79,34 +101,28 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { AddColumn(bob.ColumnDef{Name: "creator", Type: "INT", Extras: []string{"NOT NULL", "REFERENCES \"administrators\" (\"id\")"}}). ToSql() if err != nil { - log.Fatalln("11 - failed on table creation: ", err) return err } - q, err := conn2.Query(*ctx, sql) + q, err := conn.Query(*ctx, sql) if err != nil { - log.Fatalln("12 - failed on table creation: ", err) return err } defer q.Close() } - // Submission table - conn3, err := db.Acquire(*ctx) - if err != nil { - return err - } - defer conn3.Release() + return nil +} +func setupSubmissionTable(conn *pgxpool.Conn, ctx *context.Context) error { //Check if table exists var tableSubmissionExists bool - err = conn3.QueryRow(*ctx, `SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' - AND table_name = 'submission' - );`).Scan(&tableSubmissionExists) + err := conn.QueryRow(*ctx, `SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'submission' + );`).Scan(&tableSubmissionExists) if err != nil { - log.Fatalln("13 - failed on checking table: ", err) return err } @@ -120,15 +136,14 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { AddColumn(bob.ColumnDef{Name: "status", Type: "SMALLINT", Extras: []string{"DEFAULT 0"}}). ToSql() if err != nil { - log.Fatalln("14 - failed on table creation: ", err) + return err } - q, err := conn3.Query(*ctx, sql) + q, err := conn.Query(*ctx, sql) if err != nil { - log.Fatalln("15 - failed on table creation: ", err) + return err } defer q.Close() } - return nil } From c3af2c9bf0cc302af76fcabd9ae90311278ce78f Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Tue, 28 Sep 2021 14:56:11 +0700 Subject: [PATCH 08/21] chore: update golang version --- .github/workflows/api.yml | 2 +- .github/workflows/pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 1a37d3d..84d4876 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -8,7 +8,7 @@ jobs: api-build: name: Build runs-on: ubuntu-latest - container: golang:1.16.6 + container: golang:1.17 timeout-minutes: 15 services: postgres: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 84c12f2..6c7fa9c 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -62,7 +62,7 @@ jobs: api-build: name: API runs-on: ubuntu-latest - container: golang:1.16.6 + container: golang:1.17 timeout-minutes: 15 services: postgres: From 5db8f600661009a6c0783c27e738aabd434b7227 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Tue, 28 Sep 2021 14:57:58 +0700 Subject: [PATCH 09/21] test: removed hanging test on ci --- api/main_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/main_test.go b/api/main_test.go index 12c34ec..c9277be 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -27,9 +27,6 @@ func TestMain(m *testing.M) { } log.Println("---- Preparation complete") log.Print("\n") - - code := m.Run() - os.Exit(code) } func setup() error { From 729e4c039d9e043e8c4873b8db960f696dbe0366 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Tue, 28 Sep 2021 16:08:15 +0700 Subject: [PATCH 10/21] test: moved all test to a single test file for integration testing --- api/app/handler/health/health_test.go | 34 ----- api/main_test.go | 199 ++++++++++++++++++++++++-- 2 files changed, 185 insertions(+), 48 deletions(-) delete mode 100644 api/app/handler/health/health_test.go diff --git a/api/app/handler/health/health_test.go b/api/app/handler/health/health_test.go deleted file mode 100644 index 26c919d..0000000 --- a/api/app/handler/health/health_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package health_test - -import ( - "io/ioutil" - v1 "jokes-bapak2-api/app" - "net/http" - "testing" - "time" - - "github.com/gofiber/fiber/v2" - _ "github.com/joho/godotenv/autoload" - "github.com/stretchr/testify/assert" -) - -var app *fiber.App = v1.New() - -func TestHealth(t *testing.T) { - req, _ := http.NewRequest("GET", "/health", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "health") - assert.Equalf(t, 200, res.StatusCode, "health") - assert.NotEqualf(t, 0, res.ContentLength, "health") - _, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "health") -} diff --git a/api/main_test.go b/api/main_test.go index c9277be..d4580c4 100644 --- a/api/main_test.go +++ b/api/main_test.go @@ -4,13 +4,20 @@ import ( "context" "errors" "flag" + "io/ioutil" + v1 "jokes-bapak2-api/app" "jokes-bapak2-api/app/platform/database" "log" + "net/http" "os" + "strings" "testing" "time" + "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v4/pgxpool" + _ "github.com/joho/godotenv/autoload" + "github.com/stretchr/testify/assert" ) var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} @@ -20,13 +27,18 @@ var ctx context.Context = context.Background() func TestMain(m *testing.M) { flag.Parse() + log.Println("---- Preparing for integration test") + time.Sleep(time.Second * 5) err := setup() if err != nil { log.Panicln(err) } + time.Sleep(time.Second * 5) log.Println("---- Preparation complete") log.Print("\n") + + os.Exit(m.Run()) } func setup() error { @@ -39,32 +51,27 @@ func setup() error { if err != nil { return errors.New("Unable to create connection: " + err.Error()) } - defer db.Close() - dj, err := db.Query(ctx, "DROP TABLE \"jokesbapak2\"") + dj, err := db.Query(ctx, "DROP TABLE IF EXISTS \"jokesbapak2\"") if err != nil { - log.Println("busy here - 57") return err } - defer dj.Close() + dj.Close() - ds, err := db.Query(ctx, "DROP TABLE \"submission\"") + ds, err := db.Query(ctx, "DROP TABLE IF EXISTS \"submission\"") if err != nil { - log.Println("busy here - 67") return err } - defer ds.Close() + ds.Close() - da, err := db.Query(ctx, "DROP TABLE \"administrators\"") + da, err := db.Query(ctx, "DROP TABLE IF EXISTS \"administrators\"") if err != nil { - log.Println("busy here - 62") return err } - defer da.Close() + da.Close() err = database.Setup(db, &ctx) if err != nil { - log.Println("busy here - 73") return err } @@ -72,19 +79,183 @@ func setup() error { if err != nil { return err } - defer ia.Close() + ia.Close() ij, err := db.Query(ctx, "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) if err != nil { return err } - defer ij.Close() + ij.Close() is, err := db.Query(ctx, "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) if err != nil { return err } - defer is.Close() + is.Close() + + db.Close() return nil } + +var app *fiber.App = v1.New() + +func TestHealth(t *testing.T) { + req, _ := http.NewRequest("GET", "/health", nil) + res, err := app.Test(req, int(time.Minute*2)) + if err != nil { + t.Fatal(err) + } + + assert.Equalf(t, false, err != nil, "health") + assert.Equalf(t, 200, res.StatusCode, "health") + assert.NotEqualf(t, 0, res.ContentLength, "health") + _, err = ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + + assert.Nilf(t, err, "health") +} + +func TestTodayJoke(t *testing.T) { + req, _ := http.NewRequest("GET", "/today", nil) + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "today joke") + assert.Equalf(t, 200, res.StatusCode, "today joke") + assert.NotEqualf(t, 0, res.ContentLength, "today joke") + _, err = ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "today joke") +} + +func TestSingleJoke(t *testing.T) { + req, _ := http.NewRequest("GET", "/", nil) + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "single joke") + assert.Equalf(t, 200, res.StatusCode, "single joke") + assert.NotEqualf(t, 0, res.ContentLength, "single joke") + _, err = ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "single joke") +} + +func TestJokeByID_200(t *testing.T) { + req, _ := http.NewRequest("GET", "/id/1", nil) + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "joke by id") + assert.Equalf(t, 200, res.StatusCode, "joke by id") + assert.NotEqualf(t, 0, res.ContentLength, "joke by id") + _, err = ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "joke by id") +} + +func TestJokeByID_404(t *testing.T) { + req, _ := http.NewRequest("GET", "/id/300", nil) + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "joke by id") + assert.Equalf(t, 404, res.StatusCode, "joke by id") + assert.NotEqualf(t, 0, res.ContentLength, "joke by id") + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "joke by id") + assert.Equalf(t, "Requested ID was not found.", string(body), "joke by id") +} + +func TestTotalJokes(t *testing.T) { + req, _ := http.NewRequest("GET", "/total", nil) + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "joke total") + assert.Equalf(t, 200, res.StatusCode, "joke total") + assert.NotEqualf(t, 0, res.ContentLength, "joke total") + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "joke total") + assert.Equalf(t, "{\"message\":\"3\"}", string(body), "joke total") +} + +func TestAddNewJoke_201(t *testing.T) { + // TODO: Remove this line below, make this test works + t.SkipNow() + + reqBody := strings.NewReader("{\"link\":\"https://via.placeholder.com/300/04f/ff0000.png\",\"key\":\"test\",\"token\":\"password\"}") + req, _ := http.NewRequest("PUT", "/", reqBody) + req.Header.Set("content-type", "application/json") + req.Header.Add("accept", "application/json") + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "joke add") + assert.Equalf(t, 201, res.StatusCode, "joke add") + assert.NotEqualf(t, 0, res.ContentLength, "joke add") + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "joke add") + assert.Equalf(t, "{\"link\":\"https://via.placeholder.com/300/04f/ff0000.png\"}", string(body), "joke add") +} + +func TestAddNewJoke_NotValidImage(t *testing.T) { + // TODO: Remove this line below, make this test works + t.SkipNow() + + reqBody := strings.NewReader("{\"link\":\"https://google.com/\",\"key\":\"test\",\"token\":\"password\"}") + req, _ := http.NewRequest("PUT", "/", reqBody) + req.Header.Set("content-type", "application/json") + req.Header.Add("accept", "application/json") + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "joke add") + assert.Equalf(t, 400, res.StatusCode, "joke add") + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "joke add") + assert.Equalf(t, "{\"error\":\"URL provided is not a valid image\"}", string(body), "joke add") +} + +func TestGetSubmission_200(t *testing.T) { + req, _ := http.NewRequest("GET", "/submit", nil) + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "get submission") + assert.Equalf(t, 200, res.StatusCode, "get submission") + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "get submission") + assert.Equalf(t, "{\"count\":2,\"jokes\":[{\"id\":1,\"link\":\"https://via.placeholder.com/300/01f/fff.png\",\"created_at\":\"2021-08-03T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":0},{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") +} + +func TestGetSubmission_Params(t *testing.T) { + req, _ := http.NewRequest("GET", "/submit?page=1&limit=5&approved=true", nil) + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "get submission") + assert.Equalf(t, 200, res.StatusCode, "get submission") + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "get submission") + assert.Equalf(t, "{\"count\":1,\"jokes\":[{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") +} + +func TestAddSubmission_200(t *testing.T) { + // TODO: Remove this line below, make this test works + t.Skip() + + reqBody := strings.NewReader(`{"link":"https://via.placeholder.com/400/02f/fff.png","author":"Test "}`) + req, _ := http.NewRequest("POST", "/submit", reqBody) + req.Header.Set("content-type", "application/json") + req.Header.Add("accept", "application/json") + res, err := app.Test(req, int(time.Minute*2)) + + assert.Equalf(t, false, err != nil, "post submission") + assert.Equalf(t, 201, res.StatusCode, "post submission") + assert.NotEqualf(t, 0, res.ContentLength, "post submission") + _, err = ioutil.ReadAll(res.Body) + defer res.Body.Close() + assert.Nilf(t, err, "post submission") +} From c18a81a7aaab2022fada7e850ca804fbe6875727 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 18 Oct 2021 15:31:17 +0700 Subject: [PATCH 11/21] refactor: move everything to their own packages --- api/app/app.go | 27 +- api/app/core/administrator/id.go | 56 ++++ api/app/core/administrator/verify.go | 41 +++ .../core/{joke_getter.go => joke/getter.go} | 71 ++++- api/app/core/joke/getter_test.go | 73 +++++ api/app/core/joke/init_test.go | 153 ++++++++++ api/app/core/joke/setter.go | 133 +++++++++ api/app/core/joke_setter.go | 55 ---- api/app/core/schema/err.go | 6 + .../core/{schema.go => schema/image_api.go} | 13 +- api/app/core/schema/joke.go | 7 + .../{submit_setter.go => submit/setter.go} | 5 +- .../author.go} | 2 +- .../image.go} | 2 +- api/app/core/validator/joke.go | 68 +++++ api/app/handler/health/health.go | 13 +- api/app/handler/joke/dependencies.go | 13 +- api/app/handler/joke/joke_add.go | 66 ++--- api/app/handler/joke/joke_delete.go | 78 ++---- api/app/handler/joke/joke_get.go | 24 +- api/app/handler/joke/joke_total.go | 4 +- api/app/handler/joke/joke_update.go | 121 ++++---- api/app/handler/submit/dependencies.go | 13 +- api/app/handler/submit/submit_add.go | 23 +- api/app/handler/submit/submit_get.go | 2 +- api/app/middleware/auth.go | 58 +--- api/app/platform/database/create.go | 61 ++-- api/app/platform/database/placeholder.sql | 2 + api/app/routes/dependencies.go | 16 +- api/app/routes/health.go | 5 +- api/app/routes/joke.go | 17 +- api/app/routes/submit.go | 11 +- api/go.mod | 4 +- api/go.sum | 2 + api/main_test.go | 261 ------------------ 35 files changed, 821 insertions(+), 685 deletions(-) create mode 100644 api/app/core/administrator/id.go create mode 100644 api/app/core/administrator/verify.go rename api/app/core/{joke_getter.go => joke/getter.go} (62%) create mode 100644 api/app/core/joke/getter_test.go create mode 100644 api/app/core/joke/init_test.go create mode 100644 api/app/core/joke/setter.go delete mode 100644 api/app/core/joke_setter.go create mode 100644 api/app/core/schema/err.go rename api/app/core/{schema.go => schema/image_api.go} (52%) create mode 100644 api/app/core/schema/joke.go rename api/app/core/{submit_setter.go => submit/setter.go} (95%) rename api/app/core/{submit_validation.go => validator/author.go} (97%) rename api/app/core/{joke_validation.go => validator/image.go} (97%) create mode 100644 api/app/core/validator/joke.go delete mode 100644 api/main_test.go diff --git a/api/app/app.go b/api/app/app.go index 79aef00..9e02c8a 100644 --- a/api/app/app.go +++ b/api/app/app.go @@ -2,7 +2,7 @@ package app import ( "context" - "jokes-bapak2-api/app/core" + "jokes-bapak2-api/app/core/joke" "jokes-bapak2-api/app/platform/database" "jokes-bapak2-api/app/routes" "log" @@ -23,8 +23,6 @@ import ( ) func New() *fiber.App { - // Setup Context - ctx, cancel := context.WithCancel(context.Background()) // Setup PostgreSQL poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) @@ -36,7 +34,7 @@ func New() *fiber.App { poolConfig.MaxConns = 15 poolConfig.MinConns = 4 - db, err := pgxpool.ConnectConfig(ctx, poolConfig) + db, err := pgxpool.ConnectConfig(context.Background(), poolConfig) if err != nil { log.Panicln("Unable to create connection", err) } @@ -68,17 +66,18 @@ func New() *fiber.App { } defer sentry.Flush(2 * time.Second) - err = database.Setup(db, &ctx) + // TODO: These sequence below might be better wrapped as a Populate() function. + err = database.Setup(db) if err != nil { sentry.CaptureException(err) log.Panicln(err) } - err = core.SetAllJSONJoke(db, memory, &ctx) + err = joke.SetAllJSONJoke(db, context.Background(), memory) if err != nil { log.Panicln(err) } - err = core.SetTotalJoke(db, memory, &ctx) + err = joke.SetTotalJoke(db, context.Background(), memory) if err != nil { log.Panicln(err) } @@ -103,14 +102,12 @@ func New() *fiber.App { app.Use(etag.New()) route := routes.Dependencies{ - DB: db, - Redis: rdb, - Memory: memory, - HTTP: httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)), - Query: squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar), - App: app, - Context: &ctx, - Cancel: &cancel, + DB: db, + Redis: rdb, + Memory: memory, + HTTP: httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)), + Query: squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar), + App: app, } route.Health() route.Joke() diff --git a/api/app/core/administrator/id.go b/api/app/core/administrator/id.go new file mode 100644 index 0000000..8ff3f46 --- /dev/null +++ b/api/app/core/administrator/id.go @@ -0,0 +1,56 @@ +package administrator + +import ( + "context" + "time" + + "github.com/Masterminds/squirrel" + "github.com/jackc/pgx/v4/pgxpool" +) + +func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + + c1, err := db.Acquire(ctx) + if err != nil { + return 0, err + } + defer c1.Release() + + sql, args, err := query. + Update("administrators"). + Set("last_used", time.Now().UTC().Format(time.RFC3339)). + ToSql() + if err != nil { + return 0, err + } + + r, err := c1.Query(context.Background(), sql, args...) + if err != nil { + return 0, err + } + defer r.Close() + + c2, err := db.Acquire(ctx) + if err != nil { + return 0, err + } + defer c2.Release() + + sql, args, err = query. + Select("id"). + From("administrators"). + Where(squirrel.Eq{"key": key}). + ToSql() + if err != nil { + return 0, err + } + + var id int + err = c2.QueryRow(context.Background(), sql, args...).Scan(&id) + if err != nil { + return 0, err + } + + return id, nil +} diff --git a/api/app/core/administrator/verify.go b/api/app/core/administrator/verify.go new file mode 100644 index 0000000..9f6e93d --- /dev/null +++ b/api/app/core/administrator/verify.go @@ -0,0 +1,41 @@ +package administrator + +import ( + "context" + "errors" + + "github.com/Masterminds/squirrel" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" +) + +func CheckKeyExists(db *pgxpool.Pool, ctx context.Context, key string) (string, error) { + conn, err := db.Acquire(ctx) + if err != nil { + return "", err + } + defer conn.Release() + + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + + // Check if key exists + sql, args, err := query. + Select("token"). + From("administrators"). + Where(squirrel.Eq{"key": key}). + ToSql() + if err != nil { + return "", err + } + + var token string + err = conn.QueryRow(context.Background(), sql, args...).Scan(&token) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return "", nil + } + return "", err + } + + return token, nil +} diff --git a/api/app/core/joke_getter.go b/api/app/core/joke/getter.go similarity index 62% rename from api/app/core/joke_getter.go rename to api/app/core/joke/getter.go index cdb913a..78a27a3 100644 --- a/api/app/core/joke_getter.go +++ b/api/app/core/joke/getter.go @@ -1,27 +1,31 @@ -package core +package joke import ( "context" "errors" + "jokes-bapak2-api/app/core/schema" "math/rand" + "strconv" + "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" "github.com/georgysavva/scany/pgxscan" + "github.com/jackc/pgx" "github.com/jackc/pgx/v4/pgxpool" "github.com/pquerna/ffjson/ffjson" ) // GetAllJSONJokes fetch the database for all the jokes then output it as a JSON []byte. // Keep in mind, you will need to store it to memory yourself. -func GetAllJSONJokes(db *pgxpool.Pool, ctx *context.Context) ([]byte, error) { - conn, err := db.Acquire(*ctx) +func GetAllJSONJokes(db *pgxpool.Pool, ctx context.Context) ([]byte, error) { + conn, err := db.Acquire(ctx) if err != nil { - return nil, err + return []byte{}, err } defer conn.Release() - var jokes []Joke - results, err := conn.Query(*ctx, "SELECT \"id\",\"link\" FROM \"jokesbapak2\" ORDER BY \"id\"") + var jokes []schema.Joke + results, err := conn.Query(context.Background(), "SELECT \"id\",\"link\" FROM \"jokesbapak2\" ORDER BY \"id\"") if err != nil { return nil, err } @@ -40,17 +44,33 @@ func GetAllJSONJokes(db *pgxpool.Pool, ctx *context.Context) ([]byte, error) { return data, nil } +// Only return a link +func GetRandomJokeFromDB(db *pgxpool.Pool, ctx context.Context) (string, error) { + conn, err := db.Acquire(ctx) + if err != nil { + return "", err + } + + var link string + err = conn.QueryRow(context.Background(), "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) + if err != nil { + return "", err + } + + return link, nil +} + // GetRandomJokeFromCache returns a link string of a random joke from cache. func GetRandomJokeFromCache(memory *bigcache.BigCache) (string, error) { jokes, err := memory.Get("jokes") if err != nil { if errors.Is(err, bigcache.ErrEntryNotFound) { - return "", ErrNotFound + return "", schema.ErrNotFound } return "", err } - var data []Joke + var data []schema.Joke err = ffjson.Unmarshal(jokes, &data) if err != nil { return "", nil @@ -59,7 +79,7 @@ func GetRandomJokeFromCache(memory *bigcache.BigCache) (string, error) { // Return an error if the database is empty dataLength := len(data) if dataLength == 0 { - return "", ErrEmpty + return "", schema.ErrEmpty } random := rand.Intn(dataLength) @@ -99,12 +119,12 @@ func GetCachedJokeByID(memory *bigcache.BigCache, id int) (string, error) { jokes, err := memory.Get("jokes") if err != nil { if errors.Is(err, bigcache.ErrEntryNotFound) { - return "", ErrNotFound + return "", schema.ErrNotFound } return "", err } - var data []Joke + var data []schema.Joke err = ffjson.Unmarshal(jokes, &data) if err != nil { return "", nil @@ -125,10 +145,37 @@ func GetCachedTotalJokes(memory *bigcache.BigCache) (int, error) { total, err := memory.Get("total") if err != nil { if errors.Is(err, bigcache.ErrEntryNotFound) { - return 0, ErrNotFound + return 0, schema.ErrNotFound } return 0, err } return int(total[0]), nil } + +func CheckJokeExists(db *pgxpool.Pool, ctx context.Context, id string) (bool, error) { + conn, err := db.Acquire(ctx) + if err != nil { + return false, err + } + defer conn.Release() + + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + + sql, args, err := query. + Select("id"). + From("jokesbapak2"). + Where(squirrel.Eq{"id": id}). + ToSql() + if err != nil { + return false, err + } + + var jokeID int + err = conn.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + if err != nil && err != pgx.ErrNoRows { + return false, err + } + + return strconv.Itoa(jokeID) == id, nil +} diff --git a/api/app/core/joke/getter_test.go b/api/app/core/joke/getter_test.go new file mode 100644 index 0000000..5bd4be7 --- /dev/null +++ b/api/app/core/joke/getter_test.go @@ -0,0 +1,73 @@ +package joke_test + +import ( + "context" + "jokes-bapak2-api/app/core/joke" + "testing" + + "github.com/jackc/pgx/v4" +) + +func TestGetAllJSONJokes(t *testing.T) { + defer Teardown() + conn, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + + err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + _, err := t.Exec(context.Background(), "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) + if err != nil { + return err + } + _, err = t.Exec(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.GetAllJSONJokes(db, context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + + if string(j) == "" { + t.Error("j should not be empty") + } + +} + +func TestGetRandomJokeFromCache(t *testing.T) { + defer Teardown() + // +} + +func TestCheckJokesCache(t *testing.T) { + defer Teardown() + // +} + +func TestCheckTotalJokesCache(t *testing.T) { + defer Teardown() + // +} + +func TestGetCachedJokeByID(t *testing.T) { + defer Teardown() + // +} + +func TestGetCachedTotalJokes(t *testing.T) { + defer Teardown() + // +} + +func TestCheckJokeExists(t *testing.T) { + defer Teardown() + // +} diff --git a/api/app/core/joke/init_test.go b/api/app/core/joke/init_test.go new file mode 100644 index 0000000..20b608d --- /dev/null +++ b/api/app/core/joke/init_test.go @@ -0,0 +1,153 @@ +package joke_test + +import ( + "context" + "os" + "testing" + "time" + + "github.com/allegro/bigcache/v3" + "github.com/go-redis/redis/v8" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" +) + +var db *pgxpool.Pool +var cache *redis.Client +var memory *bigcache.BigCache + +var jokesData = []interface{}{ + 1, "https://via.placeholder.com/300/06f/fff.png", 1, + 2, "https://via.placeholder.com/300/07f/fff.png", 1, + 3, "https://via.placeholder.com/300/08f/fff.png", 1, +} +var submissionData = []interface{}{ + 1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, + 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1, +} +var administratorsData = []interface{}{ + 1, "very secure", "not the real one", time.Now().Format(time.RFC3339), 2, "test", "$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106", "", +} + +func TestMain(m *testing.M) { + Setup() + defer Teardown() + + os.Exit(m.Run()) +} + +func Setup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + panic(err) + } + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + panic(err) + } + + opt, err := redis.ParseURL(os.Getenv("REDIS_URL")) + if err != nil { + panic(err) + } + + cache = redis.NewClient(opt) + + memory, err = bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) + if err != nil { + panic(err) + } + + conn, err := db.Acquire(context.Background()) + if err != nil { + panic(err) + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(tx pgx.Tx) error { + _, err := tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS administrators ( + id SERIAL PRIMARY KEY, + key VARCHAR(255) NOT NULL UNIQUE, + token TEXT, + last_used VARCHAR(255) + );`, + ) + if err != nil { + return err + } + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS jokesbapak2 ( + id SERIAL PRIMARY KEY, + link TEXT UNIQUE, + creator INT NOT NULL REFERENCES "administrators" ("id") + );`, + ) + if err != nil { + return err + } + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS submission ( + id SERIAL PRIMARY KEY, + link UNIQUE NOT NULL, + created_at VARCHAR(255), + author VARCHAR(255) NOT NULL, + status SMALLINT DEFAULT 0 + );`, + ) + return err + }) + if err != nil { + panic(err) + } +} + +func Teardown() (err error) { + db.Close() + err = cache.Close() + if err != nil { + return + } + err = memory.Close() + return +} + +func TruncateTable(db *pgxpool.Pool, cache *redis.Client, memory *bigcache.BigCache) error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(tx pgx.Tx) error { + _, err := tx.Exec(context.Background(), "TRUNCATE TABLE submission;") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "TRUNCATE TABLE administrators;") + return err + }) + if err != nil { + return err + } + + err = cache.FlushAll(context.Background()).Err() + if err != nil { + return err + } + + err = memory.Reset() + if err != nil { + return err + } + + return nil +} diff --git a/api/app/core/joke/setter.go b/api/app/core/joke/setter.go new file mode 100644 index 0000000..3068ec4 --- /dev/null +++ b/api/app/core/joke/setter.go @@ -0,0 +1,133 @@ +package joke + +import ( + "context" + "jokes-bapak2-api/app/core/schema" + + "github.com/Masterminds/squirrel" + "github.com/allegro/bigcache/v3" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/pquerna/ffjson/ffjson" +) + +// SetAllJSONJoke fetches jokes data from GetAllJSONJokes then set it to memory cache. +func SetAllJSONJoke(db *pgxpool.Pool, ctx context.Context, memory *bigcache.BigCache) error { + jokes, err := GetAllJSONJokes(db, ctx) + if err != nil { + return err + } + err = memory.Set("jokes", jokes) + if err != nil { + return err + } + return nil +} + +func SetTotalJoke(db *pgxpool.Pool, ctx context.Context, memory *bigcache.BigCache) error { + check, err := CheckJokesCache(memory) + if err != nil { + return err + } + + if !check { + err = SetAllJSONJoke(db, ctx, memory) + if err != nil { + return err + } + } + + jokes, err := memory.Get("jokes") + if err != nil { + return err + } + + var data []schema.Joke + err = ffjson.Unmarshal(jokes, &data) + if err != nil { + return err + } + + var total = []byte{byte(len(data))} + err = memory.Set("total", total) + if err != nil { + return err + } + + return nil +} + +func InsertJokeIntoDB(db *pgxpool.Pool, ctx context.Context, joke schema.Joke) error { + conn, err := db.Acquire(ctx) + if err != nil { + return err + } + defer conn.Release() + + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + sql, args, err := query. + Insert("jokesbapak2"). + Columns("link", "creator"). + Values(joke.Link, joke.Creator). + ToSql() + if err != nil { + return err + } + + r, err := conn.Query(context.Background(), sql, args...) + if err != nil { + return err + } + defer r.Close() + return nil +} + +func DeleteSingleJoke(db *pgxpool.Pool, ctx context.Context, id int) error { + conn, err := db.Acquire(ctx) + if err != nil { + return err + } + defer conn.Release() + + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + sql, args, err := query. + Delete("jokesbapak2"). + Where(squirrel.Eq{"id": id}). + ToSql() + if err != nil { + return err + } + + r, err := conn.Query(context.Background(), sql, args...) + if err != nil { + return err + } + defer r.Close() + + return nil +} + +func UpdateJoke(db *pgxpool.Pool, ctx context.Context, link, creator string) error { + conn, err := db.Acquire(ctx) + if err != nil { + return err + } + defer conn.Release() + + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + sql, args, err := query. + Update("jokesbapak2"). + Set("link", link). + Set("creator", creator). + ToSql() + if err != nil { + return err + } + + r, err := conn.Query(context.Background(), sql, args...) + if err != nil { + return err + } + defer r.Close() + + return nil +} diff --git a/api/app/core/joke_setter.go b/api/app/core/joke_setter.go deleted file mode 100644 index cc4ac25..0000000 --- a/api/app/core/joke_setter.go +++ /dev/null @@ -1,55 +0,0 @@ -package core - -import ( - "context" - - "github.com/allegro/bigcache/v3" - "github.com/jackc/pgx/v4/pgxpool" - "github.com/pquerna/ffjson/ffjson" -) - -// SetAllJSONJoke fetches jokes data from GetAllJSONJokes then set it to memory cache. -func SetAllJSONJoke(db *pgxpool.Pool, memory *bigcache.BigCache, ctx *context.Context) error { - jokes, err := GetAllJSONJokes(db, ctx) - if err != nil { - return err - } - err = memory.Set("jokes", jokes) - if err != nil { - return err - } - return nil -} - -func SetTotalJoke(db *pgxpool.Pool, memory *bigcache.BigCache, ctx *context.Context) error { - check, err := CheckJokesCache(memory) - if err != nil { - return err - } - - if !check { - err = SetAllJSONJoke(db, memory, ctx) - if err != nil { - return err - } - } - - jokes, err := memory.Get("jokes") - if err != nil { - return err - } - - var data []Joke - err = ffjson.Unmarshal(jokes, &data) - if err != nil { - return err - } - - var total = []byte{byte(len(data))} - err = memory.Set("total", total) - if err != nil { - return err - } - - return nil -} diff --git a/api/app/core/schema/err.go b/api/app/core/schema/err.go new file mode 100644 index 0000000..41cc86c --- /dev/null +++ b/api/app/core/schema/err.go @@ -0,0 +1,6 @@ +package schema + +import "errors" + +var ErrNotFound = errors.New("record not found") +var ErrEmpty = errors.New("record is empty") diff --git a/api/app/core/schema.go b/api/app/core/schema/image_api.go similarity index 52% rename from api/app/core/schema.go rename to api/app/core/schema/image_api.go index b66d86b..6c3c25f 100644 --- a/api/app/core/schema.go +++ b/api/app/core/schema/image_api.go @@ -1,12 +1,4 @@ -package core - -import "errors" - -type Joke struct { - ID int `json:"id" form:"id" db:"id"` - Link string `json:"link" form:"link" db:"link"` - Creator int `json:"creator" form:"creator" db:"creator"` -} +package schema type ImageAPI struct { Data ImageAPIData `json:"data"` @@ -21,6 +13,3 @@ type ImageAPIData struct { URL string `json:"url"` DisplayURL string `json:"display_url"` } - -var ErrNotFound = errors.New("record not found") -var ErrEmpty = errors.New("record is empty") diff --git a/api/app/core/schema/joke.go b/api/app/core/schema/joke.go new file mode 100644 index 0000000..69a9914 --- /dev/null +++ b/api/app/core/schema/joke.go @@ -0,0 +1,7 @@ +package schema + +type Joke struct { + ID int `json:"id" form:"id" db:"id"` + Link string `json:"link" form:"link" db:"link"` + Creator int `json:"creator" form:"creator" db:"creator"` +} diff --git a/api/app/core/submit_setter.go b/api/app/core/submit/setter.go similarity index 95% rename from api/app/core/submit_setter.go rename to api/app/core/submit/setter.go index e490d83..49bcff2 100644 --- a/api/app/core/submit_setter.go +++ b/api/app/core/submit/setter.go @@ -1,9 +1,10 @@ -package core +package submit import ( "bytes" "io" "io/ioutil" + "jokes-bapak2-api/app/core/schema" "jokes-bapak2-api/app/utils" "mime/multipart" "net/http" @@ -70,7 +71,7 @@ func UploadImage(client *httpclient.Client, image io.Reader) (string, error) { return "", err } - var data ImageAPI + var data schema.ImageAPI err = ffjson.Unmarshal(responseBody, &data) if err != nil { return "", err diff --git a/api/app/core/submit_validation.go b/api/app/core/validator/author.go similarity index 97% rename from api/app/core/submit_validation.go rename to api/app/core/validator/author.go index a1e23af..afc7d43 100644 --- a/api/app/core/submit_validation.go +++ b/api/app/core/validator/author.go @@ -1,4 +1,4 @@ -package core +package validator import ( "regexp" diff --git a/api/app/core/joke_validation.go b/api/app/core/validator/image.go similarity index 97% rename from api/app/core/joke_validation.go rename to api/app/core/validator/image.go index b0ff300..2f5dda0 100644 --- a/api/app/core/joke_validation.go +++ b/api/app/core/validator/image.go @@ -1,4 +1,4 @@ -package core +package validator import ( "errors" diff --git a/api/app/core/validator/joke.go b/api/app/core/validator/joke.go new file mode 100644 index 0000000..ceddab1 --- /dev/null +++ b/api/app/core/validator/joke.go @@ -0,0 +1,68 @@ +package validator + +import ( + "context" + "errors" + + "github.com/Masterminds/squirrel" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" +) + +// Validate if link already exists +func LinkAlreadyExists(db *pgxpool.Pool, ctx context.Context, link string) (bool, error) { + conn, err := db.Acquire(ctx) + if err != nil { + return false, err + } + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + defer conn.Release() + + sql, args, err := query. + Select("link"). + From("jokesbapak2"). + Where(squirrel.Eq{"link": link}). + ToSql() + if err != nil { + return false, err + } + + var validateLink string + err = conn.QueryRow(context.Background(), sql, args...).Scan(&validateLink) + if err != nil && err != pgx.ErrNoRows { + return false, err + } + + return validateLink != "", nil +} + +// Check if the joke exists +func IDAlreadyExists(db *pgxpool.Pool, ctx context.Context, id int) (bool, error) { + conn, err := db.Acquire(ctx) + if err != nil { + return false, err + } + defer conn.Release() + + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + sql, args, err := query. + Select("id"). + From("jokesbapak2"). + Where(squirrel.Eq{"id": id}). + ToSql() + if err != nil { + return false, err + } + + var jokeID int + err = conn.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + if err != nil && !errors.Is(err, pgx.ErrNoRows) { + return false, err + } + + if errors.Is(err, pgx.ErrNoRows) { + return false, nil + } + + return true, nil +} diff --git a/api/app/handler/health/health.go b/api/app/handler/health/health.go index 48c1b08..fd8d767 100644 --- a/api/app/handler/health/health.go +++ b/api/app/handler/health/health.go @@ -1,28 +1,25 @@ package health import ( - "context" - "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v4/pgxpool" ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client - Context *context.Context + DB *pgxpool.Pool + Redis *redis.Client } func (d *Dependencies) Health(c *fiber.Ctx) error { - conn, err := d.DB.Acquire(*d.Context) + conn, err := d.DB.Acquire(c.Context()) if err != nil { return err } defer conn.Release() // Ping REDIS database - err = d.Redis.Ping(*d.Context).Err() + err = d.Redis.Ping(c.Context()).Err() if err != nil { return c. Status(fiber.StatusServiceUnavailable). @@ -31,7 +28,7 @@ func (d *Dependencies) Health(c *fiber.Ctx) error { }) } - _, err = conn.Query(*d.Context, "SELECT \"id\" FROM \"jokesbapak2\" LIMIT 1") + _, err = conn.Query(c.Context(), "SELECT \"id\" FROM \"jokesbapak2\" LIMIT 1") if err != nil { return c. Status(fiber.StatusServiceUnavailable). diff --git a/api/app/handler/joke/dependencies.go b/api/app/handler/joke/dependencies.go index 013f3a7..75d0b4c 100644 --- a/api/app/handler/joke/dependencies.go +++ b/api/app/handler/joke/dependencies.go @@ -1,8 +1,6 @@ package joke import ( - "context" - "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" "github.com/go-redis/redis/v8" @@ -11,10 +9,9 @@ import ( ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client - Memory *bigcache.BigCache - HTTP *httpclient.Client - Query squirrel.StatementBuilderType - Context *context.Context + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType } diff --git a/api/app/handler/joke/joke_add.go b/api/app/handler/joke/joke_add.go index a5dbb62..841c83b 100644 --- a/api/app/handler/joke/joke_add.go +++ b/api/app/handler/joke/joke_add.go @@ -1,38 +1,25 @@ package joke import ( - "jokes-bapak2-api/app/core" + core "jokes-bapak2-api/app/core/joke" + "jokes-bapak2-api/app/core/schema" + "jokes-bapak2-api/app/core/validator" - "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4" ) func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { - conn, err := d.DB.Acquire(*d.Context) - if err != nil { - return err - } - defer conn.Release() - - tx, err := conn.Begin(*d.Context) - if err != nil { - return err - } - defer tx.Rollback(*d.Context) - - var body core.Joke - err = c.BodyParser(&body) + var body schema.Joke + err := c.BodyParser(&body) if err != nil { return err } // Check link validity - valid, err := core.CheckImageValidity(d.HTTP, body.Link) + valid, err := validator.CheckImageValidity(d.HTTP, body.Link) if err != nil { return err } - if !valid { return c. Status(fiber.StatusBadRequest). @@ -40,51 +27,36 @@ func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { Error: "URL provided is not a valid image", }) } - // Validate if link already exists - sql, args, err := d.Query. - Select("link"). - From("jokesbapak2"). - Where(squirrel.Eq{"link": body.Link}). - ToSql() + + validateLink, err := validator.LinkAlreadyExists(d.DB, c.Context(), body.Link) if err != nil { return err } - var validateLink string - err = conn.QueryRow(*d.Context, sql, args...).Scan(&validateLink) - if err != nil && err != pgx.ErrNoRows { - return err - } - if err == nil && validateLink != "" { + if !validateLink { return c.Status(fiber.StatusConflict).JSON(Error{ Error: "Given link is already on the jokesbapak2 database", }) } - sql, args, err = d.Query. - Insert("jokesbapak2"). - Columns("link", "creator"). - Values(body.Link, c.Locals("userID")). - ToSql() + err = core.InsertJokeIntoDB( + d.DB, + c.Context(), + schema.Joke{ + Link: body.Link, + Creator: c.Locals("userID").(int), + }, + ) if err != nil { return err } - _, err = tx.Exec(*d.Context, sql, args...) + err = core.SetAllJSONJoke(d.DB, c.Context(), d.Memory) if err != nil { return err } - err = tx.Commit(*d.Context) - if err != nil { - return err - } - - err = core.SetAllJSONJoke(d.DB, d.Memory, d.Context) - if err != nil { - return err - } - err = core.SetTotalJoke(d.DB, d.Memory, d.Context) + err = core.SetTotalJoke(d.DB, c.Context(), d.Memory) if err != nil { return err } diff --git a/api/app/handler/joke/joke_delete.go b/api/app/handler/joke/joke_delete.go index 64b3b0f..3aad05d 100644 --- a/api/app/handler/joke/joke_delete.go +++ b/api/app/handler/joke/joke_delete.go @@ -1,75 +1,51 @@ package joke import ( - "jokes-bapak2-api/app/core" + core "jokes-bapak2-api/app/core/joke" + "jokes-bapak2-api/app/core/validator" "strconv" - "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" ) func (d *Dependencies) DeleteJoke(c *fiber.Ctx) error { - conn, err := d.DB.Acquire(*d.Context) - if err != nil { - return err - } - defer conn.Release() - id, err := strconv.Atoi(c.Params("id")) if err != nil { return err } - // Check if the joke exists - sql, args, err := d.Query. - Select("id"). - From("jokesbapak2"). - Where(squirrel.Eq{"id": id}). - ToSql() + validate, err := validator.IDAlreadyExists(d.DB, c.Context(), id) if err != nil { return err } - var jokeID int - err = conn.QueryRow(*d.Context, sql, args...).Scan(&jokeID) - if err != nil { - return err - } - - if jokeID == id { - sql, args, err = d.Query. - Delete("jokesbapak2"). - Where(squirrel.Eq{"id": id}). - ToSql() - if err != nil { - return err - } - - r, err := conn.Query(*d.Context, sql, args...) - if err != nil { - return err - } - - defer r.Close() - - err = core.SetAllJSONJoke(d.DB, d.Memory, d.Context) - if err != nil { - return err - } - err = core.SetTotalJoke(d.DB, d.Memory, d.Context) - if err != nil { - return err - } - + if validate { return c. - Status(fiber.StatusOK). - JSON(ResponseJoke{ - Message: "specified joke id has been deleted", + Status(fiber.StatusNotAcceptable). + JSON(Error{ + Error: "specified joke id does not exists", }) } + + err = core.DeleteSingleJoke(d.DB, c.Context(), id) + if err != nil { + return err + } + + err = core.SetAllJSONJoke(d.DB, c.Context(), d.Memory) + if err != nil { + return err + } + + err = core.SetTotalJoke(d.DB, c.Context(), d.Memory) + if err != nil { + return err + } + return c. - Status(fiber.StatusNotAcceptable). - JSON(Error{ - Error: "specified joke id does not exists", + Status(fiber.StatusOK). + JSON(ResponseJoke{ + Message: "specified joke id has been deleted", }) + } diff --git a/api/app/handler/joke/joke_get.go b/api/app/handler/joke/joke_get.go index 0e57e68..33efa43 100644 --- a/api/app/handler/joke/joke_get.go +++ b/api/app/handler/joke/joke_get.go @@ -1,8 +1,10 @@ package joke import ( + "errors" "io/ioutil" - "jokes-bapak2-api/app/core" + core "jokes-bapak2-api/app/core/joke" + "jokes-bapak2-api/app/core/schema" "jokes-bapak2-api/app/utils" "strconv" "time" @@ -15,7 +17,7 @@ func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { // send the joke if exists // get a new joke if it's not, then send it. var joke Today - err := d.Redis.MGet(*d.Context, "today:link", "today:date", "today:image", "today:contentType").Scan(&joke) + err := d.Redis.MGet(c.Context(), "today:link", "today:date", "today:image", "today:contentType").Scan(&joke) if err != nil { return err } @@ -30,13 +32,7 @@ func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).Send([]byte(joke.Image)) } - conn, err := d.DB.Acquire(*d.Context) - if err != nil { - return err - } - defer conn.Release() - var link string - err = conn.QueryRow(*d.Context, "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) + link, err := core.GetRandomJokeFromDB(d.DB, c.Context()) if err != nil { return err } @@ -52,7 +48,7 @@ func (d *Dependencies) TodayJoke(c *fiber.Ctx) error { } now := time.Now().UTC().Format(time.RFC3339) - err = d.Redis.MSet(*d.Context, map[string]interface{}{ + err = d.Redis.MSet(c.Context(), map[string]interface{}{ "today:link": link, "today:date": now, "today:image": string(data), @@ -73,10 +69,11 @@ func (d *Dependencies) SingleJoke(c *fiber.Ctx) error { } if !checkCache { - jokes, err := core.GetAllJSONJokes(d.DB, d.Context) + jokes, err := core.GetAllJSONJokes(d.DB, c.Context()) if err != nil { return err } + err = d.Memory.Set("jokes", jokes) if err != nil { return err @@ -84,7 +81,7 @@ func (d *Dependencies) SingleJoke(c *fiber.Ctx) error { } link, err := core.GetRandomJokeFromCache(d.Memory) - if err != nil { + if err != nil && !errors.Is(err, schema.ErrEmpty) { return err } @@ -111,10 +108,11 @@ func (d *Dependencies) JokeByID(c *fiber.Ctx) error { } if !checkCache { - jokes, err := core.GetAllJSONJokes(d.DB, d.Context) + jokes, err := core.GetAllJSONJokes(d.DB, c.Context()) if err != nil { return err } + err = d.Memory.Set("jokes", jokes) if err != nil { return err diff --git a/api/app/handler/joke/joke_total.go b/api/app/handler/joke/joke_total.go index be24a32..ea38489 100644 --- a/api/app/handler/joke/joke_total.go +++ b/api/app/handler/joke/joke_total.go @@ -2,7 +2,7 @@ package joke import ( "errors" - "jokes-bapak2-api/app/core" + core "jokes-bapak2-api/app/core/joke" "strconv" "github.com/allegro/bigcache/v3" @@ -16,7 +16,7 @@ func (d *Dependencies) TotalJokes(c *fiber.Ctx) error { } if !checkTotal { - err = core.SetTotalJoke(d.DB, d.Memory, d.Context) + err = core.SetTotalJoke(d.DB, c.Context(), d.Memory) if err != nil { return err } diff --git a/api/app/handler/joke/joke_update.go b/api/app/handler/joke/joke_update.go index 6ab0cd3..a275aed 100644 --- a/api/app/handler/joke/joke_update.go +++ b/api/app/handler/joke/joke_update.go @@ -1,94 +1,69 @@ package joke import ( - "jokes-bapak2-api/app/core" - "strconv" + core "jokes-bapak2-api/app/core/joke" + "jokes-bapak2-api/app/core/schema" + "jokes-bapak2-api/app/core/validator" - "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4" ) func (d *Dependencies) UpdateJoke(c *fiber.Ctx) error { - conn, err := d.DB.Acquire(*d.Context) - if err != nil { - return err - } - defer conn.Release() - id := c.Params("id") // Check if the joke exists - sql, args, err := d.Query. - Select("id"). - From("jokesbapak2"). - Where(squirrel.Eq{"id": id}). - ToSql() + + jokeExists, err := core.CheckJokeExists(d.DB, c.Context(), id) if err != nil { return err } - var jokeID int - err = conn.QueryRow(*d.Context, sql, args...).Scan(&jokeID) - if err != nil && err != pgx.ErrNoRows { - return err - } - - if strconv.Itoa(jokeID) == id { - body := new(core.Joke) - err = c.BodyParser(&body) - if err != nil { - return err - } - - // Check link validity - valid, err := core.CheckImageValidity(d.HTTP, body.Link) - if err != nil { - return err - } - - if !valid { - return c. - Status(fiber.StatusBadRequest). - JSON(Error{ - Error: "URL provided is not a valid image", - }) - } - - sql, args, err = d.Query. - Update("jokesbapak2"). - Set("link", body.Link). - Set("creator", c.Locals("userID")). - ToSql() - if err != nil { - return err - } - - r, err := conn.Query(*d.Context, sql, args...) - if err != nil { - return err - } - defer r.Close() - - err = core.SetAllJSONJoke(d.DB, d.Memory, d.Context) - if err != nil { - return err - } - err = core.SetTotalJoke(d.DB, d.Memory, d.Context) - if err != nil { - return err - } - + if !jokeExists { return c. - Status(fiber.StatusOK). - JSON(ResponseJoke{ - Message: "specified joke id has been updated", - Link: body.Link, + Status(fiber.StatusNotAcceptable). + JSON(Error{ + Error: "specified joke id does not exists", }) } + body := new(schema.Joke) + err = c.BodyParser(&body) + if err != nil { + return err + } + + // Check link validity + valid, err := validator.CheckImageValidity(d.HTTP, body.Link) + if err != nil { + return err + } + + if !valid { + return c. + Status(fiber.StatusBadRequest). + JSON(Error{ + Error: "URL provided is not a valid image", + }) + } + + err = core.UpdateJoke(d.DB, c.Context(), body.Link, c.Locals("userID").(string)) + if err != nil { + return err + } + + err = core.SetAllJSONJoke(d.DB, c.Context(), d.Memory) + if err != nil { + return err + } + + err = core.SetTotalJoke(d.DB, c.Context(), d.Memory) + if err != nil { + return err + } + return c. - Status(fiber.StatusNotAcceptable). - JSON(Error{ - Error: "specified joke id does not exists", + Status(fiber.StatusOK). + JSON(ResponseJoke{ + Message: "specified joke id has been updated", + Link: body.Link, }) } diff --git a/api/app/handler/submit/dependencies.go b/api/app/handler/submit/dependencies.go index 370f616..5f73c1b 100644 --- a/api/app/handler/submit/dependencies.go +++ b/api/app/handler/submit/dependencies.go @@ -1,8 +1,6 @@ package submit import ( - "context" - "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" "github.com/go-redis/redis/v8" @@ -11,10 +9,9 @@ import ( ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client - Memory *bigcache.BigCache - HTTP *httpclient.Client - Query squirrel.StatementBuilderType - Context *context.Context + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType } diff --git a/api/app/handler/submit/submit_add.go b/api/app/handler/submit/submit_add.go index 234a5cf..d3e4a6c 100644 --- a/api/app/handler/submit/submit_add.go +++ b/api/app/handler/submit/submit_add.go @@ -2,7 +2,8 @@ package submit import ( "context" - "jokes-bapak2-api/app/core" + core "jokes-bapak2-api/app/core/submit" + "jokes-bapak2-api/app/core/validator" "net/url" "strings" "time" @@ -15,7 +16,7 @@ import ( ) func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { - conn, err := d.DB.Acquire(*d.Context) + conn, err := d.DB.Acquire(c.Context()) if err != nil { return err } @@ -41,7 +42,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { }) } else { // Validate format - valid := core.ValidateAuthor(body.Author) + valid := validator.ValidateAuthor(body.Author) if !valid { return c.Status(fiber.StatusBadRequest).JSON(Error{ Error: "Please stick to the format of \"yourname \" and within 200 characters", @@ -53,7 +54,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { // Check link validity if link was provided if body.Link != "" { - valid, err := core.CheckImageValidity(d.HTTP, body.Link) + valid, err := validator.CheckImageValidity(d.HTTP, body.Link) if err != nil { return err } @@ -77,7 +78,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { } // Validate if link already exists - validateLink, err := validateIfLinkExists(conn, d.Context, d.Query, link) + validateLink, err := validateIfLinkExists(d.DB, c.Context(), d.Query, link) if err != nil { return err } @@ -101,7 +102,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { } var submission []Submission - result, err := conn.Query(*d.Context, sql, args...) + result, err := conn.Query(c.Context(), sql, args...) if err != nil { return err } @@ -121,7 +122,13 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { }) } -func validateIfLinkExists(conn *pgxpool.Conn, ctx *context.Context, query squirrel.StatementBuilderType, link string) (bool, error) { +func validateIfLinkExists(db *pgxpool.Pool, ctx context.Context, query squirrel.StatementBuilderType, link string) (bool, error) { + conn, err := db.Acquire(ctx) + if err != nil { + return false, err + } + defer conn.Release() + sql, args, err := query. Select("link"). From("submission"). @@ -132,7 +139,7 @@ func validateIfLinkExists(conn *pgxpool.Conn, ctx *context.Context, query squirr } var validateLink string - err = conn.QueryRow(*ctx, sql, args...).Scan(&validateLink) + err = conn.QueryRow(context.Background(), sql, args...).Scan(&validateLink) if err != nil && err != pgx.ErrNoRows { return false, err } diff --git a/api/app/handler/submit/submit_get.go b/api/app/handler/submit/submit_get.go index 2b16163..8c3810c 100644 --- a/api/app/handler/submit/submit_get.go +++ b/api/app/handler/submit/submit_get.go @@ -83,7 +83,7 @@ func (d *Dependencies) GetSubmission(c *fiber.Ctx) error { sql = bob.ReplacePlaceholder(sqlQuery.String(), bob.Dollar) var submissions []Submission - results, err := d.DB.Query(*d.Context, sql, args...) + results, err := d.DB.Query(c.Context(), sql, args...) if err != nil { return err } diff --git a/api/app/middleware/auth.go b/api/app/middleware/auth.go index 96ddb09..1076ecb 100644 --- a/api/app/middleware/auth.go +++ b/api/app/middleware/auth.go @@ -1,20 +1,14 @@ package middleware import ( - "context" - "errors" - "time" + "jokes-bapak2-api/app/core/administrator" - "github.com/Masterminds/squirrel" phccrypto "github.com/aldy505/phc-crypto" "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" ) -var psql = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) - -func RequireAuth(db *pgxpool.Pool, ctx *context.Context) fiber.Handler { +func RequireAuth(db *pgxpool.Pool) fiber.Handler { return func(c *fiber.Ctx) error { var auth Auth err := c.BodyParser(&auth) @@ -22,27 +16,17 @@ func RequireAuth(db *pgxpool.Pool, ctx *context.Context) fiber.Handler { return err } - // Check if key exists - sql, args, err := psql. - Select("token"). - From("administrators"). - Where(squirrel.Eq{"key": auth.Key}). - ToSql() + token, err := administrator.CheckKeyExists(db, c.Context(), auth.Key) if err != nil { return err } - var token string - err = db.QueryRow(*ctx, sql, args...).Scan(&token) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return c. - Status(fiber.StatusForbidden). - JSON(Error{ - Error: "Invalid key", - }) - } - return err + if token == "" { + return c. + Status(fiber.StatusForbidden). + JSON(Error{ + Error: "Invalid key", + }) } crypto, err := phccrypto.Use(phccrypto.Argon2, phccrypto.Config{}) @@ -56,33 +40,11 @@ func RequireAuth(db *pgxpool.Pool, ctx *context.Context) fiber.Handler { } if verify { - sql, args, err = psql. - Update("administrators"). - Set("last_used", time.Now().UTC().Format(time.RFC3339)). - ToSql() + id, err := administrator.GetUserID(db, c.Context(), auth.Key) if err != nil { return err } - _, err = db.Query(*ctx, sql, args...) - if err != nil { - return err - } - - sql, args, err = psql. - Select("id"). - From("administrators"). - Where(squirrel.Eq{"key": auth.Key}). - ToSql() - if err != nil { - return err - } - - var id int - err = db.QueryRow(*ctx, sql, args...).Scan(&id) - if err != nil { - return err - } c.Locals("userID", id) return c.Next() } diff --git a/api/app/platform/database/create.go b/api/app/platform/database/create.go index c570f02..684b589 100644 --- a/api/app/platform/database/create.go +++ b/api/app/platform/database/create.go @@ -2,45 +2,39 @@ package database import ( "context" - "log" "github.com/aldy505/bob" "github.com/jackc/pgx/v4/pgxpool" ) // Setup the table connection, create table if not exists -func Setup(db *pgxpool.Pool, ctx *context.Context) error { - conn, err := db.Acquire(*ctx) - if err != nil { - log.Fatalln("30 - err here") - return err - } - defer conn.Release() - - err = setupAuthTable(conn, ctx) +func Setup(db *pgxpool.Pool) error { + conn, err := db.Acquire(context.Background()) if err != nil { return err } - conn2, err := db.Acquire(*ctx) - if err != nil { - log.Fatalln("32 - err here") - return err - } - defer conn2.Release() - - err = setupJokesTable(conn2, ctx) + err = setupAuthTable(conn) if err != nil { return err } - conn3, err := db.Acquire(*ctx) + conn, err = db.Acquire(context.Background()) if err != nil { return err } - defer conn3.Release() - err = setupSubmissionTable(conn3, ctx) + err = setupJokesTable(conn) + if err != nil { + return err + } + + conn, err = db.Acquire(context.Background()) + if err != nil { + return err + } + + err = setupSubmissionTable(conn) if err != nil { return err } @@ -48,10 +42,12 @@ func Setup(db *pgxpool.Pool, ctx *context.Context) error { return nil } -func setupAuthTable(conn *pgxpool.Conn, ctx *context.Context) error { +func setupAuthTable(conn *pgxpool.Conn) error { + defer conn.Release() + // Check if table exists var tableAuthExists bool - err := conn.QueryRow(*ctx, `SELECT EXISTS ( + err := conn.QueryRow(context.Background(), `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'administrators' @@ -72,7 +68,7 @@ func setupAuthTable(conn *pgxpool.Conn, ctx *context.Context) error { return err } - q, err := conn.Query(*ctx, sql) + q, err := conn.Query(context.Background(), sql) if err != nil { return err } @@ -81,10 +77,12 @@ func setupAuthTable(conn *pgxpool.Conn, ctx *context.Context) error { return nil } -func setupJokesTable(conn *pgxpool.Conn, ctx *context.Context) error { +func setupJokesTable(conn *pgxpool.Conn) error { + defer conn.Release() + // Check if table exists var tableJokesExists bool - err := conn.QueryRow(*ctx, `SELECT EXISTS ( + err := conn.QueryRow(context.Background(), `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jokesbapak2' @@ -104,7 +102,7 @@ func setupJokesTable(conn *pgxpool.Conn, ctx *context.Context) error { return err } - q, err := conn.Query(*ctx, sql) + q, err := conn.Query(context.Background(), sql) if err != nil { return err } @@ -114,10 +112,12 @@ func setupJokesTable(conn *pgxpool.Conn, ctx *context.Context) error { return nil } -func setupSubmissionTable(conn *pgxpool.Conn, ctx *context.Context) error { +func setupSubmissionTable(conn *pgxpool.Conn) error { + defer conn.Release() + //Check if table exists var tableSubmissionExists bool - err := conn.QueryRow(*ctx, `SELECT EXISTS ( + err := conn.QueryRow(context.Background(), `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'submission' @@ -139,11 +139,12 @@ func setupSubmissionTable(conn *pgxpool.Conn, ctx *context.Context) error { return err } - q, err := conn.Query(*ctx, sql) + q, err := conn.Query(context.Background(), sql) if err != nil { return err } defer q.Close() } + return nil } diff --git a/api/app/platform/database/placeholder.sql b/api/app/platform/database/placeholder.sql index 07866bf..04e8b94 100644 --- a/api/app/platform/database/placeholder.sql +++ b/api/app/platform/database/placeholder.sql @@ -3,6 +3,8 @@ -- key: test -- token: password + + INSERT INTO "administrators" ("id", "key", "token", "last_used") VALUES (1, 'test', '$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106', ''); diff --git a/api/app/routes/dependencies.go b/api/app/routes/dependencies.go index 59eefdd..65f6bb5 100644 --- a/api/app/routes/dependencies.go +++ b/api/app/routes/dependencies.go @@ -1,8 +1,6 @@ package routes import ( - "context" - "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" "github.com/go-redis/redis/v8" @@ -12,12 +10,10 @@ import ( ) type Dependencies struct { - DB *pgxpool.Pool - Redis *redis.Client - Memory *bigcache.BigCache - HTTP *httpclient.Client - Query squirrel.StatementBuilderType - App *fiber.App - Context *context.Context - Cancel *context.CancelFunc + DB *pgxpool.Pool + Redis *redis.Client + Memory *bigcache.BigCache + HTTP *httpclient.Client + Query squirrel.StatementBuilderType + App *fiber.App } diff --git a/api/app/routes/health.go b/api/app/routes/health.go index 0cce80f..cf9e85c 100644 --- a/api/app/routes/health.go +++ b/api/app/routes/health.go @@ -10,9 +10,8 @@ import ( func (d *Dependencies) Health() { // Health check deps := health.Dependencies{ - DB: d.DB, - Redis: d.Redis, - Context: d.Context, + DB: d.DB, + Redis: d.Redis, } d.App.Get("/health", cache.New(cache.Config{Expiration: 30 * time.Minute}), deps.Health) diff --git a/api/app/routes/joke.go b/api/app/routes/joke.go index 8fa701b..c6c0766 100644 --- a/api/app/routes/joke.go +++ b/api/app/routes/joke.go @@ -10,12 +10,11 @@ import ( func (d *Dependencies) Joke() { deps := joke.Dependencies{ - DB: d.DB, - Redis: d.Redis, - Memory: d.Memory, - HTTP: d.HTTP, - Query: d.Query, - Context: d.Context, + DB: d.DB, + Redis: d.Redis, + Memory: d.Memory, + HTTP: d.HTTP, + Query: d.Query, } // Single route d.App.Get("/", deps.SingleJoke) @@ -34,11 +33,11 @@ func (d *Dependencies) Joke() { d.App.Get("/v1/total", cache.New(cache.Config{Expiration: 15 * time.Minute}), deps.TotalJokes) // Add new joke - d.App.Put("/", middleware.RequireAuth(d.DB, d.Context), deps.AddNewJoke) + d.App.Put("/", middleware.RequireAuth(d.DB), deps.AddNewJoke) // Update a joke - d.App.Patch("/id/:id", middleware.RequireAuth(d.DB, d.Context), middleware.OnlyIntegerAsID(), deps.UpdateJoke) + d.App.Patch("/id/:id", middleware.RequireAuth(d.DB), middleware.OnlyIntegerAsID(), deps.UpdateJoke) // Delete a joke - d.App.Delete("/id/:id", middleware.RequireAuth(d.DB, d.Context), middleware.OnlyIntegerAsID(), deps.DeleteJoke) + d.App.Delete("/id/:id", middleware.RequireAuth(d.DB), middleware.OnlyIntegerAsID(), deps.DeleteJoke) } diff --git a/api/app/routes/submit.go b/api/app/routes/submit.go index bdd13af..ce9822b 100644 --- a/api/app/routes/submit.go +++ b/api/app/routes/submit.go @@ -10,12 +10,11 @@ import ( func (d *Dependencies) Submit() { deps := submit.Dependencies{ - DB: d.DB, - Redis: d.Redis, - Memory: d.Memory, - HTTP: d.HTTP, - Query: d.Query, - Context: d.Context, + DB: d.DB, + Redis: d.Redis, + Memory: d.Memory, + HTTP: d.HTTP, + Query: d.Query, } // Get pending submitted joke diff --git a/api/go.mod b/api/go.mod index 7dae60f..1d66845 100644 --- a/api/go.mod +++ b/api/go.mod @@ -13,12 +13,13 @@ require ( github.com/go-redis/redis/v8 v8.11.0 github.com/gofiber/fiber/v2 v2.15.0 github.com/gojek/heimdall/v7 v7.0.2 + github.com/jackc/pgx v3.6.2+incompatible github.com/jackc/pgx/v4 v4.12.0 github.com/joho/godotenv v1.3.0 github.com/kr/text v0.2.0 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.7.0 // indirect golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -33,6 +34,7 @@ require ( github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/gojek/valkyrie v0.0.0-20180215180059-6aee720afcdf // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect github.com/jackc/pgconn v1.9.0 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect diff --git a/api/go.sum b/api/go.sum index 04e5f52..0148694 100644 --- a/api/go.sum +++ b/api/go.sum @@ -215,6 +215,8 @@ github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9 github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= diff --git a/api/main_test.go b/api/main_test.go deleted file mode 100644 index d4580c4..0000000 --- a/api/main_test.go +++ /dev/null @@ -1,261 +0,0 @@ -package main_test - -import ( - "context" - "errors" - "flag" - "io/ioutil" - v1 "jokes-bapak2-api/app" - "jokes-bapak2-api/app/platform/database" - "log" - "net/http" - "os" - "strings" - "testing" - "time" - - "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4/pgxpool" - _ "github.com/joho/godotenv/autoload" - "github.com/stretchr/testify/assert" -) - -var jokesData = []interface{}{1, "https://via.placeholder.com/300/06f/fff.png", 1, 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1} -var submissionData = []interface{}{1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1} -var administratorsData = []interface{}{1, "very secure", "not the real one", time.Now().Format(time.RFC3339), 2, "test", "$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106", ""} -var ctx context.Context = context.Background() - -func TestMain(m *testing.M) { - flag.Parse() - - log.Println("---- Preparing for integration test") - time.Sleep(time.Second * 5) - err := setup() - if err != nil { - log.Panicln(err) - } - time.Sleep(time.Second * 5) - log.Println("---- Preparation complete") - log.Print("\n") - - os.Exit(m.Run()) -} - -func setup() error { - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - return errors.New("Unable to create pool config: " + err.Error()) - } - - db, err := pgxpool.ConnectConfig(ctx, poolConfig) - if err != nil { - return errors.New("Unable to create connection: " + err.Error()) - } - - dj, err := db.Query(ctx, "DROP TABLE IF EXISTS \"jokesbapak2\"") - if err != nil { - return err - } - dj.Close() - - ds, err := db.Query(ctx, "DROP TABLE IF EXISTS \"submission\"") - if err != nil { - return err - } - ds.Close() - - da, err := db.Query(ctx, "DROP TABLE IF EXISTS \"administrators\"") - if err != nil { - return err - } - da.Close() - - err = database.Setup(db, &ctx) - if err != nil { - return err - } - - ia, err := db.Query(ctx, "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) - if err != nil { - return err - } - ia.Close() - - ij, err := db.Query(ctx, "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) - if err != nil { - return err - } - ij.Close() - - is, err := db.Query(ctx, "INSERT INTO \"submission\" (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10);", submissionData...) - if err != nil { - return err - } - is.Close() - - db.Close() - - return nil -} - -var app *fiber.App = v1.New() - -func TestHealth(t *testing.T) { - req, _ := http.NewRequest("GET", "/health", nil) - res, err := app.Test(req, int(time.Minute*2)) - if err != nil { - t.Fatal(err) - } - - assert.Equalf(t, false, err != nil, "health") - assert.Equalf(t, 200, res.StatusCode, "health") - assert.NotEqualf(t, 0, res.ContentLength, "health") - _, err = ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal(err) - } - defer res.Body.Close() - - assert.Nilf(t, err, "health") -} - -func TestTodayJoke(t *testing.T) { - req, _ := http.NewRequest("GET", "/today", nil) - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "today joke") - assert.Equalf(t, 200, res.StatusCode, "today joke") - assert.NotEqualf(t, 0, res.ContentLength, "today joke") - _, err = ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "today joke") -} - -func TestSingleJoke(t *testing.T) { - req, _ := http.NewRequest("GET", "/", nil) - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "single joke") - assert.Equalf(t, 200, res.StatusCode, "single joke") - assert.NotEqualf(t, 0, res.ContentLength, "single joke") - _, err = ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "single joke") -} - -func TestJokeByID_200(t *testing.T) { - req, _ := http.NewRequest("GET", "/id/1", nil) - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "joke by id") - assert.Equalf(t, 200, res.StatusCode, "joke by id") - assert.NotEqualf(t, 0, res.ContentLength, "joke by id") - _, err = ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "joke by id") -} - -func TestJokeByID_404(t *testing.T) { - req, _ := http.NewRequest("GET", "/id/300", nil) - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "joke by id") - assert.Equalf(t, 404, res.StatusCode, "joke by id") - assert.NotEqualf(t, 0, res.ContentLength, "joke by id") - body, err := ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "joke by id") - assert.Equalf(t, "Requested ID was not found.", string(body), "joke by id") -} - -func TestTotalJokes(t *testing.T) { - req, _ := http.NewRequest("GET", "/total", nil) - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "joke total") - assert.Equalf(t, 200, res.StatusCode, "joke total") - assert.NotEqualf(t, 0, res.ContentLength, "joke total") - body, err := ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "joke total") - assert.Equalf(t, "{\"message\":\"3\"}", string(body), "joke total") -} - -func TestAddNewJoke_201(t *testing.T) { - // TODO: Remove this line below, make this test works - t.SkipNow() - - reqBody := strings.NewReader("{\"link\":\"https://via.placeholder.com/300/04f/ff0000.png\",\"key\":\"test\",\"token\":\"password\"}") - req, _ := http.NewRequest("PUT", "/", reqBody) - req.Header.Set("content-type", "application/json") - req.Header.Add("accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "joke add") - assert.Equalf(t, 201, res.StatusCode, "joke add") - assert.NotEqualf(t, 0, res.ContentLength, "joke add") - body, err := ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "joke add") - assert.Equalf(t, "{\"link\":\"https://via.placeholder.com/300/04f/ff0000.png\"}", string(body), "joke add") -} - -func TestAddNewJoke_NotValidImage(t *testing.T) { - // TODO: Remove this line below, make this test works - t.SkipNow() - - reqBody := strings.NewReader("{\"link\":\"https://google.com/\",\"key\":\"test\",\"token\":\"password\"}") - req, _ := http.NewRequest("PUT", "/", reqBody) - req.Header.Set("content-type", "application/json") - req.Header.Add("accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "joke add") - assert.Equalf(t, 400, res.StatusCode, "joke add") - body, err := ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "joke add") - assert.Equalf(t, "{\"error\":\"URL provided is not a valid image\"}", string(body), "joke add") -} - -func TestGetSubmission_200(t *testing.T) { - req, _ := http.NewRequest("GET", "/submit", nil) - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "get submission") - assert.Equalf(t, 200, res.StatusCode, "get submission") - body, err := ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "get submission") - assert.Equalf(t, "{\"count\":2,\"jokes\":[{\"id\":1,\"link\":\"https://via.placeholder.com/300/01f/fff.png\",\"created_at\":\"2021-08-03T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":0},{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") -} - -func TestGetSubmission_Params(t *testing.T) { - req, _ := http.NewRequest("GET", "/submit?page=1&limit=5&approved=true", nil) - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "get submission") - assert.Equalf(t, 200, res.StatusCode, "get submission") - body, err := ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "get submission") - assert.Equalf(t, "{\"count\":1,\"jokes\":[{\"id\":2,\"link\":\"https://via.placeholder.com/300/02f/fff.png\",\"created_at\":\"2021-08-04T18:20:38Z\",\"author\":\"Test \\u003ctest@example.com\\u003e\",\"status\":1}]}", string(body), "get submission") -} - -func TestAddSubmission_200(t *testing.T) { - // TODO: Remove this line below, make this test works - t.Skip() - - reqBody := strings.NewReader(`{"link":"https://via.placeholder.com/400/02f/fff.png","author":"Test "}`) - req, _ := http.NewRequest("POST", "/submit", reqBody) - req.Header.Set("content-type", "application/json") - req.Header.Add("accept", "application/json") - res, err := app.Test(req, int(time.Minute*2)) - - assert.Equalf(t, false, err != nil, "post submission") - assert.Equalf(t, 201, res.StatusCode, "post submission") - assert.NotEqualf(t, 0, res.ContentLength, "post submission") - _, err = ioutil.ReadAll(res.Body) - defer res.Body.Close() - assert.Nilf(t, err, "post submission") -} From 521734246e1aff3ebedc3a5659a60323c6519ce3 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 18 Oct 2021 15:31:32 +0700 Subject: [PATCH 12/21] ci: abandon test for now --- .github/workflows/api.yml | 15 ++++++++------- .github/workflows/pr.yml | 15 ++++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index 84d4876..b70e5db 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -54,13 +54,14 @@ jobs: - name: Build run: go build main.go - - name: Run test & coverage - run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... - env: - ENV: development - PORT: 5000 - DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 - REDIS_URL: redis://@redis:6379 + # Remove test for now + # - name: Run test & coverage + # run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + # env: + # ENV: development + # PORT: 5000 + # DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 + # REDIS_URL: redis://@redis:6379 - name: Initialize CodeQL uses: github/codeql-action/init@v1 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 6c7fa9c..91da995 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -108,13 +108,14 @@ jobs: - name: Build run: go build main.go - - name: Run test & coverage - run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... - env: - ENV: development - PORT: 5000 - DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 - REDIS_URL: redis://@redis:6379 + # Remove test for now + # - name: Run test & coverage + # run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... + # env: + # ENV: development + # PORT: 5000 + # DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 + # REDIS_URL: redis://@redis:6379 - name: Initialize CodeQL uses: github/codeql-action/init@v1 From 31652fb3abe83cf7a70da5d9aa021839b0d947c1 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Sat, 30 Oct 2021 10:24:53 +0700 Subject: [PATCH 13/21] refactor: directory structure --- api/app/app.go | 131 ------------------ api/{app => }/core/administrator/id.go | 14 +- api/{app => }/core/administrator/verify.go | 0 api/{app => }/core/joke/getter.go | 2 +- api/{app => }/core/joke/getter_test.go | 2 +- api/{app => }/core/joke/init_test.go | 0 api/{app => }/core/joke/setter.go | 2 +- api/{app => }/core/schema/err.go | 0 api/{app => }/core/schema/image_api.go | 0 api/{app => }/core/schema/joke.go | 0 api/{app => }/core/submit/setter.go | 4 +- api/{app => }/core/validator/author.go | 0 api/{app => }/core/validator/image.go | 2 +- api/{app => }/core/validator/joke.go | 0 api/{app => }/favicon.png | Bin api/{app => }/handler/health/health.go | 0 api/{app => }/handler/health/schema.go | 0 api/{app => }/handler/joke/dependencies.go | 0 api/{app => }/handler/joke/joke_add.go | 6 +- api/{app => }/handler/joke/joke_delete.go | 4 +- api/{app => }/handler/joke/joke_get.go | 6 +- api/{app => }/handler/joke/joke_total.go | 2 +- api/{app => }/handler/joke/joke_update.go | 6 +- api/{app => }/handler/joke/schema.go | 0 api/{app => }/handler/submit/dependencies.go | 0 api/{app => }/handler/submit/schema.go | 0 api/{app => }/handler/submit/submit_add.go | 4 +- api/{app => }/handler/submit/submit_get.go | 0 api/main.go | 128 ++++++++++++++++- api/{app => }/middleware/auth.go | 2 +- api/{app => }/middleware/schema.go | 0 api/{app => }/middleware/validation.go | 0 api/{app => }/platform/database/create.go | 54 ++++---- .../platform/database/placeholder.sql | 0 api/{app => }/routes/dependencies.go | 0 api/{app => }/routes/health.go | 2 +- api/{app => }/routes/joke.go | 4 +- api/{app => }/routes/submit.go | 2 +- api/{app => }/utils/array.go | 0 api/{app => }/utils/array_test.go | 2 +- api/{app => }/utils/date.go | 0 api/{app => }/utils/date_test.go | 2 +- api/{app => }/utils/parse.go | 0 api/{app => }/utils/parse_test.go | 2 +- api/{app => }/utils/random.go | 0 api/{app => }/utils/random_test.go | 2 +- api/{app => }/utils/request.go | 0 api/{app => }/utils/request_test.go | 2 +- 48 files changed, 182 insertions(+), 205 deletions(-) delete mode 100644 api/app/app.go rename api/{app => }/core/administrator/id.go (74%) rename api/{app => }/core/administrator/verify.go (100%) rename api/{app => }/core/joke/getter.go (99%) rename api/{app => }/core/joke/getter_test.go (97%) rename api/{app => }/core/joke/init_test.go (100%) rename api/{app => }/core/joke/setter.go (98%) rename api/{app => }/core/schema/err.go (100%) rename api/{app => }/core/schema/image_api.go (100%) rename api/{app => }/core/schema/joke.go (100%) rename api/{app => }/core/submit/setter.go (95%) rename api/{app => }/core/validator/author.go (100%) rename api/{app => }/core/validator/image.go (96%) rename api/{app => }/core/validator/joke.go (100%) rename api/{app => }/favicon.png (100%) rename api/{app => }/handler/health/health.go (100%) rename api/{app => }/handler/health/schema.go (100%) rename api/{app => }/handler/joke/dependencies.go (100%) rename api/{app => }/handler/joke/joke_add.go (90%) rename api/{app => }/handler/joke/joke_delete.go (91%) rename api/{app => }/handler/joke/joke_get.go (96%) rename api/{app => }/handler/joke/joke_total.go (94%) rename api/{app => }/handler/joke/joke_update.go (91%) rename api/{app => }/handler/joke/schema.go (100%) rename api/{app => }/handler/submit/dependencies.go (100%) rename api/{app => }/handler/submit/schema.go (100%) rename api/{app => }/handler/submit/submit_add.go (97%) rename api/{app => }/handler/submit/submit_get.go (100%) rename api/{app => }/middleware/auth.go (95%) rename api/{app => }/middleware/schema.go (100%) rename api/{app => }/middleware/validation.go (100%) rename api/{app => }/platform/database/create.go (77%) rename api/{app => }/platform/database/placeholder.sql (100%) rename api/{app => }/routes/dependencies.go (100%) rename api/{app => }/routes/health.go (90%) rename api/{app => }/routes/joke.go (94%) rename api/{app => }/routes/submit.go (93%) rename api/{app => }/utils/array.go (100%) rename api/{app => }/utils/array_test.go (93%) rename api/{app => }/utils/date.go (100%) rename api/{app => }/utils/date_test.go (96%) rename api/{app => }/utils/parse.go (100%) rename api/{app => }/utils/parse_test.go (96%) rename api/{app => }/utils/random.go (100%) rename api/{app => }/utils/random_test.go (93%) rename api/{app => }/utils/request.go (100%) rename api/{app => }/utils/request_test.go (94%) diff --git a/api/app/app.go b/api/app/app.go deleted file mode 100644 index 9e02c8a..0000000 --- a/api/app/app.go +++ /dev/null @@ -1,131 +0,0 @@ -package app - -import ( - "context" - "jokes-bapak2-api/app/core/joke" - "jokes-bapak2-api/app/platform/database" - "jokes-bapak2-api/app/routes" - "log" - "os" - "time" - - "github.com/Masterminds/squirrel" - "github.com/allegro/bigcache/v3" - "github.com/getsentry/sentry-go" - "github.com/go-redis/redis/v8" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cors" - "github.com/gofiber/fiber/v2/middleware/etag" - "github.com/gofiber/fiber/v2/middleware/limiter" - "github.com/gojek/heimdall/v7/httpclient" - "github.com/jackc/pgx/v4/pgxpool" - _ "github.com/joho/godotenv/autoload" -) - -func New() *fiber.App { - - // Setup PostgreSQL - poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) - if err != nil { - log.Panicln("Unable to create pool config", err) - } - poolConfig.MaxConnIdleTime = time.Minute * 3 - poolConfig.MaxConnLifetime = time.Minute * 5 - poolConfig.MaxConns = 15 - poolConfig.MinConns = 4 - - db, err := pgxpool.ConnectConfig(context.Background(), poolConfig) - if err != nil { - log.Panicln("Unable to create connection", err) - } - - // Setup Redis - opt, err := redis.ParseURL(os.Getenv("REDIS_URL")) - if err != nil { - log.Fatalln(err) - } - rdb := redis.NewClient(opt) - - // Setup In Memory - memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) - if err != nil { - log.Panicln(err) - } - - // Setup Sentry - err = sentry.Init(sentry.ClientOptions{ - Dsn: os.Getenv("SENTRY_DSN"), - Environment: os.Getenv("ENV"), - AttachStacktrace: true, - // Enable printing of SDK debug messages. - // Useful when getting started or trying to figure something out. - Debug: true, - }) - if err != nil { - log.Panicln(err) - } - defer sentry.Flush(2 * time.Second) - - // TODO: These sequence below might be better wrapped as a Populate() function. - err = database.Setup(db) - if err != nil { - sentry.CaptureException(err) - log.Panicln(err) - } - - err = joke.SetAllJSONJoke(db, context.Background(), memory) - if err != nil { - log.Panicln(err) - } - err = joke.SetTotalJoke(db, context.Background(), memory) - if err != nil { - log.Panicln(err) - } - - timeoutDefault := time.Minute * 1 - - app := fiber.New(fiber.Config{ - ReadTimeout: timeoutDefault, - WriteTimeout: timeoutDefault, - CaseSensitive: true, - DisableKeepalive: true, - ErrorHandler: errorHandler, - }) - - app.Use(limiter.New(limiter.Config{ - Max: 30, - Expiration: 1 * time.Minute, - LimitReached: limitHandler, - })) - - app.Use(cors.New()) - app.Use(etag.New()) - - route := routes.Dependencies{ - DB: db, - Redis: rdb, - Memory: memory, - HTTP: httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)), - Query: squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar), - App: app, - } - route.Health() - route.Joke() - route.Submit() - - return app -} - -func limitHandler(c *fiber.Ctx) error { - return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ - "message": "we only allow up to 15 request per minute", - }) -} - -func errorHandler(c *fiber.Ctx, err error) error { - log.Println(err) - sentry.CaptureException(err) - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "error": "Something went wrong on our end", - }) -} diff --git a/api/app/core/administrator/id.go b/api/core/administrator/id.go similarity index 74% rename from api/app/core/administrator/id.go rename to api/core/administrator/id.go index 8ff3f46..3eb83fb 100644 --- a/api/app/core/administrator/id.go +++ b/api/core/administrator/id.go @@ -11,11 +11,11 @@ import ( func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) - c1, err := db.Acquire(ctx) + c, err := db.Acquire(ctx) if err != nil { return 0, err } - defer c1.Release() + defer c.Release() sql, args, err := query. Update("administrators"). @@ -25,18 +25,12 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { return 0, err } - r, err := c1.Query(context.Background(), sql, args...) + r, err := c.Query(context.Background(), sql, args...) if err != nil { return 0, err } defer r.Close() - c2, err := db.Acquire(ctx) - if err != nil { - return 0, err - } - defer c2.Release() - sql, args, err = query. Select("id"). From("administrators"). @@ -47,7 +41,7 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { } var id int - err = c2.QueryRow(context.Background(), sql, args...).Scan(&id) + err = c.QueryRow(context.Background(), sql, args...).Scan(&id) if err != nil { return 0, err } diff --git a/api/app/core/administrator/verify.go b/api/core/administrator/verify.go similarity index 100% rename from api/app/core/administrator/verify.go rename to api/core/administrator/verify.go diff --git a/api/app/core/joke/getter.go b/api/core/joke/getter.go similarity index 99% rename from api/app/core/joke/getter.go rename to api/core/joke/getter.go index 78a27a3..5d23316 100644 --- a/api/app/core/joke/getter.go +++ b/api/core/joke/getter.go @@ -3,7 +3,7 @@ package joke import ( "context" "errors" - "jokes-bapak2-api/app/core/schema" + "jokes-bapak2-api/core/schema" "math/rand" "strconv" diff --git a/api/app/core/joke/getter_test.go b/api/core/joke/getter_test.go similarity index 97% rename from api/app/core/joke/getter_test.go rename to api/core/joke/getter_test.go index 5bd4be7..68d26a4 100644 --- a/api/app/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -2,7 +2,7 @@ package joke_test import ( "context" - "jokes-bapak2-api/app/core/joke" + "jokes-bapak2-api/core/joke" "testing" "github.com/jackc/pgx/v4" diff --git a/api/app/core/joke/init_test.go b/api/core/joke/init_test.go similarity index 100% rename from api/app/core/joke/init_test.go rename to api/core/joke/init_test.go diff --git a/api/app/core/joke/setter.go b/api/core/joke/setter.go similarity index 98% rename from api/app/core/joke/setter.go rename to api/core/joke/setter.go index 3068ec4..48bbd4b 100644 --- a/api/app/core/joke/setter.go +++ b/api/core/joke/setter.go @@ -2,7 +2,7 @@ package joke import ( "context" - "jokes-bapak2-api/app/core/schema" + "jokes-bapak2-api/core/schema" "github.com/Masterminds/squirrel" "github.com/allegro/bigcache/v3" diff --git a/api/app/core/schema/err.go b/api/core/schema/err.go similarity index 100% rename from api/app/core/schema/err.go rename to api/core/schema/err.go diff --git a/api/app/core/schema/image_api.go b/api/core/schema/image_api.go similarity index 100% rename from api/app/core/schema/image_api.go rename to api/core/schema/image_api.go diff --git a/api/app/core/schema/joke.go b/api/core/schema/joke.go similarity index 100% rename from api/app/core/schema/joke.go rename to api/core/schema/joke.go diff --git a/api/app/core/submit/setter.go b/api/core/submit/setter.go similarity index 95% rename from api/app/core/submit/setter.go rename to api/core/submit/setter.go index 49bcff2..9607114 100644 --- a/api/app/core/submit/setter.go +++ b/api/core/submit/setter.go @@ -4,8 +4,8 @@ import ( "bytes" "io" "io/ioutil" - "jokes-bapak2-api/app/core/schema" - "jokes-bapak2-api/app/utils" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/utils" "mime/multipart" "net/http" "net/url" diff --git a/api/app/core/validator/author.go b/api/core/validator/author.go similarity index 100% rename from api/app/core/validator/author.go rename to api/core/validator/author.go diff --git a/api/app/core/validator/image.go b/api/core/validator/image.go similarity index 96% rename from api/app/core/validator/image.go rename to api/core/validator/image.go index 2f5dda0..eff0a7a 100644 --- a/api/app/core/validator/image.go +++ b/api/core/validator/image.go @@ -2,7 +2,7 @@ package validator import ( "errors" - "jokes-bapak2-api/app/utils" + "jokes-bapak2-api/utils" "net/http" "strings" diff --git a/api/app/core/validator/joke.go b/api/core/validator/joke.go similarity index 100% rename from api/app/core/validator/joke.go rename to api/core/validator/joke.go diff --git a/api/app/favicon.png b/api/favicon.png similarity index 100% rename from api/app/favicon.png rename to api/favicon.png diff --git a/api/app/handler/health/health.go b/api/handler/health/health.go similarity index 100% rename from api/app/handler/health/health.go rename to api/handler/health/health.go diff --git a/api/app/handler/health/schema.go b/api/handler/health/schema.go similarity index 100% rename from api/app/handler/health/schema.go rename to api/handler/health/schema.go diff --git a/api/app/handler/joke/dependencies.go b/api/handler/joke/dependencies.go similarity index 100% rename from api/app/handler/joke/dependencies.go rename to api/handler/joke/dependencies.go diff --git a/api/app/handler/joke/joke_add.go b/api/handler/joke/joke_add.go similarity index 90% rename from api/app/handler/joke/joke_add.go rename to api/handler/joke/joke_add.go index 841c83b..97af719 100644 --- a/api/app/handler/joke/joke_add.go +++ b/api/handler/joke/joke_add.go @@ -1,9 +1,9 @@ package joke import ( - core "jokes-bapak2-api/app/core/joke" - "jokes-bapak2-api/app/core/schema" - "jokes-bapak2-api/app/core/validator" + core "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/core/validator" "github.com/gofiber/fiber/v2" ) diff --git a/api/app/handler/joke/joke_delete.go b/api/handler/joke/joke_delete.go similarity index 91% rename from api/app/handler/joke/joke_delete.go rename to api/handler/joke/joke_delete.go index 3aad05d..2937b99 100644 --- a/api/app/handler/joke/joke_delete.go +++ b/api/handler/joke/joke_delete.go @@ -1,8 +1,8 @@ package joke import ( - core "jokes-bapak2-api/app/core/joke" - "jokes-bapak2-api/app/core/validator" + core "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/core/validator" "strconv" "github.com/gofiber/fiber/v2" diff --git a/api/app/handler/joke/joke_get.go b/api/handler/joke/joke_get.go similarity index 96% rename from api/app/handler/joke/joke_get.go rename to api/handler/joke/joke_get.go index 33efa43..ea75bfb 100644 --- a/api/app/handler/joke/joke_get.go +++ b/api/handler/joke/joke_get.go @@ -3,9 +3,9 @@ package joke import ( "errors" "io/ioutil" - core "jokes-bapak2-api/app/core/joke" - "jokes-bapak2-api/app/core/schema" - "jokes-bapak2-api/app/utils" + core "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/utils" "strconv" "time" diff --git a/api/app/handler/joke/joke_total.go b/api/handler/joke/joke_total.go similarity index 94% rename from api/app/handler/joke/joke_total.go rename to api/handler/joke/joke_total.go index ea38489..a820134 100644 --- a/api/app/handler/joke/joke_total.go +++ b/api/handler/joke/joke_total.go @@ -2,7 +2,7 @@ package joke import ( "errors" - core "jokes-bapak2-api/app/core/joke" + core "jokes-bapak2-api/core/joke" "strconv" "github.com/allegro/bigcache/v3" diff --git a/api/app/handler/joke/joke_update.go b/api/handler/joke/joke_update.go similarity index 91% rename from api/app/handler/joke/joke_update.go rename to api/handler/joke/joke_update.go index a275aed..d586553 100644 --- a/api/app/handler/joke/joke_update.go +++ b/api/handler/joke/joke_update.go @@ -1,9 +1,9 @@ package joke import ( - core "jokes-bapak2-api/app/core/joke" - "jokes-bapak2-api/app/core/schema" - "jokes-bapak2-api/app/core/validator" + core "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/core/validator" "github.com/gofiber/fiber/v2" ) diff --git a/api/app/handler/joke/schema.go b/api/handler/joke/schema.go similarity index 100% rename from api/app/handler/joke/schema.go rename to api/handler/joke/schema.go diff --git a/api/app/handler/submit/dependencies.go b/api/handler/submit/dependencies.go similarity index 100% rename from api/app/handler/submit/dependencies.go rename to api/handler/submit/dependencies.go diff --git a/api/app/handler/submit/schema.go b/api/handler/submit/schema.go similarity index 100% rename from api/app/handler/submit/schema.go rename to api/handler/submit/schema.go diff --git a/api/app/handler/submit/submit_add.go b/api/handler/submit/submit_add.go similarity index 97% rename from api/app/handler/submit/submit_add.go rename to api/handler/submit/submit_add.go index d3e4a6c..69b8119 100644 --- a/api/app/handler/submit/submit_add.go +++ b/api/handler/submit/submit_add.go @@ -2,8 +2,8 @@ package submit import ( "context" - core "jokes-bapak2-api/app/core/submit" - "jokes-bapak2-api/app/core/validator" + core "jokes-bapak2-api/core/submit" + "jokes-bapak2-api/core/validator" "net/url" "strings" "time" diff --git a/api/app/handler/submit/submit_get.go b/api/handler/submit/submit_get.go similarity index 100% rename from api/app/handler/submit/submit_get.go rename to api/handler/submit/submit_get.go diff --git a/api/main.go b/api/main.go index d4cb1b5..4091d04 100644 --- a/api/main.go +++ b/api/main.go @@ -1,26 +1,146 @@ package main import ( - "jokes-bapak2-api/app" "log" "os" "os/signal" + "context" + "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/platform/database" + "jokes-bapak2-api/routes" + + "time" + "github.com/gofiber/fiber/v2" _ "github.com/joho/godotenv/autoload" + + "github.com/Masterminds/squirrel" + "github.com/allegro/bigcache/v3" + "github.com/getsentry/sentry-go" + "github.com/go-redis/redis/v8" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v2/middleware/etag" + "github.com/gofiber/fiber/v2/middleware/limiter" + "github.com/gojek/heimdall/v7/httpclient" + "github.com/jackc/pgx/v4/pgxpool" ) func main() { - a := app.New() + // Setup PostgreSQL + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + log.Panicln("Unable to create pool config", err) + } + poolConfig.MaxConnIdleTime = time.Minute * 3 + poolConfig.MaxConnLifetime = time.Minute * 5 + poolConfig.MaxConns = 15 + poolConfig.MinConns = 4 + + db, err := pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + log.Panicln("Unable to create connection", err) + } + defer db.Close() + + // Setup Redis + opt, err := redis.ParseURL(os.Getenv("REDIS_URL")) + if err != nil { + log.Fatalln(err) + } + rdb := redis.NewClient(opt) + defer rdb.Close() + + // Setup In Memory + memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(6 * time.Hour)) + if err != nil { + log.Panicln(err) + } + defer memory.Close() + + // Setup Sentry + err = sentry.Init(sentry.ClientOptions{ + Dsn: os.Getenv("SENTRY_DSN"), + Environment: os.Getenv("ENV"), + AttachStacktrace: true, + // Enable printing of SDK debug messages. + // Useful when getting started or trying to figure something out. + Debug: true, + }) + if err != nil { + log.Panicln(err) + } + defer sentry.Flush(2 * time.Second) + + // TODO: These sequence below might be better wrapped as a Populate() function. + err = database.Setup(db) + if err != nil { + sentry.CaptureException(err) + log.Panicln(err) + } + + err = joke.SetAllJSONJoke(db, context.Background(), memory) + if err != nil { + log.Panicln(err) + } + err = joke.SetTotalJoke(db, context.Background(), memory) + if err != nil { + log.Panicln(err) + } + + timeoutDefault := time.Minute * 1 + + app := fiber.New(fiber.Config{ + ReadTimeout: timeoutDefault, + WriteTimeout: timeoutDefault, + CaseSensitive: true, + DisableKeepalive: true, + ErrorHandler: errorHandler, + }) + + app.Use(limiter.New(limiter.Config{ + Max: 30, + Expiration: 1 * time.Minute, + LimitReached: limitHandler, + })) + + app.Use(cors.New()) + app.Use(etag.New()) + + route := routes.Dependencies{ + DB: db, + Redis: rdb, + Memory: memory, + HTTP: httpclient.NewClient(httpclient.WithHTTPTimeout(10 * time.Second)), + Query: squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar), + App: app, + } + route.Health() + route.Joke() + route.Submit() // Start server (with or without graceful shutdown). if os.Getenv("ENV") == "development" { - StartServer(a) + StartServer(app) } else { - StartServerWithGracefulShutdown(a) + StartServerWithGracefulShutdown(app) } } +func limitHandler(c *fiber.Ctx) error { + return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ + "message": "we only allow up to 15 request per minute", + }) +} + +func errorHandler(c *fiber.Ctx, err error) error { + log.Println(err) + sentry.CaptureException(err) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Something went wrong on our end", + }) +} + // StartServerWithGracefulShutdown function for starting server with a graceful shutdown. func StartServerWithGracefulShutdown(a *fiber.App) { // Create channel for idle connections. diff --git a/api/app/middleware/auth.go b/api/middleware/auth.go similarity index 95% rename from api/app/middleware/auth.go rename to api/middleware/auth.go index 1076ecb..3ef847d 100644 --- a/api/app/middleware/auth.go +++ b/api/middleware/auth.go @@ -1,7 +1,7 @@ package middleware import ( - "jokes-bapak2-api/app/core/administrator" + "jokes-bapak2-api/core/administrator" phccrypto "github.com/aldy505/phc-crypto" "github.com/gofiber/fiber/v2" diff --git a/api/app/middleware/schema.go b/api/middleware/schema.go similarity index 100% rename from api/app/middleware/schema.go rename to api/middleware/schema.go diff --git a/api/app/middleware/validation.go b/api/middleware/validation.go similarity index 100% rename from api/app/middleware/validation.go rename to api/middleware/validation.go diff --git a/api/app/platform/database/create.go b/api/platform/database/create.go similarity index 77% rename from api/app/platform/database/create.go rename to api/platform/database/create.go index 684b589..e8d2da0 100644 --- a/api/app/platform/database/create.go +++ b/api/platform/database/create.go @@ -9,32 +9,17 @@ import ( // Setup the table connection, create table if not exists func Setup(db *pgxpool.Pool) error { - conn, err := db.Acquire(context.Background()) + err := setupAuthTable(db) if err != nil { return err } - err = setupAuthTable(conn) + err = setupJokesTable(db) if err != nil { return err } - conn, err = db.Acquire(context.Background()) - if err != nil { - return err - } - - err = setupJokesTable(conn) - if err != nil { - return err - } - - conn, err = db.Acquire(context.Background()) - if err != nil { - return err - } - - err = setupSubmissionTable(conn) + err = setupSubmissionTable(db) if err != nil { return err } @@ -42,12 +27,16 @@ func Setup(db *pgxpool.Pool) error { return nil } -func setupAuthTable(conn *pgxpool.Conn) error { +func setupAuthTable(db *pgxpool.Pool) error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } defer conn.Release() // Check if table exists var tableAuthExists bool - err := conn.QueryRow(context.Background(), `SELECT EXISTS ( + err = conn.QueryRow(context.Background(), `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'administrators' @@ -68,21 +57,24 @@ func setupAuthTable(conn *pgxpool.Conn) error { return err } - q, err := conn.Query(context.Background(), sql) + _, err = conn.Exec(context.Background(), sql) if err != nil { return err } - defer q.Close() } return nil } -func setupJokesTable(conn *pgxpool.Conn) error { +func setupJokesTable(db *pgxpool.Pool) error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } defer conn.Release() // Check if table exists var tableJokesExists bool - err := conn.QueryRow(context.Background(), `SELECT EXISTS ( + err = conn.QueryRow(context.Background(), `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jokesbapak2' @@ -102,22 +94,25 @@ func setupJokesTable(conn *pgxpool.Conn) error { return err } - q, err := conn.Query(context.Background(), sql) + _, err = conn.Exec(context.Background(), sql) if err != nil { return err } - defer q.Close() } return nil } -func setupSubmissionTable(conn *pgxpool.Conn) error { +func setupSubmissionTable(db *pgxpool.Pool) error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } defer conn.Release() //Check if table exists var tableSubmissionExists bool - err := conn.QueryRow(context.Background(), `SELECT EXISTS ( + err = conn.QueryRow(context.Background(), `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'submission' @@ -139,11 +134,10 @@ func setupSubmissionTable(conn *pgxpool.Conn) error { return err } - q, err := conn.Query(context.Background(), sql) + _, err = conn.Exec(context.Background(), sql) if err != nil { return err } - defer q.Close() } return nil diff --git a/api/app/platform/database/placeholder.sql b/api/platform/database/placeholder.sql similarity index 100% rename from api/app/platform/database/placeholder.sql rename to api/platform/database/placeholder.sql diff --git a/api/app/routes/dependencies.go b/api/routes/dependencies.go similarity index 100% rename from api/app/routes/dependencies.go rename to api/routes/dependencies.go diff --git a/api/app/routes/health.go b/api/routes/health.go similarity index 90% rename from api/app/routes/health.go rename to api/routes/health.go index cf9e85c..3b274b3 100644 --- a/api/app/routes/health.go +++ b/api/routes/health.go @@ -1,7 +1,7 @@ package routes import ( - "jokes-bapak2-api/app/handler/health" + "jokes-bapak2-api/handler/health" "time" "github.com/gofiber/fiber/v2/middleware/cache" diff --git a/api/app/routes/joke.go b/api/routes/joke.go similarity index 94% rename from api/app/routes/joke.go rename to api/routes/joke.go index c6c0766..88d531f 100644 --- a/api/app/routes/joke.go +++ b/api/routes/joke.go @@ -1,8 +1,8 @@ package routes import ( - "jokes-bapak2-api/app/handler/joke" - "jokes-bapak2-api/app/middleware" + "jokes-bapak2-api/handler/joke" + "jokes-bapak2-api/middleware" "time" "github.com/gofiber/fiber/v2/middleware/cache" diff --git a/api/app/routes/submit.go b/api/routes/submit.go similarity index 93% rename from api/app/routes/submit.go rename to api/routes/submit.go index ce9822b..f880e8b 100644 --- a/api/app/routes/submit.go +++ b/api/routes/submit.go @@ -1,7 +1,7 @@ package routes import ( - "jokes-bapak2-api/app/handler/submit" + "jokes-bapak2-api/handler/submit" "time" "github.com/gofiber/fiber/v2" diff --git a/api/app/utils/array.go b/api/utils/array.go similarity index 100% rename from api/app/utils/array.go rename to api/utils/array.go diff --git a/api/app/utils/array_test.go b/api/utils/array_test.go similarity index 93% rename from api/app/utils/array_test.go rename to api/utils/array_test.go index 8be25de..74c36af 100644 --- a/api/app/utils/array_test.go +++ b/api/utils/array_test.go @@ -1,7 +1,7 @@ package utils_test import ( - "jokes-bapak2-api/app/utils" + "jokes-bapak2-api/utils" "testing" ) diff --git a/api/app/utils/date.go b/api/utils/date.go similarity index 100% rename from api/app/utils/date.go rename to api/utils/date.go diff --git a/api/app/utils/date_test.go b/api/utils/date_test.go similarity index 96% rename from api/app/utils/date_test.go rename to api/utils/date_test.go index a3c6c27..de59927 100644 --- a/api/app/utils/date_test.go +++ b/api/utils/date_test.go @@ -1,7 +1,7 @@ package utils_test import ( - "jokes-bapak2-api/app/utils" + "jokes-bapak2-api/utils" "testing" "time" ) diff --git a/api/app/utils/parse.go b/api/utils/parse.go similarity index 100% rename from api/app/utils/parse.go rename to api/utils/parse.go diff --git a/api/app/utils/parse_test.go b/api/utils/parse_test.go similarity index 96% rename from api/app/utils/parse_test.go rename to api/utils/parse_test.go index 7c59a8b..4388182 100644 --- a/api/app/utils/parse_test.go +++ b/api/utils/parse_test.go @@ -1,7 +1,7 @@ package utils_test import ( - "jokes-bapak2-api/app/utils" + "jokes-bapak2-api/utils" "strings" "testing" ) diff --git a/api/app/utils/random.go b/api/utils/random.go similarity index 100% rename from api/app/utils/random.go rename to api/utils/random.go diff --git a/api/app/utils/random_test.go b/api/utils/random_test.go similarity index 93% rename from api/app/utils/random_test.go rename to api/utils/random_test.go index 8f36d66..c891510 100644 --- a/api/app/utils/random_test.go +++ b/api/utils/random_test.go @@ -1,7 +1,7 @@ package utils_test import ( - "jokes-bapak2-api/app/utils" + "jokes-bapak2-api/utils" "testing" ) diff --git a/api/app/utils/request.go b/api/utils/request.go similarity index 100% rename from api/app/utils/request.go rename to api/utils/request.go diff --git a/api/app/utils/request_test.go b/api/utils/request_test.go similarity index 94% rename from api/app/utils/request_test.go rename to api/utils/request_test.go index d47bed7..95750bb 100644 --- a/api/app/utils/request_test.go +++ b/api/utils/request_test.go @@ -1,7 +1,7 @@ package utils_test import ( - "jokes-bapak2-api/app/utils" + "jokes-bapak2-api/utils" "net/http" "testing" ) From 483a042ccb16b00179e89433e5177953335968bc Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 1 Nov 2021 19:32:33 +0700 Subject: [PATCH 14/21] refactor: configuring context and moving logic to its designated directory --- api/core/administrator/id.go | 4 +- api/core/administrator/verify.go | 2 +- api/core/joke/getter.go | 6 +- api/core/joke/setter.go | 6 +- api/core/schema/err.go | 4 + .../schema.go => core/schema/submit.go} | 6 +- api/core/submit/getter.go | 116 ++++++++++++++++++ api/core/submit/setter.go | 41 +++++++ api/core/validator/joke.go | 11 +- api/core/validator/submit.go | 38 ++++++ api/handler/joke/joke_add.go | 2 +- api/handler/joke/joke_delete.go | 2 +- api/handler/submit/submit_add.go | 75 ++--------- api/handler/submit/submit_get.go | 84 +------------ api/main.go | 10 +- api/platform/database/create.go | 32 ++--- 16 files changed, 254 insertions(+), 185 deletions(-) rename api/{handler/submit/schema.go => core/schema/submit.go} (91%) create mode 100644 api/core/submit/getter.go create mode 100644 api/core/validator/submit.go diff --git a/api/core/administrator/id.go b/api/core/administrator/id.go index 3eb83fb..dcee051 100644 --- a/api/core/administrator/id.go +++ b/api/core/administrator/id.go @@ -25,7 +25,7 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { return 0, err } - r, err := c.Query(context.Background(), sql, args...) + r, err := c.Query(ctx, sql, args...) if err != nil { return 0, err } @@ -41,7 +41,7 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { } var id int - err = c.QueryRow(context.Background(), sql, args...).Scan(&id) + err = c.QueryRow(ctx, sql, args...).Scan(&id) if err != nil { return 0, err } diff --git a/api/core/administrator/verify.go b/api/core/administrator/verify.go index 9f6e93d..bf60026 100644 --- a/api/core/administrator/verify.go +++ b/api/core/administrator/verify.go @@ -29,7 +29,7 @@ func CheckKeyExists(db *pgxpool.Pool, ctx context.Context, key string) (string, } var token string - err = conn.QueryRow(context.Background(), sql, args...).Scan(&token) + err = conn.QueryRow(ctx, sql, args...).Scan(&token) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return "", nil diff --git a/api/core/joke/getter.go b/api/core/joke/getter.go index 5d23316..c396225 100644 --- a/api/core/joke/getter.go +++ b/api/core/joke/getter.go @@ -25,7 +25,7 @@ func GetAllJSONJokes(db *pgxpool.Pool, ctx context.Context) ([]byte, error) { defer conn.Release() var jokes []schema.Joke - results, err := conn.Query(context.Background(), "SELECT \"id\",\"link\" FROM \"jokesbapak2\" ORDER BY \"id\"") + results, err := conn.Query(ctx, "SELECT \"id\",\"link\" FROM \"jokesbapak2\" ORDER BY \"id\"") if err != nil { return nil, err } @@ -52,7 +52,7 @@ func GetRandomJokeFromDB(db *pgxpool.Pool, ctx context.Context) (string, error) } var link string - err = conn.QueryRow(context.Background(), "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) + err = conn.QueryRow(ctx, "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) if err != nil { return "", err } @@ -172,7 +172,7 @@ func CheckJokeExists(db *pgxpool.Pool, ctx context.Context, id string) (bool, er } var jokeID int - err = conn.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + err = conn.QueryRow(ctx, sql, args...).Scan(&jokeID) if err != nil && err != pgx.ErrNoRows { return false, err } diff --git a/api/core/joke/setter.go b/api/core/joke/setter.go index 48bbd4b..217a348 100644 --- a/api/core/joke/setter.go +++ b/api/core/joke/setter.go @@ -73,7 +73,7 @@ func InsertJokeIntoDB(db *pgxpool.Pool, ctx context.Context, joke schema.Joke) e return err } - r, err := conn.Query(context.Background(), sql, args...) + r, err := conn.Query(ctx, sql, args...) if err != nil { return err } @@ -97,7 +97,7 @@ func DeleteSingleJoke(db *pgxpool.Pool, ctx context.Context, id int) error { return err } - r, err := conn.Query(context.Background(), sql, args...) + r, err := conn.Query(ctx, sql, args...) if err != nil { return err } @@ -123,7 +123,7 @@ func UpdateJoke(db *pgxpool.Pool, ctx context.Context, link, creator string) err return err } - r, err := conn.Query(context.Background(), sql, args...) + r, err := conn.Query(ctx, sql, args...) if err != nil { return err } diff --git a/api/core/schema/err.go b/api/core/schema/err.go index 41cc86c..055b60a 100644 --- a/api/core/schema/err.go +++ b/api/core/schema/err.go @@ -4,3 +4,7 @@ import "errors" var ErrNotFound = errors.New("record not found") var ErrEmpty = errors.New("record is empty") + +type Error struct { + Error string `json:"error"` +} diff --git a/api/handler/submit/schema.go b/api/core/schema/submit.go similarity index 91% rename from api/handler/submit/schema.go rename to api/core/schema/submit.go index 1a8584b..16b2466 100644 --- a/api/handler/submit/schema.go +++ b/api/core/schema/submit.go @@ -1,4 +1,4 @@ -package submit +package schema type Submission struct { ID int `json:"id,omitempty" db:"id"` @@ -22,7 +22,3 @@ type ResponseSubmission struct { Submission Submission `json:"submission,omitempty"` AuthorPage string `json:"author_page,omitempty"` } - -type Error struct { - Error string `json:"error"` -} diff --git a/api/core/submit/getter.go b/api/core/submit/getter.go new file mode 100644 index 0000000..3345c21 --- /dev/null +++ b/api/core/submit/getter.go @@ -0,0 +1,116 @@ +package submit + +import ( + "bytes" + "context" + "jokes-bapak2-api/core/schema" + "net/url" + "strconv" + + "github.com/aldy505/bob" + "github.com/georgysavva/scany/pgxscan" + "github.com/jackc/pgx/v4/pgxpool" +) + +func GetSubmittedItems(db *pgxpool.Pool, ctx context.Context, queries schema.SubmissionQuery) ([]schema.Submission, error) { + var err error + var limit int + var offset int + var approved bool + + if queries.Limit != "" { + limit, err = strconv.Atoi(queries.Limit) + if err != nil { + return []schema.Submission{}, err + + } + } + if queries.Page != "" { + page, err := strconv.Atoi(queries.Page) + if err != nil { + return []schema.Submission{}, err + + } + offset = (page - 1) * 20 + } + + if queries.Approved != "" { + approved, err = strconv.ParseBool(queries.Approved) + if err != nil { + return []schema.Submission{}, err + + } + } + + var status int + + if approved { + status = 1 + } else { + status = 0 + } + + sql, args, err := GetterQueryBuilder(queries, status, limit, offset) + if err != nil { + return []schema.Submission{}, err + + } + + conn, err := db.Acquire(ctx) + if err != nil { + return []schema.Submission{}, err + } + defer conn.Release() + + var submissions []schema.Submission + results, err := conn.Query(ctx, sql, args...) + if err != nil { + return []schema.Submission{}, err + + } + defer results.Close() + + err = pgxscan.ScanAll(&submissions, results) + if err != nil { + return []schema.Submission{}, err + + } + + return submissions, nil +} + +func GetterQueryBuilder(queries schema.SubmissionQuery, status, limit, offset int) (string, []interface{}, error) { + var sql string + var args []interface{} + var sqlQuery *bytes.Buffer = &bytes.Buffer{} + sqlQuery.WriteString("SELECT * FROM submission WHERE TRUE") + + if queries.Author != "" { + sqlQuery.WriteString(" AND author = ?") + escapedAuthor, err := url.QueryUnescape(queries.Author) + if err != nil { + return sql, args, err + + } + args = append(args, escapedAuthor) + } + + if queries.Approved != "" { + sqlQuery.WriteString(" AND status = ?") + args = append(args, status) + } + + if limit > 0 { + sqlQuery.WriteString(" LIMIT " + strconv.Itoa(limit)) + } else { + sqlQuery.WriteString(" LIMIT 20") + } + + if queries.Page != "" { + sqlQuery.WriteString(" OFFSET " + strconv.Itoa(offset)) + } + + sql = bob.ReplacePlaceholder(sqlQuery.String(), bob.Dollar) + + return sql, args, nil +} diff --git a/api/core/submit/setter.go b/api/core/submit/setter.go index 9607114..74e6155 100644 --- a/api/core/submit/setter.go +++ b/api/core/submit/setter.go @@ -2,6 +2,7 @@ package submit import ( "bytes" + "context" "io" "io/ioutil" "jokes-bapak2-api/core/schema" @@ -10,8 +11,12 @@ import ( "net/http" "net/url" "os" + "time" + "github.com/Masterminds/squirrel" + "github.com/georgysavva/scany/pgxscan" "github.com/gojek/heimdall/v7/httpclient" + "github.com/jackc/pgx/v4/pgxpool" "github.com/pquerna/ffjson/ffjson" ) @@ -79,3 +84,39 @@ func UploadImage(client *httpclient.Client, image io.Reader) (string, error) { return data.Data.URL, nil } + +func SubmitJoke(db *pgxpool.Pool, ctx context.Context, s schema.Submission, link string) (schema.Submission, error) { + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + + conn, err := db.Acquire(ctx) + if err != nil { + return schema.Submission{}, err + } + defer conn.Release() + + now := time.Now().UTC().Format(time.RFC3339) + + sql, args, err := query. + Insert("submission"). + Columns("link", "created_at", "author"). + Values(link, now, s.Author). + Suffix("RETURNING id,created_at,link,author,status"). + ToSql() + if err != nil { + return schema.Submission{}, err + } + + var submission schema.Submission + result, err := conn.Query(ctx, sql, args...) + if err != nil { + return schema.Submission{}, err + } + defer result.Close() + + err = pgxscan.ScanOne(&submission, result) + if err != nil { + return schema.Submission{}, err + } + + return submission, nil +} diff --git a/api/core/validator/joke.go b/api/core/validator/joke.go index ceddab1..ab1dde1 100644 --- a/api/core/validator/joke.go +++ b/api/core/validator/joke.go @@ -10,14 +10,15 @@ import ( ) // Validate if link already exists -func LinkAlreadyExists(db *pgxpool.Pool, ctx context.Context, link string) (bool, error) { +func JokeLinkExists(db *pgxpool.Pool, ctx context.Context, link string) (bool, error) { conn, err := db.Acquire(ctx) if err != nil { return false, err } - var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) defer conn.Release() + var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + sql, args, err := query. Select("link"). From("jokesbapak2"). @@ -28,7 +29,7 @@ func LinkAlreadyExists(db *pgxpool.Pool, ctx context.Context, link string) (bool } var validateLink string - err = conn.QueryRow(context.Background(), sql, args...).Scan(&validateLink) + err = conn.QueryRow(ctx, sql, args...).Scan(&validateLink) if err != nil && err != pgx.ErrNoRows { return false, err } @@ -37,7 +38,7 @@ func LinkAlreadyExists(db *pgxpool.Pool, ctx context.Context, link string) (bool } // Check if the joke exists -func IDAlreadyExists(db *pgxpool.Pool, ctx context.Context, id int) (bool, error) { +func JokeIDExists(db *pgxpool.Pool, ctx context.Context, id int) (bool, error) { conn, err := db.Acquire(ctx) if err != nil { return false, err @@ -55,7 +56,7 @@ func IDAlreadyExists(db *pgxpool.Pool, ctx context.Context, id int) (bool, error } var jokeID int - err = conn.QueryRow(context.Background(), sql, args...).Scan(&jokeID) + err = conn.QueryRow(ctx, sql, args...).Scan(&jokeID) if err != nil && !errors.Is(err, pgx.ErrNoRows) { return false, err } diff --git a/api/core/validator/submit.go b/api/core/validator/submit.go new file mode 100644 index 0000000..c8552c9 --- /dev/null +++ b/api/core/validator/submit.go @@ -0,0 +1,38 @@ +package validator + +import ( + "context" + + "github.com/Masterminds/squirrel" + "github.com/jackc/pgx/v4" + "github.com/jackc/pgx/v4/pgxpool" +) + +func SubmitLinkExists(db *pgxpool.Pool, ctx context.Context, query squirrel.StatementBuilderType, link string) (bool, error) { + conn, err := db.Acquire(ctx) + if err != nil { + return false, err + } + defer conn.Release() + + sql, args, err := query. + Select("link"). + From("submission"). + Where(squirrel.Eq{"link": link}). + ToSql() + if err != nil { + return false, err + } + + var validateLink string + err = conn.QueryRow(ctx, sql, args...).Scan(&validateLink) + if err != nil && err != pgx.ErrNoRows { + return false, err + } + + if err == nil && validateLink != "" { + return true, nil + } + + return false, nil +} diff --git a/api/handler/joke/joke_add.go b/api/handler/joke/joke_add.go index 97af719..cfc13b7 100644 --- a/api/handler/joke/joke_add.go +++ b/api/handler/joke/joke_add.go @@ -28,7 +28,7 @@ func (d *Dependencies) AddNewJoke(c *fiber.Ctx) error { }) } - validateLink, err := validator.LinkAlreadyExists(d.DB, c.Context(), body.Link) + validateLink, err := validator.JokeLinkExists(d.DB, c.Context(), body.Link) if err != nil { return err } diff --git a/api/handler/joke/joke_delete.go b/api/handler/joke/joke_delete.go index 2937b99..689ce63 100644 --- a/api/handler/joke/joke_delete.go +++ b/api/handler/joke/joke_delete.go @@ -14,7 +14,7 @@ func (d *Dependencies) DeleteJoke(c *fiber.Ctx) error { return err } - validate, err := validator.IDAlreadyExists(d.DB, c.Context(), id) + validate, err := validator.JokeIDExists(d.DB, c.Context(), id) if err != nil { return err } diff --git a/api/handler/submit/submit_add.go b/api/handler/submit/submit_add.go index 69b8119..5525c1c 100644 --- a/api/handler/submit/submit_add.go +++ b/api/handler/submit/submit_add.go @@ -1,18 +1,13 @@ package submit import ( - "context" + "jokes-bapak2-api/core/schema" core "jokes-bapak2-api/core/submit" "jokes-bapak2-api/core/validator" "net/url" "strings" - "time" - "github.com/Masterminds/squirrel" - "github.com/georgysavva/scany/pgxscan" "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v4" - "github.com/jackc/pgx/v4/pgxpool" ) func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { @@ -22,7 +17,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { } defer conn.Release() - var body Submission + var body schema.Submission err = c.BodyParser(&body) if err != nil { return err @@ -30,21 +25,21 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { // Image and/or Link should not be empty if body.Image == "" && body.Link == "" { - return c.Status(fiber.StatusBadRequest).JSON(Error{ + return c.Status(fiber.StatusBadRequest).JSON(schema.Error{ Error: "A link or an image should be supplied in a form of multipart/form-data", }) } // Author should be supplied if body.Author == "" { - return c.Status(fiber.StatusBadRequest).JSON(Error{ + return c.Status(fiber.StatusBadRequest).JSON(schema.Error{ Error: "An author key consisting on the format \"yourname \" must be supplied", }) } else { // Validate format valid := validator.ValidateAuthor(body.Author) if !valid { - return c.Status(fiber.StatusBadRequest).JSON(Error{ + return c.Status(fiber.StatusBadRequest).JSON(schema.Error{ Error: "Please stick to the format of \"yourname \" and within 200 characters", }) } @@ -59,7 +54,7 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { return err } if !valid { - return c.Status(fiber.StatusBadRequest).JSON(Error{ + return c.Status(fiber.StatusBadRequest).JSON(schema.Error{ Error: "URL provided is not a valid image", }) } @@ -78,75 +73,27 @@ func (d *Dependencies) SubmitJoke(c *fiber.Ctx) error { } // Validate if link already exists - validateLink, err := validateIfLinkExists(d.DB, c.Context(), d.Query, link) + validateLink, err := validator.SubmitLinkExists(d.DB, c.Context(), d.Query, link) if err != nil { return err } if validateLink { - return c.Status(fiber.StatusConflict).JSON(Error{ + return c.Status(fiber.StatusConflict).JSON(schema.Error{ Error: "Given link is already on the submission queue.", }) } - now := time.Now().UTC().Format(time.RFC3339) - - sql, args, err := d.Query. - Insert("submission"). - Columns("link", "created_at", "author"). - Values(link, now, body.Author). - Suffix("RETURNING id,created_at,link,author,status"). - ToSql() - if err != nil { - return err - } - - var submission []Submission - result, err := conn.Query(c.Context(), sql, args...) - if err != nil { - return err - } - defer result.Close() - - err = pgxscan.ScanAll(&submission, result) + submission, err := core.SubmitJoke(d.DB, c.Context(), body, link) if err != nil { return err } return c. Status(fiber.StatusCreated). - JSON(ResponseSubmission{ + JSON(schema.ResponseSubmission{ Message: "Joke submitted. Please wait for a few days for admin to approve your submission.", - Submission: submission[0], + Submission: submission, AuthorPage: "/submit?author=" + url.QueryEscape(body.Author), }) } - -func validateIfLinkExists(db *pgxpool.Pool, ctx context.Context, query squirrel.StatementBuilderType, link string) (bool, error) { - conn, err := db.Acquire(ctx) - if err != nil { - return false, err - } - defer conn.Release() - - sql, args, err := query. - Select("link"). - From("submission"). - Where(squirrel.Eq{"link": link}). - ToSql() - if err != nil { - return false, err - } - - var validateLink string - err = conn.QueryRow(context.Background(), sql, args...).Scan(&validateLink) - if err != nil && err != pgx.ErrNoRows { - return false, err - } - - if err == nil && validateLink != "" { - return true, nil - } - - return false, nil -} diff --git a/api/handler/submit/submit_get.go b/api/handler/submit/submit_get.go index 8c3810c..ec04888 100644 --- a/api/handler/submit/submit_get.go +++ b/api/handler/submit/submit_get.go @@ -1,96 +1,20 @@ package submit import ( - "bytes" - "net/url" - "strconv" + "jokes-bapak2-api/core/schema" + core "jokes-bapak2-api/core/submit" - "github.com/aldy505/bob" - "github.com/georgysavva/scany/pgxscan" "github.com/gofiber/fiber/v2" ) func (d *Dependencies) GetSubmission(c *fiber.Ctx) error { - query := new(SubmissionQuery) + query := new(schema.SubmissionQuery) err := c.QueryParser(query) if err != nil { return err } - var limit int - var offset int - var approved bool - - if query.Limit != "" { - limit, err = strconv.Atoi(query.Limit) - if err != nil { - return err - } - } - if query.Page != "" { - page, err := strconv.Atoi(query.Page) - if err != nil { - return err - } - offset = (page - 1) * 20 - } - - if query.Approved != "" { - approved, err = strconv.ParseBool(query.Approved) - if err != nil { - return err - } - } - - var status int - - if approved { - status = 1 - } else { - status = 0 - } - - var sql string - var args []interface{} - - var sqlQuery *bytes.Buffer = &bytes.Buffer{} - sqlQuery.WriteString("SELECT * FROM submission WHERE TRUE") - - if query.Author != "" { - sqlQuery.WriteString(" AND author = ?") - escapedAuthor, err := url.QueryUnescape(query.Author) - if err != nil { - return err - } - args = append(args, escapedAuthor) - } - - if query.Approved != "" { - sqlQuery.WriteString(" AND status = ?") - args = append(args, status) - } - - if limit > 0 { - sqlQuery.WriteString(" LIMIT " + strconv.Itoa(limit)) - } else { - sqlQuery.WriteString(" LIMIT 20") - } - - if query.Page != "" { - sqlQuery.WriteString(" OFFSET " + strconv.Itoa(offset)) - } - - sql = bob.ReplacePlaceholder(sqlQuery.String(), bob.Dollar) - - var submissions []Submission - results, err := d.DB.Query(c.Context(), sql, args...) - if err != nil { - return err - } - - defer results.Close() - - err = pgxscan.ScanAll(&submissions, results) + submissions, err := core.GetSubmittedItems(d.DB, c.Context(), *query) if err != nil { return err } diff --git a/api/main.go b/api/main.go index 4091d04..07c430d 100644 --- a/api/main.go +++ b/api/main.go @@ -72,18 +72,20 @@ func main() { } defer sentry.Flush(2 * time.Second) - // TODO: These sequence below might be better wrapped as a Populate() function. - err = database.Setup(db) + setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4)) + defer setupCancel() + + err = database.Populate(db, setupCtx) if err != nil { sentry.CaptureException(err) log.Panicln(err) } - err = joke.SetAllJSONJoke(db, context.Background(), memory) + err = joke.SetAllJSONJoke(db, setupCtx, memory) if err != nil { log.Panicln(err) } - err = joke.SetTotalJoke(db, context.Background(), memory) + err = joke.SetTotalJoke(db, setupCtx, memory) if err != nil { log.Panicln(err) } diff --git a/api/platform/database/create.go b/api/platform/database/create.go index e8d2da0..421dd86 100644 --- a/api/platform/database/create.go +++ b/api/platform/database/create.go @@ -8,18 +8,18 @@ import ( ) // Setup the table connection, create table if not exists -func Setup(db *pgxpool.Pool) error { - err := setupAuthTable(db) +func Populate(db *pgxpool.Pool, ctx context.Context) error { + err := setupAuthTable(db, ctx) if err != nil { return err } - err = setupJokesTable(db) + err = setupJokesTable(db, ctx) if err != nil { return err } - err = setupSubmissionTable(db) + err = setupSubmissionTable(db, ctx) if err != nil { return err } @@ -27,8 +27,8 @@ func Setup(db *pgxpool.Pool) error { return nil } -func setupAuthTable(db *pgxpool.Pool) error { - conn, err := db.Acquire(context.Background()) +func setupAuthTable(db *pgxpool.Pool, ctx context.Context) error { + conn, err := db.Acquire(ctx) if err != nil { return err } @@ -36,7 +36,7 @@ func setupAuthTable(db *pgxpool.Pool) error { // Check if table exists var tableAuthExists bool - err = conn.QueryRow(context.Background(), `SELECT EXISTS ( + err = conn.QueryRow(ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'administrators' @@ -57,7 +57,7 @@ func setupAuthTable(db *pgxpool.Pool) error { return err } - _, err = conn.Exec(context.Background(), sql) + _, err = conn.Exec(ctx, sql) if err != nil { return err } @@ -65,8 +65,8 @@ func setupAuthTable(db *pgxpool.Pool) error { return nil } -func setupJokesTable(db *pgxpool.Pool) error { - conn, err := db.Acquire(context.Background()) +func setupJokesTable(db *pgxpool.Pool, ctx context.Context) error { + conn, err := db.Acquire(ctx) if err != nil { return err } @@ -74,7 +74,7 @@ func setupJokesTable(db *pgxpool.Pool) error { // Check if table exists var tableJokesExists bool - err = conn.QueryRow(context.Background(), `SELECT EXISTS ( + err = conn.QueryRow(ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'jokesbapak2' @@ -94,7 +94,7 @@ func setupJokesTable(db *pgxpool.Pool) error { return err } - _, err = conn.Exec(context.Background(), sql) + _, err = conn.Exec(ctx, sql) if err != nil { return err } @@ -103,8 +103,8 @@ func setupJokesTable(db *pgxpool.Pool) error { return nil } -func setupSubmissionTable(db *pgxpool.Pool) error { - conn, err := db.Acquire(context.Background()) +func setupSubmissionTable(db *pgxpool.Pool, ctx context.Context) error { + conn, err := db.Acquire(ctx) if err != nil { return err } @@ -112,7 +112,7 @@ func setupSubmissionTable(db *pgxpool.Pool) error { //Check if table exists var tableSubmissionExists bool - err = conn.QueryRow(context.Background(), `SELECT EXISTS ( + err = conn.QueryRow(ctx, `SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'submission' @@ -134,7 +134,7 @@ func setupSubmissionTable(db *pgxpool.Pool) error { return err } - _, err = conn.Exec(context.Background(), sql) + _, err = conn.Exec(ctx, sql) if err != nil { return err } From 8c235623c59bac67016c8aec20b9775e785bdc95 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Sun, 7 Nov 2021 00:45:18 +0700 Subject: [PATCH 15/21] test: new cases for validator and joke pkg --- api/core/joke/getter_test.go | 273 ++++++++++++++++++++++++++++-- api/core/joke/init_test.go | 111 ++++++------ api/core/joke/setter.go | 7 +- api/core/joke/setter_test.go | 222 ++++++++++++++++++++++++ api/core/validator/author_test.go | 40 +++++ api/core/validator/image_test.go | 50 ++++++ api/handler/joke/joke_update.go | 19 ++- 7 files changed, 660 insertions(+), 62 deletions(-) create mode 100644 api/core/joke/setter_test.go create mode 100644 api/core/validator/author_test.go create mode 100644 api/core/validator/image_test.go diff --git a/api/core/joke/getter_test.go b/api/core/joke/getter_test.go index 68d26a4..0de67ae 100644 --- a/api/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -2,7 +2,9 @@ package joke_test import ( "context" + "encoding/json" "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/core/schema" "testing" "github.com/jackc/pgx/v4" @@ -10,17 +12,36 @@ import ( func TestGetAllJSONJokes(t *testing.T) { defer Teardown() + conn, err := db.Acquire(context.Background()) if err != nil { t.Error("an error was thrown:", err) } + defer conn.Release() err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { - _, err := t.Exec(context.Background(), "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4), ($5, $6, $7, $8);", administratorsData...) + _, err := t.Exec( + context.Background(), + `INSERT INTO "administrators" + (id, key, token, last_used) + VALUES + ($1, $2, $3, $4), + ($5, $6, $7, $8);`, + administratorsData..., + ) if err != nil { return err } - _, err = t.Exec(context.Background(), "INSERT INTO \"jokesbapak2\" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), ($7, $8, $9);", jokesData...) + _, err = t.Exec( + context.Background(), + `INSERT INTO "jokesbapak2" + (id, link, creator) + VALUES + ($1, $2, $3), + ($4, $5, $6), + ($7, $8, $9);`, + jokesData..., + ) if err != nil { return err } @@ -39,35 +60,267 @@ func TestGetAllJSONJokes(t *testing.T) { if string(j) == "" { t.Error("j should not be empty") } +} +func TestGetRandomJokeFromDB(t *testing.T) { + defer Teardown() + conn, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + _, err := t.Exec( + context.Background(), + `INSERT INTO "administrators" + (id, key, token, last_used) + VALUES + ($1, $2, $3, $4), + ($5, $6, $7, $8);`, + administratorsData..., + ) + if err != nil { + return err + } + _, err = t.Exec( + context.Background(), + `INSERT INTO "jokesbapak2" + (id, link, creator) + VALUES + ($1, $2, $3), + ($4, $5, $6), + ($7, $8, $9);`, + jokesData..., + ) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.GetRandomJokeFromDB(db, context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j == "" { + t.Error("j should not be empty") + } } func TestGetRandomJokeFromCache(t *testing.T) { defer Teardown() - // + jokes := []schema.Joke{ + {ID: 1, Link: "link1", Creator: 1}, + {ID: 2, Link: "link2", Creator: 1}, + {ID: 3, Link: "link3", Creator: 1}, + } + data, err := json.Marshal(jokes) + if err != nil { + t.Error("an error was thrown:", err) + } + + err = memory.Set("jokes", data) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.GetRandomJokeFromCache(memory) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j == "" { + t.Error("j should not be empty") + } } -func TestCheckJokesCache(t *testing.T) { +func TestCheckJokesCache_True(t *testing.T) { defer Teardown() - // + + jokes := []schema.Joke{ + {ID: 1, Link: "link1", Creator: 1}, + {ID: 2, Link: "link2", Creator: 1}, + {ID: 3, Link: "link3", Creator: 1}, + } + data, err := json.Marshal(jokes) + if err != nil { + t.Error("an error was thrown:", err) + } + + err = memory.Set("jokes", data) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.CheckJokesCache(memory) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j == false { + t.Error("j should not be false") + } } -func TestCheckTotalJokesCache(t *testing.T) { +func TestCheckJokesCache_False(t *testing.T) { defer Teardown() - // + j, err := joke.CheckJokesCache(memory) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j == true { + t.Error("j should not be true") + } +} + +func TestCheckTotalJokesCache_True(t *testing.T) { + defer Teardown() + + err := memory.Set("total", []byte("10")) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.CheckTotalJokesCache(memory) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j == false { + t.Error("j should not be false") + } +} + +func TestCheckTotalJokesCache_False(t *testing.T) { + defer Teardown() + j, err := joke.CheckTotalJokesCache(memory) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j == true { + t.Error("j should not be true") + } } func TestGetCachedJokeByID(t *testing.T) { defer Teardown() - // + + jokes := []schema.Joke{ + {ID: 1, Link: "link1", Creator: 1}, + {ID: 2, Link: "link2", Creator: 1}, + {ID: 3, Link: "link3", Creator: 1}, + } + data, err := json.Marshal(jokes) + if err != nil { + t.Error("an error was thrown:", err) + } + + err = memory.Set("jokes", data) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.GetCachedJokeByID(memory, 1) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j != "link1" { + t.Error("j should be link1, got:", j) + } + + k, err := joke.GetCachedJokeByID(memory, 4) + if err == nil { + t.Error("an error was not thrown") + } + + if k != "" { + t.Error("k should be empty, got:", k) + } } func TestGetCachedTotalJokes(t *testing.T) { defer Teardown() - // + + err := memory.Set("total", []byte("10")) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.GetCachedTotalJokes(memory) + if err != nil { + t.Error("an error was thrown:", err) + } + + if j != 10 { + t.Error("j should be 10, got:", j) + } } func TestCheckJokeExists(t *testing.T) { defer Teardown() - // + conn, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + _, err := t.Exec( + context.Background(), + `INSERT INTO "administrators" + (id, key, token, last_used) + VALUES + ($1, $2, $3, $4), + ($5, $6, $7, $8);`, + administratorsData..., + ) + if err != nil { + return err + } + _, err = t.Exec( + context.Background(), + `INSERT INTO "jokesbapak2" + (id, link, creator) + VALUES + ($1, $2, $3), + ($4, $5, $6), + ($7, $8, $9);`, + jokesData..., + ) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Error("an error was thrown:", err) + } + + j, err := joke.CheckJokeExists(db, context.Background(), "1") + if err != nil { + t.Error("an error was thrown:", err) + } + + if j == false { + t.Error("j should not be false") + } + + k, err := joke.CheckJokeExists(db, context.Background(), "4") + if err != nil { + t.Error("an error was thrown:", err) + } + + if k == true { + t.Error("k should not be true") + } } diff --git a/api/core/joke/init_test.go b/api/core/joke/init_test.go index 20b608d..6796606 100644 --- a/api/core/joke/init_test.go +++ b/api/core/joke/init_test.go @@ -8,7 +8,6 @@ import ( "github.com/allegro/bigcache/v3" "github.com/go-redis/redis/v8" - "github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4/pgxpool" ) @@ -65,42 +64,50 @@ func Setup() { } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(tx pgx.Tx) error { - _, err := tx.Exec( - context.Background(), - `CREATE TABLE IF NOT EXISTS administrators ( - id SERIAL PRIMARY KEY, - key VARCHAR(255) NOT NULL UNIQUE, - token TEXT, - last_used VARCHAR(255) - );`, - ) - if err != nil { - return err - } - _, err = tx.Exec( - context.Background(), - `CREATE TABLE IF NOT EXISTS jokesbapak2 ( - id SERIAL PRIMARY KEY, - link TEXT UNIQUE, - creator INT NOT NULL REFERENCES "administrators" ("id") - );`, - ) - if err != nil { - return err - } - _, err = tx.Exec( - context.Background(), - `CREATE TABLE IF NOT EXISTS submission ( - id SERIAL PRIMARY KEY, - link UNIQUE NOT NULL, - created_at VARCHAR(255), - author VARCHAR(255) NOT NULL, - status SMALLINT DEFAULT 0 - );`, - ) - return err - }) + tx, err := conn.Begin(context.Background()) + if err != nil { + panic(err) + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS administrators ( + id SERIAL PRIMARY KEY, + key VARCHAR(255) NOT NULL UNIQUE, + token TEXT, + last_used VARCHAR(255) + );`, + ) + if err != nil { + panic(err) + } + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS jokesbapak2 ( + id SERIAL PRIMARY KEY, + link TEXT UNIQUE, + creator INT NOT NULL REFERENCES "administrators" ("id") + );`, + ) + if err != nil { + panic(err) + } + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS submission ( + id SERIAL PRIMARY KEY, + link UNIQUE NOT NULL, + created_at VARCHAR(255), + author VARCHAR(255) NOT NULL, + status SMALLINT DEFAULT 0 + );`, + ) + if err != nil { + panic(err) + } + + err = tx.Commit(context.Background()) if err != nil { panic(err) } @@ -123,18 +130,26 @@ func TruncateTable(db *pgxpool.Pool, cache *redis.Client, memory *bigcache.BigCa } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(tx pgx.Tx) error { - _, err := tx.Exec(context.Background(), "TRUNCATE TABLE submission;") - if err != nil { - return err - } - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") - if err != nil { - return err - } - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE administrators;") + tx, err := conn.Begin(context.Background()) + if err != nil { return err - }) + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "TRUNCATE TABLE submission;") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "TRUNCATE TABLE administrators;") + if err != nil { + return err + } + + err = tx.Commit(context.Background()) if err != nil { return err } diff --git a/api/core/joke/setter.go b/api/core/joke/setter.go index 217a348..c8ef51a 100644 --- a/api/core/joke/setter.go +++ b/api/core/joke/setter.go @@ -106,7 +106,7 @@ func DeleteSingleJoke(db *pgxpool.Pool, ctx context.Context, id int) error { return nil } -func UpdateJoke(db *pgxpool.Pool, ctx context.Context, link, creator string) error { +func UpdateJoke(db *pgxpool.Pool, ctx context.Context, newJoke schema.Joke) error { conn, err := db.Acquire(ctx) if err != nil { return err @@ -116,8 +116,9 @@ func UpdateJoke(db *pgxpool.Pool, ctx context.Context, link, creator string) err var query = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) sql, args, err := query. Update("jokesbapak2"). - Set("link", link). - Set("creator", creator). + Set("link", newJoke.Link). + Set("creator", newJoke.Creator). + Where(squirrel.Eq{"id": newJoke.ID}). ToSql() if err != nil { return err diff --git a/api/core/joke/setter_test.go b/api/core/joke/setter_test.go new file mode 100644 index 0000000..c4dc887 --- /dev/null +++ b/api/core/joke/setter_test.go @@ -0,0 +1,222 @@ +package joke_test + +import ( + "context" + "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/core/schema" + "testing" + + "github.com/jackc/pgx/v4" +) + +func TestSetAllJSONJoke(t *testing.T) { + defer Teardown() + + conn, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + _, err := t.Exec( + context.Background(), + `INSERT INTO "administrators" + (id, key, token, last_used) + VALUES + ($1, $2, $3, $4), + ($5, $6, $7, $8);`, + administratorsData..., + ) + if err != nil { + return err + } + _, err = t.Exec( + context.Background(), + `INSERT INTO "jokesbapak2" + (id, link, creator) + VALUES + ($1, $2, $3), + ($4, $5, $6), + ($7, $8, $9);`, + jokesData..., + ) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Error("an error was thrown:", err) + } + + err = joke.SetAllJSONJoke(db, context.Background(), memory) + if err != nil { + t.Error("an error was thrown:", err) + } +} + +func TestSetTotalJoke(t *testing.T) { + defer Teardown() + + conn, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + _, err := t.Exec( + context.Background(), + `INSERT INTO "administrators" + (id, key, token, last_used) + VALUES + ($1, $2, $3, $4), + ($5, $6, $7, $8);`, + administratorsData..., + ) + if err != nil { + return err + } + _, err = t.Exec( + context.Background(), + `INSERT INTO "jokesbapak2" + (id, link, creator) + VALUES + ($1, $2, $3), + ($4, $5, $6), + ($7, $8, $9);`, + jokesData..., + ) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Error("an error was thrown:", err) + } + + err = joke.SetTotalJoke(db, context.Background(), memory) + if err != nil { + t.Error("an error was thrown:", err) + } +} + +func TestInsertJokeIntoDB(t *testing.T) { + defer Teardown() + + data := schema.Joke{ + ID: 1, + Link: "link1", + Creator: 1, + } + err := joke.InsertJokeIntoDB(db, context.Background(), data) + if err != nil { + t.Error("an error was thrown:", err) + } +} + +func TestDeleteSingleJoke(t *testing.T) { + defer Teardown() + + conn, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + _, err := t.Exec( + context.Background(), + `INSERT INTO "administrators" + (id, key, token, last_used) + VALUES + ($1, $2, $3, $4), + ($5, $6, $7, $8);`, + administratorsData..., + ) + if err != nil { + return err + } + _, err = t.Exec( + context.Background(), + `INSERT INTO "jokesbapak2" + (id, link, creator) + VALUES + ($1, $2, $3), + ($4, $5, $6), + ($7, $8, $9);`, + jokesData..., + ) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Error("an error was thrown:", err) + } + + err = joke.DeleteSingleJoke(db, context.Background(), 1) + if err != nil { + t.Error("an error was thrown:", err) + } +} + +func TestUpdateJoke(t *testing.T) { + defer Teardown() + + conn, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer conn.Release() + + err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + _, err := t.Exec( + context.Background(), + `INSERT INTO "administrators" + (id, key, token, last_used) + VALUES + ($1, $2, $3, $4), + ($5, $6, $7, $8);`, + administratorsData..., + ) + if err != nil { + return err + } + _, err = t.Exec( + context.Background(), + `INSERT INTO "jokesbapak2" + (id, link, creator) + VALUES + ($1, $2, $3), + ($4, $5, $6), + ($7, $8, $9);`, + jokesData..., + ) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Error("an error was thrown:", err) + } + + newJoke := schema.Joke{ + ID: 1, + Link: "link1", + Creator: 1, + } + + err = joke.UpdateJoke(db, context.Background(), newJoke) + if err != nil { + t.Error("an error was thrown:", err) + } +} diff --git a/api/core/validator/author_test.go b/api/core/validator/author_test.go new file mode 100644 index 0000000..cc1408b --- /dev/null +++ b/api/core/validator/author_test.go @@ -0,0 +1,40 @@ +package validator_test + +import ( + "jokes-bapak2-api/core/validator" + "testing" +) + +func TestValidateAuthor_False(t *testing.T) { + v := validator.ValidateAuthor("Test Author") + if v != false { + t.Error("Expected false, got true") + } + + v = validator.ValidateAuthor("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec.") + if v != false { + t.Error("Expected false, got true") + } + + v = validator.ValidateAuthor("") + if v != false { + t.Error("Expected false, got true") + } + + v = validator.ValidateAuthor("Test ") + if v != false { + t.Error("Expected false, got true") + } +} + +func TestValidateAuthor_True(t *testing.T) { + v := validator.ValidateAuthor("Test Author ") + if v != true { + t.Error("Expected true, got false") + } +} diff --git a/api/core/validator/image_test.go b/api/core/validator/image_test.go new file mode 100644 index 0000000..8bf4143 --- /dev/null +++ b/api/core/validator/image_test.go @@ -0,0 +1,50 @@ +package validator_test + +import ( + "jokes-bapak2-api/core/validator" + "testing" + + "github.com/gojek/heimdall/v7/httpclient" +) + +func TestCheckImageValidity_Error(t *testing.T) { + client := httpclient.NewClient() + b, err := validator.CheckImageValidity(client, "http://lorem-ipsum") + if err == nil { + t.Error("Expected error, got nil") + } + + if b { + t.Error("Expected false, got true") + } + + if err.Error() != "URL must use HTTPS protocol" { + t.Error("Expected error to be URL must use HTTPS protocol, got:", err) + } +} + +func TestCheckImageValidity_False(t *testing.T) { + client := httpclient.NewClient() + + b, err := validator.CheckImageValidity(client, "https://www.youtube.com/watch?v=yTJV6T37Reo") + if err != nil { + t.Error("Expected nil, got error") + } + + if b { + t.Error("Expected false, got true") + } +} + +func TestCheckImageValidity_True(t *testing.T) { + client := httpclient.NewClient() + + b, err := validator.CheckImageValidity(client, "https://i.ytimg.com/vi/yTJV6T37Reo/maxresdefault.jpg") + if err != nil { + t.Error("Expected nil, got error") + } + + if !b { + t.Error("Expected true, got false") + } +} diff --git a/api/handler/joke/joke_update.go b/api/handler/joke/joke_update.go index d586553..2562a89 100644 --- a/api/handler/joke/joke_update.go +++ b/api/handler/joke/joke_update.go @@ -4,6 +4,7 @@ import ( core "jokes-bapak2-api/core/joke" "jokes-bapak2-api/core/schema" "jokes-bapak2-api/core/validator" + "strconv" "github.com/gofiber/fiber/v2" ) @@ -45,7 +46,23 @@ func (d *Dependencies) UpdateJoke(c *fiber.Ctx) error { }) } - err = core.UpdateJoke(d.DB, c.Context(), body.Link, c.Locals("userID").(string)) + newID, err := strconv.Atoi(id) + if err != nil { + return err + } + + newCreator, err := strconv.Atoi(c.Locals("userID").(string)) + if err != nil { + return err + } + + updatedJoke := schema.Joke{ + Link: body.Link, + Creator: newCreator, + ID: newID, + } + + err = core.UpdateJoke(d.DB, c.Context(), updatedJoke) if err != nil { return err } From 9f593cd19b60dda915f67c514592169e6c1f49df Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 8 Nov 2021 16:58:41 +0700 Subject: [PATCH 16/21] test(wip): opening up connection with db --- api/core/administrator/id.go | 16 +++- api/core/administrator/id_test.go | 50 +++++++++++ api/core/administrator/init_test.go | 122 +++++++++++++++++++++++++ api/core/administrator/verify_test.go | 67 ++++++++++++++ api/core/joke/getter_test.go | 25 +++--- api/core/joke/init_test.go | 68 +++++++++----- api/core/joke/setter_test.go | 10 +-- api/core/submit/getter.go | 5 +- api/core/submit/getter_test.go | 64 ++++++++++++++ api/core/submit/init_test.go | 123 ++++++++++++++++++++++++++ api/core/submit/setter_test.go | 21 +++++ 11 files changed, 529 insertions(+), 42 deletions(-) create mode 100644 api/core/administrator/id_test.go create mode 100644 api/core/administrator/init_test.go create mode 100644 api/core/administrator/verify_test.go create mode 100644 api/core/submit/getter_test.go create mode 100644 api/core/submit/init_test.go create mode 100644 api/core/submit/setter_test.go diff --git a/api/core/administrator/id.go b/api/core/administrator/id.go index dcee051..35d9e54 100644 --- a/api/core/administrator/id.go +++ b/api/core/administrator/id.go @@ -17,6 +17,12 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { } defer c.Release() + tx, err := c.Begin(ctx) + if err != nil { + return 0, err + } + defer tx.Rollback(ctx) + sql, args, err := query. Update("administrators"). Set("last_used", time.Now().UTC().Format(time.RFC3339)). @@ -25,11 +31,10 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { return 0, err } - r, err := c.Query(ctx, sql, args...) + _, err = tx.Exec(ctx, sql, args...) if err != nil { return 0, err } - defer r.Close() sql, args, err = query. Select("id"). @@ -41,7 +46,12 @@ func GetUserID(db *pgxpool.Pool, ctx context.Context, key string) (int, error) { } var id int - err = c.QueryRow(ctx, sql, args...).Scan(&id) + err = tx.QueryRow(ctx, sql, args...).Scan(&id) + if err != nil { + return 0, err + } + + err = tx.Commit(ctx) if err != nil { return 0, err } diff --git a/api/core/administrator/id_test.go b/api/core/administrator/id_test.go new file mode 100644 index 0000000..865533b --- /dev/null +++ b/api/core/administrator/id_test.go @@ -0,0 +1,50 @@ +package administrator_test + +import ( + "context" + "jokes-bapak2-api/core/administrator" + "testing" +) + +func TestGetUserID_Success(t *testing.T) { + t.Cleanup(func() { Flush() }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec( + context.Background(), + `INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)`, + administratorsData..., + ) + if err != nil { + t.Error("an error was thrown:", err) + } + + id, err := administrator.GetUserID(db, context.Background(), "very secure") + if err != nil { + t.Error("an error was thrown:", err) + } + + if id != 1 { + t.Error("id is not correct, want: 1, got:", id) + } +} + +func TestGetUserID_Failed(t *testing.T) { + t.Cleanup(func() { Flush() }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + id, err := administrator.GetUserID(db, context.Background(), "very secure") + if err == nil { + t.Error("an error was expected, got:", id) + } +} diff --git a/api/core/administrator/init_test.go b/api/core/administrator/init_test.go new file mode 100644 index 0000000..9f656b1 --- /dev/null +++ b/api/core/administrator/init_test.go @@ -0,0 +1,122 @@ +package administrator_test + +import ( + "context" + "os" + "testing" + "time" + + "github.com/jackc/pgx/v4/pgxpool" +) + +var db *pgxpool.Pool + +var administratorsData = []interface{}{ + 1, "very secure", "not the real one", time.Now().Format(time.RFC3339), +} + +func TestMain(m *testing.M) { + defer Teardown() + Setup() + + os.Exit(m.Run()) +} + +func Setup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + panic(err) + } + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + panic(err) + } + + conn, err := db.Acquire(context.Background()) + if err != nil { + panic(err) + } + defer conn.Release() + + tx, err := conn.Begin(context.Background()) + if err != nil { + panic(err) + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + panic(err) + } + + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS administrators ( + id SERIAL PRIMARY KEY, + key VARCHAR(255) NOT NULL UNIQUE, + token TEXT, + last_used VARCHAR(255) + )`, + ) + if err != nil { + panic(err) + } + + err = tx.Commit(context.Background()) + if err != nil { + panic(err) + } +} + +func Teardown() (err error) { + tx, err := db.Begin(context.Background()) + if err != nil { + return err + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + return err + } + + err = tx.Commit(context.Background()) + if err != nil { + return err + } + + db.Close() + return +} + +func Flush() error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } + defer conn.Release() + + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators;") + if err != nil { + return err + } + + return nil +} diff --git a/api/core/administrator/verify_test.go b/api/core/administrator/verify_test.go new file mode 100644 index 0000000..aab8821 --- /dev/null +++ b/api/core/administrator/verify_test.go @@ -0,0 +1,67 @@ +package administrator_test + +import ( + "context" + "jokes-bapak2-api/core/administrator" + "testing" +) + +func TestCheckKeyExists_Success(t *testing.T) { + t.Cleanup(func() { + Flush() + }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec( + context.Background(), + "INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)", + administratorsData..., + ) + if err != nil { + t.Error("an error was thrown:", err) + } + + key, err := administrator.CheckKeyExists(db, context.Background(), "very secure") + if err != nil { + t.Error("an error was thrown:", err) + } + + if key != "not the real one" { + t.Error("key isn't not the real one, got:", key) + } +} + +func TestCheckKeyExists_Failing(t *testing.T) { + t.Cleanup(func() { + Flush() + }) + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec( + context.Background(), + "INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)", + administratorsData..., + ) + if err != nil { + t.Error("an error was thrown:", err) + } + + key, err := administrator.CheckKeyExists(db, context.Background(), "others") + if err != nil { + t.Error("an error was thrown:", err) + } + + if key != "" { + t.Error("key is not empty, got:", key) + } +} diff --git a/api/core/joke/getter_test.go b/api/core/joke/getter_test.go index 0de67ae..07b31a1 100644 --- a/api/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -11,7 +11,7 @@ import ( ) func TestGetAllJSONJokes(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -63,7 +63,8 @@ func TestGetAllJSONJokes(t *testing.T) { } func TestGetRandomJokeFromDB(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + conn, err := db.Acquire(context.Background()) if err != nil { t.Error("an error was thrown:", err) @@ -114,7 +115,8 @@ func TestGetRandomJokeFromDB(t *testing.T) { } func TestGetRandomJokeFromCache(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, {ID: 2, Link: "link2", Creator: 1}, @@ -141,7 +143,7 @@ func TestGetRandomJokeFromCache(t *testing.T) { } func TestCheckJokesCache_True(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, @@ -169,7 +171,8 @@ func TestCheckJokesCache_True(t *testing.T) { } func TestCheckJokesCache_False(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + j, err := joke.CheckJokesCache(memory) if err != nil { t.Error("an error was thrown:", err) @@ -181,7 +184,7 @@ func TestCheckJokesCache_False(t *testing.T) { } func TestCheckTotalJokesCache_True(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) err := memory.Set("total", []byte("10")) if err != nil { @@ -199,7 +202,8 @@ func TestCheckTotalJokesCache_True(t *testing.T) { } func TestCheckTotalJokesCache_False(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + j, err := joke.CheckTotalJokesCache(memory) if err != nil { t.Error("an error was thrown:", err) @@ -211,7 +215,7 @@ func TestCheckTotalJokesCache_False(t *testing.T) { } func TestGetCachedJokeByID(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, @@ -248,7 +252,7 @@ func TestGetCachedJokeByID(t *testing.T) { } func TestGetCachedTotalJokes(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) err := memory.Set("total", []byte("10")) if err != nil { @@ -266,7 +270,8 @@ func TestGetCachedTotalJokes(t *testing.T) { } func TestCheckJokeExists(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) + conn, err := db.Acquire(context.Background()) if err != nil { t.Error("an error was thrown:", err) diff --git a/api/core/joke/init_test.go b/api/core/joke/init_test.go index 6796606..fbcfd64 100644 --- a/api/core/joke/init_test.go +++ b/api/core/joke/init_test.go @@ -20,17 +20,13 @@ var jokesData = []interface{}{ 2, "https://via.placeholder.com/300/07f/fff.png", 1, 3, "https://via.placeholder.com/300/08f/fff.png", 1, } -var submissionData = []interface{}{ - 1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, - 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1, -} var administratorsData = []interface{}{ 1, "very secure", "not the real one", time.Now().Format(time.RFC3339), 2, "test", "$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106", "", } func TestMain(m *testing.M) { - Setup() defer Teardown() + Setup() os.Exit(m.Run()) } @@ -70,6 +66,20 @@ func Setup() { } defer tx.Rollback(context.Background()) + // Dropping all table first + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + panic(err) + } + _, err = tx.Exec( context.Background(), `CREATE TABLE IF NOT EXISTS administrators ( @@ -85,7 +95,7 @@ func Setup() { _, err = tx.Exec( context.Background(), `CREATE TABLE IF NOT EXISTS jokesbapak2 ( - id SERIAL PRIMARY KEY, + id SERIAL PRIMARY KEY, link TEXT UNIQUE, creator INT NOT NULL REFERENCES "administrators" ("id") );`, @@ -97,7 +107,7 @@ func Setup() { context.Background(), `CREATE TABLE IF NOT EXISTS submission ( id SERIAL PRIMARY KEY, - link UNIQUE NOT NULL, + link VARCHAR(255) UNIQUE NOT NULL, created_at VARCHAR(255), author VARCHAR(255) NOT NULL, status SMALLINT DEFAULT 0 @@ -114,7 +124,32 @@ func Setup() { } func Teardown() (err error) { + tx, err := db.Begin(context.Background()) + if err != nil { + return err + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + return err + } + + err = tx.Commit(context.Background()) + if err != nil { + return err + } + db.Close() + err = cache.Close() if err != nil { return @@ -123,33 +158,22 @@ func Teardown() (err error) { return } -func TruncateTable(db *pgxpool.Pool, cache *redis.Client, memory *bigcache.BigCache) error { +func Flush() error { conn, err := db.Acquire(context.Background()) if err != nil { return err } defer conn.Release() - tx, err := conn.Begin(context.Background()) + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission;") if err != nil { return err } - defer tx.Rollback(context.Background()) - - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE submission;") + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") if err != nil { return err } - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") - if err != nil { - return err - } - _, err = tx.Exec(context.Background(), "TRUNCATE TABLE administrators;") - if err != nil { - return err - } - - err = tx.Commit(context.Background()) + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators;") if err != nil { return err } diff --git a/api/core/joke/setter_test.go b/api/core/joke/setter_test.go index c4dc887..e1b0e62 100644 --- a/api/core/joke/setter_test.go +++ b/api/core/joke/setter_test.go @@ -10,7 +10,7 @@ import ( ) func TestSetAllJSONJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -58,7 +58,7 @@ func TestSetAllJSONJoke(t *testing.T) { } func TestSetTotalJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -106,7 +106,7 @@ func TestSetTotalJoke(t *testing.T) { } func TestInsertJokeIntoDB(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) data := schema.Joke{ ID: 1, @@ -120,7 +120,7 @@ func TestInsertJokeIntoDB(t *testing.T) { } func TestDeleteSingleJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { @@ -168,7 +168,7 @@ func TestDeleteSingleJoke(t *testing.T) { } func TestUpdateJoke(t *testing.T) { - defer Teardown() + t.Cleanup(func() { Flush() }) conn, err := db.Acquire(context.Background()) if err != nil { diff --git a/api/core/submit/getter.go b/api/core/submit/getter.go index 3345c21..6ed653f 100644 --- a/api/core/submit/getter.go +++ b/api/core/submit/getter.go @@ -1,11 +1,11 @@ package submit import ( - "bytes" "context" "jokes-bapak2-api/core/schema" "net/url" "strconv" + "strings" "github.com/aldy505/bob" "github.com/georgysavva/scany/pgxscan" @@ -82,7 +82,8 @@ func GetSubmittedItems(db *pgxpool.Pool, ctx context.Context, queries schema.Sub func GetterQueryBuilder(queries schema.SubmissionQuery, status, limit, offset int) (string, []interface{}, error) { var sql string var args []interface{} - var sqlQuery *bytes.Buffer = &bytes.Buffer{} + var sqlQuery strings.Builder + sqlQuery.WriteString("SELECT * FROM submission WHERE TRUE") if queries.Author != "" { diff --git a/api/core/submit/getter_test.go b/api/core/submit/getter_test.go new file mode 100644 index 0000000..d033fd8 --- /dev/null +++ b/api/core/submit/getter_test.go @@ -0,0 +1,64 @@ +package submit_test + +import ( + "context" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/core/submit" + "testing" +) + +func TestGetSubmittedItems(t *testing.T) { + defer Flush() + + c, err := db.Acquire(context.Background()) + if err != nil { + t.Error("an error was thrown:", err) + } + defer c.Release() + + _, err = c.Exec(context.Background(), "INSERT INTO submission (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10)", submissionData...) + if err != nil { + t.Error("an error was thrown:", err) + } + + items, err := submit.GetSubmittedItems(db, context.Background(), schema.SubmissionQuery{}) + if err != nil { + t.Error("an error was thrown:", err) + } + + if len(items) != 2 { + t.Error("expected 2 items, got", len(items)) + } +} + +func TestGetterQueryBuilder(t *testing.T) { + s, _, err := submit.GetterQueryBuilder(schema.SubmissionQuery{}, 0, 0, 0) + if err != nil { + t.Error("an error was thrown:", err) + } + + if s != "SELECT * FROM submission WHERE TRUE LIMIT 20" { + t.Error("expected query to be", "SELECT * FROM submission WHERE TRUE LIMIT 20", "got", s) + } + + s, i, err := submit.GetterQueryBuilder(schema.SubmissionQuery{ + Author: "Test ", + Approved: "true", + Page: "2", + }, 2, 15, 10) + if err != nil { + t.Error("an error was thrown:", err) + } + + if s != "SELECT * FROM submission WHERE TRUE AND author = $1 AND status = $2 LIMIT 15 OFFSET 10" { + t.Error("expected query to be", "SELECT * FROM submission WHERE TRUE AND author = $1 AND status = $2 LIMIT 15 OFFSET 15", "got:", s) + } + + if i[0].(string) != "Test " { + t.Error("expected first arg to be Test , got:", i[0].(string)) + } + + if i[1].(int) != 1 { + t.Error("expected second arg to be 1, got:", i[1].(int)) + } +} diff --git a/api/core/submit/init_test.go b/api/core/submit/init_test.go new file mode 100644 index 0000000..d92d5f1 --- /dev/null +++ b/api/core/submit/init_test.go @@ -0,0 +1,123 @@ +package submit_test + +import ( + "context" + "os" + "testing" + + "github.com/jackc/pgx/v4/pgxpool" +) + +var db *pgxpool.Pool + +var submissionData = []interface{}{ + 1, "https://via.placeholder.com/300/01f/fff.png", "2021-08-03T18:20:38Z", "Test ", 0, + 2, "https://via.placeholder.com/300/02f/fff.png", "2021-08-04T18:20:38Z", "Test ", 1, +} + +func TestMain(m *testing.M) { + defer Teardown() + Setup() + + os.Exit(m.Run()) +} + +func Setup() { + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) + if err != nil { + panic(err) + } + + db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err != nil { + panic(err) + } + + conn, err := db.Acquire(context.Background()) + if err != nil { + panic(err) + } + defer conn.Release() + + tx, err := conn.Begin(context.Background()) + if err != nil { + panic(err) + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + panic(err) + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + panic(err) + } + + _, err = tx.Exec( + context.Background(), + `CREATE TABLE IF NOT EXISTS submission ( + id SERIAL PRIMARY KEY, + link VARCHAR(255) UNIQUE NOT NULL, + created_at VARCHAR(255), + author VARCHAR(255) NOT NULL, + status SMALLINT DEFAULT 0 + );`, + ) + if err != nil { + panic(err) + } + + err = tx.Commit(context.Background()) + if err != nil { + panic(err) + } +} + +func Teardown() (err error) { + tx, err := db.Begin(context.Background()) + if err != nil { + return err + } + defer tx.Rollback(context.Background()) + + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + if err != nil { + return err + } + _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + if err != nil { + return err + } + + err = tx.Commit(context.Background()) + if err != nil { + return err + } + + db.Close() + return +} + +func Flush() error { + conn, err := db.Acquire(context.Background()) + if err != nil { + return err + } + defer conn.Release() + + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission") + if err != nil { + return err + } + + return nil +} diff --git a/api/core/submit/setter_test.go b/api/core/submit/setter_test.go new file mode 100644 index 0000000..31c59bb --- /dev/null +++ b/api/core/submit/setter_test.go @@ -0,0 +1,21 @@ +package submit_test + +import ( + "context" + "jokes-bapak2-api/core/schema" + "jokes-bapak2-api/core/submit" + "testing" +) + +func TestSubmitJoke(t *testing.T) { + defer Flush() + + s, err := submit.SubmitJoke(db, context.Background(), schema.Submission{Author: "Test "}, "https://example.net/img.png") + if err != nil { + t.Error("an error was thrown:", err) + } + + if s.Link != "https://example.net/img.png" { + t.Error("link is not correct, got:", s.Link) + } +} From a308c0a7a2d2c56f752d51e705abffdbd62e1eff Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 8 Nov 2021 17:01:43 +0700 Subject: [PATCH 17/21] style: nitpicking on semicolons --- api/core/administrator/init_test.go | 2 +- api/core/joke/init_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/core/administrator/init_test.go b/api/core/administrator/init_test.go index 9f656b1..1814fdc 100644 --- a/api/core/administrator/init_test.go +++ b/api/core/administrator/init_test.go @@ -113,7 +113,7 @@ func Flush() error { } defer conn.Release() - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators;") + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators") if err != nil { return err } diff --git a/api/core/joke/init_test.go b/api/core/joke/init_test.go index fbcfd64..edf24c0 100644 --- a/api/core/joke/init_test.go +++ b/api/core/joke/init_test.go @@ -165,15 +165,15 @@ func Flush() error { } defer conn.Release() - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission;") + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission") if err != nil { return err } - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2;") + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2") if err != nil { return err } - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators;") + _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators") if err != nil { return err } From 05120c23ab8627dbadd96783f35b7be07dfda5c9 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 8 Nov 2021 19:39:29 +0700 Subject: [PATCH 18/21] test: added more tests --- api/core/administrator/id_test.go | 21 ++++-- api/core/administrator/init_test.go | 66 +++++++++-------- api/core/administrator/verify_test.go | 27 ++++--- api/core/joke/getter.go | 1 + api/core/joke/getter_test.go | 78 ++++++++++--------- api/core/joke/init_test.go | 87 +++++++++++----------- api/core/joke/setter_test.go | 84 ++++++++++++--------- api/core/submit/getter.go | 2 - api/core/submit/getter_test.go | 12 ++- api/core/submit/init_test.go | 103 ++++++++++++++------------ api/core/submit/setter_test.go | 6 +- api/platform/database/placeholder.sql | 2 - 12 files changed, 270 insertions(+), 219 deletions(-) diff --git a/api/core/administrator/id_test.go b/api/core/administrator/id_test.go index 865533b..dbd74e4 100644 --- a/api/core/administrator/id_test.go +++ b/api/core/administrator/id_test.go @@ -4,19 +4,23 @@ import ( "context" "jokes-bapak2-api/core/administrator" "testing" + "time" ) func TestGetUserID_Success(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - c, err := db.Acquire(context.Background()) + c, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer c.Release() _, err = c.Exec( - context.Background(), + ctx, `INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)`, administratorsData..., ) @@ -24,7 +28,7 @@ func TestGetUserID_Success(t *testing.T) { t.Error("an error was thrown:", err) } - id, err := administrator.GetUserID(db, context.Background(), "very secure") + id, err := administrator.GetUserID(db, ctx, "very secure") if err != nil { t.Error("an error was thrown:", err) } @@ -35,15 +39,18 @@ func TestGetUserID_Success(t *testing.T) { } func TestGetUserID_Failed(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() - c, err := db.Acquire(context.Background()) + defer Flush() + + c, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer c.Release() - id, err := administrator.GetUserID(db, context.Background(), "very secure") + id, err := administrator.GetUserID(db, ctx, "very secure") if err == nil { t.Error("an error was expected, got:", id) } diff --git a/api/core/administrator/init_test.go b/api/core/administrator/init_test.go index 1814fdc..21807ad 100644 --- a/api/core/administrator/init_test.go +++ b/api/core/administrator/init_test.go @@ -23,43 +23,32 @@ func TestMain(m *testing.M) { } func Setup() { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) + defer cancel() + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { panic(err) } - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + db, err = pgxpool.ConnectConfig(ctx, poolConfig) if err != nil { panic(err) } - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { panic(err) } defer conn.Release() - - tx, err := conn.Begin(context.Background()) - if err != nil { - panic(err) - } - defer tx.Rollback(context.Background()) - - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") - if err != nil { - panic(err) - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") - if err != nil { - panic(err) - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + tx, err := conn.Begin(ctx) if err != nil { panic(err) } + defer tx.Rollback(ctx) _, err = tx.Exec( - context.Background(), + ctx, `CREATE TABLE IF NOT EXISTS administrators ( id SERIAL PRIMARY KEY, key VARCHAR(255) NOT NULL UNIQUE, @@ -71,49 +60,62 @@ func Setup() { panic(err) } - err = tx.Commit(context.Background()) + err = tx.Commit(ctx) if err != nil { panic(err) } } func Teardown() (err error) { - tx, err := db.Begin(context.Background()) - if err != nil { - return err - } - defer tx.Rollback(context.Background()) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) + defer cancel() - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + defer db.Close() + + c, err := db.Acquire(ctx) if err != nil { return err } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + defer c.Release() + + tx, err := c.Begin(ctx) if err != nil { return err } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + defer tx.Rollback(ctx) + + _, err = tx.Exec(ctx, "TRUNCATE TABLE submission RESTART IDENTITY CASCADE") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "TRUNCATE TABLE jokesbapak2 RESTART IDENTITY CASCADE") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "TRUNCATE TABLE administrators RESTART IDENTITY CASCADE") if err != nil { return err } - err = tx.Commit(context.Background()) + err = tx.Commit(ctx) if err != nil { return err } - db.Close() return } func Flush() error { - conn, err := db.Acquire(context.Background()) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + conn, err := db.Acquire(ctx) if err != nil { return err } defer conn.Release() - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators") + _, err = conn.Exec(ctx, "TRUNCATE TABLE administrators RESTART IDENTITY CASCADE") if err != nil { return err } diff --git a/api/core/administrator/verify_test.go b/api/core/administrator/verify_test.go index aab8821..ba213fb 100644 --- a/api/core/administrator/verify_test.go +++ b/api/core/administrator/verify_test.go @@ -4,21 +4,23 @@ import ( "context" "jokes-bapak2-api/core/administrator" "testing" + "time" ) func TestCheckKeyExists_Success(t *testing.T) { - t.Cleanup(func() { - Flush() - }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() - c, err := db.Acquire(context.Background()) + defer Flush() + + c, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer c.Release() _, err = c.Exec( - context.Background(), + ctx, "INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)", administratorsData..., ) @@ -26,7 +28,7 @@ func TestCheckKeyExists_Success(t *testing.T) { t.Error("an error was thrown:", err) } - key, err := administrator.CheckKeyExists(db, context.Background(), "very secure") + key, err := administrator.CheckKeyExists(db, ctx, "very secure") if err != nil { t.Error("an error was thrown:", err) } @@ -37,18 +39,19 @@ func TestCheckKeyExists_Success(t *testing.T) { } func TestCheckKeyExists_Failing(t *testing.T) { - t.Cleanup(func() { - Flush() - }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - c, err := db.Acquire(context.Background()) + c, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer c.Release() _, err = c.Exec( - context.Background(), + ctx, "INSERT INTO administrators (id, key, token, last_used) VALUES ($1, $2, $3, $4)", administratorsData..., ) @@ -56,7 +59,7 @@ func TestCheckKeyExists_Failing(t *testing.T) { t.Error("an error was thrown:", err) } - key, err := administrator.CheckKeyExists(db, context.Background(), "others") + key, err := administrator.CheckKeyExists(db, ctx, "others") if err != nil { t.Error("an error was thrown:", err) } diff --git a/api/core/joke/getter.go b/api/core/joke/getter.go index c396225..4e26768 100644 --- a/api/core/joke/getter.go +++ b/api/core/joke/getter.go @@ -50,6 +50,7 @@ func GetRandomJokeFromDB(db *pgxpool.Pool, ctx context.Context) (string, error) if err != nil { return "", err } + defer conn.Release() var link string err = conn.QueryRow(ctx, "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link) diff --git a/api/core/joke/getter_test.go b/api/core/joke/getter_test.go index 07b31a1..83ca8e5 100644 --- a/api/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -6,40 +6,44 @@ import ( "jokes-bapak2-api/core/joke" "jokes-bapak2-api/core/schema" "testing" + "time" "github.com/jackc/pgx/v4" ) func TestGetAllJSONJokes(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + err = conn.BeginFunc(ctx, func(t pgx.Tx) error { _, err := t.Exec( - context.Background(), + ctx, `INSERT INTO "administrators" (id, key, token, last_used) VALUES ($1, $2, $3, $4), - ($5, $6, $7, $8);`, + ($5, $6, $7, $8)`, administratorsData..., ) if err != nil { return err } _, err = t.Exec( - context.Background(), + ctx, `INSERT INTO "jokesbapak2" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), - ($7, $8, $9);`, + ($7, $8, $9)`, jokesData..., ) if err != nil { @@ -52,7 +56,7 @@ func TestGetAllJSONJokes(t *testing.T) { t.Error("an error was thrown:", err) } - j, err := joke.GetAllJSONJokes(db, context.Background()) + j, err := joke.GetAllJSONJokes(db, ctx) if err != nil { t.Error("an error was thrown:", err) } @@ -63,35 +67,38 @@ func TestGetAllJSONJokes(t *testing.T) { } func TestGetRandomJokeFromDB(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + err = conn.BeginFunc(ctx, func(t pgx.Tx) error { _, err := t.Exec( - context.Background(), + ctx, `INSERT INTO "administrators" (id, key, token, last_used) VALUES ($1, $2, $3, $4), - ($5, $6, $7, $8);`, + ($5, $6, $7, $8)`, administratorsData..., ) if err != nil { return err } _, err = t.Exec( - context.Background(), + ctx, `INSERT INTO "jokesbapak2" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), - ($7, $8, $9);`, + ($7, $8, $9)`, jokesData..., ) if err != nil { @@ -104,7 +111,7 @@ func TestGetRandomJokeFromDB(t *testing.T) { t.Error("an error was thrown:", err) } - j, err := joke.GetRandomJokeFromDB(db, context.Background()) + j, err := joke.GetRandomJokeFromDB(db, ctx) if err != nil { t.Error("an error was thrown:", err) } @@ -114,8 +121,8 @@ func TestGetRandomJokeFromDB(t *testing.T) { } } -func TestGetRandomJokeFromCache(t *testing.T) { - t.Cleanup(func() { Flush() }) +func TestGetRandomJokeFromCache(t *testing.T) { + defer Flush() jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, @@ -143,7 +150,7 @@ func TestGetRandomJokeFromCache(t *testing.T) { } func TestCheckJokesCache_True(t *testing.T) { - t.Cleanup(func() { Flush() }) + defer Flush() jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, @@ -171,7 +178,7 @@ func TestCheckJokesCache_True(t *testing.T) { } func TestCheckJokesCache_False(t *testing.T) { - t.Cleanup(func() { Flush() }) + defer Flush() j, err := joke.CheckJokesCache(memory) if err != nil { @@ -184,7 +191,7 @@ func TestCheckJokesCache_False(t *testing.T) { } func TestCheckTotalJokesCache_True(t *testing.T) { - t.Cleanup(func() { Flush() }) + defer Flush() err := memory.Set("total", []byte("10")) if err != nil { @@ -202,7 +209,7 @@ func TestCheckTotalJokesCache_True(t *testing.T) { } func TestCheckTotalJokesCache_False(t *testing.T) { - t.Cleanup(func() { Flush() }) + defer Flush() j, err := joke.CheckTotalJokesCache(memory) if err != nil { @@ -215,7 +222,7 @@ func TestCheckTotalJokesCache_False(t *testing.T) { } func TestGetCachedJokeByID(t *testing.T) { - t.Cleanup(func() { Flush() }) + defer Flush() jokes := []schema.Joke{ {ID: 1, Link: "link1", Creator: 1}, @@ -243,7 +250,7 @@ func TestGetCachedJokeByID(t *testing.T) { k, err := joke.GetCachedJokeByID(memory, 4) if err == nil { - t.Error("an error was not thrown") + t.Error("an error was not thrown, k:", k) } if k != "" { @@ -252,7 +259,7 @@ func TestGetCachedJokeByID(t *testing.T) { } func TestGetCachedTotalJokes(t *testing.T) { - t.Cleanup(func() { Flush() }) + defer Flush() err := memory.Set("total", []byte("10")) if err != nil { @@ -270,35 +277,38 @@ func TestGetCachedTotalJokes(t *testing.T) { } func TestCheckJokeExists(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + err = conn.BeginFunc(ctx, func(t pgx.Tx) error { _, err := t.Exec( - context.Background(), + ctx, `INSERT INTO "administrators" (id, key, token, last_used) VALUES ($1, $2, $3, $4), - ($5, $6, $7, $8);`, + ($5, $6, $7, $8)`, administratorsData..., ) if err != nil { return err } _, err = t.Exec( - context.Background(), + ctx, `INSERT INTO "jokesbapak2" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), - ($7, $8, $9);`, + ($7, $8, $9)`, jokesData..., ) if err != nil { @@ -311,7 +321,7 @@ func TestCheckJokeExists(t *testing.T) { t.Error("an error was thrown:", err) } - j, err := joke.CheckJokeExists(db, context.Background(), "1") + j, err := joke.CheckJokeExists(db, ctx, "1") if err != nil { t.Error("an error was thrown:", err) } @@ -320,7 +330,7 @@ func TestCheckJokeExists(t *testing.T) { t.Error("j should not be false") } - k, err := joke.CheckJokeExists(db, context.Background(), "4") + k, err := joke.CheckJokeExists(db, ctx, "4") if err != nil { t.Error("an error was thrown:", err) } diff --git a/api/core/joke/init_test.go b/api/core/joke/init_test.go index edf24c0..b3b376e 100644 --- a/api/core/joke/init_test.go +++ b/api/core/joke/init_test.go @@ -27,17 +27,21 @@ var administratorsData = []interface{}{ func TestMain(m *testing.M) { defer Teardown() Setup() + time.Sleep(3 * time.Second) os.Exit(m.Run()) } func Setup() { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) + defer cancel() + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { panic(err) } - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + db, err = pgxpool.ConnectConfig(ctx, poolConfig) if err != nil { panic(err) } @@ -54,102 +58,98 @@ func Setup() { panic(err) } - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { panic(err) } defer conn.Release() - tx, err := conn.Begin(context.Background()) - if err != nil { - panic(err) - } - defer tx.Rollback(context.Background()) - - // Dropping all table first - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") - if err != nil { - panic(err) - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") - if err != nil { - panic(err) - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + tx, err := conn.Begin(ctx) if err != nil { panic(err) } + defer tx.Rollback(ctx) _, err = tx.Exec( - context.Background(), + ctx, `CREATE TABLE IF NOT EXISTS administrators ( id SERIAL PRIMARY KEY, key VARCHAR(255) NOT NULL UNIQUE, token TEXT, last_used VARCHAR(255) - );`, + )`, ) if err != nil { panic(err) } + _, err = tx.Exec( - context.Background(), + ctx, `CREATE TABLE IF NOT EXISTS jokesbapak2 ( - id SERIAL PRIMARY KEY, + id SERIAL PRIMARY KEY, link TEXT UNIQUE, creator INT NOT NULL REFERENCES "administrators" ("id") - );`, + )`, ) if err != nil { panic(err) } _, err = tx.Exec( - context.Background(), + ctx, `CREATE TABLE IF NOT EXISTS submission ( id SERIAL PRIMARY KEY, link VARCHAR(255) UNIQUE NOT NULL, created_at VARCHAR(255), author VARCHAR(255) NOT NULL, status SMALLINT DEFAULT 0 - );`, + )`, ) if err != nil { panic(err) } - err = tx.Commit(context.Background()) + err = tx.Commit(ctx) if err != nil { panic(err) } } func Teardown() (err error) { - tx, err := db.Begin(context.Background()) - if err != nil { - return err - } - defer tx.Rollback(context.Background()) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer db.Close() - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") + conn, err := db.Acquire(ctx) if err != nil { return err } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") + defer conn.Release() + + tx, err := conn.Begin(ctx) if err != nil { return err } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + defer tx.Rollback(ctx) + + _, err = tx.Exec(ctx, "TRUNCATE TABLE submission RESTART IDENTITY CASCADE") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "TRUNCATE TABLE jokesbapak2 RESTART IDENTITY CASCADE") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "TRUNCATE TABLE administrators RESTART IDENTITY CASCADE") if err != nil { return err } - err = tx.Commit(context.Background()) + err = tx.Commit(ctx) if err != nil { return err } - db.Close() - err = cache.Close() if err != nil { return @@ -159,26 +159,29 @@ func Teardown() (err error) { } func Flush() error { - conn, err := db.Acquire(context.Background()) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + conn, err := db.Acquire(ctx) if err != nil { return err } defer conn.Release() - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission") + _, err = conn.Exec(ctx, "TRUNCATE TABLE submission RESTART IDENTITY CASCADE") if err != nil { return err } - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE jokesbapak2") + _, err = conn.Exec(ctx, "TRUNCATE TABLE jokesbapak2 RESTART IDENTITY CASCADE") if err != nil { return err } - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE administrators") + _, err = conn.Exec(ctx, "TRUNCATE TABLE administrators RESTART IDENTITY CASCADE") if err != nil { return err } - err = cache.FlushAll(context.Background()).Err() + err = cache.FlushAll(ctx).Err() if err != nil { return err } diff --git a/api/core/joke/setter_test.go b/api/core/joke/setter_test.go index e1b0e62..f2b4949 100644 --- a/api/core/joke/setter_test.go +++ b/api/core/joke/setter_test.go @@ -5,40 +5,44 @@ import ( "jokes-bapak2-api/core/joke" "jokes-bapak2-api/core/schema" "testing" + "time" "github.com/jackc/pgx/v4" ) func TestSetAllJSONJoke(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() - conn, err := db.Acquire(context.Background()) + defer Flush() + + conn, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + err = conn.BeginFunc(ctx, func(t pgx.Tx) error { _, err := t.Exec( - context.Background(), + ctx, `INSERT INTO "administrators" (id, key, token, last_used) VALUES ($1, $2, $3, $4), - ($5, $6, $7, $8);`, + ($5, $6, $7, $8)`, administratorsData..., ) if err != nil { return err } _, err = t.Exec( - context.Background(), + ctx, `INSERT INTO "jokesbapak2" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), - ($7, $8, $9);`, + ($7, $8, $9)`, jokesData..., ) if err != nil { @@ -51,42 +55,45 @@ func TestSetAllJSONJoke(t *testing.T) { t.Error("an error was thrown:", err) } - err = joke.SetAllJSONJoke(db, context.Background(), memory) + err = joke.SetAllJSONJoke(db, ctx, memory) if err != nil { t.Error("an error was thrown:", err) } } func TestSetTotalJoke(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + err = conn.BeginFunc(ctx, func(t pgx.Tx) error { _, err := t.Exec( - context.Background(), + ctx, `INSERT INTO "administrators" (id, key, token, last_used) VALUES ($1, $2, $3, $4), - ($5, $6, $7, $8);`, + ($5, $6, $7, $8)`, administratorsData..., ) if err != nil { return err } _, err = t.Exec( - context.Background(), + ctx, `INSERT INTO "jokesbapak2" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), - ($7, $8, $9);`, + ($7, $8, $9)`, jokesData..., ) if err != nil { @@ -99,56 +106,62 @@ func TestSetTotalJoke(t *testing.T) { t.Error("an error was thrown:", err) } - err = joke.SetTotalJoke(db, context.Background(), memory) + err = joke.SetTotalJoke(db, ctx, memory) if err != nil { t.Error("an error was thrown:", err) } } func TestInsertJokeIntoDB(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() data := schema.Joke{ ID: 1, Link: "link1", Creator: 1, } - err := joke.InsertJokeIntoDB(db, context.Background(), data) + err := joke.InsertJokeIntoDB(db, ctx, data) if err != nil { t.Error("an error was thrown:", err) } } func TestDeleteSingleJoke(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + err = conn.BeginFunc(ctx, func(t pgx.Tx) error { _, err := t.Exec( - context.Background(), + ctx, `INSERT INTO "administrators" (id, key, token, last_used) VALUES ($1, $2, $3, $4), - ($5, $6, $7, $8);`, + ($5, $6, $7, $8)`, administratorsData..., ) if err != nil { return err } _, err = t.Exec( - context.Background(), + ctx, `INSERT INTO "jokesbapak2" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), - ($7, $8, $9);`, + ($7, $8, $9)`, jokesData..., ) if err != nil { @@ -161,42 +174,45 @@ func TestDeleteSingleJoke(t *testing.T) { t.Error("an error was thrown:", err) } - err = joke.DeleteSingleJoke(db, context.Background(), 1) + err = joke.DeleteSingleJoke(db, ctx, 1) if err != nil { t.Error("an error was thrown:", err) } } func TestUpdateJoke(t *testing.T) { - t.Cleanup(func() { Flush() }) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + defer Flush() - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer conn.Release() - err = conn.BeginFunc(context.Background(), func(t pgx.Tx) error { + err = conn.BeginFunc(ctx, func(t pgx.Tx) error { _, err := t.Exec( - context.Background(), + ctx, `INSERT INTO "administrators" (id, key, token, last_used) VALUES ($1, $2, $3, $4), - ($5, $6, $7, $8);`, + ($5, $6, $7, $8)`, administratorsData..., ) if err != nil { return err } _, err = t.Exec( - context.Background(), + ctx, `INSERT INTO "jokesbapak2" (id, link, creator) VALUES ($1, $2, $3), ($4, $5, $6), - ($7, $8, $9);`, + ($7, $8, $9)`, jokesData..., ) if err != nil { @@ -215,7 +231,7 @@ func TestUpdateJoke(t *testing.T) { Creator: 1, } - err = joke.UpdateJoke(db, context.Background(), newJoke) + err = joke.UpdateJoke(db, ctx, newJoke) if err != nil { t.Error("an error was thrown:", err) } diff --git a/api/core/submit/getter.go b/api/core/submit/getter.go index 6ed653f..a447a62 100644 --- a/api/core/submit/getter.go +++ b/api/core/submit/getter.go @@ -66,14 +66,12 @@ func GetSubmittedItems(db *pgxpool.Pool, ctx context.Context, queries schema.Sub results, err := conn.Query(ctx, sql, args...) if err != nil { return []schema.Submission{}, err - } defer results.Close() err = pgxscan.ScanAll(&submissions, results) if err != nil { return []schema.Submission{}, err - } return submissions, nil diff --git a/api/core/submit/getter_test.go b/api/core/submit/getter_test.go index d033fd8..c8cbd30 100644 --- a/api/core/submit/getter_test.go +++ b/api/core/submit/getter_test.go @@ -5,23 +5,27 @@ import ( "jokes-bapak2-api/core/schema" "jokes-bapak2-api/core/submit" "testing" + "time" ) func TestGetSubmittedItems(t *testing.T) { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + defer Flush() - c, err := db.Acquire(context.Background()) + c, err := db.Acquire(ctx) if err != nil { t.Error("an error was thrown:", err) } defer c.Release() - _, err = c.Exec(context.Background(), "INSERT INTO submission (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10)", submissionData...) + _, err = c.Exec(ctx, "INSERT INTO submission (id, link, created_at, author, status) VALUES ($1, $2, $3, $4, $5), ($6, $7, $8, $9, $10)", submissionData...) if err != nil { t.Error("an error was thrown:", err) } - items, err := submit.GetSubmittedItems(db, context.Background(), schema.SubmissionQuery{}) + items, err := submit.GetSubmittedItems(db, ctx, schema.SubmissionQuery{}) if err != nil { t.Error("an error was thrown:", err) } @@ -58,7 +62,7 @@ func TestGetterQueryBuilder(t *testing.T) { t.Error("expected first arg to be Test , got:", i[0].(string)) } - if i[1].(int) != 1 { + if i[1].(int) != 2 { t.Error("expected second arg to be 1, got:", i[1].(int)) } } diff --git a/api/core/submit/init_test.go b/api/core/submit/init_test.go index d92d5f1..08d5f01 100644 --- a/api/core/submit/init_test.go +++ b/api/core/submit/init_test.go @@ -4,6 +4,7 @@ import ( "context" "os" "testing" + "time" "github.com/jackc/pgx/v4/pgxpool" ) @@ -18,103 +19,107 @@ var submissionData = []interface{}{ func TestMain(m *testing.M) { defer Teardown() Setup() + time.Sleep(3 * time.Second) os.Exit(m.Run()) } func Setup() { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) + defer cancel() + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { panic(err) } - db, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + db, err = pgxpool.ConnectConfig(ctx, poolConfig) if err != nil { panic(err) } - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { panic(err) } defer conn.Release() - tx, err := conn.Begin(context.Background()) - if err != nil { - panic(err) - } - defer tx.Rollback(context.Background()) - - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") - if err != nil { - panic(err) - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") - if err != nil { - panic(err) - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") + tx, err := conn.Begin(ctx) if err != nil { panic(err) } + defer tx.Rollback(ctx) _, err = tx.Exec( - context.Background(), + ctx, `CREATE TABLE IF NOT EXISTS submission ( id SERIAL PRIMARY KEY, link VARCHAR(255) UNIQUE NOT NULL, created_at VARCHAR(255), author VARCHAR(255) NOT NULL, status SMALLINT DEFAULT 0 - );`, + )`, ) if err != nil { panic(err) } - err = tx.Commit(context.Background()) + err = tx.Commit(ctx) if err != nil { panic(err) } } func Teardown() (err error) { - tx, err := db.Begin(context.Background()) - if err != nil { - return err - } - defer tx.Rollback(context.Background()) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS submission") - if err != nil { - return err - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS jokesbapak2") - if err != nil { - return err - } - _, err = tx.Exec(context.Background(), "DROP TABLE IF EXISTS administrators") - if err != nil { - return err - } + defer db.Close() - err = tx.Commit(context.Background()) - if err != nil { - return err - } - - db.Close() - return -} - -func Flush() error { - conn, err := db.Acquire(context.Background()) + conn, err := db.Acquire(ctx) if err != nil { return err } defer conn.Release() - _, err = conn.Exec(context.Background(), "TRUNCATE TABLE submission") + tx, err := conn.Begin(ctx) + if err != nil { + return err + } + defer tx.Rollback(ctx) + + _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS submission CASCADE") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS jokesbapak2 CASCADE") + if err != nil { + return err + } + _, err = tx.Exec(ctx, "DROP TABLE IF EXISTS administrators CASCADE") + if err != nil { + return err + } + + err = tx.Commit(ctx) + if err != nil { + return err + } + + return +} + +func Flush() error { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + + conn, err := db.Acquire(ctx) + if err != nil { + return err + } + defer conn.Release() + + _, err = conn.Exec(ctx, "TRUNCATE TABLE submission RESTART IDENTITY CASCADE") if err != nil { return err } diff --git a/api/core/submit/setter_test.go b/api/core/submit/setter_test.go index 31c59bb..b491897 100644 --- a/api/core/submit/setter_test.go +++ b/api/core/submit/setter_test.go @@ -5,12 +5,16 @@ import ( "jokes-bapak2-api/core/schema" "jokes-bapak2-api/core/submit" "testing" + "time" ) func TestSubmitJoke(t *testing.T) { + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) + defer cancel() + defer Flush() - s, err := submit.SubmitJoke(db, context.Background(), schema.Submission{Author: "Test "}, "https://example.net/img.png") + s, err := submit.SubmitJoke(db, ctx, schema.Submission{Author: "Test "}, "https://example.net/img.png") if err != nil { t.Error("an error was thrown:", err) } diff --git a/api/platform/database/placeholder.sql b/api/platform/database/placeholder.sql index 04e8b94..07866bf 100644 --- a/api/platform/database/placeholder.sql +++ b/api/platform/database/placeholder.sql @@ -3,8 +3,6 @@ -- key: test -- token: password - - INSERT INTO "administrators" ("id", "key", "token", "last_used") VALUES (1, 'test', '$argon2id$v=19$m=65536,t=16,p=4$3a08c79fbf2222467a623df9a9ebf75802c65a4f9be36eb1df2f5d2052d53cb7$ce434bd38f7ba1fc1f2eb773afb8a1f7f2dad49140803ac6cb9d7256ce9826fb3b4afa1e2488da511c852fc6c33a76d5657eba6298a8e49d617b9972645b7106', ''); From 955313b45e7d4238b466320a07e1f9a8bdef4a6f Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 8 Nov 2021 22:40:17 +0700 Subject: [PATCH 19/21] ci: update github actions --- .github/workflows/api.yml | 15 +++++++-------- .github/workflows/pr.yml | 15 +++++++-------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/.github/workflows/api.yml b/.github/workflows/api.yml index b70e5db..a5a3ae9 100644 --- a/.github/workflows/api.yml +++ b/.github/workflows/api.yml @@ -54,14 +54,13 @@ jobs: - name: Build run: go build main.go - # Remove test for now - # - name: Run test & coverage - # run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... - # env: - # ENV: development - # PORT: 5000 - # DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 - # REDIS_URL: redis://@redis:6379 + - name: Run test & coverage + run: go test -v -coverprofile=coverage.out -covermode=atomic ./... + env: + ENV: development + PORT: 5000 + DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 + REDIS_URL: redis://@redis:6379 - name: Initialize CodeQL uses: github/codeql-action/init@v1 diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 91da995..36e9566 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -108,14 +108,13 @@ jobs: - name: Build run: go build main.go - # Remove test for now - # - name: Run test & coverage - # run: go test -v -race -coverprofile=coverage.out -covermode=atomic ./... - # env: - # ENV: development - # PORT: 5000 - # DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 - # REDIS_URL: redis://@redis:6379 + - name: Run test & coverage + run: go test -v -coverprofile=coverage.out -covermode=atomic ./... + env: + ENV: development + PORT: 5000 + DATABASE_URL: postgres://postgres:password@postgres:5432/jokesbapak2 + REDIS_URL: redis://@redis:6379 - name: Initialize CodeQL uses: github/codeql-action/init@v1 From 843f1d77c360de48c52d3ff51aac2002cce5afd6 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 8 Nov 2021 22:55:07 +0700 Subject: [PATCH 20/21] fix: fixing up some logic --- api/core/administrator/id_test.go | 2 +- api/core/administrator/init_test.go | 2 +- api/core/administrator/verify_test.go | 2 +- api/core/joke/getter.go | 10 +++++++--- api/core/joke/getter_test.go | 8 ++++---- api/core/joke/init_test.go | 4 ++-- api/core/joke/setter_test.go | 8 ++++---- api/core/submit/getter_test.go | 2 +- api/core/submit/init_test.go | 2 +- api/core/submit/setter_test.go | 2 +- 10 files changed, 23 insertions(+), 19 deletions(-) diff --git a/api/core/administrator/id_test.go b/api/core/administrator/id_test.go index dbd74e4..7549513 100644 --- a/api/core/administrator/id_test.go +++ b/api/core/administrator/id_test.go @@ -10,7 +10,7 @@ import ( func TestGetUserID_Success(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() c, err := db.Acquire(ctx) diff --git a/api/core/administrator/init_test.go b/api/core/administrator/init_test.go index 21807ad..a29863e 100644 --- a/api/core/administrator/init_test.go +++ b/api/core/administrator/init_test.go @@ -71,7 +71,7 @@ func Teardown() (err error) { defer cancel() defer db.Close() - + c, err := db.Acquire(ctx) if err != nil { return err diff --git a/api/core/administrator/verify_test.go b/api/core/administrator/verify_test.go index ba213fb..6e21672 100644 --- a/api/core/administrator/verify_test.go +++ b/api/core/administrator/verify_test.go @@ -41,7 +41,7 @@ func TestCheckKeyExists_Success(t *testing.T) { func TestCheckKeyExists_Failing(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() c, err := db.Acquire(ctx) diff --git a/api/core/joke/getter.go b/api/core/joke/getter.go index 4e26768..4ca97f9 100644 --- a/api/core/joke/getter.go +++ b/api/core/joke/getter.go @@ -128,7 +128,7 @@ func GetCachedJokeByID(memory *bigcache.BigCache, id int) (string, error) { var data []schema.Joke err = ffjson.Unmarshal(jokes, &data) if err != nil { - return "", nil + return "", err } // This is a simple solution, might convert it to goroutines and channels sometime soon. @@ -150,8 +150,12 @@ func GetCachedTotalJokes(memory *bigcache.BigCache) (int, error) { } return 0, err } + i, err := strconv.Atoi(string(total)) + if err != nil { + return 0, err + } - return int(total[0]), nil + return i, nil } func CheckJokeExists(db *pgxpool.Pool, ctx context.Context, id string) (bool, error) { @@ -174,7 +178,7 @@ func CheckJokeExists(db *pgxpool.Pool, ctx context.Context, id string) (bool, er var jokeID int err = conn.QueryRow(ctx, sql, args...).Scan(&jokeID) - if err != nil && err != pgx.ErrNoRows { + if err != nil && errors.Is(err, pgx.ErrNoRows) { return false, err } diff --git a/api/core/joke/getter_test.go b/api/core/joke/getter_test.go index 83ca8e5..4de0fda 100644 --- a/api/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -14,7 +14,7 @@ import ( func TestGetAllJSONJokes(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() conn, err := db.Acquire(ctx) @@ -69,7 +69,7 @@ func TestGetAllJSONJokes(t *testing.T) { func TestGetRandomJokeFromDB(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() conn, err := db.Acquire(ctx) @@ -121,7 +121,7 @@ func TestGetRandomJokeFromDB(t *testing.T) { } } -func TestGetRandomJokeFromCache(t *testing.T) { +func TestGetRandomJokeFromCache(t *testing.T) { defer Flush() jokes := []schema.Joke{ @@ -279,7 +279,7 @@ func TestGetCachedTotalJokes(t *testing.T) { func TestCheckJokeExists(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() conn, err := db.Acquire(ctx) diff --git a/api/core/joke/init_test.go b/api/core/joke/init_test.go index b3b376e..34a4e65 100644 --- a/api/core/joke/init_test.go +++ b/api/core/joke/init_test.go @@ -35,7 +35,7 @@ func TestMain(m *testing.M) { func Setup() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Minute)) defer cancel() - + poolConfig, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL")) if err != nil { panic(err) @@ -117,7 +117,7 @@ func Setup() { func Teardown() (err error) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer db.Close() conn, err := db.Acquire(ctx) diff --git a/api/core/joke/setter_test.go b/api/core/joke/setter_test.go index f2b4949..eb24245 100644 --- a/api/core/joke/setter_test.go +++ b/api/core/joke/setter_test.go @@ -64,7 +64,7 @@ func TestSetAllJSONJoke(t *testing.T) { func TestSetTotalJoke(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() conn, err := db.Acquire(ctx) @@ -115,7 +115,7 @@ func TestSetTotalJoke(t *testing.T) { func TestInsertJokeIntoDB(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() data := schema.Joke{ @@ -132,7 +132,7 @@ func TestInsertJokeIntoDB(t *testing.T) { func TestDeleteSingleJoke(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() conn, err := db.Acquire(ctx) @@ -183,7 +183,7 @@ func TestDeleteSingleJoke(t *testing.T) { func TestUpdateJoke(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() conn, err := db.Acquire(ctx) diff --git a/api/core/submit/getter_test.go b/api/core/submit/getter_test.go index c8cbd30..8a5e759 100644 --- a/api/core/submit/getter_test.go +++ b/api/core/submit/getter_test.go @@ -11,7 +11,7 @@ import ( func TestGetSubmittedItems(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() c, err := db.Acquire(ctx) diff --git a/api/core/submit/init_test.go b/api/core/submit/init_test.go index 08d5f01..2901baa 100644 --- a/api/core/submit/init_test.go +++ b/api/core/submit/init_test.go @@ -112,7 +112,7 @@ func Teardown() (err error) { func Flush() error { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + conn, err := db.Acquire(ctx) if err != nil { return err diff --git a/api/core/submit/setter_test.go b/api/core/submit/setter_test.go index b491897..524f18c 100644 --- a/api/core/submit/setter_test.go +++ b/api/core/submit/setter_test.go @@ -11,7 +11,7 @@ import ( func TestSubmitJoke(t *testing.T) { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(30*time.Second)) defer cancel() - + defer Flush() s, err := submit.SubmitJoke(db, ctx, schema.Submission{Author: "Test "}, "https://example.net/img.png") From 9d22f9696ae2892f07ecdb4750ff9ef47ddedc89 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Mon, 8 Nov 2021 23:02:01 +0700 Subject: [PATCH 21/21] test: wrong logic --- api/core/joke/getter_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/joke/getter_test.go b/api/core/joke/getter_test.go index 4de0fda..54783bb 100644 --- a/api/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -249,8 +249,8 @@ func TestGetCachedJokeByID(t *testing.T) { } k, err := joke.GetCachedJokeByID(memory, 4) - if err == nil { - t.Error("an error was not thrown, k:", k) + if err != nil { + t.Error("an error was thrown:", err) } if k != "" {