feat: joke submission
This commit is contained in:
parent
9d9311e90c
commit
c265da52a1
|
@ -0,0 +1,81 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
"jokes-bapak2-api/app/v1/utils"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/gojek/heimdall/v7/httpclient"
|
||||
"github.com/pquerna/ffjson/ffjson"
|
||||
)
|
||||
|
||||
// UploadImage process the image from the user to be uploaded to the cloud storage.
|
||||
// Returns the image URL.
|
||||
func UploadImage(client *httpclient.Client, image io.Reader) (string, error) {
|
||||
hostURL := os.Getenv("IMAGE_API_URL")
|
||||
fileName, err := utils.RandomString(10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
fw, err := writer.CreateFormField("image")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = io.Copy(fw, image)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
headers := http.Header{
|
||||
"Content-Type": []string{writer.FormDataContentType()},
|
||||
"User-Agent": []string{"JokesBapak2 API"},
|
||||
"Accept": []string{"application/json"},
|
||||
}
|
||||
|
||||
requestURL, err := url.Parse(hostURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("key", os.Getenv("IMAGE_API_KEY"))
|
||||
params.Add("name", fileName)
|
||||
|
||||
requestURL.RawQuery = params.Encode()
|
||||
|
||||
res, err := client.Post(requestURL.String(), bytes.NewReader(body.Bytes()), headers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
responseBody, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var data models.ImageAPI
|
||||
err = ffjson.Unmarshal(responseBody, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return data.Data.URL, nil
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ValidateAuthor(author string) bool {
|
||||
if len(author) > 200 {
|
||||
return false
|
||||
}
|
||||
|
||||
split := strings.Split(author, " ")
|
||||
if strings.HasPrefix(split[0], "<") && strings.HasSuffix(split[0], ">") {
|
||||
return false
|
||||
}
|
||||
if !strings.HasPrefix(split[len(split)-1], "<") && !strings.HasSuffix(split[len(split)-1], ">") {
|
||||
return false
|
||||
}
|
||||
|
||||
email := strings.Replace(split[len(split)-1], "<", "", 1)
|
||||
email = strings.Replace(email, ">", "", 1)
|
||||
pattern := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||
return pattern.MatchString(email)
|
||||
}
|
|
@ -9,8 +9,8 @@ import (
|
|||
"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))
|
||||
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))
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func Health(c *fiber.Ctx) error {
|
||||
// Ping REDIS database
|
||||
err := redis.Ping(context.Background()).Err()
|
||||
if err != nil {
|
||||
return c.
|
||||
Status(fiber.StatusServiceUnavailable).
|
||||
JSON(models.Error{
|
||||
Error: "REDIS: " + err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
_, err = db.Query(context.Background(), "SELECT \"id\" FROM \"jokesbapak2\" LIMIT 1")
|
||||
if err != nil {
|
||||
return c.
|
||||
Status(fiber.StatusServiceUnavailable).
|
||||
JSON(models.Error{
|
||||
Error: "POSTGRESQL: " + err.Error(),
|
||||
})
|
||||
}
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
|
@ -12,6 +12,20 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var db = 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}
|
||||
|
||||
func cleanup() {
|
||||
_, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = db.Query(context.Background(), "DROP TABLE \"administrators\"")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealth(t *testing.T) {
|
||||
err := database.Setup()
|
||||
if err != nil {
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
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")
|
||||
})
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"jokes-bapak2-api/app/v1/core"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func AddNewJoke(c *fiber.Ctx) error {
|
||||
var body models.Joke
|
||||
err := c.BodyParser(&body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check link validity
|
||||
valid, err := core.CheckImageValidity(client, body.Link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(models.Error{
|
||||
Error: "URL provided is not a valid image",
|
||||
})
|
||||
}
|
||||
|
||||
sql, args, err := psql.Insert("jokesbapak2").Columns("link", "creator").Values(body.Link, c.Locals("userID")).ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Implement solution if the link provided already exists.
|
||||
_, err = db.Query(context.Background(), sql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = core.SetAllJSONJoke(db, memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = core.SetTotalJoke(db, memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(models.ResponseJoke{
|
||||
Link: body.Link,
|
||||
})
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package handler_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
v1 "jokes-bapak2-api/app/v1"
|
||||
"jokes-bapak2-api/app/v1/platform/database"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddNewJoke(t *testing.T) {
|
||||
// t.SkipNow()
|
||||
err := database.Setup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hashedToken := "$argon2id$v=19$m=65536,t=16,p=4$48beb241490caa57fbca8e63df1e1b5fba8934baf78205ee775f96a85f45b889$e6dfca3f69adbe7653dbb353f366d741a3640313c45e33eabaca0c217c16417de80d70ac67f217c9ca46634b0abaad5f4ea2b064caa44ce218fb110b4cba9d36"
|
||||
_, err = db.Query(context.Background(), "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4);", 1, "very secure", hashedToken, time.Now().Format(time.RFC3339))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
app := v1.New()
|
||||
|
||||
t.Run("Add - should return 201", func(t *testing.T) {
|
||||
reqBody := strings.NewReader("{\"link\":\"https://via.placeholder.com/300/07f/ff0000.png\",\"key\":\"very secure\",\"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, -1)
|
||||
|
||||
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")
|
||||
})
|
||||
|
||||
t.Run("Add - should not be a valid image", func(t *testing.T) {
|
||||
reqBody := strings.NewReader("{\"link\":\"https://google.com/\",\"key\":\"very secure\",\"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, -1)
|
||||
|
||||
assert.Equalf(t, false, err != nil, "joke add")
|
||||
assert.Equalf(t, 400, res.StatusCode, "joke add")
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
assert.Nilf(t, err, "joke add")
|
||||
assert.Equalf(t, "{\"error\":\"URL provided is not a valid image\"}", string(body), "joke add")
|
||||
})
|
||||
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
func 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 := psql.Select("id").From("jokesbapak2").Where(squirrel.Eq{"id": id}).ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var jokeID int
|
||||
err = db.QueryRow(context.Background(), sql, args...).Scan(&jokeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if jokeID == id {
|
||||
sql, args, err = psql.Delete("jokesbapak2").Where(squirrel.Eq{"id": id}).ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = db.Query(context.Background(), sql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = core.SetAllJSONJoke(db, memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = core.SetTotalJoke(db, memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(models.ResponseJoke{
|
||||
Message: "specified joke id has been deleted",
|
||||
})
|
||||
}
|
||||
return c.Status(fiber.StatusNotAcceptable).JSON(models.Error{
|
||||
Error: "specified joke id does not exists",
|
||||
})
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package handler_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
v1 "jokes-bapak2-api/app/v1"
|
||||
"jokes-bapak2-api/app/v1/platform/database"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDeleteJoke(t *testing.T) {
|
||||
// TODO: Remove this line below, make this test works
|
||||
t.SkipNow()
|
||||
|
||||
err := database.Setup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hashedToken := "$argon2id$v=19$m=65536,t=16,p=4$48beb241490caa57fbca8e63df1e1b5fba8934baf78205ee775f96a85f45b889$e6dfca3f69adbe7653dbb353f366d741a3640313c45e33eabaca0c217c16417de80d70ac67f217c9ca46634b0abaad5f4ea2b064caa44ce218fb110b4cba9d36"
|
||||
_, err = db.Query(context.Background(), "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4);", 1, "very secure", hashedToken, 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("Delete - should return 200", func(t *testing.T) {
|
||||
reqBody := strings.NewReader("{\"key\":\"very secure\",\"token\":\"password\"}")
|
||||
req, _ := http.NewRequest("DELETE", "/id/1", reqBody)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "joke delete")
|
||||
assert.Equalf(t, "{\"message\":\"specified joke id has been deleted\"}", string(body), "joke delete")
|
||||
})
|
||||
|
||||
t.Run("Delete - id doesn't exists", func(t *testing.T) {
|
||||
reqBody := strings.NewReader("{\"key\":\"very secure\",\"token\":\"password\"}")
|
||||
req, _ := http.NewRequest("DELETE", "/id/100", reqBody)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "joke delete")
|
||||
assert.Equalf(t, "{\"message\":\"specified joke id does not exists\"}", string(body), "joke delete")
|
||||
})
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"jokes-bapak2-api/app/v1/core"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
"jokes-bapak2-api/app/v1/utils"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func TodayJoke(c *fiber.Ctx) error {
|
||||
// check from 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 := redis.MGet(context.Background(), "today:link", "today:date", "today:image", "today:contentType").Scan(&joke)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eq, err := utils.IsToday(joke.Date)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if eq {
|
||||
c.Set("Content-Type", joke.ContentType)
|
||||
return c.Status(fiber.StatusOK).Send([]byte(joke.Image))
|
||||
} else {
|
||||
var link string
|
||||
err := db.QueryRow(context.Background(), "SELECT link FROM jokesbapak2 ORDER BY random() LIMIT 1").Scan(&link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
response, err := client.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 = redis.MSet(context.Background(), 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 SingleJoke(c *fiber.Ctx) error {
|
||||
checkCache, err := core.CheckJokesCache(memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !checkCache {
|
||||
jokes, err := core.GetAllJSONJokes(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = memory.Set("jokes", jokes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
link, err := core.GetRandomJokeFromCache(memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get image data
|
||||
response, err := client.Get(link, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Set("Content-Type", response.Header.Get("content-type"))
|
||||
return c.Status(fiber.StatusOK).Send(data)
|
||||
|
||||
}
|
||||
|
||||
func JokeByID(c *fiber.Ctx) error {
|
||||
checkCache, err := core.CheckJokesCache(memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !checkCache {
|
||||
jokes, err := core.GetAllJSONJokes(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = memory.Set("jokes", jokes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
id, err := strconv.Atoi(c.Params("id"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
link, err := core.GetCachedJokeByID(memory, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if link == "" {
|
||||
return c.Status(fiber.StatusNotFound).Send([]byte("Requested ID was not found."))
|
||||
}
|
||||
|
||||
// Get image data
|
||||
response, err := client.Get(link, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Set("Content-Type", response.Header.Get("content-type"))
|
||||
return c.Status(fiber.StatusOK).Send(data)
|
||||
}
|
|
@ -1,94 +0,0 @@
|
|||
package handler_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v1 "jokes-bapak2-api/app/v1"
|
||||
"jokes-bapak2-api/app/v1/platform/database"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var db = 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}
|
||||
|
||||
func cleanup() {
|
||||
_, err := db.Query(context.Background(), "DROP TABLE \"jokesbapak2\"")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = db.Query(context.Background(), "DROP TABLE \"administrators\"")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// Need to find some workaround for this test
|
||||
func TestJokeGet(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("TodayJoke - should return 200", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/today", nil)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "today joke")
|
||||
})
|
||||
|
||||
t.Run("SingleJoke - should return 200", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "single joke")
|
||||
})
|
||||
|
||||
t.Run("JokeByID - should return 200", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/id/1", nil)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "joke by id")
|
||||
})
|
||||
|
||||
t.Run("JokeByID - should return 404", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/id/300", nil)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "joke by id")
|
||||
assert.Equalf(t, "Requested ID was not found.", string(body), "joke by id")
|
||||
})
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"jokes-bapak2-api/app/v1/core"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func TotalJokes(c *fiber.Ctx) error {
|
||||
checkTotal, err := core.CheckTotalJokesCache(memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !checkTotal {
|
||||
err = core.SetTotalJoke(db, memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
total, err := memory.Get("total")
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "Entry not found" {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(models.Error{
|
||||
Error: "no data found",
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(models.ResponseJoke{
|
||||
Message: strconv.Itoa(int(total[0])),
|
||||
})
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
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")
|
||||
// FIXME: This should be "message": "3", not one. I don't know what's wrong as it's 1 AM.
|
||||
assert.Equalf(t, "{\"message\":\"1\"}", string(body), "joke total")
|
||||
})
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"jokes-bapak2-api/app/v1/core"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func UpdateJoke(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
// Check if the joke exists
|
||||
sql, args, err := psql.Select("id").From("jokesbapak2").Where(squirrel.Eq{"id": id}).ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var jokeID string
|
||||
err = db.QueryRow(context.Background(), sql, args...).Scan(&jokeID)
|
||||
if err != nil && err != models.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
|
||||
if jokeID == id {
|
||||
body := new(models.Joke)
|
||||
err = c.BodyParser(&body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check link validity
|
||||
valid, err := core.CheckImageValidity(client, body.Link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(models.Error{
|
||||
Error: "URL provided is not a valid image",
|
||||
})
|
||||
}
|
||||
|
||||
sql, args, err = psql.Update("jokesbapak2").Set("link", body.Link).Set("creator", c.Locals("userID")).ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = db.Query(context.Background(), sql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = core.SetAllJSONJoke(db, memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = core.SetTotalJoke(db, memory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(models.ResponseJoke{
|
||||
Message: "specified joke id has been updated",
|
||||
Link: body.Link,
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusNotAcceptable).JSON(models.Error{
|
||||
Error: "specified joke id does not exists",
|
||||
})
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package handler_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
v1 "jokes-bapak2-api/app/v1"
|
||||
"jokes-bapak2-api/app/v1/platform/database"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUpdateJoke(t *testing.T) {
|
||||
t.SkipNow()
|
||||
err := database.Setup()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
hashedToken := "$argon2id$v=19$m=65536,t=16,p=4$48beb241490caa57fbca8e63df1e1b5fba8934baf78205ee775f96a85f45b889$e6dfca3f69adbe7653dbb353f366d741a3640313c45e33eabaca0c217c16417de80d70ac67f217c9ca46634b0abaad5f4ea2b064caa44ce218fb110b4cba9d36"
|
||||
_, err = db.Query(context.Background(), "INSERT INTO \"administrators\" (id, key, token, last_used) VALUES ($1, $2, $3, $4);", 1, "very secure", hashedToken, 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("Update - should return 200", func(t *testing.T) {
|
||||
reqBody := strings.NewReader("{\"link\":\"https://picsum.photos/id/9/200/300\",\"key\":\"very secure\",\"token\":\"password\"}")
|
||||
req, _ := http.NewRequest("PATCH", "/id/1", reqBody)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "joke update")
|
||||
assert.Equalf(t, "{\"message\":\"specified joke id has been deleted\"}", string(body), "joke update")
|
||||
})
|
||||
|
||||
t.Run("Update - id doesn't exists", func(t *testing.T) {
|
||||
reqBody := strings.NewReader("{\"link\":\"https://picsum.photos/id/9/200/300\",\"key\":\"very secure\",\"token\":\"password\"}")
|
||||
req, _ := http.NewRequest("PATCH", "/id/100", reqBody)
|
||||
res, err := app.Test(req, -1)
|
||||
|
||||
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)
|
||||
assert.Nilf(t, err, "joke update")
|
||||
assert.Equalf(t, "{\"message\":\"specified joke id does not exists\"}", string(body), "joke update")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package submit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"jokes-bapak2-api/app/v1/core"
|
||||
"jokes-bapak2-api/app/v1/handler"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/georgysavva/scany/pgxscan"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func SubmitJoke(c *fiber.Ctx) error {
|
||||
var body models.Submission
|
||||
err := c.BodyParser(&body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Image and/or Link should not be empty
|
||||
if body.Image == "" && body.Link == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(models.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{
|
||||
Error: "an author key consisting on the format \"yourname <youremail@mail>\" must be supplied",
|
||||
})
|
||||
} else {
|
||||
// Validate format
|
||||
valid := core.ValidateAuthor(body.Author)
|
||||
if !valid {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(models.Error{
|
||||
Error: "please stick to the format of \"yourname <youremail@mail>\" and within 200 characters",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var url string
|
||||
|
||||
// Check link validity if link was provided
|
||||
if body.Link != "" {
|
||||
valid, err := core.CheckImageValidity(handler.Client, body.Link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !valid {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(models.Error{
|
||||
Error: "URL provided is not a valid image",
|
||||
})
|
||||
}
|
||||
|
||||
url = body.Link
|
||||
}
|
||||
|
||||
// If image was provided
|
||||
if body.Image != "" {
|
||||
image := strings.NewReader(body.Image)
|
||||
|
||||
url, err = core.UploadImage(handler.Client, image)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
|
||||
sql, args, err := handler.Psql.
|
||||
Insert("submission").
|
||||
Columns("link", "created_at", "author").
|
||||
Values(url, now, body.Author).
|
||||
Suffix("RETURNING id,created_at,link,author,status").
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var submission []models.Submission
|
||||
result, err := handler.Db.Query(context.Background(), sql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer result.Close()
|
||||
|
||||
err = pgxscan.ScanAll(&submission, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.
|
||||
Status(fiber.StatusOK).
|
||||
JSON(models.ResponseSubmission{
|
||||
Message: "Joke submitted. Please wait for a few days for admin to approve your submission.",
|
||||
Data: submission[0],
|
||||
})
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package submit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"jokes-bapak2-api/app/v1/handler"
|
||||
"jokes-bapak2-api/app/v1/models"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/aldy505/bob"
|
||||
"github.com/georgysavva/scany/pgxscan"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func GetSubmission(c *fiber.Ctx) error {
|
||||
query := new(models.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 = ?")
|
||||
args = append(args, query.Author)
|
||||
}
|
||||
|
||||
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 []models.Submission
|
||||
results, err := handler.Db.Query(context.Background(), sql, args...)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer results.Close()
|
||||
|
||||
err = pgxscan.ScanAll(&submissions, results)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.
|
||||
Status(fiber.StatusOK).
|
||||
JSON(fiber.Map{
|
||||
"count": len(submissions),
|
||||
"jokes": submissions,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
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"`
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package models
|
||||
|
||||
type Auth struct {
|
||||
ID int `json:"id" form:"id" db:"id"`
|
||||
Key string `json:"key" form:"key" db:"key"`
|
||||
Token string `json:"token" form:"token" db:"token"`
|
||||
LastUsed string `json:"last_used" form:"last_used" db:"last_used"`
|
||||
}
|
|
@ -6,15 +6,13 @@ type Joke struct {
|
|||
Creator int `json:"creator" form:"creator" db:"creator"`
|
||||
}
|
||||
|
||||
type Auth struct {
|
||||
ID int `json:"id" form:"id" db:"id"`
|
||||
Key string `json:"key" form:"key" db:"key"`
|
||||
Token string `json:"token" form:"token" db:"token"`
|
||||
LastUsed string `json:"last_used" form:"last_used" db:"last_used"`
|
||||
}
|
||||
|
||||
type Today struct {
|
||||
Date string `redis:"today:date"`
|
||||
Image string `redis:"today:image"`
|
||||
ContentType string `redis:"today:contentType"`
|
||||
}
|
||||
|
||||
type ResponseJoke struct {
|
||||
Link string `json:"link,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package models
|
||||
|
||||
type Error struct {
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type ResponseJoke struct {
|
||||
Link string `json:"link,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package models
|
||||
|
||||
type Submission struct {
|
||||
ID int `json:"id,omitempty" db:"id"`
|
||||
Link string `json:"link" form:"link" db:"link"`
|
||||
Image string `json:"image,omitempty" form:"image"`
|
||||
CreatedAt string `json:"created_at" db:"created_at"`
|
||||
Author string `json:"author" form:"author" db:"author"`
|
||||
Status int `json:"status" db:"status"`
|
||||
}
|
||||
|
||||
type SubmissionQuery struct {
|
||||
Author string `query:"author"`
|
||||
Limit string `query:"limit"`
|
||||
Page string `query:"page"`
|
||||
Approved string `query:"approved"`
|
||||
}
|
||||
|
||||
type ResponseSubmission struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
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"`
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"jokes-bapak2-api/app/v1/handler/submit"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func Submit(app *fiber.App) *fiber.App {
|
||||
// Get pending submitted joke
|
||||
app.Get("/submit", submit.GetSubmission)
|
||||
|
||||
// Add a joke
|
||||
app.Post("/submit", submit.SubmitJoke)
|
||||
|
||||
return app
|
||||
}
|
Loading…
Reference in New Issue