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.

329 lines
7.6 KiB

  1. package resolver
  2. import (
  3. "context"
  4. "time"
  5. "git.aiterp.net/rpdata/api/internal/session"
  6. "git.aiterp.net/rpdata/api/model/change"
  7. "git.aiterp.net/rpdata/api/model/story"
  8. "git.aiterp.net/rpdata/api/resolver/types"
  9. )
  10. // StoryArgs is args for story query
  11. type StoryArgs struct {
  12. ID string
  13. }
  14. // Story implements the story query
  15. func (r *QueryResolver) Story(ctx context.Context, args *StoryArgs) (*types.StoryResolver, error) {
  16. story, err := story.FindID(args.ID)
  17. if err != nil {
  18. return nil, err
  19. }
  20. return &types.StoryResolver{S: story}, nil
  21. }
  22. // StoriesArg is args for stories query
  23. type StoriesArg struct {
  24. Filter *struct {
  25. Author *string
  26. Tags *[]TagInput
  27. EarliestFictionalDate *string
  28. LatestFictionalDate *string
  29. Limit *int32
  30. }
  31. }
  32. // Stories implements the stories query
  33. func (r *QueryResolver) Stories(ctx context.Context, args *StoriesArg) ([]*types.StoryResolver, error) {
  34. filter := args.Filter
  35. author := ""
  36. if filter != nil && filter.Author != nil {
  37. author = *filter.Author
  38. }
  39. tags := make([]story.Tag, 0, 8)
  40. if filter != nil && filter.Tags != nil {
  41. for _, tagInput := range *filter.Tags {
  42. tags = append(tags, story.Tag{
  43. Kind: tagInput.Kind,
  44. Name: tagInput.Name,
  45. })
  46. }
  47. }
  48. earliest := time.Time{}
  49. err := error(nil)
  50. if filter != nil && filter.EarliestFictionalDate != nil {
  51. earliest, err = time.Parse(time.RFC3339Nano, *filter.EarliestFictionalDate)
  52. if err != nil {
  53. return nil, err
  54. }
  55. }
  56. latest := time.Time{}
  57. if filter != nil && filter.LatestFictionalDate != nil {
  58. latest, err = time.Parse(time.RFC3339Nano, *filter.LatestFictionalDate)
  59. if err != nil {
  60. return nil, err
  61. }
  62. }
  63. limit := 30
  64. if filter != nil && filter.Limit != nil {
  65. limit = int(*filter.Limit)
  66. }
  67. stories, err := story.List(author, tags, earliest, latest, limit)
  68. if err != nil {
  69. return nil, err
  70. }
  71. resolvers := make([]*types.StoryResolver, len(stories))
  72. for i, story := range stories {
  73. resolvers[i] = &types.StoryResolver{S: story}
  74. }
  75. return resolvers, nil
  76. }
  77. // StoryAddArgs is args for the addStory mutation
  78. type StoryAddArgs struct {
  79. Input *struct {
  80. Name string
  81. Category string
  82. Author *string
  83. Open *bool
  84. Listed *bool
  85. Tags *[]TagInput
  86. FictionalDate *string
  87. }
  88. }
  89. // AddStory implements the addStory mutation
  90. func (r *MutationResolver) AddStory(ctx context.Context, args *StoryAddArgs) (*types.StoryResolver, error) {
  91. input := args.Input
  92. user := session.FromContext(ctx).User()
  93. if user == nil || !user.Permitted("member", "story.add") {
  94. return nil, ErrUnauthorized
  95. }
  96. author := user.ID
  97. if input.Author != nil {
  98. author = *input.Author
  99. if user.ID != author && !user.Permitted("story.add") {
  100. return nil, ErrPermissionDenied
  101. }
  102. }
  103. listed := (input.Listed != nil && *input.Listed == true)
  104. open := (input.Open != nil && *input.Open == true)
  105. tags := make([]story.Tag, 0, 8)
  106. if input.Tags != nil {
  107. for _, tagInput := range *input.Tags {
  108. tags = append(tags, story.Tag{
  109. Kind: tagInput.Kind,
  110. Name: tagInput.Name,
  111. })
  112. }
  113. }
  114. fictionalDate := time.Time{}
  115. if input.FictionalDate != nil {
  116. date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
  117. if err != nil {
  118. return nil, err
  119. }
  120. fictionalDate = date
  121. }
  122. story, err := story.New(input.Name, author, input.Category, listed, open, tags, time.Now(), fictionalDate)
  123. if err != nil {
  124. return nil, err
  125. }
  126. go change.Submit("Story", "add", user.ID, story.ID, map[string]interface{}{
  127. "name": input.Name,
  128. "category": input.Category,
  129. "author": input.Author,
  130. })
  131. return &types.StoryResolver{S: story}, nil
  132. }
  133. // StoryTagAddArgs is args for the addStoryTag mutation
  134. type StoryTagAddArgs struct {
  135. Input *struct {
  136. ID string
  137. Tag TagInput
  138. }
  139. }
  140. // AddStoryTag implements the addStoryTag mutation
  141. func (r *MutationResolver) AddStoryTag(ctx context.Context, args *StoryTagAddArgs) (*types.StoryResolver, error) {
  142. input := args.Input
  143. tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
  144. user := session.FromContext(ctx).User()
  145. if user == nil || !user.Permitted("member", "story.edit") {
  146. return nil, ErrUnauthorized
  147. }
  148. story, err := story.FindID(input.ID)
  149. if err != nil {
  150. return nil, err
  151. }
  152. if story.Author != user.ID && !user.Permitted("story.edit") {
  153. return nil, ErrPermissionDenied
  154. }
  155. err = story.AddTag(tag)
  156. if err != nil {
  157. return nil, err
  158. }
  159. go change.Submit("Story", "add.tag", user.ID, story.ID, map[string]interface{}{
  160. "kind": tag.Kind,
  161. "name": tag.Name,
  162. })
  163. return &types.StoryResolver{S: story}, nil
  164. }
  165. // StoryTagRemoveArgs is args for the removeStoryTag mutation
  166. type StoryTagRemoveArgs struct {
  167. Input *struct {
  168. ID string
  169. Tag TagInput
  170. }
  171. }
  172. // RemoveStoryTag implements the removeStoryTag mutation
  173. func (r *MutationResolver) RemoveStoryTag(ctx context.Context, args *StoryTagRemoveArgs) (*types.StoryResolver, error) {
  174. input := args.Input
  175. tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
  176. user := session.FromContext(ctx).User()
  177. if user == nil || !user.Permitted("member", "story.edit") {
  178. return nil, ErrUnauthorized
  179. }
  180. story, err := story.FindID(input.ID)
  181. if err != nil {
  182. return nil, err
  183. }
  184. if story.Author != user.ID && !user.Permitted("story.edit") {
  185. return nil, ErrPermissionDenied
  186. }
  187. err = story.RemoveTag(tag)
  188. if err != nil {
  189. return nil, err
  190. }
  191. go change.Submit("Story", "remove.tag", user.ID, story.ID, map[string]interface{}{
  192. "kind": tag.Kind,
  193. "name": tag.Name,
  194. })
  195. return &types.StoryResolver{S: story}, nil
  196. }
  197. // StoryEditArgs is args for the addStory mutation
  198. type StoryEditArgs struct {
  199. Input *struct {
  200. ID string
  201. Name *string
  202. Category *string
  203. Author *string
  204. Open *bool
  205. Listed *bool
  206. FictionalDate *string
  207. }
  208. }
  209. // EditStory implements the editStory mutation
  210. func (r *MutationResolver) EditStory(ctx context.Context, args *StoryEditArgs) (*types.StoryResolver, error) {
  211. input := args.Input
  212. user := session.FromContext(ctx).User()
  213. if user == nil || !user.Permitted("member", "story.edit") {
  214. return nil, ErrUnauthorized
  215. }
  216. story, err := story.FindID(input.ID)
  217. if err != nil {
  218. return nil, err
  219. }
  220. if story.Author != user.ID && !user.Permitted("story.edit") {
  221. return nil, ErrPermissionDenied
  222. }
  223. var fictionalDate *time.Time
  224. if input.FictionalDate != nil {
  225. date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
  226. if err != nil {
  227. return nil, err
  228. }
  229. fictionalDate = &date
  230. }
  231. err = story.Edit(input.Name, input.Category, input.Listed, input.Open, fictionalDate)
  232. if err != nil {
  233. return nil, err
  234. }
  235. go change.Submit("Story", "edit", user.ID, story.ID, map[string]interface{}{
  236. "name": input.Name,
  237. "category": input.Category,
  238. "author": input.Author,
  239. "open": input.Open,
  240. "listed": input.Listed,
  241. "fictionalDate": input.FictionalDate,
  242. })
  243. return &types.StoryResolver{S: story}, nil
  244. }
  245. // StoryRemoveArgs is args for the removeStory mutation
  246. type StoryRemoveArgs struct {
  247. ID string
  248. }
  249. // RemoveStory implements the removeStory mutation
  250. func (r *MutationResolver) RemoveStory(ctx context.Context, args *StoryRemoveArgs) (*types.StoryResolver, error) {
  251. user := session.FromContext(ctx).User()
  252. if user == nil || !user.Permitted("member", "story.edit") {
  253. return nil, ErrUnauthorized
  254. }
  255. story, err := story.FindID(args.ID)
  256. if err != nil {
  257. return nil, err
  258. }
  259. if story.Author != user.ID && !user.Permitted("story.remove") {
  260. return nil, ErrPermissionDenied
  261. }
  262. err = story.Remove()
  263. if err != nil {
  264. return nil, err
  265. }
  266. go change.Submit("Story", "remove", user.ID, story.ID, nil)
  267. return &types.StoryResolver{S: story}, nil
  268. }