backend tag options
This commit is contained in:
parent
c69dadc4bf
commit
950963d7dc
|
@ -18,7 +18,7 @@ func GetCardTags(cardID int, projectID int) ([]types.FullCardTag, error) {
|
|||
projectID = card.ProjectID
|
||||
}
|
||||
|
||||
rows, err := db.Query(`SELECT t.id, t.title, COALESCE(ct.value, '')
|
||||
rows, err := db.Query(`SELECT t.id, t.title, t.type, COALESCE(ct.value, '')
|
||||
FROM tags t
|
||||
LEFT JOIN cardtags ct ON ct.tag_id = t.id AND ct.card_id = ?
|
||||
WHERE t.project_id = ?
|
||||
|
@ -31,7 +31,7 @@ func GetCardTags(cardID int, projectID int) ([]types.FullCardTag, error) {
|
|||
var cardtags []types.FullCardTag
|
||||
for rows.Next() {
|
||||
ct := types.FullCardTag{CardID: cardID}
|
||||
if err := rows.Scan(&ct.TagID, &ct.TagTitle, &ct.Value); err != nil {
|
||||
if err := rows.Scan(&ct.TagID, &ct.TagTitle, &ct.TagType, &ct.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cardtags = append(cardtags, ct)
|
||||
|
|
|
@ -46,6 +46,12 @@ func InitDB(driver string, connStr string) error {
|
|||
PRIMARY KEY(card_id, tag_id),
|
||||
FOREIGN KEY(card_id) REFERENCES cards(id)
|
||||
FOREIGN KEY(tag_id) REFERENCES tags(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS tagsoptions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
tag_id INTEGER,
|
||||
value TEXT,
|
||||
FOREIGN KEY(tag_id) REFERENCES tags(id)
|
||||
);
|
||||
`)
|
||||
if err != nil {
|
||||
|
|
|
@ -16,19 +16,25 @@ func CreateTag(t types.Tag) (int, error) {
|
|||
return int(id), nil
|
||||
}
|
||||
|
||||
func GetProjectTags(projectID int) ([]types.Tag, error) {
|
||||
func GetProjectTags(projectID int) ([]types.FullTag, error) {
|
||||
rows, err := db.Query("SELECT * FROM tags WHERE project_id = ?", projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var tags []types.Tag
|
||||
var tags []types.FullTag
|
||||
for rows.Next() {
|
||||
var t types.Tag
|
||||
var t types.FullTag
|
||||
if err := rows.Scan(&t.ID, &t.ProjectID, &t.Title, &t.Type); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.Options, err = GetTagOptions(t.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tags = append(tags, t)
|
||||
}
|
||||
|
||||
|
@ -81,3 +87,61 @@ func ExistTag(id int) (bool, error) {
|
|||
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func CreateTagOption(to types.TagOption) (int, error) {
|
||||
res, err := db.Exec("INSERT INTO tagsoptions (tag_id, value) VALUES (?, ?)", to.TagID, to.Value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
id, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int(id), nil
|
||||
}
|
||||
|
||||
func GetTagOptions(tagID int) ([]types.TagOption, error) {
|
||||
rows, err := db.Query("SELECT * FROM tagsoptions WHERE tag_id = ?", tagID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var options []types.TagOption
|
||||
for rows.Next() {
|
||||
var to types.TagOption
|
||||
if err := rows.Scan(&to.ID, &to.TagID, &to.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options = append(options, to)
|
||||
}
|
||||
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
func DeleteTagOption(id int) (int64, error) {
|
||||
res, err := db.Exec("DELETE FROM tagsoptions WHERE id = ?", id)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
func UpdateTagOption(to types.TagOption) (int64, error) {
|
||||
res, err := db.Exec("UPDATE tagsoptions SET tag_id = ?, value = ? WHERE id = ?", to.TagID, to.Value, to.ID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
|
||||
func DeleteTagOptions(tagID int) error {
|
||||
_, err := db.Exec("DELETE FROM tagsoptions WHERE tag_id = ?", tagID)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ require (
|
|||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||
|
@ -20,7 +18,4 @@ require (
|
|||
golang.org/x/sys v0.14.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/mattn/go-sqlite3 v1.14.19
|
||||
)
|
||||
require github.com/mattn/go-sqlite3 v1.14.19
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
|
||||
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
|
@ -20,15 +15,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
|
|||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
|
||||
|
|
|
@ -14,6 +14,11 @@ func tagsRouter(router fiber.Router) {
|
|||
router.Get("/:id", GetTag)
|
||||
router.Delete("/:id", DeleteTag)
|
||||
router.Put("/:id", UpdateTag)
|
||||
router.Post("/:id/options", CreateTagOption)
|
||||
router.Get("/:id/options", GetTagOptions)
|
||||
router.Delete("/:id/options/:option_id", DeleteTagOption)
|
||||
router.Put("/:id/options/:option_id", UpdateTagOption)
|
||||
router.Delete("/:id/options", DeleteTagOptions)
|
||||
}
|
||||
|
||||
func CreateTag(c *fiber.Ctx) error {
|
||||
|
@ -50,7 +55,7 @@ func GetTag(c *fiber.Ctx) error {
|
|||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
return c.JSON(tag)
|
||||
return c.Status(fiber.StatusOK).JSON(tag)
|
||||
}
|
||||
|
||||
func DeleteTag(c *fiber.Ctx) error {
|
||||
|
@ -99,3 +104,171 @@ func UpdateTag(c *fiber.Ctx) error {
|
|||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func CreateTagOption(c *fiber.Ctx) error {
|
||||
tagID, err := strconv.Atoi(c.Params("id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||
}
|
||||
|
||||
option := types.TagOption{TagID: tagID}
|
||||
if err := c.BodyParser(&option); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||
}
|
||||
|
||||
exist, err := db.ExistTag(tagID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Error finding tag",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
if !exist {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Tag not found"})
|
||||
}
|
||||
|
||||
id, err := db.CreateTagOption(option)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Cannot create tag option",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"id": id})
|
||||
}
|
||||
|
||||
func GetTagOptions(c *fiber.Ctx) error {
|
||||
tagID, err := strconv.Atoi(c.Params("id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||
}
|
||||
|
||||
exist, err := db.ExistTag(tagID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Error finding tag",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
if !exist {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Tag not found"})
|
||||
}
|
||||
|
||||
options, err := db.GetTagOptions(tagID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Cannot retrieve tag options",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(options)
|
||||
}
|
||||
|
||||
func DeleteTagOption(c *fiber.Ctx) error {
|
||||
tagID, err := strconv.Atoi(c.Params("id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||
}
|
||||
|
||||
optionID, err := strconv.Atoi(c.Params("option_id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag option ID"})
|
||||
}
|
||||
|
||||
exist, err := db.ExistTag(tagID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Error finding tag",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
if !exist {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Tag not found"})
|
||||
}
|
||||
|
||||
count, err := db.DeleteTagOption(optionID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Cannot delete tag option",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
}
|
||||
|
||||
func UpdateTagOption(c *fiber.Ctx) error {
|
||||
tagID, err := strconv.Atoi(c.Params("id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||
}
|
||||
|
||||
optionID, err := strconv.Atoi(c.Params("option_id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag option ID"})
|
||||
}
|
||||
|
||||
option := types.TagOption{ID: optionID, TagID: tagID}
|
||||
if err := c.BodyParser(&option); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Cannot parse request"})
|
||||
}
|
||||
|
||||
exist, err := db.ExistTag(tagID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Error finding tag",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
if !exist {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Tag not found"})
|
||||
}
|
||||
|
||||
count, err := db.UpdateTagOption(option)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Cannot update tag option",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func DeleteTagOptions(c *fiber.Ctx) error {
|
||||
tagID, err := strconv.Atoi(c.Params("id"))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid tag ID"})
|
||||
}
|
||||
|
||||
exist, err := db.ExistTag(tagID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Error finding tag",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
if !exist {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Tag not found"})
|
||||
}
|
||||
|
||||
err = db.DeleteTagOptions(tagID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Cannot delete tag options",
|
||||
"trace": fmt.Sprint(err),
|
||||
})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
}
|
||||
|
|
|
@ -10,5 +10,6 @@ type FullCardTag struct {
|
|||
CardID int `json:"card_id"`
|
||||
TagID int `json:"tag_id"`
|
||||
TagTitle string `json:"tag_title"`
|
||||
TagType int `json:"tag_type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
|
|
@ -6,3 +6,17 @@ type Tag struct {
|
|||
Title string `json:"title"`
|
||||
Type int `json:"type"`
|
||||
}
|
||||
|
||||
type TagOption struct {
|
||||
ID int `json:"id"`
|
||||
TagID int `json:"tag_id"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type FullTag struct {
|
||||
ID int `json:"id"`
|
||||
ProjectID int `json:"project_id"`
|
||||
Title string `json:"title"`
|
||||
Type int `json:"type"`
|
||||
Options []TagOption `json:"options"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue