fix: method complexity

This commit is contained in:
Reinaldy Rafli 2021-09-28 13:34:02 +07:00
parent fad708e08d
commit 9c497b81aa
No known key found for this signature in database
GPG Key ID: CFDB9400255D8CB6
12 changed files with 117 additions and 457 deletions

View File

@ -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.

View File

@ -1,9 +0,0 @@
package joke_test
import (
v1 "jokes-bapak2-api/app"
"github.com/gofiber/fiber/v2"
)
var app *fiber.App = v1.New()

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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 {

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -1,9 +0,0 @@
package submit_test
import (
v1 "jokes-bapak2-api/app"
"github.com/gofiber/fiber/v2"
)
var app *fiber.App = v1.New()

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}