stufflog graphql server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
4.5 KiB

  1. package mysqldriver
  2. import (
  3. "context"
  4. "database/sql"
  5. "git.aiterp.net/stufflog/server/internal/generate"
  6. "git.aiterp.net/stufflog/server/internal/slerrors"
  7. "git.aiterp.net/stufflog/server/models"
  8. sq "github.com/Masterminds/squirrel"
  9. "github.com/jmoiron/sqlx"
  10. )
  11. type itemRepository struct {
  12. db *sqlx.DB
  13. }
  14. func (r *itemRepository) Find(ctx context.Context, id string) (*models.Item, error) {
  15. item := models.Item{}
  16. err := r.db.GetContext(ctx, &item, "SELECT * FROM item WHERE item_id=?", id)
  17. if err != nil {
  18. if err == sql.ErrNoRows {
  19. return nil, slerrors.NotFound("Project")
  20. }
  21. return nil, err
  22. }
  23. err = r.db.SelectContext(ctx, &item.Tags, "SELECT tag FROM item_tag WHERE item_id=? ORDER BY tag", id)
  24. if err != nil {
  25. return nil, err
  26. }
  27. return &item, nil
  28. }
  29. func (r *itemRepository) List(ctx context.Context, filter models.ItemFilter) ([]*models.Item, error) {
  30. q := sq.Select("item.*").From("item").OrderBy("name")
  31. if len(filter.ItemIDs) > 0 {
  32. q = q.Where(sq.Eq{"item_id": filter.ItemIDs})
  33. }
  34. if len(filter.Tags) > 0 {
  35. q = q.LeftJoin("item_tag ON item_tag.item_id = item.item_id").
  36. Where(sq.Eq{"item_tag.tag": filter.Tags}).
  37. GroupBy("item.item_id")
  38. }
  39. query, args, err := q.ToSql()
  40. if err != nil {
  41. return nil, err
  42. }
  43. results := make([]*models.Item, 0, 16)
  44. err = r.db.SelectContext(ctx, &results, query, args...)
  45. if err != nil {
  46. if err == sql.ErrNoRows {
  47. return []*models.Item{}, nil
  48. }
  49. return nil, err
  50. }
  51. err = r.fillTags(ctx, results)
  52. if err != nil {
  53. return nil, err
  54. }
  55. return results, nil
  56. }
  57. func (r *itemRepository) Insert(ctx context.Context, item models.Item) (*models.Item, error) {
  58. item.ID = generate.ItemID()
  59. tx, err := r.db.BeginTxx(ctx, nil)
  60. if err != nil {
  61. return nil, err
  62. }
  63. _, err = tx.NamedExecContext(ctx, `
  64. INSERT INTO item (item_id, name, description, image_url)
  65. VALUES (:item_id, :name, :description, :image_url)
  66. `, item)
  67. if err != nil {
  68. _ = tx.Rollback()
  69. return nil, err
  70. }
  71. if len(item.Tags) > 0 {
  72. q := sq.Insert("item_tag").Columns("item_id", "tag")
  73. for _, tag := range item.Tags {
  74. q = q.Values(item.ID, tag)
  75. }
  76. tagQuery, args, err := q.ToSql()
  77. if err != nil {
  78. _ = tx.Rollback()
  79. return nil, err
  80. }
  81. _, err = r.db.ExecContext(ctx, tagQuery, args...)
  82. if err != nil {
  83. _ = tx.Rollback()
  84. return nil, err
  85. }
  86. }
  87. err = tx.Commit()
  88. if err != nil {
  89. return nil, err
  90. }
  91. return &item, nil
  92. }
  93. func (r *itemRepository) Save(ctx context.Context, item models.Item) error {
  94. tx, err := r.db.BeginTxx(ctx, nil)
  95. if err != nil {
  96. return err
  97. }
  98. _, err = tx.NamedExecContext(ctx, `
  99. UPDATE item
  100. SET name=:name, description=:description, image_url=:image_url
  101. WHERE item_id=:item_id
  102. `, item)
  103. if err != nil {
  104. _ = tx.Rollback()
  105. return err
  106. }
  107. _, err = r.db.ExecContext(ctx, "DELETE FROM item_tag WHERE item_id=?", item.ID)
  108. if err != nil && err != sql.ErrNoRows {
  109. _ = tx.Rollback()
  110. return err
  111. }
  112. if len(item.Tags) > 0 {
  113. q := sq.Insert("item_tag").Columns("item_id", "tag")
  114. for _, tag := range item.Tags {
  115. q = q.Values(item.ID, tag)
  116. }
  117. tagQuery, args, err := q.ToSql()
  118. if err != nil {
  119. _ = tx.Rollback()
  120. return err
  121. }
  122. _, err = r.db.ExecContext(ctx, tagQuery, args...)
  123. if err != nil {
  124. _ = tx.Rollback()
  125. return err
  126. }
  127. }
  128. err = tx.Commit()
  129. if err != nil {
  130. return err
  131. }
  132. return nil
  133. }
  134. func (r *itemRepository) Delete(ctx context.Context, item models.Item) error {
  135. _, err := r.db.ExecContext(ctx, "DELETE FROM item WHERE item_id=?", item.ID)
  136. if err != nil {
  137. return err
  138. }
  139. _, err = r.db.ExecContext(ctx, "DELETE FROM item_tag WHERE item_id=?", item.ID)
  140. if err != nil {
  141. return err
  142. }
  143. return err
  144. }
  145. func (r *itemRepository) GetTags(ctx context.Context) ([]string, error) {
  146. tags := make([]string, 0, 16)
  147. err := r.db.SelectContext(ctx, &tags, "SELECT DISTINCT(tag) FROM item_tag")
  148. if err != nil {
  149. return nil, err
  150. }
  151. return tags, nil
  152. }
  153. func (r *itemRepository) fillTags(ctx context.Context, items []*models.Item) error {
  154. ids := make([]string, len(items))
  155. idMap := make(map[string]int, len(items))
  156. for i, item := range items {
  157. ids[i] = item.ID
  158. idMap[item.ID] = i
  159. }
  160. query, args, err := sq.Select("*").From("item_tag").Where(sq.Eq{"item_id": ids}).ToSql()
  161. if err != nil {
  162. return err
  163. }
  164. results := make([]struct {
  165. ItemID string `db:"item_id"`
  166. Tag string `db:"tag"`
  167. }, 0, len(items)*4)
  168. err = r.db.SelectContext(ctx, &results, query, args...)
  169. if err != nil {
  170. return err
  171. }
  172. for _, result := range results {
  173. item := items[idMap[result.ItemID]]
  174. item.Tags = append(item.Tags, result.Tag)
  175. }
  176. return nil
  177. }