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.

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