From 7404aed13977410719e5d5465ed5ff0dd90f1ad0 Mon Sep 17 00:00:00 2001 From: Bhasher Date: Wed, 10 Jan 2024 15:35:12 +0100 Subject: [PATCH] Backend for filters --- backend/db/filters.go | 76 +++++++++++++++++++++ backend/db/main.go | 11 +++ backend/db/views.go | 10 +++ backend/handlers/filters.go | 131 ++++++++++++++++++++++++++++++++++++ backend/handlers/main.go | 1 + backend/handlers/views.go | 19 +++++- backend/types/filter.go | 9 +++ 7 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 backend/db/filters.go create mode 100644 backend/handlers/filters.go create mode 100644 backend/types/filter.go diff --git a/backend/db/filters.go b/backend/db/filters.go new file mode 100644 index 0000000..f96b84b --- /dev/null +++ b/backend/db/filters.go @@ -0,0 +1,76 @@ +package db + +import "git.bhasher.com/bhasher/focus/backend/types" + +func CreateFilter(filter types.Filter) (int, error) { + res, err := db.Exec("INSERT INTO filters (view_id, tag_id, filter_type, option_id) VALUES (?, ?, ?, ?)", filter.ViewID, filter.TagID, filter.FilterType, filter.OptionID) + if err != nil { + return 0, err + } + + id, err := res.LastInsertId() + if err != nil { + return 0, err + } + + return int(id), nil +} + +func GetViewFilters(viewID int) ([]types.Filter, error) { + rows, err := db.Query("SELECT * FROM filters WHERE view_id = ?", viewID) + if err != nil { + return nil, err + } + defer rows.Close() + + var filters []types.Filter + for rows.Next() { + var f types.Filter + if err := rows.Scan(&f.ID, &f.ViewID, &f.TagID, &f.FilterType, &f.OptionID); err != nil { + return nil, err + } + + filters = append(filters, f) + } + + if err = rows.Err(); err != nil { + return nil, err + } + + return filters, nil +} + +func GetFilter(id int) (*types.Filter, error) { + rows, err := db.Query("SELECT * FROM filters WHERE id = ?", id) + if err != nil { + return nil, err + } + defer rows.Close() + + if !rows.Next() { + return nil, nil + } + + var f types.Filter + rows.Scan(&f.ID, &f.ViewID, &f.TagID, &f.FilterType, &f.OptionID) + + return &f, nil +} + +func UpdateFilter(f types.Filter) (int64, error) { + res, err := db.Exec("UPDATE filters SET view_id = ?, tag_id = ?, filter_type = ?, option_id = ? WHERE id = ?", f.ViewID, f.TagID, f.FilterType, f.OptionID, f.ID) + if err != nil { + return 0, err + } + + return res.RowsAffected() +} + +func DeleteFilter(id int) (int64, error) { + res, err := db.Exec("DELETE FROM filters WHERE id = ?", id) + if err != nil { + return 0, err + } + + return res.RowsAffected() +} \ No newline at end of file diff --git a/backend/db/main.go b/backend/db/main.go index 14dfe1c..b25f418 100644 --- a/backend/db/main.go +++ b/backend/db/main.go @@ -67,6 +67,17 @@ func InitDB(driver string, connStr string) error { FOREIGN KEY(secondary_tag_id) REFERENCES tags(id) ); + CREATE TABLE IF NOT EXISTS filters ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + view_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + filter_type INTEGER NOT NULL, + option_id INTEGER, + FOREIGN KEY(view_id) REFERENCES views(id), + FOREIGN KEY(tag_id) REFERENCES tags(id), + FOREIGN KEY(option_id) REFERENCES tagsoptions(id) + ); + INSERT INTO schema_version (version) SELECT ? WHERE NOT EXISTS (SELECT 1 FROM schema_version); `, DB_VERSION) diff --git a/backend/db/views.go b/backend/db/views.go index d400ad2..210d9da 100644 --- a/backend/db/views.go +++ b/backend/db/views.go @@ -74,3 +74,13 @@ func DeleteView(id int) (int64, error) { return res.RowsAffected() } + +func ExistView(id int) (bool, error) { + var count int + err := db.QueryRow("SELECT COUNT(*) FROM views WHERE id = ?", id).Scan(&count) + if err != nil { + return false, err + } + + return count > 0, nil +} diff --git a/backend/handlers/filters.go b/backend/handlers/filters.go new file mode 100644 index 0000000..7102ea8 --- /dev/null +++ b/backend/handlers/filters.go @@ -0,0 +1,131 @@ +package handlers + +import ( + "fmt" + "strconv" + + "git.bhasher.com/bhasher/focus/backend/db" + "git.bhasher.com/bhasher/focus/backend/types" + + "github.com/gofiber/fiber/v2" +) + +func filtersRouter(router fiber.Router) error { + router.Post("/", CreateFilter) + router.Get("/:id", GetFilter) + router.Put("/:id", UpdateFilter) + router.Delete("/:id", DeleteFilter) + + return nil +} + +func CreateFilter(c *fiber.Ctx) error { + filter := types.Filter{} + if err := c.BodyParser(&filter); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Cannot parse request", + "trace": fmt.Sprint(err), + }) + } + + exist, err := db.ExistView(filter.ViewID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Error finding view", + "trace": fmt.Sprint(err), + }) + } + if !exist { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "View not found"}) + } + + id, err := db.CreateFilter(filter) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Cannot create filter", + "trace": fmt.Sprint(err), + }) + } + + return c.Status(fiber.StatusCreated).JSON(fiber.Map{ + "id": id, + }) +} + +func GetFilter(c *fiber.Ctx) error { + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid filter ID"}) + } + + filter, err := db.GetFilter(id) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Cannot retrieve filter", + "trace": fmt.Sprint(err), + }) + } + if filter == nil { + return c.SendStatus(fiber.StatusNotFound) + } + + return c.Status(fiber.StatusOK).JSON(filter) +} + +func UpdateFilter(c *fiber.Ctx) error { + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid filter ID"}) + } + + filter := types.Filter{ID: id} + if err := c.BodyParser(&filter); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Cannot parse request", + "trace": fmt.Sprint(err), + }) + } + exist, err := db.ExistView(filter.ViewID) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Error finding view", + "trace": fmt.Sprint(err), + }) + } + if !exist { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "View not found"}) + } + + count, err := db.UpdateFilter(filter) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Cannot update filter", + "trace": fmt.Sprint(err), + }) + } + if count == 0 { + return c.SendStatus(fiber.StatusNotFound) + } + + return c.SendStatus(fiber.StatusNoContent) +} + +func DeleteFilter(c *fiber.Ctx) error { + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid filter ID"}) + } + + count, err := db.DeleteFilter(id) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Cannot delete filter", + "trace": fmt.Sprint(err), + }) + } + if count == 0 { + return c.SendStatus(fiber.StatusNotFound) + } + + return c.SendStatus(fiber.StatusNoContent) +} \ No newline at end of file diff --git a/backend/handlers/main.go b/backend/handlers/main.go index 7ae9fb7..e5456b1 100644 --- a/backend/handlers/main.go +++ b/backend/handlers/main.go @@ -11,6 +11,7 @@ func v1Router(router fiber.Router) error { cardsRouter(router.Group("/cards")) tagsRouter(router.Group("/tags")) viewsRouter(router.Group("/views")) + filtersRouter(router.Group("/filters")) return nil } diff --git a/backend/handlers/views.go b/backend/handlers/views.go index ab75386..cc27b7c 100644 --- a/backend/handlers/views.go +++ b/backend/handlers/views.go @@ -14,7 +14,7 @@ func viewsRouter(router fiber.Router) error { router.Get("/:id", GetView) router.Put("/:id", UpdateView) router.Delete("/:id", DeleteView) - + router.Get("/:id/filters", GetView) return nil } @@ -108,3 +108,20 @@ func DeleteView(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusNoContent) } + +func GetViewFilters(c *fiber.Ctx) error { + id, err := strconv.Atoi(c.Params("id")) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid view ID"}) + } + + filters, err := db.GetViewFilters(id) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Cannot retrieve view filters", + "trace": fmt.Sprint(err), + }) + } + + return c.Status(fiber.StatusOK).JSON(filters) +} \ No newline at end of file diff --git a/backend/types/filter.go b/backend/types/filter.go new file mode 100644 index 0000000..7bd38a8 --- /dev/null +++ b/backend/types/filter.go @@ -0,0 +1,9 @@ +package types + +type Filter struct { + ID int `json:"id"` + ViewID int `json:"view_id"` + TagID int `json:"tag_id"` + FilterType int `json:"filter_type"` + OptionID int `json:"option_id"` +} \ No newline at end of file