From ed3e384d559d585a4af4a4f4873fd84182e2feb8 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sun, 15 Jul 2018 15:10:35 +0200 Subject: [PATCH] Moved query and mutation resolvers into individual files --- cmd/rpdata-graphiql/main.go | 2 +- graphql/resolver/channel.go | 156 --------- graphql/resolver/chapter.go | 161 --------- graphql/resolver/character.go | 299 ---------------- graphql/resolver/error.go | 15 - graphql/resolver/file.go | 115 ------ graphql/resolver/log.go | 229 ------------ graphql/resolver/mutations/addChannel.go | 59 ++++ graphql/resolver/mutations/addChapter.go | 67 ++++ graphql/resolver/mutations/addCharacter.go | 70 ++++ .../resolver/mutations/addCharacterNick.go | 48 +++ graphql/resolver/mutations/addLog.go | 70 ++++ graphql/resolver/mutations/addPost.go | 60 ++++ graphql/resolver/mutations/addStory.go | 83 +++++ graphql/resolver/mutations/addStoryTag.go | 53 +++ graphql/resolver/mutations/editChannel.go | 50 +++ graphql/resolver/mutations/editChapter.go | 63 ++++ graphql/resolver/mutations/editCharacter.go | 64 ++++ graphql/resolver/mutations/editFile.go | 43 +++ graphql/resolver/mutations/editLog.go | 51 +++ graphql/resolver/mutations/editPost.go | 63 ++++ graphql/resolver/mutations/editStory.go | 69 ++++ graphql/resolver/mutations/login.go | 26 ++ graphql/resolver/mutations/logout.go | 16 + graphql/resolver/mutations/movePost.go | 45 +++ graphql/resolver/mutations/removeChannel.go | 37 ++ graphql/resolver/mutations/removeChapter.go | 37 ++ graphql/resolver/mutations/removeCharacter.go | 44 +++ .../resolver/mutations/removeCharacterNick.go | 48 +++ graphql/resolver/mutations/removeFile.go | 37 ++ graphql/resolver/mutations/removeLog.go | 37 ++ graphql/resolver/mutations/removePost.go | 36 ++ graphql/resolver/mutations/removeStory.go | 41 +++ graphql/resolver/mutations/removeStoryTag.go | 53 +++ graphql/resolver/mutations/resolver.go | 14 + graphql/resolver/post.go | 208 ----------- graphql/resolver/queries/channel.go | 23 ++ graphql/resolver/queries/channels.go | 28 ++ graphql/resolver/queries/chapter.go | 23 ++ graphql/resolver/queries/character.go | 43 +++ graphql/resolver/queries/characters.go | 54 +++ graphql/resolver/queries/file.go | 23 ++ graphql/resolver/queries/files.go | 43 +++ graphql/resolver/queries/log.go | 32 ++ graphql/resolver/queries/logs.go | 79 +++++ graphql/resolver/queries/post.go | 23 ++ graphql/resolver/queries/posts.go | 28 ++ graphql/resolver/queries/resolver.go | 11 + graphql/resolver/queries/session.go | 13 + graphql/resolver/queries/stories.go | 77 ++++ graphql/resolver/queries/story.go | 23 ++ graphql/resolver/{tag.go => queries/tags.go} | 10 +- graphql/resolver/resolver.go | 13 + graphql/resolver/root.go | 13 - graphql/resolver/session.go | 39 --- graphql/resolver/story.go | 329 ------------------ 56 files changed, 1923 insertions(+), 1573 deletions(-) delete mode 100644 graphql/resolver/channel.go delete mode 100644 graphql/resolver/chapter.go delete mode 100644 graphql/resolver/character.go delete mode 100644 graphql/resolver/error.go delete mode 100644 graphql/resolver/file.go delete mode 100644 graphql/resolver/log.go create mode 100644 graphql/resolver/mutations/addChannel.go create mode 100644 graphql/resolver/mutations/addChapter.go create mode 100644 graphql/resolver/mutations/addCharacter.go create mode 100644 graphql/resolver/mutations/addCharacterNick.go create mode 100644 graphql/resolver/mutations/addLog.go create mode 100644 graphql/resolver/mutations/addPost.go create mode 100644 graphql/resolver/mutations/addStory.go create mode 100644 graphql/resolver/mutations/addStoryTag.go create mode 100644 graphql/resolver/mutations/editChannel.go create mode 100644 graphql/resolver/mutations/editChapter.go create mode 100644 graphql/resolver/mutations/editCharacter.go create mode 100644 graphql/resolver/mutations/editFile.go create mode 100644 graphql/resolver/mutations/editLog.go create mode 100644 graphql/resolver/mutations/editPost.go create mode 100644 graphql/resolver/mutations/editStory.go create mode 100644 graphql/resolver/mutations/login.go create mode 100644 graphql/resolver/mutations/logout.go create mode 100644 graphql/resolver/mutations/movePost.go create mode 100644 graphql/resolver/mutations/removeChannel.go create mode 100644 graphql/resolver/mutations/removeChapter.go create mode 100644 graphql/resolver/mutations/removeCharacter.go create mode 100644 graphql/resolver/mutations/removeCharacterNick.go create mode 100644 graphql/resolver/mutations/removeFile.go create mode 100644 graphql/resolver/mutations/removeLog.go create mode 100644 graphql/resolver/mutations/removePost.go create mode 100644 graphql/resolver/mutations/removeStory.go create mode 100644 graphql/resolver/mutations/removeStoryTag.go create mode 100644 graphql/resolver/mutations/resolver.go delete mode 100644 graphql/resolver/post.go create mode 100644 graphql/resolver/queries/channel.go create mode 100644 graphql/resolver/queries/channels.go create mode 100644 graphql/resolver/queries/chapter.go create mode 100644 graphql/resolver/queries/character.go create mode 100644 graphql/resolver/queries/characters.go create mode 100644 graphql/resolver/queries/file.go create mode 100644 graphql/resolver/queries/files.go create mode 100644 graphql/resolver/queries/log.go create mode 100644 graphql/resolver/queries/logs.go create mode 100644 graphql/resolver/queries/post.go create mode 100644 graphql/resolver/queries/posts.go create mode 100644 graphql/resolver/queries/resolver.go create mode 100644 graphql/resolver/queries/session.go create mode 100644 graphql/resolver/queries/stories.go create mode 100644 graphql/resolver/queries/story.go rename graphql/resolver/{tag.go => queries/tags.go} (73%) create mode 100644 graphql/resolver/resolver.go delete mode 100644 graphql/resolver/root.go delete mode 100644 graphql/resolver/session.go delete mode 100644 graphql/resolver/story.go diff --git a/cmd/rpdata-graphiql/main.go b/cmd/rpdata-graphiql/main.go index ab91a87..932a22b 100644 --- a/cmd/rpdata-graphiql/main.go +++ b/cmd/rpdata-graphiql/main.go @@ -30,7 +30,7 @@ func main() { } log.Println("Updated characters on", n, "logs") - schema, err := graphql.ParseSchema(schema.String(), &resolver.RootResolver{}, graphql.MaxParallelism(48)) + schema, err := graphql.ParseSchema(schema.String(), &resolver.Resolver{}, graphql.MaxParallelism(48)) if err != nil { log.Fatalln("Failed to parse schema:", err) } diff --git a/graphql/resolver/channel.go b/graphql/resolver/channel.go deleted file mode 100644 index fa7c620..0000000 --- a/graphql/resolver/channel.go +++ /dev/null @@ -1,156 +0,0 @@ -package resolver - -import ( - "context" - - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/internal/session" - "git.aiterp.net/rpdata/api/model/change" - "git.aiterp.net/rpdata/api/model/channel" -) - -// ChannelArgs is args for channel query -type ChannelArgs struct { - Name string -} - -// Channel implements the channel query -func (r *QueryResolver) Channel(ctx context.Context, args *ChannelArgs) (*types.ChannelResolver, error) { - channel, err := channel.FindName(args.Name) - if err != nil { - return nil, err - } - - return &types.ChannelResolver{C: channel}, nil -} - -// ChannelsArgs is args for channel query -type ChannelsArgs struct { - Logged *bool -} - -// Channels implements the channels query -func (r *QueryResolver) Channels(ctx context.Context, args *ChannelsArgs) ([]*types.ChannelResolver, error) { - channels, err := channel.List(args.Logged != nil && *args.Logged) - if err != nil { - return nil, err - } - - resolvers := make([]*types.ChannelResolver, len(channels)) - for i := range channels { - resolvers[i] = &types.ChannelResolver{C: channels[i]} - } - - return resolvers, nil -} - -// ChannelAddArgs is input for the addChannel mutation -type ChannelAddArgs struct { - Input *struct { - Name string - Logged *bool - Hub *bool - EventName *string - LocationName *string - } -} - -// AddChannel resolves the addChannel mutation -func (r *MutationResolver) AddChannel(ctx context.Context, args *ChannelAddArgs) (*types.ChannelResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("channel.add") { - return nil, ErrUnauthorized - } - - logged := input.Logged != nil && *input.Logged - - hub := input.Hub != nil && *input.Hub - - eventName := "" - if input.EventName != nil { - eventName = *input.EventName - } - - locationName := "" - if input.LocationName != nil { - locationName = *input.LocationName - } - - channel, err := channel.New(input.Name, logged, hub, eventName, locationName) - if err != nil { - return nil, err - } - - go change.Submit("Channel", "add", user.ID, channel.Name, map[string]interface{}{ - "logged": channel.Logged, - "hub": channel.Hub, - "location": input.LocationName, - "event": input.EventName, - }) - - return &types.ChannelResolver{C: channel}, nil -} - -// ChannelEditArgs is input for the editChannel mutation -type ChannelEditArgs struct { - Input *struct { - Name string - Logged *bool - Hub *bool - EventName *string - LocationName *string - } -} - -// EditChannel resolves the editChannel mutation -func (r *MutationResolver) EditChannel(ctx context.Context, args *ChannelEditArgs) (*types.ChannelResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("channel.edit") { - return nil, ErrUnauthorized - } - - channel, err := channel.FindName(input.Name) - if err != nil { - return nil, err - } - - err = channel.Edit(input.Logged, input.Hub, input.EventName, input.LocationName) - if err != nil { - return nil, err - } - - go change.Submit("Channel", "edit", user.ID, channel.Name, map[string]interface{}{ - "logged": input.Logged, - "hub": input.Hub, - "location": input.LocationName, - "event": input.EventName, - }) - - return &types.ChannelResolver{C: channel}, nil -} - -// RemoveChannel resolves the editChannel mutation -func (r *MutationResolver) RemoveChannel(ctx context.Context, args ChannelArgs) (*types.ChannelResolver, error) { - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("channel.remove") { - return nil, ErrUnauthorized - } - - channel, err := channel.FindName(args.Name) - if err != nil { - return nil, err - } - - err = channel.Remove() - if err != nil { - return nil, err - } - - go change.Submit("Channel", "remove", user.ID, channel.Name, nil) - - return &types.ChannelResolver{C: channel}, nil -} diff --git a/graphql/resolver/chapter.go b/graphql/resolver/chapter.go deleted file mode 100644 index ef2348a..0000000 --- a/graphql/resolver/chapter.go +++ /dev/null @@ -1,161 +0,0 @@ -package resolver - -import ( - "context" - "time" - - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/internal/session" - "git.aiterp.net/rpdata/api/model/change" - "git.aiterp.net/rpdata/api/model/story" -) - -// ChapterArgs is args for chapter query -type ChapterArgs struct { - ID string -} - -// Chapter implements the chapter query -func (r *QueryResolver) Chapter(ctx context.Context, args *ChapterArgs) (*types.ChapterResolver, error) { - chapter, err := story.FindChapterID(args.ID) - if err != nil { - return nil, err - } - - return &types.ChapterResolver{C: chapter}, nil -} - -// AddChapterArgs is args for the addChapter mutation -type AddChapterArgs struct { - Input *struct { - StoryID string - Title string - Author *string - Source string - FictionalDate *string - } -} - -// AddChapter implements the addChapter mutation -func (r *MutationResolver) AddChapter(ctx context.Context, args *AddChapterArgs) (*types.ChapterResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "chapter.add") { - return nil, ErrUnauthorized - } - - story, err := story.FindID(input.StoryID) - if err != nil { - return nil, err - } - - author := user.ID - if input.Author != nil { - author = *input.Author - - if user.ID != author && !user.Permitted("chapter.add") { - return nil, ErrPermissionDenied - } - } - - fictionalDate := time.Time{} - if input.FictionalDate != nil { - fictionalDate, err = time.Parse(time.RFC3339Nano, *input.FictionalDate) - if err != nil { - return nil, err - } - } - - chapter, err := story.AddChapter(input.Title, author, input.Source, time.Now(), fictionalDate) - if err != nil { - return nil, err - } - - go change.Submit("Chapter", "add", user.ID, chapter.ID, map[string]interface{}{ - "title": input.Title, - "author": author, - "fictionalDate": fictionalDate, - }) - - return &types.ChapterResolver{C: chapter}, nil -} - -// EditChapterArgs is args for the editChapter mutation -type EditChapterArgs struct { - Input *struct { - ID string - Title *string - Source *string - FictionalDate *string - } -} - -// EditChapter implements the editChapter mutation -func (r *MutationResolver) EditChapter(ctx context.Context, args *EditChapterArgs) (*types.ChapterResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "chapter.edit") { - return nil, ErrUnauthorized - } - - chapter, err := story.FindChapterID(input.ID) - if err != nil { - return nil, err - } - - if chapter.Author != user.ID && !user.Permitted("chapter.edit") { - return nil, ErrPermissionDenied - } - - var fictionalDate *time.Time - if input.FictionalDate != nil { - date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate) - if err != nil { - return nil, err - } - - fictionalDate = &date - } - - err = chapter.Edit(input.Title, input.Source, fictionalDate) - if err != nil { - return nil, err - } - - go change.Submit("Chapter", "edit", user.ID, chapter.ID, map[string]interface{}{ - "title": input.Title, - "source": input.Source, - "fictionalDate": fictionalDate, - }) - - return &types.ChapterResolver{C: chapter}, nil -} - -// DeleteChapterArgs is args for the addChapter mutation -type DeleteChapterArgs struct { - ID string -} - -// RemoveChapter implements the removeChapter mutation -func (r *MutationResolver) RemoveChapter(ctx context.Context, args *DeleteChapterArgs) (*types.ChapterResolver, error) { - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "chapter.edit") { - return nil, ErrUnauthorized - } - - chapter, err := story.FindChapterID(args.ID) - if err != nil { - return nil, err - } - - err = chapter.Remove() - if err != nil { - return nil, err - } - - go change.Submit("Chapter", "remove", user.ID, chapter.ID, nil) - - return &types.ChapterResolver{C: chapter}, nil -} diff --git a/graphql/resolver/character.go b/graphql/resolver/character.go deleted file mode 100644 index a648cdd..0000000 --- a/graphql/resolver/character.go +++ /dev/null @@ -1,299 +0,0 @@ -package resolver - -import ( - "context" - "errors" - "strings" - - "git.aiterp.net/rpdata/api/graphql/loader" - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/internal/session" - "git.aiterp.net/rpdata/api/model/change" - "git.aiterp.net/rpdata/api/model/character" - "git.aiterp.net/rpdata/api/model/log" -) - -// CharacterArgs is an arg -type CharacterArgs struct { - ID *string - Nick *string -} - -// Character resolver -func (r *QueryResolver) Character(ctx context.Context, args *CharacterArgs) (*types.CharacterResolver, error) { - var char character.Character - var err error - - loader := loader.FromContext(ctx) - if loader == nil { - return nil, errors.New("no loader") - } - - switch { - case args.ID != nil: - char, err = character.FindID(*args.ID) - case args.Nick != nil: - char, err = character.FindNick(*args.Nick) - default: - err = ErrCannotResolve - } - - if err != nil { - return nil, err - } - - return &types.CharacterResolver{C: char}, nil -} - -// CharactersArgs is an arg -type CharactersArgs struct { - IDs *[]string - Nicks *[]string - Author *string -} - -// Characters resolves the characters query -func (r *QueryResolver) Characters(ctx context.Context, args *CharactersArgs) ([]*types.CharacterResolver, error) { - var chars []character.Character - var err error - - loader := loader.FromContext(ctx) - if loader == nil { - return nil, errors.New("no loader") - } - - switch { - case args.IDs != nil: - chars, err = character.ListIDs(*args.IDs...) - case args.Nicks != nil: - chars, err = character.ListNicks(*args.Nicks...) - case args.Author != nil: - chars, err = character.ListAuthor(*args.Author) - default: - chars, err = character.List() - } - - if err != nil { - return nil, err - } - - resolvers := make([]*types.CharacterResolver, 0, len(chars)) - for i := range chars { - if chars[i].ID == "" { - continue - } - - resolvers = append(resolvers, &types.CharacterResolver{C: chars[i]}) - } - - return resolvers, nil -} - -// AddCharacterInput is args for the addCharacter mutation -type AddCharacterInput struct { - Nick string - Name string - ShortName *string - Author *string - Description *string -} - -// AddCharacter resolves the addCharacter mutation -func (r *MutationResolver) AddCharacter(ctx context.Context, args struct{ Input *AddCharacterInput }) (*types.CharacterResolver, error) { - input := args.Input - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "character.add") { - return nil, ErrUnauthorized - } - - nick := input.Nick - - name := input.Name - - shortName := "" - if input.ShortName != nil { - shortName = *input.ShortName - } else { - shortName = strings.SplitN(input.Name, " ", 2)[0] - } - - author := user.ID - if input.Author != nil { - author = *input.Author - - if author != user.ID && !user.Permitted("character.add") { - return nil, ErrPermissionDenied - } - } - - description := "" - if input.Description != nil { - description = *input.Description - } - - character, err := character.New(nick, name, shortName, author, description) - if err != nil { - return nil, err - } - - go change.Submit("Character", "add", user.ID, character.ID, map[string]interface{}{ - "name": character.Name, - "nick": character.Nicks[0], - "author": character.Author, - }) - - log.ScheduleCharacterUpdate() - - return &types.CharacterResolver{C: character}, nil -} - -// CharacterNickInput is args for mutation addCharacterNick/removeCharacterNick -type CharacterNickInput struct { - ID string - Nick string -} - -// AddCharacterNick resolves the addCharacterNick mutation -func (r *MutationResolver) AddCharacterNick(ctx context.Context, args struct{ Input *CharacterNickInput }) (*types.CharacterResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member") { - return nil, ErrUnauthorized - } - - character, err := character.FindID(input.ID) - if err != nil { - return nil, err - } - if character.Author != user.ID && !user.Permitted("character.edit") { - return nil, ErrPermissionDenied - } - - err = character.AddNick(input.Nick) - if err != nil { - return nil, err - } - - go change.Submit("Character", "add.nick", user.ID, character.ID, map[string]interface{}{ - "nick": input.Nick, - }) - - log.ScheduleCharacterUpdate() - - return &types.CharacterResolver{C: character}, nil -} - -// RemoveCharacterNick resolves the removeCharacterNick mutation -func (r *MutationResolver) RemoveCharacterNick(ctx context.Context, args struct{ Input *CharacterNickInput }) (*types.CharacterResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member") { - return nil, ErrUnauthorized - } - - character, err := character.FindID(input.ID) - if err != nil { - return nil, err - } - if character.Author != user.ID && !user.Permitted("character.edit") { - return nil, ErrPermissionDenied - } - - err = character.RemoveNick(input.Nick) - if err != nil { - return nil, err - } - - go change.Submit("Character", "remove.nick", user.ID, character.ID, map[string]interface{}{ - "nick": input.Nick, - }) - - log.ScheduleCharacterUpdate() - - return &types.CharacterResolver{C: character}, nil -} - -// CharacterEditInput is args for mutation addCharacterNick/removeCharacterNick -type CharacterEditInput struct { - ID string - Name *string - ShortName *string - Description *string -} - -// EditCharacter resolves the editCharacter mutation -func (r *MutationResolver) EditCharacter(ctx context.Context, args struct{ Input *CharacterEditInput }) (*types.CharacterResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member") { - return nil, ErrUnauthorized - } - - character, err := character.FindID(input.ID) - if err != nil { - return nil, err - } - if character.Author != user.ID && !user.Permitted("character.edit") { - return nil, ErrPermissionDenied - } - - name := "" - if input.Name != nil { - name = *input.Name - } - - shortName := "" - if input.ShortName != nil { - shortName = *input.ShortName - } - - description := "" - if input.Description != nil { - description = *input.Description - } - - err = character.Edit(name, shortName, description) - if err != nil { - return nil, err - } - - go change.Submit("Character", "edit", user.ID, character.ID, map[string]interface{}{ - "name": character.Name, - "shortName": character.ShortName, - "description": character.Description, - }) - - return &types.CharacterResolver{C: character}, nil -} - -// RemoveCharacter resolves the removeCharacter mutation -func (r *MutationResolver) RemoveCharacter(ctx context.Context, args struct{ ID string }) (*types.CharacterResolver, error) { - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member") { - return nil, ErrUnauthorized - } - - character, err := character.FindID(args.ID) - if err != nil { - return nil, err - } - if character.Author != user.ID && !user.Permitted("character.remove") { - return nil, ErrPermissionDenied - } - - err = character.Remove() - if err != nil { - return nil, err - } - - go change.Submit("Character", "remove", user.ID, character.ID, map[string]interface{}{ - "name": character.Name, - "author": character.Author, - "nicks": character.Nicks, - }) - - return &types.CharacterResolver{C: character}, nil -} diff --git a/graphql/resolver/error.go b/graphql/resolver/error.go deleted file mode 100644 index fe2a538..0000000 --- a/graphql/resolver/error.go +++ /dev/null @@ -1,15 +0,0 @@ -package resolver - -import "errors" - -// ErrCannotResolve is returned when a resolver constructor is at its wit's end -var ErrCannotResolve = errors.New("Cannot resolve due to invalid arguments") - -// ErrNotImplemented is for TODOs -var ErrNotImplemented = errors.New("Resolver not implemented") - -// ErrUnauthorized is when a guest acts like they own the place -var ErrUnauthorized = errors.New("Unauthorized") - -// ErrPermissionDenied is returned when users act above their station -var ErrPermissionDenied = errors.New("Permission denied") diff --git a/graphql/resolver/file.go b/graphql/resolver/file.go deleted file mode 100644 index 5d27ad3..0000000 --- a/graphql/resolver/file.go +++ /dev/null @@ -1,115 +0,0 @@ -package resolver - -import ( - "context" - - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/internal/session" - "git.aiterp.net/rpdata/api/model/file" -) - -// FileArgs is an arg -type FileArgs struct { - ID string -} - -// File implements the file query -func (r *QueryResolver) File(ctx context.Context, args *FileArgs) (*types.FileResolver, error) { - file, err := file.FindID(args.ID) - if err != nil { - return nil, err - } - - return &types.FileResolver{F: file}, nil -} - -// FilesArgs is an arg -type FilesArgs struct { - IncludePublic *bool - MimeTypes *[]string -} - -// Files implements the file query -func (r *QueryResolver) Files(ctx context.Context, args *FilesArgs) ([]*types.FileResolver, error) { - user := session.FromContext(ctx).User() - author := "" - if user != nil { - author = user.ID - } - - public := args.IncludePublic != nil && *args.IncludePublic == true - - mimeTypes := []string(nil) - if args.MimeTypes != nil { - mimeTypes = *args.MimeTypes - } - - files, err := file.List(author, public, mimeTypes) - if err != nil { - return nil, err - } - - resolvers := make([]*types.FileResolver, len(files)) - for i := range files { - resolvers[i] = &types.FileResolver{F: files[i]} - } - - return resolvers, nil -} - -// FileEditArgs is args for the editFile mutation -type FileEditArgs struct { - Input *struct { - ID string - Name *string - Public *bool - } -} - -// EditFile resolves the editFile mutation -func (r *MutationResolver) EditFile(ctx context.Context, args *FileEditArgs) (*types.FileResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member") { - return nil, ErrUnauthorized - } - - file, err := file.FindID(input.ID) - if err != nil { - return nil, err - } - if file.Author != user.ID && !user.Permitted("file.edit") { - return nil, ErrUnauthorized - } - - err = file.Edit(input.Name, input.Public) - if err != nil { - return nil, err - } - - return &types.FileResolver{F: file}, nil -} - -// RemoveFile resolves the removeFIle mutation -func (r *MutationResolver) RemoveFile(ctx context.Context, args *FileArgs) (*types.FileResolver, error) { - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member") { - return nil, ErrUnauthorized - } - - file, err := file.FindID(args.ID) - if err != nil { - return nil, err - } - if file.Author != user.ID && !user.Permitted("file.remove") { - return nil, ErrUnauthorized - } - - err = file.Delete() - if err != nil { - return nil, err - } - - return &types.FileResolver{F: file}, nil -} diff --git a/graphql/resolver/log.go b/graphql/resolver/log.go deleted file mode 100644 index 7e55b4d..0000000 --- a/graphql/resolver/log.go +++ /dev/null @@ -1,229 +0,0 @@ -package resolver - -import ( - "context" - "time" - - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/model/change" - - "git.aiterp.net/rpdata/api/internal/session" - "git.aiterp.net/rpdata/api/model/log" -) - -// LogArgs is an arg -type LogArgs struct { - ID *string -} - -// Log finds log -func (r *QueryResolver) Log(ctx context.Context, args *LogArgs) (*types.LogResolver, error) { - var l log.Log - var err error - - switch { - case args.ID != nil: - l, err = log.FindID(*args.ID) - default: - err = ErrCannotResolve - } - - if err != nil { - return nil, err - } - - return &types.LogResolver{L: l}, nil -} - -// LogQueryArgs is args for the logs query -type LogQueryArgs struct { - Filter *struct { - Search *string - Characters *[]string - Channels *[]string - Events *[]string - Open *bool - Limit *int32 - } -} - -// Logs resolves the logs query -func (r *QueryResolver) Logs(ctx context.Context, args *LogQueryArgs) ([]*types.LogResolver, error) { - var logs []log.Log - var err error - - filter := args.Filter - - if filter != nil { - limit := 100 - search := "" - - if filter.Search != nil { - search = *filter.Search - limit = 0 - } - - channels := []string(nil) - if filter.Channels != nil { - channels = *filter.Channels - limit = 0 - } - - characters := []string(nil) - if filter.Characters != nil { - characters = *filter.Characters - limit = 0 - } - - events := []string(nil) - if filter.Events != nil { - events = *filter.Events - limit = 0 - } - - if filter.Limit != nil { - limit = int(*filter.Limit) - } - - open := filter.Open != nil && *filter.Open == true - - logs, err = log.ListSearch(search, channels, characters, events, open, limit) - if err != nil { - return nil, err - } - } else { - logs, err = log.List(100) - if err != nil { - return nil, err - } - } - - resolvers := make([]*types.LogResolver, len(logs)) - for i := range logs { - resolvers[i] = &types.LogResolver{L: logs[i]} - } - - return resolvers, nil -} - -// LogAddArgs is args for the addLog mutation -type LogAddArgs struct { - Input *struct { - Date string - Channel string - Title *string - Open *bool - Event *string - Description *string - } -} - -// AddLog resolves the addLog mutation -func (r *MutationResolver) AddLog(ctx context.Context, args *LogAddArgs) (*types.LogResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("log.add") { - return nil, ErrUnauthorized - } - - date, err := time.Parse(time.RFC3339Nano, args.Input.Date) - if err != nil { - return nil, err - } - - title := "" - if input.Title != nil { - title = *input.Title - } - - event := "" - if input.Event != nil { - event = *input.Event - } - - description := "" - if input.Description != nil { - description = *input.Description - } - - open := input.Open != nil && *input.Open == true - - log, err := log.New(date, input.Channel, title, event, description, open) - if err != nil { - return nil, err - } - - go change.Submit("Log", "add", user.ID, log.ID, map[string]interface{}{ - "channel": log.Channel, - "title": log.Title, - "event": log.Event, - "description": log.Description, - "open": log.Open, - }) - - return &types.LogResolver{L: log}, nil -} - -// LogEditArgs is an input -type LogEditArgs struct { - Input *struct { - ID string - Title *string - Event *string - Description *string - Open *bool - } -} - -// EditLog resolves the editLog mutation -func (r *MutationResolver) EditLog(ctx context.Context, args *LogEditArgs) (*types.LogResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("log.edit") { - return nil, ErrUnauthorized - } - - log, err := log.FindID(input.ID) - if err != nil { - return nil, err - } - - err = log.Edit(input.Title, input.Event, input.Description, input.Open) - if err != nil { - return nil, err - } - - go change.Submit("Log", "edit", user.ID, log.ID, map[string]interface{}{ - "channel": log.Channel, - "title": input.Title, - "event": input.Event, - "description": input.Description, - "open": input.Open, - }) - - return &types.LogResolver{L: log}, nil -} - -// RemoveLog resolves the removeLog mutation -func (r *MutationResolver) RemoveLog(ctx context.Context, args *struct{ ID string }) (*types.LogResolver, error) { - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("log.remove") { - return nil, ErrUnauthorized - } - - log, err := log.FindID(args.ID) - if err != nil { - return nil, err - } - - err = log.Remove() - if err != nil { - return nil, err - } - - go change.Submit("Log", "remove", user.ID, log.ID, nil) - - return &types.LogResolver{L: log}, nil -} diff --git a/graphql/resolver/mutations/addChannel.go b/graphql/resolver/mutations/addChannel.go new file mode 100644 index 0000000..a4b6876 --- /dev/null +++ b/graphql/resolver/mutations/addChannel.go @@ -0,0 +1,59 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/channel" +) + +// ChannelAddArgs is input for the addChannel mutation +type ChannelAddArgs struct { + Input *struct { + Name string + Logged *bool + Hub *bool + EventName *string + LocationName *string + } +} + +// AddChannel resolves the addChannel mutation +func (r *MutationResolver) AddChannel(ctx context.Context, args *ChannelAddArgs) (*types.ChannelResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("channel.add") { + return nil, ErrUnauthorized + } + + logged := input.Logged != nil && *input.Logged + + hub := input.Hub != nil && *input.Hub + + eventName := "" + if input.EventName != nil { + eventName = *input.EventName + } + + locationName := "" + if input.LocationName != nil { + locationName = *input.LocationName + } + + channel, err := channel.New(input.Name, logged, hub, eventName, locationName) + if err != nil { + return nil, err + } + + go change.Submit("Channel", "add", user.ID, channel.Name, map[string]interface{}{ + "logged": channel.Logged, + "hub": channel.Hub, + "location": input.LocationName, + "event": input.EventName, + }) + + return &types.ChannelResolver{C: channel}, nil +} diff --git a/graphql/resolver/mutations/addChapter.go b/graphql/resolver/mutations/addChapter.go new file mode 100644 index 0000000..1ab302b --- /dev/null +++ b/graphql/resolver/mutations/addChapter.go @@ -0,0 +1,67 @@ +package mutations + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// AddChapterArgs is args for the addChapter mutation +type AddChapterArgs struct { + Input *struct { + StoryID string + Title string + Author *string + Source string + FictionalDate *string + } +} + +// AddChapter resolves the addChapter mutation +func (r *MutationResolver) AddChapter(ctx context.Context, args *AddChapterArgs) (*types.ChapterResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "chapter.add") { + return nil, ErrUnauthorized + } + + story, err := story.FindID(input.StoryID) + if err != nil { + return nil, err + } + + author := user.ID + if input.Author != nil { + author = *input.Author + + if user.ID != author && !user.Permitted("chapter.add") { + return nil, ErrPermissionDenied + } + } + + fictionalDate := time.Time{} + if input.FictionalDate != nil { + fictionalDate, err = time.Parse(time.RFC3339Nano, *input.FictionalDate) + if err != nil { + return nil, err + } + } + + chapter, err := story.AddChapter(input.Title, author, input.Source, time.Now(), fictionalDate) + if err != nil { + return nil, err + } + + go change.Submit("Chapter", "add", user.ID, chapter.ID, map[string]interface{}{ + "title": input.Title, + "author": author, + "fictionalDate": fictionalDate, + }) + + return &types.ChapterResolver{C: chapter}, nil +} diff --git a/graphql/resolver/mutations/addCharacter.go b/graphql/resolver/mutations/addCharacter.go new file mode 100644 index 0000000..8fc60e4 --- /dev/null +++ b/graphql/resolver/mutations/addCharacter.go @@ -0,0 +1,70 @@ +package mutations + +import ( + "context" + "strings" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/character" + "git.aiterp.net/rpdata/api/model/log" +) + +// AddCharacterInput is args for the addCharacter mutation +type AddCharacterInput struct { + Nick string + Name string + ShortName *string + Author *string + Description *string +} + +// AddCharacter resolves the addCharacter mutation +func (r *MutationResolver) AddCharacter(ctx context.Context, args struct{ Input *AddCharacterInput }) (*types.CharacterResolver, error) { + input := args.Input + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "character.add") { + return nil, ErrUnauthorized + } + + nick := input.Nick + + name := input.Name + + shortName := "" + if input.ShortName != nil { + shortName = *input.ShortName + } else { + shortName = strings.SplitN(input.Name, " ", 2)[0] + } + + author := user.ID + if input.Author != nil { + author = *input.Author + + if author != user.ID && !user.Permitted("character.add") { + return nil, ErrPermissionDenied + } + } + + description := "" + if input.Description != nil { + description = *input.Description + } + + character, err := character.New(nick, name, shortName, author, description) + if err != nil { + return nil, err + } + + go change.Submit("Character", "add", user.ID, character.ID, map[string]interface{}{ + "name": character.Name, + "nick": character.Nicks[0], + "author": character.Author, + }) + + log.ScheduleCharacterUpdate() + + return &types.CharacterResolver{C: character}, nil +} diff --git a/graphql/resolver/mutations/addCharacterNick.go b/graphql/resolver/mutations/addCharacterNick.go new file mode 100644 index 0000000..b07e0ea --- /dev/null +++ b/graphql/resolver/mutations/addCharacterNick.go @@ -0,0 +1,48 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/character" + "git.aiterp.net/rpdata/api/model/log" +) + +// AddCharacterNickInput is args for the addCharacterNick mutation +type AddCharacterNickInput struct { + ID string + Nick string +} + +// AddCharacterNick resolves the addCharacterNick mutation +func (r *MutationResolver) AddCharacterNick(ctx context.Context, args struct{ Input *AddCharacterNickInput }) (*types.CharacterResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + character, err := character.FindID(input.ID) + if err != nil { + return nil, err + } + if character.Author != user.ID && !user.Permitted("character.edit") { + return nil, ErrPermissionDenied + } + + err = character.AddNick(input.Nick) + if err != nil { + return nil, err + } + + go change.Submit("Character", "add.nick", user.ID, character.ID, map[string]interface{}{ + "nick": input.Nick, + }) + + log.ScheduleCharacterUpdate() + + return &types.CharacterResolver{C: character}, nil +} diff --git a/graphql/resolver/mutations/addLog.go b/graphql/resolver/mutations/addLog.go new file mode 100644 index 0000000..5fa7852 --- /dev/null +++ b/graphql/resolver/mutations/addLog.go @@ -0,0 +1,70 @@ +package mutations + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/log" +) + +// LogAddArgs is args for the addLog mutation +type LogAddArgs struct { + Input *struct { + Date string + Channel string + Title *string + Open *bool + Event *string + Description *string + } +} + +// AddLog resolves the addLog mutation +func (r *MutationResolver) AddLog(ctx context.Context, args *LogAddArgs) (*types.LogResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("log.add") { + return nil, ErrUnauthorized + } + + date, err := time.Parse(time.RFC3339Nano, args.Input.Date) + if err != nil { + return nil, err + } + + title := "" + if input.Title != nil { + title = *input.Title + } + + event := "" + if input.Event != nil { + event = *input.Event + } + + description := "" + if input.Description != nil { + description = *input.Description + } + + open := input.Open != nil && *input.Open == true + + log, err := log.New(date, input.Channel, title, event, description, open) + if err != nil { + return nil, err + } + + go change.Submit("Log", "add", user.ID, log.ID, map[string]interface{}{ + "channel": log.Channel, + "title": log.Title, + "event": log.Event, + "description": log.Description, + "open": log.Open, + }) + + return &types.LogResolver{L: log}, nil +} diff --git a/graphql/resolver/mutations/addPost.go b/graphql/resolver/mutations/addPost.go new file mode 100644 index 0000000..7989191 --- /dev/null +++ b/graphql/resolver/mutations/addPost.go @@ -0,0 +1,60 @@ +package mutations + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/log" +) + +// PostAddArgs is args for addPost mutation +type PostAddArgs struct { + Input *struct { + LogID string + Time string + Kind string + Nick string + Text string + } +} + +// AddPost resolves the addPost mutation +func (r *MutationResolver) AddPost(ctx context.Context, args *PostAddArgs) (*types.PostResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("post.add") { + return nil, ErrUnauthorized + } + + postTime, err := time.Parse(time.RFC3339Nano, input.Time) + if err != nil { + return nil, err + } + + log, err := log.FindID(input.LogID) + if err != nil { + return nil, err + } + + post, err := log.NewPost(postTime, input.Kind, input.Nick, input.Text) + if err != nil { + return nil, err + } + + go change.Submit("Post", "add", user.ID, post.ID, map[string]interface{}{ + "logId": post.LogID, + "time": post.Time, + "kind": post.Kind, + "nick": post.Nick, + "text": post.Text, + "position": post.Position, + }) + + go log.UpdateCharacters() + + return &types.PostResolver{P: post}, nil +} diff --git a/graphql/resolver/mutations/addStory.go b/graphql/resolver/mutations/addStory.go new file mode 100644 index 0000000..1cc6d50 --- /dev/null +++ b/graphql/resolver/mutations/addStory.go @@ -0,0 +1,83 @@ +package mutations + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// StoryAddArgs is args for the addStory mutation +type StoryAddArgs struct { + Input *struct { + Name string + Category string + Author *string + Open *bool + Listed *bool + FictionalDate *string + Tags *[]struct { + Kind string + Name string + } + } +} + +// AddStory resolves the addStory mutation +func (r *MutationResolver) AddStory(ctx context.Context, args *StoryAddArgs) (*types.StoryResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.add") { + return nil, ErrUnauthorized + } + + author := user.ID + if input.Author != nil { + author = *input.Author + + if user.ID != author && !user.Permitted("story.add") { + return nil, ErrPermissionDenied + } + } + + listed := (input.Listed != nil && *input.Listed == true) + + open := (input.Open != nil && *input.Open == true) + + tags := make([]story.Tag, 0, 8) + if input.Tags != nil { + for _, tagInput := range *input.Tags { + tags = append(tags, story.Tag{ + Kind: tagInput.Kind, + Name: tagInput.Name, + }) + } + } + + fictionalDate := time.Time{} + if input.FictionalDate != nil { + date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate) + if err != nil { + return nil, err + } + + fictionalDate = date + } + + story, err := story.New(input.Name, author, input.Category, listed, open, tags, time.Now(), fictionalDate) + if err != nil { + return nil, err + } + + go change.Submit("Story", "add", user.ID, story.ID, map[string]interface{}{ + "name": input.Name, + "category": input.Category, + "author": input.Author, + }) + + return &types.StoryResolver{S: story}, nil +} diff --git a/graphql/resolver/mutations/addStoryTag.go b/graphql/resolver/mutations/addStoryTag.go new file mode 100644 index 0000000..b49d68d --- /dev/null +++ b/graphql/resolver/mutations/addStoryTag.go @@ -0,0 +1,53 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// StoryTagAddArgs is args for the addStoryTag mutation +type StoryTagAddArgs struct { + Input *struct { + ID string + Tag struct { + Kind string + Name string + } + } +} + +// AddStoryTag resolves the addStoryTag mutation +func (r *MutationResolver) AddStoryTag(ctx context.Context, args *StoryTagAddArgs) (*types.StoryResolver, error) { + input := args.Input + tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name} + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.edit") { + return nil, ErrUnauthorized + } + + story, err := story.FindID(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 + } + + go change.Submit("Story", "add.tag", user.ID, story.ID, map[string]interface{}{ + "kind": tag.Kind, + "name": tag.Name, + }) + + return &types.StoryResolver{S: story}, nil +} diff --git a/graphql/resolver/mutations/editChannel.go b/graphql/resolver/mutations/editChannel.go new file mode 100644 index 0000000..6b4db7a --- /dev/null +++ b/graphql/resolver/mutations/editChannel.go @@ -0,0 +1,50 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/channel" +) + +// ChannelEditArgs is input for the editChannel mutation +type ChannelEditArgs struct { + Input *struct { + Name string + Logged *bool + Hub *bool + EventName *string + LocationName *string + } +} + +// EditChannel resolves the editChannel mutation +func (r *MutationResolver) EditChannel(ctx context.Context, args *ChannelEditArgs) (*types.ChannelResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("channel.edit") { + return nil, ErrUnauthorized + } + + channel, err := channel.FindName(input.Name) + if err != nil { + return nil, err + } + + err = channel.Edit(input.Logged, input.Hub, input.EventName, input.LocationName) + if err != nil { + return nil, err + } + + go change.Submit("Channel", "edit", user.ID, channel.Name, map[string]interface{}{ + "logged": input.Logged, + "hub": input.Hub, + "location": input.LocationName, + "event": input.EventName, + }) + + return &types.ChannelResolver{C: channel}, nil +} diff --git a/graphql/resolver/mutations/editChapter.go b/graphql/resolver/mutations/editChapter.go new file mode 100644 index 0000000..38dcd29 --- /dev/null +++ b/graphql/resolver/mutations/editChapter.go @@ -0,0 +1,63 @@ +package mutations + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// EditChapterArgs is args for the editChapter mutation +type EditChapterArgs struct { + Input *struct { + ID string + Title *string + Source *string + FictionalDate *string + } +} + +// EditChapter resolves the editChapter mutation +func (r *MutationResolver) EditChapter(ctx context.Context, args *EditChapterArgs) (*types.ChapterResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "chapter.edit") { + return nil, ErrUnauthorized + } + + chapter, err := story.FindChapterID(input.ID) + if err != nil { + return nil, err + } + + if chapter.Author != user.ID && !user.Permitted("chapter.edit") { + return nil, ErrPermissionDenied + } + + var fictionalDate *time.Time + if input.FictionalDate != nil { + date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate) + if err != nil { + return nil, err + } + + fictionalDate = &date + } + + err = chapter.Edit(input.Title, input.Source, fictionalDate) + if err != nil { + return nil, err + } + + go change.Submit("Chapter", "edit", user.ID, chapter.ID, map[string]interface{}{ + "title": input.Title, + "source": input.Source, + "fictionalDate": fictionalDate, + }) + + return &types.ChapterResolver{C: chapter}, nil +} diff --git a/graphql/resolver/mutations/editCharacter.go b/graphql/resolver/mutations/editCharacter.go new file mode 100644 index 0000000..0fb79b6 --- /dev/null +++ b/graphql/resolver/mutations/editCharacter.go @@ -0,0 +1,64 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/character" +) + +// CharacterEditInput is args for mutation addCharacterNick/removeCharacterNick +type CharacterEditInput struct { + ID string + Name *string + ShortName *string + Description *string +} + +// EditCharacter resolves the editCharacter mutation +func (r *MutationResolver) EditCharacter(ctx context.Context, args struct{ Input *CharacterEditInput }) (*types.CharacterResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + character, err := character.FindID(input.ID) + if err != nil { + return nil, err + } + if character.Author != user.ID && !user.Permitted("character.edit") { + return nil, ErrPermissionDenied + } + + name := "" + if input.Name != nil { + name = *input.Name + } + + shortName := "" + if input.ShortName != nil { + shortName = *input.ShortName + } + + description := "" + if input.Description != nil { + description = *input.Description + } + + err = character.Edit(name, shortName, description) + if err != nil { + return nil, err + } + + go change.Submit("Character", "edit", user.ID, character.ID, map[string]interface{}{ + "name": character.Name, + "shortName": character.ShortName, + "description": character.Description, + }) + + return &types.CharacterResolver{C: character}, nil +} diff --git a/graphql/resolver/mutations/editFile.go b/graphql/resolver/mutations/editFile.go new file mode 100644 index 0000000..454cba5 --- /dev/null +++ b/graphql/resolver/mutations/editFile.go @@ -0,0 +1,43 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/file" +) + +// FileEditArgs is args for the editFile mutation +type FileEditArgs struct { + Input *struct { + ID string + Name *string + Public *bool + } +} + +// EditFile resolves the editFile mutation +func (r *MutationResolver) EditFile(ctx context.Context, args *FileEditArgs) (*types.FileResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + file, err := file.FindID(input.ID) + if err != nil { + return nil, err + } + if file.Author != user.ID && !user.Permitted("file.edit") { + return nil, ErrUnauthorized + } + + err = file.Edit(input.Name, input.Public) + if err != nil { + return nil, err + } + + return &types.FileResolver{F: file}, nil +} diff --git a/graphql/resolver/mutations/editLog.go b/graphql/resolver/mutations/editLog.go new file mode 100644 index 0000000..4d54e07 --- /dev/null +++ b/graphql/resolver/mutations/editLog.go @@ -0,0 +1,51 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/log" +) + +// LogEditArgs is args for the editLog mutation +type LogEditArgs struct { + Input *struct { + ID string + Title *string + Event *string + Description *string + Open *bool + } +} + +// EditLog resolves the editLog mutation +func (r *MutationResolver) EditLog(ctx context.Context, args *LogEditArgs) (*types.LogResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("log.edit") { + return nil, ErrUnauthorized + } + + log, err := log.FindID(input.ID) + if err != nil { + return nil, err + } + + err = log.Edit(input.Title, input.Event, input.Description, input.Open) + if err != nil { + return nil, err + } + + go change.Submit("Log", "edit", user.ID, log.ID, map[string]interface{}{ + "channel": log.Channel, + "title": input.Title, + "event": input.Event, + "description": input.Description, + "open": input.Open, + }) + + return &types.LogResolver{L: log}, nil +} diff --git a/graphql/resolver/mutations/editPost.go b/graphql/resolver/mutations/editPost.go new file mode 100644 index 0000000..002e9b7 --- /dev/null +++ b/graphql/resolver/mutations/editPost.go @@ -0,0 +1,63 @@ +package mutations + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/log" +) + +// PostEditArgs is args for the editPost mutation +type PostEditArgs struct { + Input *struct { + ID string + Time *string + Kind *string + Nick *string + Text *string + } +} + +// EditPost resolves the editPost mutation +func (r *MutationResolver) EditPost(ctx context.Context, args *PostEditArgs) (*types.PostResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("post.edit") { + return nil, ErrUnauthorized + } + + postTime := (*time.Time)(nil) + if args.Input.Time != nil { + t, err := time.Parse(time.RFC3339Nano, *input.Time) + if err != nil { + return nil, err + } + + postTime = &t + } + + post, err := log.FindPostID(input.ID) + if err != nil { + return nil, err + } + + err = post.Edit(postTime, input.Kind, input.Nick, input.Text) + if err != nil { + return nil, err + } + + go change.Submit("Post", "edit", user.ID, post.ID, map[string]interface{}{ + "time": postTime, + "kind": input.Kind, + "nick": input.Nick, + "text": input.Text, + }) + + go log.UpdateCharacters(post.LogID) + + return &types.PostResolver{P: post}, nil +} diff --git a/graphql/resolver/mutations/editStory.go b/graphql/resolver/mutations/editStory.go new file mode 100644 index 0000000..55b07c0 --- /dev/null +++ b/graphql/resolver/mutations/editStory.go @@ -0,0 +1,69 @@ +package mutations + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// 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 resolves the editStory mutation +func (r *MutationResolver) EditStory(ctx context.Context, args *StoryEditArgs) (*types.StoryResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.edit") { + return nil, ErrUnauthorized + } + + story, err := story.FindID(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 input.FictionalDate != nil { + date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate) + if err != nil { + return nil, err + } + + fictionalDate = &date + } + + err = story.Edit(input.Name, input.Category, input.Listed, input.Open, fictionalDate) + if err != nil { + return nil, err + } + + go change.Submit("Story", "edit", user.ID, story.ID, map[string]interface{}{ + "name": input.Name, + "category": input.Category, + "author": input.Author, + "open": input.Open, + "listed": input.Listed, + "fictionalDate": input.FictionalDate, + }) + + return &types.StoryResolver{S: story}, nil +} diff --git a/graphql/resolver/mutations/login.go b/graphql/resolver/mutations/login.go new file mode 100644 index 0000000..9bea911 --- /dev/null +++ b/graphql/resolver/mutations/login.go @@ -0,0 +1,26 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" +) + +// LoginArgs is args for the login mutation +type LoginArgs struct { + Username string + Password string +} + +// Login resolves the login mutation +func (r *MutationResolver) Login(ctx context.Context, args *LoginArgs) (*types.SessionResolver, error) { + session := session.FromContext(ctx) + + err := session.Login(args.Username, args.Password) + if err != nil { + return nil, err + } + + return &types.SessionResolver{S: session}, nil +} diff --git a/graphql/resolver/mutations/logout.go b/graphql/resolver/mutations/logout.go new file mode 100644 index 0000000..ed9f8ec --- /dev/null +++ b/graphql/resolver/mutations/logout.go @@ -0,0 +1,16 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" +) + +// Logout resolves mutation.logout +func (r *MutationResolver) Logout(ctx context.Context) (*types.SessionResolver, error) { + session := session.FromContext(ctx) + session.Logout() + + return &types.SessionResolver{S: session}, nil +} diff --git a/graphql/resolver/mutations/movePost.go b/graphql/resolver/mutations/movePost.go new file mode 100644 index 0000000..7e48d6c --- /dev/null +++ b/graphql/resolver/mutations/movePost.go @@ -0,0 +1,45 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/log" +) + +// PostMoveArgs is args for movePost mutation +type PostMoveArgs struct { + Input *struct { + ID string + ToPosition int32 + } +} + +// MovePost resolves the movePost mutation +func (r *MutationResolver) MovePost(ctx context.Context, args *PostMoveArgs) (*types.PostResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("post.move") { + return nil, ErrUnauthorized + } + + post, err := log.FindPostID(input.ID) + if err != nil { + return nil, err + } + + err = post.Move(int(input.ToPosition)) + if err != nil { + return nil, err + } + + go change.Submit("Post", "move", user.ID, post.ID, map[string]interface{}{ + "logId": post.LogID, + "targetIndex": input.ToPosition, + }) + + return &types.PostResolver{P: post}, nil +} diff --git a/graphql/resolver/mutations/removeChannel.go b/graphql/resolver/mutations/removeChannel.go new file mode 100644 index 0000000..8b47b86 --- /dev/null +++ b/graphql/resolver/mutations/removeChannel.go @@ -0,0 +1,37 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/channel" +) + +// RemoveChannelArgs is args for removeChannels mutation +type RemoveChannelArgs struct { + Name string +} + +// RemoveChannel resolves the editChannel mutation +func (r *MutationResolver) RemoveChannel(ctx context.Context, args RemoveChannelArgs) (*types.ChannelResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("channel.remove") { + return nil, ErrUnauthorized + } + + channel, err := channel.FindName(args.Name) + if err != nil { + return nil, err + } + + err = channel.Remove() + if err != nil { + return nil, err + } + + go change.Submit("Channel", "remove", user.ID, channel.Name, nil) + + return &types.ChannelResolver{C: channel}, nil +} diff --git a/graphql/resolver/mutations/removeChapter.go b/graphql/resolver/mutations/removeChapter.go new file mode 100644 index 0000000..2350e20 --- /dev/null +++ b/graphql/resolver/mutations/removeChapter.go @@ -0,0 +1,37 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// RemoveChapterArgs is args for the addChapter mutation +type RemoveChapterArgs struct { + ID string +} + +// RemoveChapter resolves the removeChapter mutation +func (r *MutationResolver) RemoveChapter(ctx context.Context, args *RemoveChapterArgs) (*types.ChapterResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "chapter.edit") { + return nil, ErrUnauthorized + } + + chapter, err := story.FindChapterID(args.ID) + if err != nil { + return nil, err + } + + err = chapter.Remove() + if err != nil { + return nil, err + } + + go change.Submit("Chapter", "remove", user.ID, chapter.ID, nil) + + return &types.ChapterResolver{C: chapter}, nil +} diff --git a/graphql/resolver/mutations/removeCharacter.go b/graphql/resolver/mutations/removeCharacter.go new file mode 100644 index 0000000..7fefcbb --- /dev/null +++ b/graphql/resolver/mutations/removeCharacter.go @@ -0,0 +1,44 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/character" +) + +// RemoveCharacterArgs is args for the removeCharacter mutation +type RemoveCharacterArgs struct { + ID string +} + +// RemoveCharacter resolves the removeCharacter mutation +func (r *MutationResolver) RemoveCharacter(ctx context.Context, args RemoveCharacterArgs) (*types.CharacterResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + character, err := character.FindID(args.ID) + if err != nil { + return nil, err + } + if character.Author != user.ID && !user.Permitted("character.remove") { + return nil, ErrPermissionDenied + } + + err = character.Remove() + if err != nil { + return nil, err + } + + go change.Submit("Character", "remove", user.ID, character.ID, map[string]interface{}{ + "name": character.Name, + "author": character.Author, + "nicks": character.Nicks, + }) + + return &types.CharacterResolver{C: character}, nil +} diff --git a/graphql/resolver/mutations/removeCharacterNick.go b/graphql/resolver/mutations/removeCharacterNick.go new file mode 100644 index 0000000..4d9cd42 --- /dev/null +++ b/graphql/resolver/mutations/removeCharacterNick.go @@ -0,0 +1,48 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/character" + "git.aiterp.net/rpdata/api/model/log" +) + +// RemoveCharacterNick is args for the addCharacterNick mutation +type RemoveCharacterNick struct { + ID string + Nick string +} + +// RemoveCharacterNick resolves the removeCharacterNick mutation +func (r *MutationResolver) RemoveCharacterNick(ctx context.Context, args struct{ Input *RemoveCharacterNick }) (*types.CharacterResolver, error) { + input := args.Input + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + character, err := character.FindID(input.ID) + if err != nil { + return nil, err + } + if character.Author != user.ID && !user.Permitted("character.edit") { + return nil, ErrPermissionDenied + } + + err = character.RemoveNick(input.Nick) + if err != nil { + return nil, err + } + + go change.Submit("Character", "remove.nick", user.ID, character.ID, map[string]interface{}{ + "nick": input.Nick, + }) + + log.ScheduleCharacterUpdate() + + return &types.CharacterResolver{C: character}, nil +} diff --git a/graphql/resolver/mutations/removeFile.go b/graphql/resolver/mutations/removeFile.go new file mode 100644 index 0000000..61be7db --- /dev/null +++ b/graphql/resolver/mutations/removeFile.go @@ -0,0 +1,37 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/file" +) + +// RemoveFileArgs is an arg +type RemoveFileArgs struct { + ID string +} + +// RemoveFile resolves the removeFIle mutation +func (r *MutationResolver) RemoveFile(ctx context.Context, args *RemoveFileArgs) (*types.FileResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member") { + return nil, ErrUnauthorized + } + + file, err := file.FindID(args.ID) + if err != nil { + return nil, err + } + if file.Author != user.ID && !user.Permitted("file.remove") { + return nil, ErrUnauthorized + } + + err = file.Delete() + if err != nil { + return nil, err + } + + return &types.FileResolver{F: file}, nil +} diff --git a/graphql/resolver/mutations/removeLog.go b/graphql/resolver/mutations/removeLog.go new file mode 100644 index 0000000..af822bb --- /dev/null +++ b/graphql/resolver/mutations/removeLog.go @@ -0,0 +1,37 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/log" +) + +// RemoveLogArgs is args for the removeLog mutation +type RemoveLogArgs struct { + ID string +} + +// RemoveLog resolves the removeLog mutation +func (r *MutationResolver) RemoveLog(ctx context.Context, args *RemoveLogArgs) (*types.LogResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("log.remove") { + return nil, ErrUnauthorized + } + + log, err := log.FindID(args.ID) + if err != nil { + return nil, err + } + + err = log.Remove() + if err != nil { + return nil, err + } + + go change.Submit("Log", "remove", user.ID, log.ID, nil) + + return &types.LogResolver{L: log}, nil +} diff --git a/graphql/resolver/mutations/removePost.go b/graphql/resolver/mutations/removePost.go new file mode 100644 index 0000000..f656dbd --- /dev/null +++ b/graphql/resolver/mutations/removePost.go @@ -0,0 +1,36 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/log" +) + +// PostRemoveArgs is args for the removePost mutation +type PostRemoveArgs struct { + ID string +} + +// RemovePost resolves the removePost mutation +func (r *MutationResolver) RemovePost(ctx context.Context, args PostRemoveArgs) (*types.PostResolver, error) { + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("post.remove") { + return nil, ErrUnauthorized + } + + post, err := log.RemovePost(args.ID) + if err != nil { + return nil, err + } + + go change.Submit("Post", "remove", user.ID, post.ID, map[string]interface{}{ + "logId": post.LogID, + }) + + go log.UpdateCharacters(post.LogID) + + return &types.PostResolver{P: post}, nil +} diff --git a/graphql/resolver/mutations/removeStory.go b/graphql/resolver/mutations/removeStory.go new file mode 100644 index 0000000..d077ff8 --- /dev/null +++ b/graphql/resolver/mutations/removeStory.go @@ -0,0 +1,41 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// StoryRemoveArgs is args for the removeStory mutation +type StoryRemoveArgs struct { + ID string +} + +// RemoveStory resolves the removeStory mutation +func (r *MutationResolver) RemoveStory(ctx context.Context, args *StoryRemoveArgs) (*types.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 + } + + go change.Submit("Story", "remove", user.ID, story.ID, nil) + + return &types.StoryResolver{S: story}, nil +} diff --git a/graphql/resolver/mutations/removeStoryTag.go b/graphql/resolver/mutations/removeStoryTag.go new file mode 100644 index 0000000..05fbb1b --- /dev/null +++ b/graphql/resolver/mutations/removeStoryTag.go @@ -0,0 +1,53 @@ +package mutations + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/change" + "git.aiterp.net/rpdata/api/model/story" +) + +// StoryTagRemoveArgs is args for the removeStoryTag mutation +type StoryTagRemoveArgs struct { + Input *struct { + ID string + Tag struct { + Kind string + Name string + } + } +} + +// RemoveStoryTag resolves the removeStoryTag mutation +func (r *MutationResolver) RemoveStoryTag(ctx context.Context, args *StoryTagRemoveArgs) (*types.StoryResolver, error) { + input := args.Input + tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name} + + user := session.FromContext(ctx).User() + if user == nil || !user.Permitted("member", "story.edit") { + return nil, ErrUnauthorized + } + + story, err := story.FindID(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 + } + + go change.Submit("Story", "remove.tag", user.ID, story.ID, map[string]interface{}{ + "kind": tag.Kind, + "name": tag.Name, + }) + + return &types.StoryResolver{S: story}, nil +} diff --git a/graphql/resolver/mutations/resolver.go b/graphql/resolver/mutations/resolver.go new file mode 100644 index 0000000..9be4cb7 --- /dev/null +++ b/graphql/resolver/mutations/resolver.go @@ -0,0 +1,14 @@ +// Package mutations contains resolvers for individual GrahpQL mutations. They were previously mixed in with one another, but that caused +// difficulty seeking out specific mutations. +package mutations + +import "errors" + +// MutationResolver is a resolver for all mutations. The stuttery name is due to being embedded in the root resolver. +type MutationResolver struct{} + +// ErrUnauthorized is when a guest acts like they own the place +var ErrUnauthorized = errors.New("Unauthorized") + +// ErrPermissionDenied is returned when users act above their station +var ErrPermissionDenied = errors.New("Permission denied") diff --git a/graphql/resolver/post.go b/graphql/resolver/post.go deleted file mode 100644 index 819f43b..0000000 --- a/graphql/resolver/post.go +++ /dev/null @@ -1,208 +0,0 @@ -package resolver - -import ( - "context" - "time" - - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/internal/session" - "git.aiterp.net/rpdata/api/model/change" - "git.aiterp.net/rpdata/api/model/log" -) - -// PostArgs is an arg -type PostArgs struct { - ID string -} - -// Post implements the post query -func (r *QueryResolver) Post(ctx context.Context, args *PostArgs) (*types.PostResolver, error) { - post, err := log.FindPostID(args.ID) - if err != nil { - return nil, err - } - - return &types.PostResolver{P: post}, nil -} - -// PostsArgs is an arg -type PostsArgs struct { - IDs []string -} - -// Posts implements the posts query -func (r *QueryResolver) Posts(ctx context.Context, args *PostsArgs) ([]*types.PostResolver, error) { - posts, err := log.ListPostIDs(args.IDs...) - if err != nil { - return nil, err - } - - resolvers := make([]*types.PostResolver, len(posts)) - for i := range resolvers { - resolvers[i] = &types.PostResolver{P: posts[i]} - } - - return resolvers, nil -} - -// PostAddArgs is args for addPost mutation -type PostAddArgs struct { - Input *struct { - LogID string - Time string - Kind string - Nick string - Text string - } -} - -// AddPost resolves the addPost mutation -func (r *MutationResolver) AddPost(ctx context.Context, args *PostAddArgs) (*types.PostResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("post.add") { - return nil, ErrUnauthorized - } - - postTime, err := time.Parse(time.RFC3339Nano, input.Time) - if err != nil { - return nil, err - } - - log, err := log.FindID(input.LogID) - if err != nil { - return nil, err - } - - post, err := log.NewPost(postTime, input.Kind, input.Nick, input.Text) - if err != nil { - return nil, err - } - - go change.Submit("Post", "add", user.ID, post.ID, map[string]interface{}{ - "logId": post.LogID, - "time": post.Time, - "kind": post.Kind, - "nick": post.Nick, - "text": post.Text, - "position": post.Position, - }) - - go log.UpdateCharacters() - - return &types.PostResolver{P: post}, nil -} - -// PostEditArgs is args for the editPost mutation -type PostEditArgs struct { - Input *struct { - ID string - Time *string - Kind *string - Nick *string - Text *string - } -} - -// EditPost resolves the editPost mutation -func (r *MutationResolver) EditPost(ctx context.Context, args *PostEditArgs) (*types.PostResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("post.edit") { - return nil, ErrUnauthorized - } - - postTime := (*time.Time)(nil) - if args.Input.Time != nil { - t, err := time.Parse(time.RFC3339Nano, *input.Time) - if err != nil { - return nil, err - } - - postTime = &t - } - - post, err := log.FindPostID(input.ID) - if err != nil { - return nil, err - } - - err = post.Edit(postTime, input.Kind, input.Nick, input.Text) - if err != nil { - return nil, err - } - - go change.Submit("Post", "edit", user.ID, post.ID, map[string]interface{}{ - "time": postTime, - "kind": input.Kind, - "nick": input.Nick, - "text": input.Text, - }) - - go log.UpdateCharacters(post.LogID) - - return &types.PostResolver{P: post}, nil -} - -// PostMoveArgs is args for movePost mutation -type PostMoveArgs struct { - Input *struct { - ID string - ToPosition int32 - } -} - -// MovePost resolves the movePost mutation -func (r *MutationResolver) MovePost(ctx context.Context, args *PostMoveArgs) (*types.PostResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("post.move") { - return nil, ErrUnauthorized - } - - post, err := log.FindPostID(input.ID) - if err != nil { - return nil, err - } - - err = post.Move(int(input.ToPosition)) - if err != nil { - return nil, err - } - - go change.Submit("Post", "move", user.ID, post.ID, map[string]interface{}{ - "logId": post.LogID, - "targetIndex": input.ToPosition, - }) - - return &types.PostResolver{P: post}, nil -} - -// PostRemoveArgs is an arg -type PostRemoveArgs struct { - ID string -} - -// RemovePost resolves the removePost mutation -func (r *MutationResolver) RemovePost(ctx context.Context, args PostRemoveArgs) (*types.PostResolver, error) { - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("post.remove") { - return nil, ErrUnauthorized - } - - post, err := log.RemovePost(args.ID) - if err != nil { - return nil, err - } - - go change.Submit("Post", "remove", user.ID, post.ID, map[string]interface{}{ - "logId": post.LogID, - }) - - go log.UpdateCharacters(post.LogID) - - return &types.PostResolver{P: post}, nil -} diff --git a/graphql/resolver/queries/channel.go b/graphql/resolver/queries/channel.go new file mode 100644 index 0000000..13bc668 --- /dev/null +++ b/graphql/resolver/queries/channel.go @@ -0,0 +1,23 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/channel" +) + +// ChannelArgs is args for channel query +type ChannelArgs struct { + Name string +} + +// Channel resolves the channel query +func (r *QueryResolver) Channel(ctx context.Context, args *ChannelArgs) (*types.ChannelResolver, error) { + channel, err := channel.FindName(args.Name) + if err != nil { + return nil, err + } + + return &types.ChannelResolver{C: channel}, nil +} diff --git a/graphql/resolver/queries/channels.go b/graphql/resolver/queries/channels.go new file mode 100644 index 0000000..0b66686 --- /dev/null +++ b/graphql/resolver/queries/channels.go @@ -0,0 +1,28 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/channel" +) + +// ChannelsArgs is args for channel query +type ChannelsArgs struct { + Logged *bool +} + +// Channels resolves the channels query +func (r *QueryResolver) Channels(ctx context.Context, args *ChannelsArgs) ([]*types.ChannelResolver, error) { + channels, err := channel.List(args.Logged != nil && *args.Logged) + if err != nil { + return nil, err + } + + resolvers := make([]*types.ChannelResolver, len(channels)) + for i := range channels { + resolvers[i] = &types.ChannelResolver{C: channels[i]} + } + + return resolvers, nil +} diff --git a/graphql/resolver/queries/chapter.go b/graphql/resolver/queries/chapter.go new file mode 100644 index 0000000..8f390ab --- /dev/null +++ b/graphql/resolver/queries/chapter.go @@ -0,0 +1,23 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/story" +) + +// ChapterArgs is args for chapter query +type ChapterArgs struct { + ID string +} + +// Chapter resolves the chapter query +func (r *QueryResolver) Chapter(ctx context.Context, args *ChapterArgs) (*types.ChapterResolver, error) { + chapter, err := story.FindChapterID(args.ID) + if err != nil { + return nil, err + } + + return &types.ChapterResolver{C: chapter}, nil +} diff --git a/graphql/resolver/queries/character.go b/graphql/resolver/queries/character.go new file mode 100644 index 0000000..6016b32 --- /dev/null +++ b/graphql/resolver/queries/character.go @@ -0,0 +1,43 @@ +package queries + +import ( + "context" + "errors" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/character" +) + +// ErrIncorrectArguments is returned by character query. +var ErrIncorrectArguments = errors.New("You can only query character by ID or Nick") + +// CharacterArgs is args for character query +type CharacterArgs struct { + ID *string + Nick *string +} + +// Character resolves the character query +func (r *QueryResolver) Character(ctx context.Context, args *CharacterArgs) (*types.CharacterResolver, error) { + var char character.Character + var err error + + if args.Nick != nil && args.ID != nil { + return nil, ErrIncorrectArguments + } + + switch { + case args.ID != nil: + char, err = character.FindID(*args.ID) + case args.Nick != nil: + char, err = character.FindNick(*args.Nick) + default: + err = ErrIncorrectArguments + } + + if err != nil { + return nil, err + } + + return &types.CharacterResolver{C: char}, nil +} diff --git a/graphql/resolver/queries/characters.go b/graphql/resolver/queries/characters.go new file mode 100644 index 0000000..b6d994a --- /dev/null +++ b/graphql/resolver/queries/characters.go @@ -0,0 +1,54 @@ +package queries + +import ( + "context" + "errors" + + "git.aiterp.net/rpdata/api/graphql/loader" + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/character" +) + +// CharactersArgs is args for characters query +type CharactersArgs struct { + IDs *[]string + Nicks *[]string + Author *string +} + +// Characters resolves the characters query +func (r *QueryResolver) Characters(ctx context.Context, args *CharactersArgs) ([]*types.CharacterResolver, error) { + var chars []character.Character + var err error + + loader := loader.FromContext(ctx) + if loader == nil { + return nil, errors.New("no loader") + } + + switch { + case args.IDs != nil: + chars, err = character.ListIDs(*args.IDs...) + case args.Nicks != nil: + chars, err = character.ListNicks(*args.Nicks...) + case args.Author != nil: + chars, err = character.ListAuthor(*args.Author) + default: + chars, err = character.List() + } + + if err != nil { + return nil, err + } + + resolvers := make([]*types.CharacterResolver, 0, len(chars)) + for i := range chars { + if chars[i].ID == "" { + continue + } + + resolvers = append(resolvers, &types.CharacterResolver{C: chars[i]}) + } + + return resolvers, nil +} diff --git a/graphql/resolver/queries/file.go b/graphql/resolver/queries/file.go new file mode 100644 index 0000000..90d0623 --- /dev/null +++ b/graphql/resolver/queries/file.go @@ -0,0 +1,23 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/file" +) + +// FileArgs is args for file query +type FileArgs struct { + ID string +} + +// File resolves the file query +func (r *QueryResolver) File(ctx context.Context, args *FileArgs) (*types.FileResolver, error) { + file, err := file.FindID(args.ID) + if err != nil { + return nil, err + } + + return &types.FileResolver{F: file}, nil +} diff --git a/graphql/resolver/queries/files.go b/graphql/resolver/queries/files.go new file mode 100644 index 0000000..1806839 --- /dev/null +++ b/graphql/resolver/queries/files.go @@ -0,0 +1,43 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" + "git.aiterp.net/rpdata/api/model/file" +) + +// FilesArgs is args for files query +type FilesArgs struct { + IncludePublic *bool + MimeTypes *[]string +} + +// Files resolves the file query +func (r *QueryResolver) Files(ctx context.Context, args *FilesArgs) ([]*types.FileResolver, error) { + user := session.FromContext(ctx).User() + author := "" + if user != nil { + author = user.ID + } + + public := args.IncludePublic != nil && *args.IncludePublic == true + + mimeTypes := []string(nil) + if args.MimeTypes != nil { + mimeTypes = *args.MimeTypes + } + + files, err := file.List(author, public, mimeTypes) + if err != nil { + return nil, err + } + + resolvers := make([]*types.FileResolver, len(files)) + for i := range files { + resolvers[i] = &types.FileResolver{F: files[i]} + } + + return resolvers, nil +} diff --git a/graphql/resolver/queries/log.go b/graphql/resolver/queries/log.go new file mode 100644 index 0000000..f6e6e43 --- /dev/null +++ b/graphql/resolver/queries/log.go @@ -0,0 +1,32 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/log" +) + +// LogArgs is args for log query +type LogArgs struct { + ID *string +} + +// Log resolves the log query +func (r *QueryResolver) Log(ctx context.Context, args *LogArgs) (*types.LogResolver, error) { + var l log.Log + var err error + + switch { + case args.ID != nil: + l, err = log.FindID(*args.ID) + default: + err = ErrCannotResolve + } + + if err != nil { + return nil, err + } + + return &types.LogResolver{L: l}, nil +} diff --git a/graphql/resolver/queries/logs.go b/graphql/resolver/queries/logs.go new file mode 100644 index 0000000..5dd9992 --- /dev/null +++ b/graphql/resolver/queries/logs.go @@ -0,0 +1,79 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/log" +) + +// LogsArgs is args for the logs query +type LogsArgs struct { + Filter *struct { + Search *string + Characters *[]string + Channels *[]string + Events *[]string + Open *bool + Limit *int32 + } +} + +// Logs resolves the logs query +func (r *QueryResolver) Logs(ctx context.Context, args *LogsArgs) ([]*types.LogResolver, error) { + var logs []log.Log + var err error + + filter := args.Filter + + if filter != nil { + limit := 100 + search := "" + + if filter.Search != nil { + search = *filter.Search + limit = 0 + } + + channels := []string(nil) + if filter.Channels != nil { + channels = *filter.Channels + limit = 0 + } + + characters := []string(nil) + if filter.Characters != nil { + characters = *filter.Characters + limit = 0 + } + + events := []string(nil) + if filter.Events != nil { + events = *filter.Events + limit = 0 + } + + if filter.Limit != nil { + limit = int(*filter.Limit) + } + + open := filter.Open != nil && *filter.Open == true + + logs, err = log.ListSearch(search, channels, characters, events, open, limit) + if err != nil { + return nil, err + } + } else { + logs, err = log.List(100) + if err != nil { + return nil, err + } + } + + resolvers := make([]*types.LogResolver, len(logs)) + for i := range logs { + resolvers[i] = &types.LogResolver{L: logs[i]} + } + + return resolvers, nil +} diff --git a/graphql/resolver/queries/post.go b/graphql/resolver/queries/post.go new file mode 100644 index 0000000..1757292 --- /dev/null +++ b/graphql/resolver/queries/post.go @@ -0,0 +1,23 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/log" +) + +// PostArgs is args for the post query +type PostArgs struct { + ID string +} + +// Post resolves the post query +func (r *QueryResolver) Post(ctx context.Context, args *PostArgs) (*types.PostResolver, error) { + post, err := log.FindPostID(args.ID) + if err != nil { + return nil, err + } + + return &types.PostResolver{P: post}, nil +} diff --git a/graphql/resolver/queries/posts.go b/graphql/resolver/queries/posts.go new file mode 100644 index 0000000..54ed7e0 --- /dev/null +++ b/graphql/resolver/queries/posts.go @@ -0,0 +1,28 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/log" +) + +// PostsArgs is args for the posts query +type PostsArgs struct { + IDs []string +} + +// Posts resolves the posts query +func (r *QueryResolver) Posts(ctx context.Context, args *PostsArgs) ([]*types.PostResolver, error) { + posts, err := log.ListPostIDs(args.IDs...) + if err != nil { + return nil, err + } + + resolvers := make([]*types.PostResolver, len(posts)) + for i := range resolvers { + resolvers[i] = &types.PostResolver{P: posts[i]} + } + + return resolvers, nil +} diff --git a/graphql/resolver/queries/resolver.go b/graphql/resolver/queries/resolver.go new file mode 100644 index 0000000..7b547ae --- /dev/null +++ b/graphql/resolver/queries/resolver.go @@ -0,0 +1,11 @@ +// Package queries contains resolvers for individual GrahpQL queries. They were previously mixed in with one another, but that caused +// difficulty seeking out specific queries. +package queries + +import "errors" + +// QueryResolver is a resolver for all queries. The stuttery name is due to being embedded in the root resolver. +type QueryResolver struct{} + +// ErrCannotResolve is returned when a resolver constructor is at its wit's end +var ErrCannotResolve = errors.New("Cannot resolve due to invalid arguments") diff --git a/graphql/resolver/queries/session.go b/graphql/resolver/queries/session.go new file mode 100644 index 0000000..b076131 --- /dev/null +++ b/graphql/resolver/queries/session.go @@ -0,0 +1,13 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/internal/session" +) + +// Session resolves query.session +func (r *QueryResolver) Session(ctx context.Context) (*types.SessionResolver, error) { + return &types.SessionResolver{S: session.FromContext(ctx)}, nil +} diff --git a/graphql/resolver/queries/stories.go b/graphql/resolver/queries/stories.go new file mode 100644 index 0000000..cbf572e --- /dev/null +++ b/graphql/resolver/queries/stories.go @@ -0,0 +1,77 @@ +package queries + +import ( + "context" + "time" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/story" +) + +// StoriesArg is args for stories query +type StoriesArg struct { + Filter *struct { + Author *string + Tags *[]struct { + Kind string + Name string + } + EarliestFictionalDate *string + LatestFictionalDate *string + Limit *int32 + } +} + +// Stories resolves the stories query +func (r *QueryResolver) Stories(ctx context.Context, args *StoriesArg) ([]*types.StoryResolver, error) { + filter := args.Filter + + author := "" + if filter != nil && filter.Author != nil { + author = *filter.Author + } + + tags := make([]story.Tag, 0, 8) + if filter != nil && filter.Tags != nil { + for _, tagInput := range *filter.Tags { + tags = append(tags, story.Tag{ + Kind: tagInput.Kind, + Name: tagInput.Name, + }) + } + } + + earliest := time.Time{} + err := error(nil) + if filter != nil && filter.EarliestFictionalDate != nil { + earliest, err = time.Parse(time.RFC3339Nano, *filter.EarliestFictionalDate) + if err != nil { + return nil, err + } + } + + latest := time.Time{} + if filter != nil && filter.LatestFictionalDate != nil { + latest, err = time.Parse(time.RFC3339Nano, *filter.LatestFictionalDate) + if err != nil { + return nil, err + } + } + + limit := 30 + if filter != nil && filter.Limit != nil { + limit = int(*filter.Limit) + } + + stories, err := story.List(author, tags, earliest, latest, limit) + if err != nil { + return nil, err + } + + resolvers := make([]*types.StoryResolver, len(stories)) + for i, story := range stories { + resolvers[i] = &types.StoryResolver{S: story} + } + + return resolvers, nil +} diff --git a/graphql/resolver/queries/story.go b/graphql/resolver/queries/story.go new file mode 100644 index 0000000..a9d1d81 --- /dev/null +++ b/graphql/resolver/queries/story.go @@ -0,0 +1,23 @@ +package queries + +import ( + "context" + + "git.aiterp.net/rpdata/api/graphql/resolver/types" + "git.aiterp.net/rpdata/api/model/story" +) + +// StoryArgs is args for story query +type StoryArgs struct { + ID string +} + +// Story resolves the story query +func (r *QueryResolver) Story(ctx context.Context, args *StoryArgs) (*types.StoryResolver, error) { + story, err := story.FindID(args.ID) + if err != nil { + return nil, err + } + + return &types.StoryResolver{S: story}, nil +} diff --git a/graphql/resolver/tag.go b/graphql/resolver/queries/tags.go similarity index 73% rename from graphql/resolver/tag.go rename to graphql/resolver/queries/tags.go index 07ed69a..4dce550 100644 --- a/graphql/resolver/tag.go +++ b/graphql/resolver/queries/tags.go @@ -1,17 +1,11 @@ -package resolver +package queries import ( "git.aiterp.net/rpdata/api/graphql/resolver/types" "git.aiterp.net/rpdata/api/model/story" ) -// TagInput resolves the TagInput input -type TagInput struct { - Kind string - Name string -} - -// Tags implements the tags query +// Tags resolves the tags query func (r *QueryResolver) Tags() ([]*types.TagResolver, error) { tags, err := story.ListTags() if err != nil { diff --git a/graphql/resolver/resolver.go b/graphql/resolver/resolver.go new file mode 100644 index 0000000..84606c2 --- /dev/null +++ b/graphql/resolver/resolver.go @@ -0,0 +1,13 @@ +package resolver + +import ( + "git.aiterp.net/rpdata/api/graphql/resolver/mutations" + "git.aiterp.net/rpdata/api/graphql/resolver/queries" +) + +// The Resolver combines the query and mutation resolvers from the subpackages. +// This is the one to pass along with the schema. +type Resolver struct { + queries.QueryResolver + mutations.MutationResolver +} diff --git a/graphql/resolver/root.go b/graphql/resolver/root.go deleted file mode 100644 index 9830c0b..0000000 --- a/graphql/resolver/root.go +++ /dev/null @@ -1,13 +0,0 @@ -package resolver - -// The RootResolver brings queries and mutations together. The rest is just for readability. -type RootResolver struct { - MutationResolver - QueryResolver -} - -// The QueryResolver is the entry point for all top-level read operations. -type QueryResolver struct{} - -// The MutationResolver is the entry point for all top-level mutation operations. -type MutationResolver struct{} diff --git a/graphql/resolver/session.go b/graphql/resolver/session.go deleted file mode 100644 index 2676193..0000000 --- a/graphql/resolver/session.go +++ /dev/null @@ -1,39 +0,0 @@ -package resolver - -import ( - "context" - - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/internal/session" -) - -// LoginArgs is args -type LoginArgs struct { - Username string - Password string -} - -// Session resolves query.session -func (r *QueryResolver) Session(ctx context.Context) (*types.SessionResolver, error) { - return &types.SessionResolver{S: session.FromContext(ctx)}, nil -} - -// Login resolves mutation.login -func (r *MutationResolver) Login(ctx context.Context, args *LoginArgs) (*types.SessionResolver, error) { - session := session.FromContext(ctx) - - err := session.Login(args.Username, args.Password) - if err != nil { - return nil, err - } - - return &types.SessionResolver{S: session}, nil -} - -// Logout resolves mutation.logout -func (r *MutationResolver) Logout(ctx context.Context) (*types.SessionResolver, error) { - session := session.FromContext(ctx) - session.Logout() - - return &types.SessionResolver{S: session}, nil -} diff --git a/graphql/resolver/story.go b/graphql/resolver/story.go deleted file mode 100644 index 6cf2ea8..0000000 --- a/graphql/resolver/story.go +++ /dev/null @@ -1,329 +0,0 @@ -package resolver - -import ( - "context" - "time" - - "git.aiterp.net/rpdata/api/graphql/resolver/types" - "git.aiterp.net/rpdata/api/internal/session" - "git.aiterp.net/rpdata/api/model/change" - "git.aiterp.net/rpdata/api/model/story" -) - -// StoryArgs is args for story query -type StoryArgs struct { - ID string -} - -// Story implements the story query -func (r *QueryResolver) Story(ctx context.Context, args *StoryArgs) (*types.StoryResolver, error) { - story, err := story.FindID(args.ID) - if err != nil { - return nil, err - } - - return &types.StoryResolver{S: story}, nil -} - -// StoriesArg is args for stories query -type StoriesArg struct { - Filter *struct { - Author *string - Tags *[]TagInput - EarliestFictionalDate *string - LatestFictionalDate *string - Limit *int32 - } -} - -// Stories implements the stories query -func (r *QueryResolver) Stories(ctx context.Context, args *StoriesArg) ([]*types.StoryResolver, error) { - filter := args.Filter - - author := "" - if filter != nil && filter.Author != nil { - author = *filter.Author - } - - tags := make([]story.Tag, 0, 8) - if filter != nil && filter.Tags != nil { - for _, tagInput := range *filter.Tags { - tags = append(tags, story.Tag{ - Kind: tagInput.Kind, - Name: tagInput.Name, - }) - } - } - - earliest := time.Time{} - err := error(nil) - if filter != nil && filter.EarliestFictionalDate != nil { - earliest, err = time.Parse(time.RFC3339Nano, *filter.EarliestFictionalDate) - if err != nil { - return nil, err - } - } - - latest := time.Time{} - if filter != nil && filter.LatestFictionalDate != nil { - latest, err = time.Parse(time.RFC3339Nano, *filter.LatestFictionalDate) - if err != nil { - return nil, err - } - } - - limit := 30 - if filter != nil && filter.Limit != nil { - limit = int(*filter.Limit) - } - - stories, err := story.List(author, tags, earliest, latest, limit) - if err != nil { - return nil, err - } - - resolvers := make([]*types.StoryResolver, len(stories)) - for i, story := range stories { - resolvers[i] = &types.StoryResolver{S: 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) (*types.StoryResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "story.add") { - return nil, ErrUnauthorized - } - - author := user.ID - if input.Author != nil { - author = *input.Author - - if user.ID != author && !user.Permitted("story.add") { - return nil, ErrPermissionDenied - } - } - - listed := (input.Listed != nil && *input.Listed == true) - - open := (input.Open != nil && *input.Open == true) - - tags := make([]story.Tag, 0, 8) - if input.Tags != nil { - for _, tagInput := range *input.Tags { - tags = append(tags, story.Tag{ - Kind: tagInput.Kind, - Name: tagInput.Name, - }) - } - } - - fictionalDate := time.Time{} - if input.FictionalDate != nil { - date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate) - if err != nil { - return nil, err - } - - fictionalDate = date - } - - story, err := story.New(input.Name, author, input.Category, listed, open, tags, time.Now(), fictionalDate) - if err != nil { - return nil, err - } - - go change.Submit("Story", "add", user.ID, story.ID, map[string]interface{}{ - "name": input.Name, - "category": input.Category, - "author": input.Author, - }) - - return &types.StoryResolver{S: story}, nil -} - -// StoryTagAddArgs is args for the addStoryTag mutation -type StoryTagAddArgs struct { - Input *struct { - ID string - Tag TagInput - } -} - -// AddStoryTag implements the addStoryTag mutation -func (r *MutationResolver) AddStoryTag(ctx context.Context, args *StoryTagAddArgs) (*types.StoryResolver, error) { - input := args.Input - tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name} - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "story.edit") { - return nil, ErrUnauthorized - } - - story, err := story.FindID(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 - } - - go change.Submit("Story", "add.tag", user.ID, story.ID, map[string]interface{}{ - "kind": tag.Kind, - "name": tag.Name, - }) - - return &types.StoryResolver{S: story}, nil -} - -// StoryTagRemoveArgs is args for the removeStoryTag mutation -type StoryTagRemoveArgs struct { - Input *struct { - ID string - Tag TagInput - } -} - -// RemoveStoryTag implements the removeStoryTag mutation -func (r *MutationResolver) RemoveStoryTag(ctx context.Context, args *StoryTagRemoveArgs) (*types.StoryResolver, error) { - input := args.Input - tag := story.Tag{Kind: input.Tag.Kind, Name: input.Tag.Name} - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "story.edit") { - return nil, ErrUnauthorized - } - - story, err := story.FindID(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 - } - - go change.Submit("Story", "remove.tag", user.ID, story.ID, map[string]interface{}{ - "kind": tag.Kind, - "name": tag.Name, - }) - - return &types.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) (*types.StoryResolver, error) { - input := args.Input - - user := session.FromContext(ctx).User() - if user == nil || !user.Permitted("member", "story.edit") { - return nil, ErrUnauthorized - } - - story, err := story.FindID(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 input.FictionalDate != nil { - date, err := time.Parse(time.RFC3339Nano, *input.FictionalDate) - if err != nil { - return nil, err - } - - fictionalDate = &date - } - - err = story.Edit(input.Name, input.Category, input.Listed, input.Open, fictionalDate) - if err != nil { - return nil, err - } - - go change.Submit("Story", "edit", user.ID, story.ID, map[string]interface{}{ - "name": input.Name, - "category": input.Category, - "author": input.Author, - "open": input.Open, - "listed": input.Listed, - "fictionalDate": input.FictionalDate, - }) - - return &types.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) (*types.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 - } - - go change.Submit("Story", "remove", user.ID, story.ID, nil) - - return &types.StoryResolver{S: story}, nil -}