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.

211 lines
5.1 KiB

  1. package models
  2. import (
  3. "fmt"
  4. "reflect"
  5. "strings"
  6. "time"
  7. )
  8. // Change represents a change in the rpdata history through the API.
  9. type Change struct {
  10. ID string `bson:"_id"`
  11. Model ChangeModel `bson:"model"`
  12. Op string `bson:"op"`
  13. Author string `bson:"author"`
  14. Listed bool `bson:"listed"`
  15. Keys []ChangeKey `bson:"keys"`
  16. Date time.Time `bson:"date"`
  17. ChangeObjectSet
  18. }
  19. type ChangeObjectSet struct {
  20. Logs []*Log `bson:"logs" json:"logs,omitempty"`
  21. Characters []*Character `bson:"characters" json:"characters,omitempty"`
  22. Channels []*Channel `bson:"channels" json:"channels,omitempty"`
  23. Posts []*Post `bson:"posts" json:"posts,omitempty"`
  24. Stories []*Story `bson:"stories" json:"stories,omitempty"`
  25. Tags []*Tag `bson:"tags" json:"tags,omitempty"`
  26. Chapters []*Chapter `bson:"chapters" json:"chapters,omitempty"`
  27. Comments []*Comment `bson:"comments" json:"comments,omitempty"`
  28. Files []*File `bson:"files" json:"files,omitempty"`
  29. }
  30. // AddObject adds the model into the appropriate array.
  31. func (change *ChangeObjectSet) AddObject(object interface{}) bool {
  32. if v := reflect.ValueOf(object); v.Kind() == reflect.Struct {
  33. ptr := reflect.PtrTo(v.Type())
  34. ptrValue := reflect.New(ptr.Elem())
  35. ptrValue.Elem().Set(v)
  36. object = ptrValue.Interface()
  37. }
  38. switch object := object.(type) {
  39. case *Log:
  40. change.Logs = append(change.Logs, object)
  41. case []*Log:
  42. change.Logs = append(change.Logs, object...)
  43. case *Character:
  44. change.Characters = append(change.Characters, object)
  45. case []*Character:
  46. change.Characters = append(change.Characters, object...)
  47. case *Channel:
  48. change.Channels = append(change.Channels, object)
  49. case []*Channel:
  50. change.Channels = append(change.Channels, object...)
  51. case *Post:
  52. change.Posts = append(change.Posts, object)
  53. case []*Post:
  54. change.Posts = append(change.Posts, object...)
  55. case *Story:
  56. change.Stories = append(change.Stories, object)
  57. case []*Story:
  58. change.Stories = append(change.Stories, object...)
  59. case *Tag:
  60. change.Tags = append(change.Tags, object)
  61. case []*Tag:
  62. change.Tags = append(change.Tags, object...)
  63. case *Chapter:
  64. change.Chapters = append(change.Chapters, object)
  65. case []*Chapter:
  66. change.Chapters = append(change.Chapters, object...)
  67. case *Comment:
  68. change.Comments = append(change.Comments, object)
  69. case []*Comment:
  70. change.Comments = append(change.Comments, object...)
  71. case *File:
  72. change.Files = append(change.Files, object)
  73. case []*File:
  74. change.Files = append(change.Files, object...)
  75. default:
  76. return false
  77. }
  78. return true
  79. }
  80. // Objects makes a combined, mixed array of all the models stored in this change.
  81. func (change *Change) Objects() []interface{} {
  82. data := make([]interface{}, 0, 8)
  83. for _, log := range change.Logs {
  84. data = append(data, log)
  85. }
  86. for _, channel := range change.Channels {
  87. data = append(data, channel)
  88. }
  89. for _, character := range change.Characters {
  90. data = append(data, character)
  91. }
  92. for _, post := range change.Posts {
  93. data = append(data, post)
  94. }
  95. for _, story := range change.Stories {
  96. data = append(data, story)
  97. }
  98. for _, tag := range change.Tags {
  99. data = append(data, tag)
  100. }
  101. for _, chapter := range change.Chapters {
  102. data = append(data, chapter)
  103. }
  104. for _, comment := range change.Comments {
  105. data = append(data, comment)
  106. }
  107. for _, file := range change.Files {
  108. data = append(data, file)
  109. }
  110. return data
  111. }
  112. func (change *Change) PassesFilter(filter ChangeFilter) bool {
  113. if filter.Author != nil && change.Author != *filter.Author {
  114. return false
  115. }
  116. // For unlisted changes, pass it only if the filter refers to the specific index.
  117. if !change.Listed {
  118. hasSpecificKey := false
  119. KeyFindLoop:
  120. for _, key := range filter.Keys {
  121. if key.ID == "*" {
  122. continue
  123. }
  124. for _, changeKey := range change.Keys {
  125. if changeKey.Model == key.Model && changeKey.ID == key.ID {
  126. hasSpecificKey = true
  127. break KeyFindLoop
  128. }
  129. }
  130. }
  131. if !hasSpecificKey {
  132. return false
  133. }
  134. }
  135. if filter.EarliestDate != nil && filter.EarliestDate.Before(change.Date) {
  136. return false
  137. }
  138. if len(filter.Keys) > 0 {
  139. foundKey := false
  140. KeyFindLoop2:
  141. for _, key := range filter.Keys {
  142. for _, changeKey := range change.Keys {
  143. if changeKey == key {
  144. foundKey = true
  145. break KeyFindLoop2
  146. }
  147. }
  148. }
  149. if !foundKey {
  150. return false
  151. }
  152. }
  153. return true
  154. }
  155. // ChangeKey is a key for a change that can be used when subscribing to them.
  156. type ChangeKey struct {
  157. Model ChangeModel `bson:"model"`
  158. ID string `bson:"id"`
  159. }
  160. func (ck *ChangeKey) String() string {
  161. return ck.Model.String() + ":" + ck.ID
  162. }
  163. func (ck *ChangeKey) Decode(str string) error {
  164. split := strings.Split(str, ":")
  165. if len(split) != 2 {
  166. return fmt.Errorf("invalid change key: %s", str)
  167. }
  168. model := ChangeModel(split[0])
  169. if !model.IsValid() {
  170. return fmt.Errorf("invalid change key model: %s", model)
  171. }
  172. ck.Model = model
  173. ck.ID = split[1]
  174. return nil
  175. }
  176. // ChangeFilter is a filter for listing changes.
  177. type ChangeFilter struct {
  178. Keys []ChangeKey
  179. EarliestDate *time.Time
  180. LatestDate *time.Time
  181. Author *string
  182. Limit *int
  183. }