From 60431d3e383c4bee68eaf2e44908d3041522f2a3 Mon Sep 17 00:00:00 2001 From: Reinaldy Rafli Date: Sun, 18 Jul 2021 12:28:24 +0700 Subject: [PATCH] refactor: swap package, clean up, added test --- api/README.md | 2 + api/app/v1/app.go | 44 +++++++++++++++++-- api/app/v1/core/jokes.go | 46 +++++++++++++------- api/app/v1/handler/health_test.go | 43 ++++++++++++++++++ api/app/v1/handler/joke_add.go | 6 ++- api/app/v1/handler/joke_delete.go | 14 ++++-- api/app/v1/handler/joke_get.go | 18 +++++--- api/app/v1/handler/joke_get_test.go | 12 ++--- api/app/v1/handler/joke_total.go | 28 +++++++----- api/app/v1/handler/joke_total_test.go | 45 +++++++++++++++++++ api/app/v1/handler/joke_update.go | 7 ++- api/app/v1/middleware/validation.go | 26 +++++++++++ api/app/v1/platform/cache/cache.go | 10 +++-- api/app/v1/platform/database/placeholder.sql | 21 +++++++++ api/app/v1/routes/joke.go | 6 +-- api/app/v1/utils/parse.go | 5 ++- api/app/v1/utils/parse_test.go | 7 +-- api/go.mod | 2 + api/go.sum | 4 ++ api/main.go | 34 +-------------- 20 files changed, 286 insertions(+), 94 deletions(-) create mode 100644 api/app/v1/handler/health_test.go create mode 100644 api/app/v1/handler/joke_total_test.go create mode 100644 api/app/v1/middleware/validation.go create mode 100644 api/app/v1/platform/database/placeholder.sql diff --git a/api/README.md b/api/README.md index 8e0497d..5e4b58c 100644 --- a/api/README.md +++ b/api/README.md @@ -17,6 +17,8 @@ $ go run main.go $ go build main.go ``` +There is a placeholder data ready for you to query it manually in `/app/v1/platform/database/placeholder.sql`. Have a good time developing! + ## Used packages | Name | Version | Type | diff --git a/api/app/v1/app.go b/api/app/v1/app.go index 309373c..98f29ba 100644 --- a/api/app/v1/app.go +++ b/api/app/v1/app.go @@ -6,9 +6,13 @@ import ( "jokes-bapak2-api/app/v1/platform/database" "jokes-bapak2-api/app/v1/routes" "log" + "os" + "time" + "github.com/getsentry/sentry-go" "github.com/gofiber/fiber/v2" - gocache "github.com/patrickmn/go-cache" + "github.com/gofiber/fiber/v2/middleware/cors" + "github.com/gofiber/fiber/v2/middleware/etag" ) var memory = cache.InMemory() @@ -18,23 +22,57 @@ func New() *fiber.App { app := fiber.New(fiber.Config{ DisableKeepalive: true, CaseSensitive: true, + ErrorHandler: errorHandler, }) - checkCache := core.CheckJokesCache(memory) + err := sentry.Init(sentry.ClientOptions{ + Dsn: os.Getenv("SENTRY_DSN"), + Environment: os.Getenv("ENV"), + // Enable printing of SDK debug messages. + // Useful when getting started or trying to figure something out. + Debug: true, + }) + if err != nil { + log.Fatal(err) + } + + defer sentry.Flush(2 * time.Second) + + err = database.Setup() + if err != nil { + sentry.CaptureException(err) + log.Fatal(err) + } + + checkCache, err := core.CheckJokesCache(memory) + if err != nil { + log.Fatalln(err) + } if !checkCache { jokes, err := core.GetAllJSONJokes(db) if err != nil { log.Fatalln(err) } - memory.Set("jokes", jokes, gocache.NoExpiration) + err = memory.Set("jokes", jokes) if err != nil { log.Fatalln(err) } } + app.Use(cors.New()) + app.Use(etag.New()) + routes.Health(app) routes.Joke(app) return app } + +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/v1/core/jokes.go b/api/app/v1/core/jokes.go index bb7cfa9..799aad4 100644 --- a/api/app/v1/core/jokes.go +++ b/api/app/v1/core/jokes.go @@ -2,13 +2,13 @@ package core import ( "context" - "encoding/json" "jokes-bapak2-api/app/v1/models" "math/rand" + "github.com/allegro/bigcache/v3" "github.com/georgysavva/scany/pgxscan" "github.com/jackc/pgx/v4/pgxpool" - "github.com/patrickmn/go-cache" + "github.com/pquerna/ffjson/ffjson" ) // GetAllJSONJokes fetch the database for all the jokes then output it as a JSON []byte. @@ -25,7 +25,7 @@ func GetAllJSONJokes(db *pgxpool.Pool) ([]byte, error) { return nil, err } - data, err := json.Marshal(jokes) + data, err := ffjson.Marshal(jokes) if err != nil { return nil, err } @@ -34,18 +34,22 @@ func GetAllJSONJokes(db *pgxpool.Pool) ([]byte, error) { } // GetRandomJokeFromCache returns a link string of a random joke from cache. -func GetRandomJokeFromCache(memory *cache.Cache) (string, error) { - jokes, found := memory.Get("jokes") - if !found { - return "", models.ErrNotFound +func GetRandomJokeFromCache(memory *bigcache.BigCache) (string, error) { + jokes, err := memory.Get("jokes") + if err != nil { + if err.Error() == "Entry not found" { + return "", models.ErrNotFound + } + return "", err } var data []models.Joke - err := json.Unmarshal(jokes.([]byte), &data) + err = ffjson.Unmarshal(jokes, &data) if err != nil { return "", nil } + // Return an error if the database is empty dataLength := len(data) if dataLength == 0 { return "", models.ErrEmpty @@ -58,20 +62,30 @@ func GetRandomJokeFromCache(memory *cache.Cache) (string, error) { } // CheckJokesCache checks if there is some value inside jokes cache. -func CheckJokesCache(memory *cache.Cache) bool { - _, found := memory.Get("jokes") - return found +func CheckJokesCache(memory *bigcache.BigCache) (bool, error) { + _, err := memory.Get("jokes") + if err != nil { + if err.Error() == "Entry not found" { + return false, nil + } + return false, err + } + + return true, nil } // GetCachedJokeByID returns a link string of a certain ID from cache. -func GetCachedJokeByID(memory *cache.Cache, id int) (string, error) { - jokes, found := memory.Get("jokes") - if !found { - return "", models.ErrNotFound +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 + } + return "", err } var data []models.Joke - err := json.Unmarshal(jokes.([]byte), &data) + err = ffjson.Unmarshal(jokes, &data) if err != nil { return "", nil } diff --git a/api/app/v1/handler/health_test.go b/api/app/v1/handler/health_test.go new file mode 100644 index 0000000..f6cca84 --- /dev/null +++ b/api/app/v1/handler/health_test.go @@ -0,0 +1,43 @@ +package handler_test + +import ( + "context" + "io/ioutil" + v1 "jokes-bapak2-api/app/v1" + "jokes-bapak2-api/app/v1/platform/database" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestHealth(t *testing.T) { + err := database.Setup() + if err != nil { + t.Fatal(err) + } + _, 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 { + t.Fatal(err) + } + _, 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 { + t.Fatal(err) + } + + t.Cleanup(cleanup) + + app := v1.New() + + t.Run("Health - should return 200", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/health", nil) + res, err := app.Test(req, -1) + + 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) + assert.Nilf(t, err, "health") + }) +} diff --git a/api/app/v1/handler/joke_add.go b/api/app/v1/handler/joke_add.go index 732fa00..c150ed0 100644 --- a/api/app/v1/handler/joke_add.go +++ b/api/app/v1/handler/joke_add.go @@ -7,7 +7,6 @@ import ( "jokes-bapak2-api/app/v1/models" "github.com/gofiber/fiber/v2" - "github.com/patrickmn/go-cache" ) func AddNewJoke(c *fiber.Ctx) error { @@ -32,7 +31,10 @@ func AddNewJoke(c *fiber.Ctx) error { if err != nil { return err } - memory.Set("jokes", jokes, cache.NoExpiration) + err = memory.Set("jokes", jokes) + if err != nil { + return err + } return c.Status(fiber.StatusCreated).JSON(models.ResponseJoke{ Link: body.Link, diff --git a/api/app/v1/handler/joke_delete.go b/api/app/v1/handler/joke_delete.go index d46c2ce..e3c97d0 100644 --- a/api/app/v1/handler/joke_delete.go +++ b/api/app/v1/handler/joke_delete.go @@ -2,17 +2,20 @@ package handler import ( "context" + "strconv" "jokes-bapak2-api/app/v1/core" "jokes-bapak2-api/app/v1/models" "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" - "github.com/patrickmn/go-cache" ) func DeleteJoke(c *fiber.Ctx) error { - id := c.Params("id") + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return err + } // Check if the joke exists sql, args, err := psql.Select("id").From("jokesbapak2").Where(squirrel.Eq{"id": id}).ToSql() @@ -20,7 +23,7 @@ func DeleteJoke(c *fiber.Ctx) error { return err } - var jokeID string + var jokeID int err = db.QueryRow(context.Background(), sql, args...).Scan(&jokeID) if err != nil { return err @@ -41,7 +44,10 @@ func DeleteJoke(c *fiber.Ctx) error { if err != nil { return err } - memory.Set("jokes", jokes, cache.NoExpiration) + err = memory.Set("jokes", jokes) + if err != nil { + return err + } return c.Status(fiber.StatusOK).JSON(models.ResponseJoke{ Message: "specified joke id has been deleted", diff --git a/api/app/v1/handler/joke_get.go b/api/app/v1/handler/joke_get.go index 61de98a..463b028 100644 --- a/api/app/v1/handler/joke_get.go +++ b/api/app/v1/handler/joke_get.go @@ -11,7 +11,6 @@ import ( "jokes-bapak2-api/app/v1/utils" "github.com/gofiber/fiber/v2" - "github.com/patrickmn/go-cache" ) func TodayJoke(c *fiber.Ctx) error { @@ -67,14 +66,20 @@ func TodayJoke(c *fiber.Ctx) error { } func SingleJoke(c *fiber.Ctx) error { - checkCache := core.CheckJokesCache(memory) + checkCache, err := core.CheckJokesCache(memory) + if err != nil { + return err + } if !checkCache { jokes, err := core.GetAllJSONJokes(db) if err != nil { return err } - memory.Set("jokes", jokes, cache.NoExpiration) + err = memory.Set("jokes", jokes) + if err != nil { + return err + } } link, err := core.GetRandomJokeFromCache(memory) @@ -99,14 +104,17 @@ func SingleJoke(c *fiber.Ctx) error { } func JokeByID(c *fiber.Ctx) error { - checkCache := core.CheckJokesCache(memory) + checkCache, err := core.CheckJokesCache(memory) + if err != nil { + return err + } if !checkCache { jokes, err := core.GetAllJSONJokes(db) if err != nil { return err } - memory.Set("jokes", jokes, cache.NoExpiration) + err = memory.Set("jokes", jokes) if err != nil { return err } diff --git a/api/app/v1/handler/joke_get_test.go b/api/app/v1/handler/joke_get_test.go index fa8a21b..a40e200 100644 --- a/api/app/v1/handler/joke_get_test.go +++ b/api/app/v1/handler/joke_get_test.go @@ -15,7 +15,7 @@ import ( ) var db = database.New() -var jokesData = []interface{}{1, "https://loremflickr.com/320/240", 1, 2, "https://loremflickr.com/320/240", 1, 3, "https://loremflickr.com/320/240", 1} +var jokesData = []interface{}{1, "https://picsum.photos/id/1/200/300", 1, 2, "https://picsum.photos/id/2/200/300", 1, 3, "https://picsum.photos/id/3/200/300", 1} func cleanup() { _, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"") @@ -34,7 +34,7 @@ func TestJokeGet(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = db.Query(context.Background(), "INSERT INTO \"administrators\" (key, token, last_used) VALUES ($1, $2, $3);", "very secure", "not the real one", time.Now().Format(time.RFC3339)) + _, 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 { t.Fatal(err) } @@ -48,7 +48,6 @@ func TestJokeGet(t *testing.T) { app := v1.New() t.Run("TodayJoke - should return 200", func(t *testing.T) { - t.SkipNow() req, _ := http.NewRequest("GET", "/today", nil) res, err := app.Test(req, -1) @@ -60,7 +59,6 @@ func TestJokeGet(t *testing.T) { }) t.Run("SingleJoke - should return 200", func(t *testing.T) { - t.SkipNow() req, _ := http.NewRequest("GET", "/", nil) res, err := app.Test(req, -1) @@ -72,8 +70,7 @@ func TestJokeGet(t *testing.T) { }) t.Run("JokeByID - should return 200", func(t *testing.T) { - t.SkipNow() - req, _ := http.NewRequest("GET", "/2", nil) + req, _ := http.NewRequest("GET", "/id/1", nil) res, err := app.Test(req, -1) assert.Equalf(t, false, err != nil, "joke by id") @@ -84,8 +81,7 @@ func TestJokeGet(t *testing.T) { }) t.Run("JokeByID - should return 404", func(t *testing.T) { - t.SkipNow() - req, _ := http.NewRequest("GET", "/300", nil) + req, _ := http.NewRequest("GET", "/id/300", nil) res, err := app.Test(req, -1) assert.Equalf(t, false, err != nil, "joke by id") diff --git a/api/app/v1/handler/joke_total.go b/api/app/v1/handler/joke_total.go index b95dafc..4fdbb6b 100644 --- a/api/app/v1/handler/joke_total.go +++ b/api/app/v1/handler/joke_total.go @@ -1,35 +1,43 @@ package handler import ( - "encoding/json" "jokes-bapak2-api/app/v1/core" "jokes-bapak2-api/app/v1/models" "strconv" "github.com/gofiber/fiber/v2" - "github.com/patrickmn/go-cache" + "github.com/pquerna/ffjson/ffjson" ) func TotalJokes(c *fiber.Ctx) error { - checkCache := core.CheckJokesCache(memory) + checkCache, err := core.CheckJokesCache(memory) + if err != nil { + return err + } if !checkCache { jokes, err := core.GetAllJSONJokes(db) if err != nil { return err } - memory.Set("jokes", jokes, cache.NoExpiration) + err = memory.Set("jokes", jokes) + if err != nil { + return err + } } - jokes, found := memory.Get("jokes") - if !found { - return c.Status(fiber.StatusInternalServerError).JSON(models.Error{ - Error: "no data found", - }) + jokes, err := memory.Get("jokes") + if err != nil { + if err.Error() == "Entry not found" { + return c.Status(fiber.StatusInternalServerError).JSON(models.Error{ + Error: "no data found", + }) + } + return err } var data []models.Joke - err := json.Unmarshal(jokes.([]byte), &data) + err = ffjson.Unmarshal(jokes, &data) if err != nil { return err } diff --git a/api/app/v1/handler/joke_total_test.go b/api/app/v1/handler/joke_total_test.go new file mode 100644 index 0000000..717eea6 --- /dev/null +++ b/api/app/v1/handler/joke_total_test.go @@ -0,0 +1,45 @@ +package handler_test + +import ( + "context" + "io/ioutil" + v1 "jokes-bapak2-api/app/v1" + "jokes-bapak2-api/app/v1/platform/database" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestTotalJokes(t *testing.T) { + err := database.Setup() + if err != nil { + t.Fatal(err) + } + _, 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 { + t.Fatal(err) + } + _, 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 { + t.Fatal(err) + } + + t.Cleanup(cleanup) + + app := v1.New() + + t.Run("Total - should return 200", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/total", nil) + res, err := app.Test(req, -1) + + 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) + assert.Nilf(t, err, "joke total") + assert.Equalf(t, "{\"message\":\"3\"}", string(body), "joke total") + + }) +} diff --git a/api/app/v1/handler/joke_update.go b/api/app/v1/handler/joke_update.go index 49fafec..958380b 100644 --- a/api/app/v1/handler/joke_update.go +++ b/api/app/v1/handler/joke_update.go @@ -8,7 +8,6 @@ import ( "github.com/Masterminds/squirrel" "github.com/gofiber/fiber/v2" - "github.com/patrickmn/go-cache" ) func UpdateJoke(c *fiber.Ctx) error { @@ -46,7 +45,11 @@ func UpdateJoke(c *fiber.Ctx) error { if err != nil { return err } - memory.Set("jokes", jokes, cache.NoExpiration) + + err = memory.Set("jokes", jokes) + if err != nil { + return err + } return c.Status(fiber.StatusOK).JSON(models.ResponseJoke{ Message: "specified joke id has been updated", diff --git a/api/app/v1/middleware/validation.go b/api/app/v1/middleware/validation.go new file mode 100644 index 0000000..c1f913f --- /dev/null +++ b/api/app/v1/middleware/validation.go @@ -0,0 +1,26 @@ +package middleware + +import ( + "jokes-bapak2-api/app/v1/models" + "regexp" + + "github.com/gofiber/fiber/v2" +) + +func OnlyIntegerAsID() fiber.Handler { + return func(c *fiber.Ctx) error { + regex, err := regexp.Compile(`([0-9]+)`) + if err != nil { + return err + } + + loc := regex.FindStringIndex(c.Params("id")) + if loc[1] == len(c.Params("id")) { + return c.Next() + } + + return c.Status(fiber.StatusBadRequest).JSON(models.Error{ + Error: "only numbers are allowed as ID", + }) + } +} diff --git a/api/app/v1/platform/cache/cache.go b/api/app/v1/platform/cache/cache.go index 24c0588..d2493e1 100644 --- a/api/app/v1/platform/cache/cache.go +++ b/api/app/v1/platform/cache/cache.go @@ -1,12 +1,16 @@ package cache import ( + "log" "time" - gocache "github.com/patrickmn/go-cache" + "github.com/allegro/bigcache/v3" ) -func InMemory() *gocache.Cache { - cache := gocache.New(6*time.Hour, 6*time.Hour) +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/database/placeholder.sql b/api/app/v1/platform/database/placeholder.sql new file mode 100644 index 0000000..71023e5 --- /dev/null +++ b/api/app/v1/platform/database/placeholder.sql @@ -0,0 +1,21 @@ +-- Access the data from your HTTP Request software (Postman or Insomnia) +-- with this auth: +-- 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', ''); + +-- 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 diff --git a/api/app/v1/routes/joke.go b/api/app/v1/routes/joke.go index e977168..79a90c3 100644 --- a/api/app/v1/routes/joke.go +++ b/api/app/v1/routes/joke.go @@ -15,7 +15,7 @@ func Joke(app *fiber.App) *fiber.App { app.Get("/today", handler.TodayJoke) // Joke by ID - app.Get("/id/:id", handler.JokeByID) + app.Get("/id/:id", middleware.OnlyIntegerAsID(), handler.JokeByID) // Count total jokes app.Get("/total", handler.TotalJokes) @@ -24,10 +24,10 @@ func Joke(app *fiber.App) *fiber.App { app.Put("/", middleware.RequireAuth(), handler.AddNewJoke) // Update a joke - app.Patch("/:id", middleware.RequireAuth(), handler.UpdateJoke) + app.Patch("/id/:id", middleware.RequireAuth(), middleware.OnlyIntegerAsID(), handler.UpdateJoke) // Delete a joke - app.Delete("/:id", middleware.RequireAuth(), handler.DeleteJoke) + app.Delete("/id/:id", middleware.RequireAuth(), middleware.OnlyIntegerAsID(), handler.DeleteJoke) return app } diff --git a/api/app/v1/utils/parse.go b/api/app/v1/utils/parse.go index 44a1b06..36e04da 100644 --- a/api/app/v1/utils/parse.go +++ b/api/app/v1/utils/parse.go @@ -1,8 +1,9 @@ package utils import ( - "encoding/json" "strconv" + + "github.com/pquerna/ffjson/ffjson" ) // ParseToFormBody converts a body to form data type @@ -25,7 +26,7 @@ func ParseToFormBody(body map[string]interface{}) ([]byte, error) { // ParseToJSONBody converts a body to json data type func ParseToJSONBody(body map[string]interface{}) ([]byte, error) { - b, err := json.Marshal(body) + b, err := ffjson.Marshal(body) if err != nil { return nil, err } diff --git a/api/app/v1/utils/parse_test.go b/api/app/v1/utils/parse_test.go index 9c2c2c3..1728c9b 100644 --- a/api/app/v1/utils/parse_test.go +++ b/api/app/v1/utils/parse_test.go @@ -1,6 +1,7 @@ package utils_test import ( + "strings" "testing" "jokes-bapak2-api/app/v1/utils" @@ -27,16 +28,16 @@ func TestParseToJSONBody(t *testing.T) { func TestParseToFormBody(t *testing.T) { t.Run("should be able to parse a form body", func(t *testing.T) { body := map[string]interface{}{ - "name": "Scott", "age": 32, "fat": true, + "name": "Scott", } parsed, err := utils.ParseToFormBody(body) if err != nil { t.Error(err.Error()) } - result := "name=Scott&age=32&fat=true&" - if string(parsed) != result { + result := [3]string{"age=32&", "fat=true&", "name=Scott&"} + if !strings.Contains(string(parsed), result[0]) && !strings.Contains(string(parsed), result[1]) && !strings.Contains(string(parsed), result[2]) { t.Error("parsed string is not the same as result:", string(parsed)) } }) diff --git a/api/go.mod b/api/go.mod index 174f67b..fe59e06 100644 --- a/api/go.mod +++ b/api/go.mod @@ -6,6 +6,7 @@ require ( github.com/Masterminds/squirrel v1.5.0 github.com/aldy505/bob v0.0.1 github.com/aldy505/phc-crypto v1.1.0 + github.com/allegro/bigcache/v3 v3.0.0 github.com/georgysavva/scany v0.2.9 github.com/getsentry/sentry-go v0.11.0 github.com/go-redis/redis/v8 v8.11.0 @@ -14,5 +15,6 @@ require ( github.com/jackc/pgx/v4 v4.11.0 github.com/joho/godotenv v1.3.0 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 github.com/stretchr/testify v1.5.1 ) diff --git a/api/go.sum b/api/go.sum index 07cf40e..00f11d3 100644 --- a/api/go.sum +++ b/api/go.sum @@ -24,6 +24,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/allegro/bigcache/v3 v3.0.0 h1:5Hxq+GTy8gHEeQccCZZDCfZRTydUfErdUf0iVDcMAFg= +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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -404,6 +406,8 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J 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= +github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20= +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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= diff --git a/api/main.go b/api/main.go index ab8dd3a..7c13e30 100644 --- a/api/main.go +++ b/api/main.go @@ -7,12 +7,8 @@ import ( "time" v1 "jokes-bapak2-api/app/v1" - "jokes-bapak2-api/app/v1/platform/database" - "github.com/getsentry/sentry-go" "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/favicon" "github.com/gofiber/fiber/v2/middleware/limiter" _ "github.com/joho/godotenv/autoload" @@ -20,37 +16,17 @@ import ( func main() { timeoutDefault, _ := time.ParseDuration("1m") - err := sentry.Init(sentry.ClientOptions{ - Dsn: os.Getenv("SENTRY_DSN"), - - // Enable printing of SDK debug messages. - // Useful when getting started or trying to figure something out. - Debug: true, - }) - if err != nil { - log.Fatal(err) - } - - defer sentry.Flush(2 * time.Second) - - err = database.Setup() - if err != nil { - sentry.CaptureException(err) - log.Fatal(err) - } app := fiber.New(fiber.Config{ ReadTimeout: timeoutDefault, WriteTimeout: timeoutDefault, - ErrorHandler: errorHandler, }) - app.Use(cors.New()) + app.Use(limiter.New(limiter.Config{ Max: 15, Expiration: 1 * time.Minute, LimitReached: limitHandler, })) - app.Use(etag.New()) app.Use(favicon.New(favicon.Config{ File: "./favicon.png", })) @@ -65,14 +41,6 @@ func main() { } } -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", - }) -} - func limitHandler(c *fiber.Ctx) error { return c.Status(fiber.StatusTooManyRequests).JSON(fiber.Map{ "message": "we only allow up to 15 request per minute",