GraphQL API and utilities for the rpdata project
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.

234 lines
5.6 KiB

  1. package postgres
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "fmt"
  7. "git.aiterp.net/rpdata/api/database/postgres/psqlcore"
  8. "git.aiterp.net/rpdata/api/models"
  9. "strconv"
  10. "strings"
  11. )
  12. var ErrNickConflict = errors.New("nick already in use by another character")
  13. type characterRepository struct {
  14. insertWithIDs bool
  15. db *sql.DB
  16. }
  17. func (r *characterRepository) Find(ctx context.Context, id string) (*models.Character, error) {
  18. row, err := psqlcore.New(r.db).SelectCharacterByID(ctx, id)
  19. if err != nil {
  20. return nil, err
  21. }
  22. return r.character(row), nil
  23. }
  24. func (r *characterRepository) FindNick(ctx context.Context, nick string) (*models.Character, error) {
  25. row, err := psqlcore.New(r.db).SelectCharacterByNick(ctx, nick)
  26. if err != nil {
  27. return nil, err
  28. }
  29. return r.character(psqlcore.SelectCharacterByIDRow(row)), nil
  30. }
  31. func (r *characterRepository) FindName(ctx context.Context, name string) (*models.Character, error) {
  32. row, err := psqlcore.New(r.db).SelectCharacterByName(ctx, name)
  33. if err != nil {
  34. return nil, err
  35. }
  36. return r.character(psqlcore.SelectCharacterByIDRow(row)), nil
  37. }
  38. func (r *characterRepository) List(ctx context.Context, filter models.CharacterFilter) ([]*models.Character, error) {
  39. params := psqlcore.SelectCharactersParams{
  40. LimitSize: 0,
  41. }
  42. if filter.IDs != nil {
  43. params.FilterID = true
  44. params.Ids = filter.IDs
  45. }
  46. if filter.Nicks != nil {
  47. params.FilterNick = true
  48. params.Nicks = filter.Nicks
  49. }
  50. if filter.Names != nil {
  51. params.FilterName = true
  52. params.Names = filter.Names
  53. }
  54. if filter.Author != nil {
  55. params.FilterAuthor = true
  56. params.Author = *filter.Author
  57. }
  58. if filter.Search != nil {
  59. params.FilterSearch = true
  60. params.Search = TSQueryFromSearch(*filter.Search)
  61. }
  62. if filter.Limit > 0 {
  63. params.LimitSize = int32(filter.Limit)
  64. }
  65. rows, err := psqlcore.New(r.db).SelectCharacters(ctx, params)
  66. if err != nil {
  67. return nil, err
  68. }
  69. return r.characters(rows), nil
  70. }
  71. func (r *characterRepository) Insert(ctx context.Context, character models.Character) (*models.Character, error) {
  72. tx, err := r.db.BeginTx(ctx, nil)
  73. if err != nil {
  74. return nil, err
  75. }
  76. defer func() { _ = tx.Rollback() }()
  77. q := psqlcore.New(tx)
  78. if !r.insertWithIDs || character.ID == "" {
  79. next, err := q.IncrementCounter(ctx, "data_character_id")
  80. if err != nil {
  81. return nil, err
  82. }
  83. character.ID = fmt.Sprintf("C%d", next)
  84. } else {
  85. n, err := strconv.Atoi(character.ID[1:])
  86. if err != nil {
  87. return nil, err
  88. }
  89. err = q.BumpCounter(ctx, psqlcore.BumpCounterParams{ID: "data_character_id", Value: int32(n)})
  90. if err != nil {
  91. return nil, err
  92. }
  93. }
  94. rows, err := q.SelectCharacters(ctx, psqlcore.SelectCharactersParams{
  95. FilterNick: true,
  96. Nicks: character.Nicks,
  97. LimitSize: 1,
  98. })
  99. if err != nil {
  100. return nil, fmt.Errorf("failed to select: %s", err)
  101. }
  102. if len(rows) != 0 {
  103. return nil, ErrNickConflict
  104. }
  105. err = q.InsertCharacter(ctx, psqlcore.InsertCharacterParams{
  106. ID: character.ID,
  107. Nicks: character.Nicks,
  108. Name: character.Name,
  109. ShortName: character.ShortName,
  110. Author: character.Author,
  111. Description: character.Description,
  112. })
  113. if err != nil {
  114. return nil, err
  115. }
  116. err = tx.Commit()
  117. if err != nil {
  118. return nil, err
  119. }
  120. return &character, nil
  121. }
  122. func (r *characterRepository) Update(ctx context.Context, character models.Character, update models.CharacterUpdate) (*models.Character, error) {
  123. character.ApplyUpdate(update)
  124. err := psqlcore.New(r.db).UpdateCharacter(ctx, psqlcore.UpdateCharacterParams{
  125. ID: character.ID,
  126. Name: character.Name,
  127. ShortName: character.ShortName,
  128. Description: character.Description,
  129. })
  130. if err != nil {
  131. return nil, err
  132. }
  133. return &character, nil
  134. }
  135. func (r *characterRepository) AddNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
  136. tx, err := r.db.BeginTx(ctx, nil)
  137. if err != nil {
  138. return nil, err
  139. }
  140. defer func() { _ = tx.Rollback() }()
  141. q := psqlcore.New(tx)
  142. rows, err := q.SelectCharacters(ctx, psqlcore.SelectCharactersParams{
  143. FilterNick: true,
  144. Nicks: []string{nick},
  145. LimitSize: 1,
  146. })
  147. if err != nil {
  148. return nil, err
  149. }
  150. if len(rows) != 0 {
  151. return nil, ErrNickConflict
  152. }
  153. err = q.AddCharacterNick(ctx, psqlcore.AddCharacterNickParams{ID: character.ID, Nick: nick})
  154. if err != nil {
  155. return nil, err
  156. }
  157. character.Nicks = append(character.Nicks, nick)
  158. err = tx.Commit()
  159. if err != nil {
  160. return nil, err
  161. }
  162. return &character, nil
  163. }
  164. func (r *characterRepository) RemoveNick(ctx context.Context, character models.Character, nick string) (*models.Character, error) {
  165. err := psqlcore.New(r.db).RemoveCharacterNick(ctx, psqlcore.RemoveCharacterNickParams{ID: character.ID, Nick: nick})
  166. if err != nil {
  167. return nil, err
  168. }
  169. for i, nick2 := range character.Nicks {
  170. if nick2 == nick {
  171. character.Nicks = append(character.Nicks[:i], character.Nicks[i+1:]...)
  172. break
  173. }
  174. }
  175. return &character, nil
  176. }
  177. func (r *characterRepository) Delete(ctx context.Context, character models.Character) error {
  178. return psqlcore.New(r.db).DeleteCharacter(ctx, character.ID)
  179. }
  180. func (r *characterRepository) character(row psqlcore.SelectCharacterByIDRow) *models.Character {
  181. return &models.Character{
  182. ID: strings.Trim(row.ID, " "),
  183. Nicks: row.Nicks,
  184. Name: row.Name,
  185. ShortName: row.ShortName,
  186. Author: row.Author,
  187. Description: row.Description,
  188. }
  189. }
  190. func (r *characterRepository) characters(rows []psqlcore.SelectCharactersRow) []*models.Character {
  191. results := make([]*models.Character, 0, len(rows))
  192. for _, row := range rows {
  193. results = append(results, r.character(psqlcore.SelectCharacterByIDRow(row)))
  194. }
  195. return results
  196. }