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.

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