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.

401 lines
8.9 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. )
  9. // StoryResolver for the Story graphql type
  10. type StoryResolver struct{ S story.Story }
  11. // StoryArgs is args for story query
  12. type StoryArgs struct {
  13. ID string
  14. }
  15. // Story implements the story query
  16. func (r *QueryResolver) Story(ctx context.Context, args *StoryArgs) (*StoryResolver, error) {
  17. story, err := story.FindID(args.ID)
  18. if err != nil {
  19. return nil, err
  20. }
  21. return &StoryResolver{S: story}, nil
  22. }
  23. // StoriesArg is args for stories query
  24. type StoriesArg struct {
  25. Input *struct {
  26. Author *string
  27. Tags *[]TagInput
  28. EarliestFictionalDate *string
  29. LatestFictionalDate *string
  30. Limit *int32
  31. }
  32. }
  33. // Stories implements the stories query
  34. func (r *QueryResolver) Stories(ctx context.Context, args *StoriesArg) ([]*StoryResolver, error) {
  35. input := args.Input
  36. author := ""
  37. if input.Author != nil {
  38. author = *input.Author
  39. }
  40. tags := make([]story.Tag, 0, 8)
  41. if input.Tags != nil {
  42. for _, tagInput := range *input.Tags {
  43. tags = append(tags, story.Tag{
  44. Kind: tagInput.Kind,
  45. Name: tagInput.Name,
  46. })
  47. }
  48. }
  49. earliest := time.Time{}
  50. err := error(nil)
  51. if input.EarliestFictionalDate != nil {
  52. earliest, err = time.Parse(time.RFC3339Nano, *input.EarliestFictionalDate)
  53. if err != nil {
  54. return nil, err
  55. }
  56. }
  57. latest := time.Time{}
  58. if input.LatestFictionalDate != nil {
  59. latest, err = time.Parse(time.RFC3339Nano, *input.LatestFictionalDate)
  60. if err != nil {
  61. return nil, err
  62. }
  63. }
  64. limit := 30
  65. if input.Limit != nil {
  66. limit = int(*input.Limit)
  67. }
  68. stories, err := story.List(author, tags, earliest, latest, limit)
  69. if err != nil {
  70. return nil, err
  71. }
  72. resolvers := make([]*StoryResolver, len(stories))
  73. for i, story := range stories {
  74. resolvers[i] = &StoryResolver{S: story}
  75. }
  76. return resolvers, nil
  77. }
  78. // StoryAddArgs is args for the addStory mutation
  79. type StoryAddArgs struct {
  80. Input *struct {
  81. Name string
  82. Category string
  83. Author *string
  84. Open *bool
  85. Listed *bool
  86. Tags *[]TagInput
  87. FictionalDate *string
  88. }
  89. }
  90. // AddStory implements the addStory mutation
  91. func (r *MutationResolver) AddStory(ctx context.Context, args *StoryAddArgs) (*StoryResolver, error) {
  92. input := args.Input
  93. user := session.FromContext(ctx).User()
  94. if user == nil || !user.Permitted("member", "story.add") {
  95. return nil, ErrUnauthorized
  96. }
  97. author := user.ID
  98. if input.Author != nil {
  99. author = *input.Author
  100. if user.ID != author && !user.Permitted("story.add") {
  101. return nil, ErrPermissionDenied
  102. }
  103. }
  104. listed := (input.Listed != nil && *input.Listed == true)
  105. open := (input.Open != nil && *input.Open == true)
  106. tags := make([]story.Tag, 0, 8)
  107. if input.Tags != nil {
  108. for _, tagInput := range *input.Tags {
  109. tags = append(tags, story.Tag{
  110. Kind: tagInput.Kind,
  111. Name: tagInput.Name,
  112. })
  113. }
  114. }
  115. fictionalDate := time.Time{}
  116. if input.FictionalDate != nil {
  117. date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
  118. if err != nil {
  119. return nil, err
  120. }
  121. fictionalDate = date
  122. }
  123. story, err := story.New(input.Name, author, input.Category, listed, open, tags, time.Now(), fictionalDate)
  124. if err != nil {
  125. return nil, err
  126. }
  127. go change.Submit("Story", "add", user.ID, story.ID, map[string]interface{}{
  128. "name": input.Name,
  129. "category": input.Category,
  130. "author": input.Author,
  131. })
  132. return &StoryResolver{S: story}, nil
  133. }
  134. // StoryTagAddArgs is args for the addStoryTag mutation
  135. type StoryTagAddArgs struct {
  136. Input *struct {
  137. ID string
  138. Tag TagInput
  139. }
  140. }
  141. // AddStoryTag implements the addStoryTag mutation
  142. func (r *MutationResolver) AddStoryTag(ctx context.Context, args *StoryTagAddArgs) (*StoryResolver, error) {
  143. input := args.Input
  144. tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
  145. user := session.FromContext(ctx).User()
  146. if user == nil || !user.Permitted("member", "story.edit") {
  147. return nil, ErrUnauthorized
  148. }
  149. story, err := story.FindID(input.ID)
  150. if err != nil {
  151. return nil, err
  152. }
  153. if story.Author != user.ID && !user.Permitted("story.edit") {
  154. return nil, ErrPermissionDenied
  155. }
  156. err = story.AddTag(tag)
  157. if err != nil {
  158. return nil, err
  159. }
  160. go change.Submit("Story", "add.tag", user.ID, story.ID, map[string]interface{}{
  161. "kind": tag.Kind,
  162. "name": tag.Name,
  163. })
  164. return &StoryResolver{S: story}, nil
  165. }
  166. // StoryTagRemoveArgs is args for the removeStoryTag mutation
  167. type StoryTagRemoveArgs struct {
  168. Input *struct {
  169. ID string
  170. Tag TagInput
  171. }
  172. }
  173. // RemoveStoryTag implements the removeStoryTag mutation
  174. func (r *MutationResolver) RemoveStoryTag(ctx context.Context, args *StoryTagRemoveArgs) (*StoryResolver, error) {
  175. input := args.Input
  176. tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name}
  177. user := session.FromContext(ctx).User()
  178. if user == nil || !user.Permitted("member", "story.edit") {
  179. return nil, ErrUnauthorized
  180. }
  181. story, err := story.FindID(input.ID)
  182. if err != nil {
  183. return nil, err
  184. }
  185. if story.Author != user.ID && !user.Permitted("story.edit") {
  186. return nil, ErrPermissionDenied
  187. }
  188. err = story.RemoveTag(tag)
  189. if err != nil {
  190. return nil, err
  191. }
  192. go change.Submit("Story", "remove.tag", user.ID, story.ID, map[string]interface{}{
  193. "kind": tag.Kind,
  194. "name": tag.Name,
  195. })
  196. return &StoryResolver{S: story}, nil
  197. }
  198. // StoryEditArgs is args for the addStory mutation
  199. type StoryEditArgs struct {
  200. Input *struct {
  201. ID string
  202. Name *string
  203. Category *string
  204. Author *string
  205. Open *bool
  206. Listed *bool
  207. FictionalDate *string
  208. }
  209. }
  210. // EditStory implements the editStory mutation
  211. func (r *MutationResolver) EditStory(ctx context.Context, args *StoryEditArgs) (*StoryResolver, error) {
  212. input := args.Input
  213. user := session.FromContext(ctx).User()
  214. if user == nil || !user.Permitted("member", "story.edit") {
  215. return nil, ErrUnauthorized
  216. }
  217. story, err := story.FindID(input.ID)
  218. if err != nil {
  219. return nil, err
  220. }
  221. if story.Author != user.ID && !user.Permitted("story.edit") {
  222. return nil, ErrPermissionDenied
  223. }
  224. var fictionalDate *time.Time
  225. if input.FictionalDate != nil {
  226. date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate)
  227. if err != nil {
  228. return nil, err
  229. }
  230. fictionalDate = &date
  231. }
  232. err = story.Edit(input.Name, input.Category, input.Listed, input.Open, fictionalDate)
  233. if err != nil {
  234. return nil, err
  235. }
  236. go change.Submit("Story", "edit", user.ID, story.ID, map[string]interface{}{
  237. "name": input.Name,
  238. "category": input.Category,
  239. "author": input.Author,
  240. "open": input.Open,
  241. "listed": input.Listed,
  242. "fictionalDate": input.FictionalDate,
  243. })
  244. return &StoryResolver{S: story}, nil
  245. }
  246. // StoryRemoveArgs is args for the removeStory mutation
  247. type StoryRemoveArgs struct {
  248. ID string
  249. }
  250. // RemoveStory implements the removeStory mutation
  251. func (r *MutationResolver) RemoveStory(ctx context.Context, args *StoryRemoveArgs) (*StoryResolver, error) {
  252. user := session.FromContext(ctx).User()
  253. if user == nil || !user.Permitted("member", "story.edit") {
  254. return nil, ErrUnauthorized
  255. }
  256. story, err := story.FindID(args.ID)
  257. if err != nil {
  258. return nil, err
  259. }
  260. if story.Author != user.ID && !user.Permitted("story.remove") {
  261. return nil, ErrPermissionDenied
  262. }
  263. err = story.Remove()
  264. if err != nil {
  265. return nil, err
  266. }
  267. go change.Submit("Story", "remove", user.ID, story.ID, nil)
  268. return &StoryResolver{S: story}, nil
  269. }
  270. // ID resolves Story.id
  271. func (r *StoryResolver) ID() string {
  272. return r.S.ID
  273. }
  274. // Author resolves Story.author
  275. func (r *StoryResolver) Author() string {
  276. return r.S.Author
  277. }
  278. // Name resolves Story.name
  279. func (r *StoryResolver) Name() string {
  280. return r.S.Name
  281. }
  282. // Category resolves Story.category
  283. func (r *StoryResolver) Category() string {
  284. return r.S.Category
  285. }
  286. // Open resolves Story.open
  287. func (r *StoryResolver) Open() bool {
  288. return r.S.Open
  289. }
  290. // Listed resolves Story.listed
  291. func (r *StoryResolver) Listed() bool {
  292. return r.S.Listed
  293. }
  294. // Tags resolves Story.tags
  295. func (r *StoryResolver) Tags() []*TagResolver {
  296. resolvers := make([]*TagResolver, len(r.S.Tags))
  297. for i, tag := range r.S.Tags {
  298. resolvers[i] = &TagResolver{T: tag}
  299. }
  300. return resolvers
  301. }
  302. // Chapters resolves Story.chapters
  303. func (r *StoryResolver) Chapters() ([]*ChapterResolver, error) {
  304. chapters, err := r.S.Chapters()
  305. if err != nil {
  306. return nil, err
  307. }
  308. resolvers := make([]*ChapterResolver, len(chapters))
  309. for i, chapter := range chapters {
  310. resolvers[i] = &ChapterResolver{C: chapter}
  311. }
  312. return resolvers, nil
  313. }
  314. // CreatedDate resolves Story.createdDate
  315. func (r *StoryResolver) CreatedDate() string {
  316. return r.S.CreatedDate.Format(time.RFC3339Nano)
  317. }
  318. // FictionalDate resolves Story.fictionalDate
  319. func (r *StoryResolver) FictionalDate() string {
  320. return r.S.FictionalDate.Format(time.RFC3339Nano)
  321. }
  322. // UpdatedDate resolves Story.updatedDate
  323. func (r *StoryResolver) UpdatedDate() string {
  324. return r.S.UpdatedDate.Format(time.RFC3339Nano)
  325. }