From 5895e09f37a04d82abd6c4a6375a7c266fe1079e Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sun, 8 Jul 2018 00:28:52 +0200 Subject: [PATCH] Added addStory, addStoryTag, removeStoryTag, editStory and removeStory mutations --- resolver/story.go | 210 +++++++++++++++++++++++++++++++++++++ schema/root.graphql | 16 +++ schema/types/story.graphql | 72 ++++++++++++- 3 files changed, 297 insertions(+), 1 deletion(-) diff --git a/resolver/story.go b/resolver/story.go index bfa25d8..3d288c8 100644 --- a/resolver/story.go +++ b/resolver/story.go @@ -4,6 +4,8 @@ import ( "context" "time" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" "git.aiterp.net/rpdata/api/model/story" ) @@ -91,6 +93,214 @@ func (r *QueryResolver) Stories(ctx context.Context, args *StoriesArg) ([]*Story return resolvers, nil } +// StoryAddArgs is args for the addStory mutation +type StoryAddArgs struct { + Input *struct { + Name string + Category string + Author *string + Open *bool + Listed *bool + Tags *[]TagInput + FictionalDate *string + } +} + +// AddStory implements the addStory mutation +func (r *MutationResolver) AddStory(ctx context.Context, args *StoryAddArgs) (*StoryResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.add") { + return nil, ErrUnauthorized + } + + author := user.ID + if args.Input.Author != nil { + author = *args.Input.Author + + if user.ID != author && !user.Permitted("story.add") { + return nil, ErrPermissionDenied + } + } + + listed := (args.Input.Listed != nil && *args.Input.Listed == true) + + open := (args.Input.Open != nil && *args.Input.Open == true) + + tags := make([]story.Tag, 0, 8) + if args.Input.Tags != nil { + for _, tagInput := range *args.Input.Tags { + tags = append(tags, story.Tag{ + Kind: tagInput.Kind, + Name: tagInput.Name, + }) + } + } + + fictionalDate := time.Time{} + if args.Input.FictionalDate != nil { + date, err := time.Parse(time.RFC3339Nano, *args.Input.FictionalDate) + if err != nil { + return nil, err + } + + fictionalDate = date + } + + story, err := story.New(args.Input.Name, author, args.Input.Category, listed, open, tags, time.Now(), fictionalDate) + if err != nil { + return nil, err + } + + return &StoryResolver{S: story}, nil +} + +// StoryTagArgs is args for the addStory mutation +type StoryTagArgs struct { + Input *struct { + ID string + Tag TagInput + } +} + +// AddStoryTag implements the addStoryTag mutation +func (r *MutationResolver) AddStoryTag(ctx context.Context, args *StoryTagArgs) (*StoryResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.edit") { + return nil, ErrUnauthorized + } + + tag := story.Tag{Kind: args.Input.Tag.Kind, Name: args.Input.Tag.Name} + + story, err := story.FindID(args.Input.ID) + if err != nil { + return nil, err + } + + if story.Author != user.ID && !user.Permitted("story.edit") { + return nil, ErrPermissionDenied + } + + err = story.AddTag(tag) + if err != nil { + return nil, err + } + + change.Submit("Story", "add.tag", user.ID, story.ID, map[string]interface{}{ + "kind": tag.Kind, + "name": tag.Name, + }) + + return &StoryResolver{S: story}, nil +} + +// RemoveStoryTag implements the removeStoryTag mutation +func (r *MutationResolver) RemoveStoryTag(ctx context.Context, args *StoryTagArgs) (*StoryResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.edit") { + return nil, ErrUnauthorized + } + + tag := story.Tag{Kind: args.Input.Tag.Kind, Name: args.Input.Tag.Name} + + story, err := story.FindID(args.Input.ID) + if err != nil { + return nil, err + } + + if story.Author != user.ID && !user.Permitted("story.edit") { + return nil, ErrPermissionDenied + } + + err = story.RemoveTag(tag) + if err != nil { + return nil, err + } + + change.Submit("Story", "remove.tag", user.ID, story.ID, map[string]interface{}{ + "kind": tag.Kind, + "name": tag.Name, + }) + + return &StoryResolver{S: story}, nil +} + +// StoryEditArgs is args for the addStory mutation +type StoryEditArgs struct { + Input *struct { + ID string + Name *string + Category *string + Author *string + Open *bool + Listed *bool + FictionalDate *string + } +} + +// EditStory implements the editStory mutation +func (r *MutationResolver) EditStory(ctx context.Context, args *StoryEditArgs) (*StoryResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.edit") { + return nil, ErrUnauthorized + } + + story, err := story.FindID(args.Input.ID) + if err != nil { + return nil, err + } + + if story.Author != user.ID && !user.Permitted("story.edit") { + return nil, ErrPermissionDenied + } + + var fictionalDate *time.Time + if args.Input.FictionalDate != nil { + date, err := time.Parse(time.RFC3339Nano, *args.Input.FictionalDate) + if err != nil { + return nil, err + } + + fictionalDate = &date + } + + input := args.Input + err = story.Edit(input.Name, input.Category, input.Listed, input.Open, fictionalDate) + if err != nil { + return nil, err + } + + return &StoryResolver{S: story}, nil +} + +// StoryRemoveArgs is args for the removeStory mutation +type StoryRemoveArgs struct { + ID string +} + +// RemoveStory implements the removeStory mutation +func (r *MutationResolver) RemoveStory(ctx context.Context, args *StoryRemoveArgs) (*StoryResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.edit") { + return nil, ErrUnauthorized + } + + story, err := story.FindID(args.ID) + if err != nil { + return nil, err + } + + if story.Author != user.ID && !user.Permitted("story.remove") { + return nil, ErrPermissionDenied + } + + err = story.Remove() + if err != nil { + return nil, err + } + + return &StoryResolver{S: story}, nil +} + // ID resolves Story.id func (r *StoryResolver) ID() string { return r.S.ID diff --git a/schema/root.graphql b/schema/root.graphql index 7396897..9b008a0 100644 --- a/schema/root.graphql +++ b/schema/root.graphql @@ -113,6 +113,22 @@ type Mutation { removeFile(id: String!): File + # Add a story + addStory(input: StoryAddInput!): Story! + + # Add a story tag + addStoryTag(input: StoryTagAddInput!): Story! + + # Remove a story tag + removeStoryTag(input: StoryTagRemoveInput!): Story! + + # Edit a story + editStory(input: StoryEditInput!): Story! + + # Remove a story + removeStory(id: String!): Story! + + # Add a chapter to a story addChapter(input: ChapterAddInput!): Chapter! diff --git a/schema/types/story.graphql b/schema/types/story.graphql index b382ed3..8dc1794 100644 --- a/schema/types/story.graphql +++ b/schema/types/story.graphql @@ -32,7 +32,7 @@ type Story { fictionalDate: String! # The date of the last major update to the story (RFC3339 format). This being bumped means a new chapter has been added. - fictionalDate: String! + updatedDate: String! } input StoriesQueryInput { @@ -52,6 +52,76 @@ input StoriesQueryInput { limit: Int } +# Input for the addStory mutation +input StoryAddInput { + # Set the name of the story, which will show up as the title, and as a suggestion for + # the first chapter being added. + name: String! + + # Set the category for the new story. + category: StoryCategory! + + # Add the story under another name. This requires the story.add permission, and should not be + # abused. The action will be logged with the actual creator's user ID. + author: String + + # Allow other users to post chapters to the story. + open: Boolean + + # Allow other users to see this page in any lists or search results. + listed: Boolean + + # Set which tags the story should have. + tags: [TagInput!] + + # Set the fictional date of the story (RFC3339 format). + fictionalDate: String +} + +# Input for the editStory mutation +input StoryEditInput { + # What story to edit + id: String! + + # Set the name of the story, which will show up as the title, and as a suggestion for + # the first chapter being added. + name: String + + # Set the category for the new story. + category: StoryCategory + + # Add the story under another name. This requires the story.add permission, and should not be + # abused. The action will be logged with the actual creator's user ID. + author: String + + # Change whether to allow others to add chapters + open: Boolean + + # Change whether to show this story in the list and search results + listed: Boolean + + # Set the fictional date of the story (RFC3339 format). + fictionalDate: String +} + +# Input for the addStoryTag mutation +input StoryTagAddInput { + # What story to add the tag to. + id: String! + + # The tag to add. + tag: TagInput! +} + +# Input for the removeStoryTag mutation +input StoryTagRemoveInput { + # What story to remove the tag from. + id: String! + + # The tag to remove. + tag: TagInput! +} + # Possible values for Story.category enum StoryCategory { # Description and content of a document or item