From 162ccf6bfbda21bf546958305ccd35577dd2d084 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Fri, 17 May 2019 11:51:23 +0200 Subject: [PATCH] The great module madness continus, now with pointers. --- graph2/graph.go | 4 -- graph2/queries/changes.go | 2 +- graph2/queries/channel.go | 27 +++++++------ graph2/queries/chapter.go | 69 +++++++++++++++++---------------- graph2/queries/character.go | 76 +++++++++++++++++++++---------------- graph2/queries/comment.go | 57 +++++++++++++++------------- graph2/queries/file.go | 9 ++++- graph2/queries/log.go | 37 ++++++++++-------- graph2/queries/post.go | 39 ++++++++++--------- graph2/queries/story.go | 65 ++++++++++++++++--------------- graph2/queries/token.go | 6 +-- graph2/types/change.go | 8 ++-- graph2/types/file.go | 16 -------- graph2/types/log.go | 4 +- graph2/types/token.go | 9 ++++- internal/loader/channel.go | 10 ++--- models/changes/subscribe.go | 12 +++--- models/channel.go | 6 +++ models/chapter.go | 6 +++ models/character.go | 6 +++ models/comment.go | 6 +++ models/file.go | 6 +++ models/log.go | 6 +++ models/post.go | 6 +++ models/story.go | 6 +++ models/tag.go | 6 +++ 26 files changed, 295 insertions(+), 209 deletions(-) delete mode 100644 graph2/types/file.go diff --git a/graph2/graph.go b/graph2/graph.go index f810fe5..f8bb3a8 100644 --- a/graph2/graph.go +++ b/graph2/graph.go @@ -47,10 +47,6 @@ func (r *rootResolver) Story() StoryResolver { return &types.StoryResolver } -func (r *rootResolver) File() FileResolver { - return &types.FileResolver -} - func (r *rootResolver) Change() ChangeResolver { return &types.ChangeResolver } diff --git a/graph2/queries/changes.go b/graph2/queries/changes.go index 0d53269..c167287 100644 --- a/graph2/queries/changes.go +++ b/graph2/queries/changes.go @@ -16,7 +16,7 @@ func (r *resolver) Changes(ctx context.Context, filter *changes.Filter) ([]model /// Subscriptions -func (r *subscriptionResolver) Changes(ctx context.Context, keys []models.ChangeKey) (<-chan models.Change, error) { +func (r *subscriptionResolver) Changes(ctx context.Context, keys []models.ChangeKey) (<-chan *models.Change, error) { if len(keys) == 0 { return nil, errors.New("At least one key is required for a subscription") } diff --git a/graph2/queries/channel.go b/graph2/queries/channel.go index 79b5049..16760f3 100644 --- a/graph2/queries/channel.go +++ b/graph2/queries/channel.go @@ -14,8 +14,13 @@ import ( // Queries -func (r *resolver) Channel(ctx context.Context, name string) (models.Channel, error) { - return channels.FindName(name) +func (r *resolver) Channel(ctx context.Context, name string) (*models.Channel, error) { + channel, err := channels.FindName(name) + if err != nil { + return nil, err + } + + return &channel, nil } func (r *resolver) Channels(ctx context.Context, filter *channels.Filter) ([]models.Channel, error) { @@ -24,10 +29,10 @@ func (r *resolver) Channels(ctx context.Context, filter *channels.Filter) ([]mod // Mutations -func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAddInput) (models.Channel, error) { +func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAddInput) (*models.Channel, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("channel.add") { - return models.Channel{}, errors.New("You are not permitted to add channels") + return nil, errors.New("You are not permitted to add channels") } logged := false @@ -49,31 +54,31 @@ func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAd channel, err := channels.Add(input.Name, logged, hub, eventName, locationName) if err != nil { - return models.Channel{}, errors.New("Failed to add channel: " + err.Error()) + return nil, errors.New("Failed to add channel: " + err.Error()) } go changes.Submit("Channel", "add", token.UserID, true, changekeys.Listed(channel), channel) - return channel, nil + return &channel, nil } -func (r *mutationResolver) EditChannel(ctx context.Context, input input.ChannelEditInput) (models.Channel, error) { +func (r *mutationResolver) EditChannel(ctx context.Context, input input.ChannelEditInput) (*models.Channel, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("channel.edit") { - return models.Channel{}, errors.New("You are not permitted to edit channels") + return nil, errors.New("You are not permitted to edit channels") } channel, err := channels.FindName(input.Name) if err != nil { - return models.Channel{}, errors.New("Channel not found") + return nil, errors.New("Channel not found") } channel, err = channels.Edit(channel, input.Logged, input.Hub, input.EventName, input.LocationName) if err != nil { - return models.Channel{}, errors.New("Failed to edit channel: " + err.Error()) + return nil, errors.New("Failed to edit channel: " + err.Error()) } go changes.Submit("Channel", "edit", token.UserID, true, changekeys.Listed(channel), channel) - return channel, nil + return &channel, nil } diff --git a/graph2/queries/chapter.go b/graph2/queries/chapter.go index 55582ca..c807b2b 100644 --- a/graph2/queries/chapter.go +++ b/graph2/queries/chapter.go @@ -5,48 +5,51 @@ import ( "errors" "time" + "git.aiterp.net/rpdata/api/graph2/input" + "git.aiterp.net/rpdata/api/internal/auth" + "git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models/changekeys" "git.aiterp.net/rpdata/api/models/changes" + "git.aiterp.net/rpdata/api/models/chapters" "git.aiterp.net/rpdata/api/models/comments" - - "git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/models/stories" - - "git.aiterp.net/rpdata/api/graph2/input" - "git.aiterp.net/rpdata/api/models" - "git.aiterp.net/rpdata/api/models/chapters" ) // Queries -func (r *resolver) Chapter(ctx context.Context, id string) (models.Chapter, error) { - return chapters.FindID(id) +func (r *resolver) Chapter(ctx context.Context, id string) (*models.Chapter, error) { + chapter, err := chapters.FindID(id) + if err != nil { + return nil, err + } + + return &chapter, nil } // Mutations -func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAddInput) (models.Chapter, error) { +func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAddInput) (*models.Chapter, error) { story, err := stories.FindID(input.StoryID) if err != nil { - return models.Chapter{}, errors.New("Story not found") + return nil, errors.New("Story not found") } token := auth.TokenFromContext(ctx) if !token.Permitted("member", "story.add") { - return models.Chapter{}, errors.New("Unauthorized") + return nil, errors.New("Unauthorized") } author := token.UserID if input.Author != nil && *input.Author != author { if !token.Permitted("story.add") { - return models.Chapter{}, errors.New("False pretender") + return nil, errors.New("False pretender") } author = *input.Author } if !story.Open && story.Author != author { - return models.Chapter{}, errors.New("Story is not open") + return nil, errors.New("Story is not open") } commentMode := models.ChapterCommentModeDisabled @@ -56,38 +59,38 @@ func (r *mutationResolver) AddChapter(ctx context.Context, input input.ChapterAd chapter, err := chapters.Add(story, input.Title, author, input.Source, time.Now(), input.FictionalDate, commentMode) if err != nil { - return models.Chapter{}, errors.New("Failed to create chapter: " + err.Error()) + return nil, errors.New("Failed to create chapter: " + err.Error()) } go changes.Submit("Chapter", "add", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter) - return chapter, nil + return &chapter, nil } -func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterMoveInput) (models.Chapter, error) { +func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterMoveInput) (*models.Chapter, error) { chapter, err := chapters.FindID(input.ID) if err != nil { - return models.Chapter{}, errors.New("Chapter not found") + return nil, errors.New("Chapter not found") } token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.move") { - return models.Chapter{}, errors.New("You are not allowed to move this chapter") + return nil, errors.New("You are not allowed to move this chapter") } target, err := stories.FindID(input.StoryID) if err != nil { - return models.Chapter{}, errors.New("Target story not found") + return nil, errors.New("Target story not found") } if !target.Open && !token.PermittedUser(target.Author, "member", "chapter.move") { - return models.Chapter{}, errors.New("You are not permitted to move chapters to this story") + return nil, errors.New("You are not permitted to move chapters to this story") } oldStoryID := chapter.StoryID chapter, err = chapters.Move(chapter, target) if err != nil { - return models.Chapter{}, errors.New("Failed to move chapter: " + err.Error()) + return nil, errors.New("Failed to move chapter: " + err.Error()) } go func() { @@ -105,18 +108,18 @@ func (r *mutationResolver) MoveChapter(ctx context.Context, input input.ChapterM changes.Submit("Chapter", "move-in", token.UserID, story.Listed, changekeys.Listed(story, chapter), story, chapter) }() - return chapter, nil + return &chapter, nil } -func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterEditInput) (models.Chapter, error) { +func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterEditInput) (*models.Chapter, error) { chapter, err := chapters.FindID(input.ID) if err != nil { - return models.Chapter{}, errors.New("Chapter not found") + return nil, errors.New("Chapter not found") } token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.edit") { - return models.Chapter{}, errors.New("Unauthorized") + return nil, errors.New("Unauthorized") } if input.ClearFictionalDate != nil && *input.ClearFictionalDate == true { @@ -125,7 +128,7 @@ func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterE chapter, err = chapters.Edit(chapter, input.Title, input.Source, input.FictionalDate, input.CommentMode, input.CommentsLocked) if err != nil { - return models.Chapter{}, errors.New("Failed to edit chapter: " + err.Error()) + return nil, errors.New("Failed to edit chapter: " + err.Error()) } go func() { @@ -137,28 +140,28 @@ func (r *mutationResolver) EditChapter(ctx context.Context, input input.ChapterE changes.Submit("Chapter", "edit", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter) }() - return chapter, nil + return &chapter, nil } -func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.ChapterRemoveInput) (models.Chapter, error) { +func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.ChapterRemoveInput) (*models.Chapter, error) { chapter, err := chapters.FindID(input.ID) if err != nil { - return models.Chapter{}, errors.New("Chapter not found") + return nil, errors.New("Chapter not found") } token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.PermittedUser(chapter.Author, "member", "chapter.remove") { - return models.Chapter{}, errors.New("Unauthorized") + return nil, errors.New("Unauthorized") } chapter, err = chapters.Remove(chapter) if err != nil { - return models.Chapter{}, errors.New("Failed to remove chapter: " + err.Error()) + return nil, errors.New("Failed to remove chapter: " + err.Error()) } err = comments.RemoveChapter(chapter) if err != nil { - return models.Chapter{}, errors.New("Chapter was removed, but comment removal failed: " + err.Error()) + return nil, errors.New("Chapter was removed, but comment removal failed: " + err.Error()) } go func() { @@ -170,5 +173,5 @@ func (r *mutationResolver) RemoveChapter(ctx context.Context, input input.Chapte changes.Submit("Chapter", "remove", token.UserID, story.Listed, changekeys.Many(story, chapter), chapter) }() - return chapter, nil + return &chapter, nil } diff --git a/graph2/queries/character.go b/graph2/queries/character.go index fb48aa3..42e1879 100644 --- a/graph2/queries/character.go +++ b/graph2/queries/character.go @@ -16,13 +16,23 @@ import ( // Queries -func (r *resolver) Character(ctx context.Context, id *string, nick *string) (models.Character, error) { +func (r *resolver) Character(ctx context.Context, id *string, nick *string) (*models.Character, error) { if id != nil { - return characters.FindID(*id) + character, err := characters.FindID(*id) + if err != nil { + return nil, err + } + + return &character, nil } else if nick != nil { - return characters.FindNick(*nick) + character, err := characters.FindNick(*nick) + if err != nil { + return nil, err + } + + return &character, nil } else { - return models.Character{}, errors.New("You must specify either an ID or a nick") + return nil, errors.New("You must specify either an ID or a nick") } } @@ -32,13 +42,13 @@ func (r *resolver) Characters(ctx context.Context, filter *characters.Filter) ([ // Mutations -func (r *mutationResolver) AddCharacter(ctx context.Context, input input.CharacterAddInput) (models.Character, error) { +func (r *mutationResolver) AddCharacter(ctx context.Context, input input.CharacterAddInput) (*models.Character, error) { token := auth.TokenFromContext(ctx) if !token.Permitted("member", "character.add") { - return models.Character{}, errors.New("You are not permitted to add characters") + return nil, errors.New("You are not permitted to add characters") } if len(input.Name) < 2 || len(input.Nick) < 2 { - return models.Character{}, errors.New("You need to provide a name and a nick (min length: 2)") + return nil, errors.New("You need to provide a name and a nick (min length: 2)") } shortName := "" @@ -56,7 +66,7 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact author := token.UserID if input.Author != nil && *input.Author != author { if !token.Permitted("character.add") { - return models.Character{}, errors.New("You are only permitted to add your own characters") + return nil, errors.New("You are only permitted to add your own characters") } author = *input.Author @@ -66,109 +76,109 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact character, err := characters.Add(input.Nick, input.Name, shortName, author, description) if err != nil { - return models.Character{}, errors.New("Adding character failed: " + err.Error()) + return nil, errors.New("Adding character failed: " + err.Error()) } go changes.Submit("Character", "add", token.UserID, true, changekeys.Listed(character), character) - return character, nil + return &character, nil } -func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.CharacterNickInput) (models.Character, error) { +func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.CharacterNickInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { - return models.Character{}, errors.New("Character not found") + return nil, errors.New("Character not found") } if len(input.Nick) < 2 { - return models.Character{}, errors.New("You need to provide a valid nick (min length: 2)") + return nil, errors.New("You need to provide a valid nick (min length: 2)") } token := auth.TokenFromContext(ctx) if !token.PermittedUser(character.Author, "member", "character.edit") { - return models.Character{}, errors.New("You are not permitted to edit this character") + return nil, errors.New("You are not permitted to edit this character") } logs.ScheduleFullUpdate() character, err = characters.AddNick(character, input.Nick) if err != nil { - return models.Character{}, errors.New("Failed to add nick: " + err.Error()) + return nil, errors.New("Failed to add nick: " + err.Error()) } go logs.ScheduleFullUpdate() go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) - return character, nil + return &character, nil } -func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.CharacterNickInput) (models.Character, error) { +func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.CharacterNickInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { - return models.Character{}, errors.New("Character not found") + return nil, errors.New("Character not found") } token := auth.TokenFromContext(ctx) if !token.PermittedUser(character.Author, "member", "character.edit") { - return models.Character{}, errors.New("You are not permitted to edit this character") + return nil, errors.New("You are not permitted to edit this character") } character, err = characters.RemoveNick(character, input.Nick) if err != nil { - return models.Character{}, errors.New("Failed to remove nick: " + err.Error()) + return nil, errors.New("Failed to remove nick: " + err.Error()) } go logs.ScheduleFullUpdate() go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) - return character, nil + return &character, nil } -func (r *mutationResolver) EditCharacter(ctx context.Context, input input.CharacterEditInput) (models.Character, error) { +func (r *mutationResolver) EditCharacter(ctx context.Context, input input.CharacterEditInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { - return models.Character{}, errors.New("Character not found") + return nil, errors.New("Character not found") } if input.Name != nil && len(*input.Name) < 2 { - return models.Character{}, errors.New("You need to provide a valid name (min length: 2)") + return nil, errors.New("You need to provide a valid name (min length: 2)") } if input.ShortName != nil && len(*input.ShortName) < 2 { - return models.Character{}, errors.New("You need to provide a valid short name (min length: 2)") + return nil, errors.New("You need to provide a valid short name (min length: 2)") } token := auth.TokenFromContext(ctx) if !token.PermittedUser(character.Author, "member", "character.edit") { - return models.Character{}, errors.New("You are not permitted to edit this character") + return nil, errors.New("You are not permitted to edit this character") } character, err = characters.Edit(character, input.Name, input.ShortName, input.Description) if err != nil { - return models.Character{}, errors.New("Failed to edit character: " + err.Error()) + return nil, errors.New("Failed to edit character: " + err.Error()) } go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character) - return character, nil + return &character, nil } -func (r *mutationResolver) RemoveCharacter(ctx context.Context, input input.CharacterRemoveInput) (models.Character, error) { +func (r *mutationResolver) RemoveCharacter(ctx context.Context, input input.CharacterRemoveInput) (*models.Character, error) { character, err := characters.FindID(input.ID) if err != nil { - return models.Character{}, errors.New("Character not found") + return nil, errors.New("Character not found") } token := auth.TokenFromContext(ctx) if !token.PermittedUser(character.Author, "member", "character.remove") { - return models.Character{}, errors.New("You are not permitted to remove this character") + return nil, errors.New("You are not permitted to remove this character") } character, err = characters.Remove(character) if err != nil { - return models.Character{}, errors.New("Failed to remove character: " + err.Error()) + return nil, errors.New("Failed to remove character: " + err.Error()) } go changes.Submit("Character", "remove", token.UserID, true, changekeys.Listed(character), character) - return character, nil + return &character, nil } diff --git a/graph2/queries/comment.go b/graph2/queries/comment.go index 421335a..155666f 100644 --- a/graph2/queries/comment.go +++ b/graph2/queries/comment.go @@ -24,34 +24,39 @@ import ( // Queries -func (r *resolver) Comment(ctx context.Context, id string) (models.Comment, error) { - return comments.Find(id) +func (r *resolver) Comment(ctx context.Context, id string) (*models.Comment, error) { + comment, err := comments.Find(id) + if err != nil { + return nil, err + } + + return &comment, nil } // Mutations -func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAddInput) (models.Comment, error) { +func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAddInput) (*models.Comment, error) { chapter, err := chapters.FindID(input.ChapterID) if err != nil { - return models.Comment{}, errors.New("Chapter not found") + return nil, errors.New("Chapter not found") } token := auth.TokenFromContext(ctx) if !token.Permitted("member", "story.edit") { - return models.Comment{}, errors.New("Unauthorized") + return nil, errors.New("Unauthorized") } if !chapter.CanComment() { - return models.Comment{}, errors.New("Comments are disabled or locked") + return nil, errors.New("Comments are disabled or locked") } var characterPtr *models.Character if input.CharacterID != nil { character, err := characters.FindID(*input.CharacterID) if err != nil { - return models.Comment{}, errors.New("Character not found") + return nil, errors.New("Character not found") } else if character.Author != token.UserID { - return models.Comment{}, errors.New("That is not your character") + return nil, errors.New("That is not your character") } characterPtr = &character @@ -69,7 +74,7 @@ func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAd comment, err := comments.Add(chapter, subject, token.UserID, input.Source, input.CharacterName, characterPtr, time.Now(), fictionalDate) if err != nil { - return models.Comment{}, errors.New("Failed to add comment: " + err.Error()) + return nil, errors.New("Failed to add comment: " + err.Error()) } go func() { @@ -82,27 +87,27 @@ func (r *mutationResolver) AddComment(ctx context.Context, input input.CommentAd changes.Submit("Comment", "add", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story) }() - return comment, nil + return &comment, nil } -func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentEditInput) (models.Comment, error) { +func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentEditInput) (*models.Comment, error) { comment, err := comments.Find(input.CommentID) if err != nil { - return models.Comment{}, errors.New("Comment not found") + return nil, errors.New("Comment not found") } token := auth.TokenFromContext(ctx) if !token.PermittedUser(comment.Author, "member", "story.edit") { - return models.Comment{}, errors.New("You cannot edit this comment") + return nil, errors.New("You cannot edit this comment") } chapter, err := chapters.FindID(comment.ChapterID) if err != nil { - return models.Comment{}, errors.New("Comment's chapter not found") + return nil, errors.New("Comment's chapter not found") } if !chapter.CanComment() { - return models.Comment{}, errors.New("Comments are disabled or locked") + return nil, errors.New("Comments are disabled or locked") } if input.ClearFictionalDate != nil && *input.ClearFictionalDate == true { @@ -112,15 +117,15 @@ func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentE if input.CharacterID != nil && *input.CharacterID != "" { character, err := characters.FindID(*input.CharacterID) if err != nil { - return models.Comment{}, errors.New("Character not found") + return nil, errors.New("Character not found") } else if character.Author != token.UserID { - return models.Comment{}, errors.New("That is not your character") + return nil, errors.New("That is not your character") } } comment, err = comments.Edit(comment, input.Source, input.CharacterName, input.CharacterID, input.Subject, input.FictionalDate) if err != nil { - return models.Comment{}, errors.New("Could not post comment: " + err.Error()) + return nil, errors.New("Could not post comment: " + err.Error()) } go func() { @@ -133,32 +138,32 @@ func (r *mutationResolver) EditComment(ctx context.Context, input input.CommentE changes.Submit("Comment", "edit", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story) }() - return comment, nil + return &comment, nil } -func (r *mutationResolver) RemoveComment(ctx context.Context, input input.CommentRemoveInput) (models.Comment, error) { +func (r *mutationResolver) RemoveComment(ctx context.Context, input input.CommentRemoveInput) (*models.Comment, error) { comment, err := comments.Find(input.CommentID) if err != nil { - return models.Comment{}, errors.New("Comment not found") + return nil, errors.New("Comment not found") } token := auth.TokenFromContext(ctx) if !token.PermittedUser(comment.Author, "member", "story.edit") { - return models.Comment{}, errors.New("You cannot remove this comment") + return nil, errors.New("You cannot remove this comment") } chapter, err := chapters.FindID(comment.ChapterID) if err != nil { - return models.Comment{}, errors.New("Comment's chapter not found") + return nil, errors.New("Comment's chapter not found") } if !chapter.CanComment() { - return models.Comment{}, errors.New("Comments are disabled or locked") + return nil, errors.New("Comments are disabled or locked") } err = comments.Remove(comment) if err != nil { - return models.Comment{}, errors.New("Failed to remove comment: " + err.Error()) + return nil, errors.New("Failed to remove comment: " + err.Error()) } go func() { @@ -171,5 +176,5 @@ func (r *mutationResolver) RemoveComment(ctx context.Context, input input.Commen changes.Submit("Comment", "remove", token.UserID, true, changekeys.Many(comment, chapter, models.Story{ID: chapter.StoryID}), comment, chapter, story) }() - return comment, nil + return &comment, nil } diff --git a/graph2/queries/file.go b/graph2/queries/file.go index 67e521c..d5e6124 100644 --- a/graph2/queries/file.go +++ b/graph2/queries/file.go @@ -8,8 +8,13 @@ import ( "git.aiterp.net/rpdata/api/models/files" ) -func (r *resolver) File(ctx context.Context, id string) (models.File, error) { - return files.FindID(id) +func (r *resolver) File(ctx context.Context, id string) (*models.File, error) { + file, err := files.FindID(id) + if err != nil { + return nil, err + } + + return &file, nil } func (r *resolver) Files(ctx context.Context, filter *files.Filter) ([]models.File, error) { diff --git a/graph2/queries/log.go b/graph2/queries/log.go index 2bd3939..ea269de 100644 --- a/graph2/queries/log.go +++ b/graph2/queries/log.go @@ -20,8 +20,13 @@ import ( // Queries -func (r *resolver) Log(ctx context.Context, id string) (models.Log, error) { - return logs.FindID(id) +func (r *resolver) Log(ctx context.Context, id string) (*models.Log, error) { + log, err := logs.FindID(id) + if err != nil { + return nil, err + } + + return &log, nil } func (r *resolver) Logs(ctx context.Context, filter *logs.Filter) ([]models.Log, error) { @@ -56,10 +61,10 @@ func (r *resolver) Logs(ctx context.Context, filter *logs.Filter) ([]models.Log, // Mutations -func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) (models.Log, error) { +func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) (*models.Log, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("log.add") { - return models.Log{}, errors.New("You are not permitted to add logs") + return nil, errors.New("You are not permitted to add logs") } open := input.Open != nil && *input.Open == true @@ -80,7 +85,7 @@ func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) log, err := logs.Add(input.Date, input.Channel, title, event, description, open) if !token.Authenticated() || !token.Permitted("log.add") { - return models.Log{}, errors.New("Failed to create log: " + err.Error()) + return nil, errors.New("Failed to create log: " + err.Error()) } go func() { @@ -94,7 +99,7 @@ func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput) changes.Submit("Log", "add", token.UserID, true, changekeys.Listed(log), log) }() - return log, nil + return &log, nil } func (r *mutationResolver) ImportLog(ctx context.Context, input input.LogImportInput) ([]models.Log, error) { @@ -141,44 +146,44 @@ func (r *mutationResolver) ImportLog(ctx context.Context, input input.LogImportI return newLogs, nil } -func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput) (models.Log, error) { +func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput) (*models.Log, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("log.edit") { - return models.Log{}, errors.New("You are not permitted to edit logs") + return nil, errors.New("You are not permitted to edit logs") } log, err := logs.FindID(input.ID) if err != nil { - return models.Log{}, errors.New("Log not found") + return nil, errors.New("Log not found") } log, err = logs.Edit(log, input.Title, input.Event, input.Description, input.Open) if err != nil { - return models.Log{}, errors.New("Failed to edit log: " + err.Error()) + return nil, errors.New("Failed to edit log: " + err.Error()) } go changes.Submit("Log", "edit", token.UserID, true, changekeys.Listed(log), log) - return log, nil + return &log, nil } -func (r *mutationResolver) RemoveLog(ctx context.Context, input input.LogRemoveInput) (models.Log, error) { +func (r *mutationResolver) RemoveLog(ctx context.Context, input input.LogRemoveInput) (*models.Log, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("log.remove") { - return models.Log{}, errors.New("You are not permitted to remove logs") + return nil, errors.New("You are not permitted to remove logs") } log, err := logs.FindID(input.ID) if err != nil { - return models.Log{}, errors.New("Log not found") + return nil, errors.New("Log not found") } log, err = logs.Remove(log) if err != nil { - return models.Log{}, errors.New("Failed to remove log: " + err.Error()) + return nil, errors.New("Failed to remove log: " + err.Error()) } go changes.Submit("Log", "remove", token.UserID, true, changekeys.Listed(log), log) - return log, nil + return &log, nil } diff --git a/graph2/queries/post.go b/graph2/queries/post.go index cdf52b4..b17095a 100644 --- a/graph2/queries/post.go +++ b/graph2/queries/post.go @@ -16,8 +16,13 @@ import ( // Queries -func (r *resolver) Post(ctx context.Context, id string) (models.Post, error) { - return posts.FindID(id) +func (r *resolver) Post(ctx context.Context, id string) (*models.Post, error) { + post, err := posts.FindID(id) + if err != nil { + return nil, err + } + + return &post, nil } func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]models.Post, error) { @@ -43,37 +48,37 @@ func (r *resolver) Posts(ctx context.Context, filter *posts.Filter) ([]models.Po // Mutation -func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput) (models.Post, error) { +func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput) (*models.Post, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("post.add") { - return models.Post{}, errors.New("You are not permitted to edit logs") + return nil, errors.New("You are not permitted to edit logs") } log, err := logs.FindID(input.LogID) if err != nil { - return models.Post{}, err + return nil, err } post, err := posts.Add(log, input.Time, input.Kind, input.Nick, input.Text) if err != nil { - return models.Post{}, err + return nil, err } go logs.UpdateCharacters(log, nil) go changes.Submit("Post", "add", token.UserID, true, changekeys.Many(log, post), post) - return post, nil + return &post, nil } -func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (models.Post, error) { +func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (*models.Post, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("post.edit") { - return models.Post{}, errors.New("You are not permitted to edit logs") + return nil, errors.New("You are not permitted to edit logs") } post, err := posts.FindID(input.ID) if err != nil { - return models.Post{}, errors.New("Post not found") + return nil, errors.New("Post not found") } if input.Nick != nil { @@ -89,7 +94,7 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp post, err = posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text) if err != nil { - return models.Post{}, errors.New("Adding post failed: " + err.Error()) + return nil, errors.New("Adding post failed: " + err.Error()) } go func() { @@ -101,7 +106,7 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp changes.Submit("Post", "edit", token.UserID, true, changekeys.Many(log, post), post) }() - return post, nil + return &post, nil } func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInput) ([]models.Post, error) { @@ -132,20 +137,20 @@ func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInp return posts, nil } -func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemoveInput) (models.Post, error) { +func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemoveInput) (*models.Post, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() || !token.Permitted("post.remove") { - return models.Post{}, errors.New("You are not permitted to edit logs") + return nil, errors.New("You are not permitted to edit logs") } post, err := posts.FindID(input.ID) if err != nil { - return models.Post{}, errors.New("Post not found (before removing, of course)") + return nil, errors.New("Post not found (before removing, of course)") } post, err = posts.Remove(post) if err != nil { - return models.Post{}, errors.New("Could not remove post: " + err.Error()) + return nil, errors.New("Could not remove post: " + err.Error()) } go func() { @@ -158,5 +163,5 @@ func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemov changes.Submit("Post", "remove", token.UserID, true, changekeys.Many(log, post), post) }() - return post, nil + return &post, nil } diff --git a/graph2/queries/story.go b/graph2/queries/story.go index a857664..ba2b983 100644 --- a/graph2/queries/story.go +++ b/graph2/queries/story.go @@ -15,8 +15,13 @@ import ( "git.aiterp.net/rpdata/api/models/stories" ) -func (r *resolver) Story(ctx context.Context, id string) (models.Story, error) { - return stories.FindID(id) +func (r *resolver) Story(ctx context.Context, id string) (*models.Story, error) { + story, err := stories.FindID(id) + if err != nil { + return nil, err + } + + return &story, nil } func (r *resolver) Stories(ctx context.Context, filter *stories.Filter) ([]models.Story, error) { @@ -38,16 +43,16 @@ func (r *resolver) Stories(ctx context.Context, filter *stories.Filter) ([]model // Mutations -func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInput) (models.Story, error) { +func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) if token == nil || !token.Permitted("member", "story.add") { - return models.Story{}, errors.New("Permission denied") + return nil, errors.New("Permission denied") } author := token.UserID if input.Author != nil && *input.Author != author { if !token.Permitted("story.add") { - return models.Story{}, errors.New("You are not permitted to add a story in another author's name") + return nil, errors.New("You are not permitted to add a story in another author's name") } author = *input.Author @@ -63,80 +68,80 @@ func (r *mutationResolver) AddStory(ctx context.Context, input input.StoryAddInp story, err := stories.Add(input.Name, author, input.Category, listed, open, input.Tags, time.Now(), fictionalDate) if err != nil { - return models.Story{}, errors.New("Failed to add story: " + err.Error()) + return nil, errors.New("Failed to add story: " + err.Error()) } go changes.Submit("Story", "add", token.UserID, story.Listed, changekeys.Listed(story), story) - return story, nil + return &story, nil } -func (r *mutationResolver) AddStoryTag(ctx context.Context, input input.StoryTagAddInput) (models.Story, error) { +func (r *mutationResolver) AddStoryTag(ctx context.Context, input input.StoryTagAddInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) if err != nil { - return models.Story{}, errors.New("Story not found") + return nil, errors.New("Story not found") } if story.Open { if !token.Permitted("member") { - return models.Story{}, errors.New("You are not permitted to edit this story") + return nil, errors.New("You are not permitted to edit this story") } } else { if !token.PermittedUser(story.Author, "member", "story.edit") { - return models.Story{}, errors.New("You are not permitted to edit this story") + return nil, errors.New("You are not permitted to edit this story") } } story, err = stories.AddTag(story, input.Tag) if err != nil { - return models.Story{}, errors.New("Failed to add story: " + err.Error()) + return nil, errors.New("Failed to add story: " + err.Error()) } go changes.Submit("Story", "tag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag) - return story, nil + return &story, nil } -func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input input.StoryTagRemoveInput) (models.Story, error) { +func (r *mutationResolver) RemoveStoryTag(ctx context.Context, input input.StoryTagRemoveInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) if err != nil { - return models.Story{}, errors.New("Story not found") + return nil, errors.New("Story not found") } if story.Open { if !token.Permitted("member") { - return models.Story{}, errors.New("You are not permitted to edit this story") + return nil, errors.New("You are not permitted to edit this story") } } else { if !token.PermittedUser(story.Author, "member", "story.edit") { - return models.Story{}, errors.New("You are not permitted to edit this story") + return nil, errors.New("You are not permitted to edit this story") } } story, err = stories.RemoveTag(story, input.Tag) if err != nil { - return models.Story{}, errors.New("Failed to add story: " + err.Error()) + return nil, errors.New("Failed to add story: " + err.Error()) } go changes.Submit("Story", "untag", token.UserID, story.Listed, changekeys.Listed(story), story, input.Tag) - return story, nil + return &story, nil } -func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditInput) (models.Story, error) { +func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) if err != nil { - return models.Story{}, errors.New("Story not found") + return nil, errors.New("Story not found") } if !token.PermittedUser(story.Author, "member", "story.edit") { - return models.Story{}, errors.New("You are not permitted to remove this story") + return nil, errors.New("You are not permitted to remove this story") } if input.ClearFictionalDate != nil && *input.ClearFictionalDate { @@ -145,37 +150,37 @@ func (r *mutationResolver) EditStory(ctx context.Context, input input.StoryEditI story, err = stories.Edit(story, input.Name, input.Category, input.Listed, input.Open, input.FictionalDate) if err != nil { - return models.Story{}, errors.New("Failed to add story: " + err.Error()) + return nil, errors.New("Failed to add story: " + err.Error()) } go changes.Submit("Story", "edit", token.UserID, story.Listed, changekeys.Listed(story), story) - return story, nil + return &story, nil } -func (r *mutationResolver) RemoveStory(ctx context.Context, input input.StoryRemoveInput) (models.Story, error) { +func (r *mutationResolver) RemoveStory(ctx context.Context, input input.StoryRemoveInput) (*models.Story, error) { token := auth.TokenFromContext(ctx) story, err := stories.FindID(input.ID) if err != nil { - return models.Story{}, errors.New("Story not found") + return nil, errors.New("Story not found") } if !token.PermittedUser(story.Author, "member", "story.remove") { - return models.Story{}, errors.New("You are not permitted to remove this story") + return nil, errors.New("You are not permitted to remove this story") } story, err = stories.Remove(story) if err != nil { - return models.Story{}, err + return nil, err } err = chapters.RemoveStory(story) if err != nil { - return models.Story{}, errors.New("Failed to remove chapters, but story is removed: " + err.Error()) + return nil, errors.New("Failed to remove chapters, but story is removed: " + err.Error()) } go changes.Submit("Story", "remove", token.UserID, story.Listed, changekeys.Listed(story), story) - return story, nil + return &story, nil } diff --git a/graph2/queries/token.go b/graph2/queries/token.go index 893a0fd..fbf1ebd 100644 --- a/graph2/queries/token.go +++ b/graph2/queries/token.go @@ -8,11 +8,11 @@ import ( "git.aiterp.net/rpdata/api/models" ) -func (r *resolver) Token(ctx context.Context) (models.Token, error) { +func (r *resolver) Token(ctx context.Context) (*models.Token, error) { token := auth.TokenFromContext(ctx) if !token.Authenticated() { - return models.Token{}, errors.New("No (valid) token") + return nil, errors.New("No (valid) token") } - return *token, nil + return token, nil } diff --git a/graph2/types/change.go b/graph2/types/change.go index b1f9a48..e9da7a7 100644 --- a/graph2/types/change.go +++ b/graph2/types/change.go @@ -12,9 +12,11 @@ type changeResolver struct{} func (r *changeResolver) Objects(ctx context.Context, obj *models.Change) ([]input.ChangeObject, error) { objects := obj.Objects() - results := make([]input.ChangeObject, len(objects)) - for i := range objects { - results[i] = objects[i] + results := make([]input.ChangeObject, 0, len(objects)) + for _, object := range objects { + if changeObject, ok := object.(input.ChangeObject); ok { + results = append(results, changeObject) + } } return results, nil diff --git a/graph2/types/file.go b/graph2/types/file.go deleted file mode 100644 index 3cc8c9e..0000000 --- a/graph2/types/file.go +++ /dev/null @@ -1,16 +0,0 @@ -package types - -import ( - "context" - - "git.aiterp.net/rpdata/api/models" -) - -type fileResolver struct{} - -func (r *fileResolver) Size(ctx context.Context, file *models.File) (int, error) { - return int(file.Size), nil -} - -// FileResolver is a resolver -var FileResolver fileResolver diff --git a/graph2/types/log.go b/graph2/types/log.go index 52143c8..01217f7 100644 --- a/graph2/types/log.go +++ b/graph2/types/log.go @@ -11,10 +11,10 @@ import ( type logResolver struct{} -func (r *logResolver) Channel(ctx context.Context, log *models.Log) (models.Channel, error) { +func (r *logResolver) Channel(ctx context.Context, log *models.Log) (*models.Channel, error) { loader := loader.FromContext(ctx) if loader == nil { - return models.Channel{}, errors.New("no loader") + return nil, errors.New("no loader") } return loader.Channel("name", log.ChannelName) diff --git a/graph2/types/token.go b/graph2/types/token.go index bfa4264..3e2ef66 100644 --- a/graph2/types/token.go +++ b/graph2/types/token.go @@ -9,8 +9,13 @@ import ( type tokenResolver struct{} -func (r *tokenResolver) User(ctx context.Context, token *models.Token) (models.User, error) { - return users.Find(token.UserID) +func (r *tokenResolver) User(ctx context.Context, token *models.Token) (*models.User, error) { + user, err := users.Find(token.UserID) + if err != nil { + return nil, err + } + + return &user, nil } // TokenResolver is a resolver diff --git a/internal/loader/channel.go b/internal/loader/channel.go index e2a8e29..549f01e 100644 --- a/internal/loader/channel.go +++ b/internal/loader/channel.go @@ -11,13 +11,13 @@ import ( ) // Channel gets a character by key -func (loader *Loader) Channel(key, value string) (models.Channel, error) { +func (loader *Loader) Channel(key, value string) (*models.Channel, error) { if !strings.HasPrefix(key, "Channel.") { key = "Channel." + key } if loader.loaders[key] == nil { - return models.Channel{}, errors.New("unsupported key") + return nil, errors.New("unsupported key") } loader.loadPrimed(key) @@ -25,15 +25,15 @@ func (loader *Loader) Channel(key, value string) (models.Channel, error) { thunk := loader.loaders[key].Load(loader.ctx, dataloader.StringKey(value)) res, err := thunk() if err != nil { - return models.Channel{}, err + return nil, err } channel, ok := res.(models.Channel) if !ok { - return channel, errors.New("incorrect type") + return nil, errors.New("incorrect type") } - return channel, nil + return &channel, nil } // PrimeChannels primes channels for loading along with the first one. diff --git a/models/changes/subscribe.go b/models/changes/subscribe.go index b5b9cd6..ffe96a3 100644 --- a/models/changes/subscribe.go +++ b/models/changes/subscribe.go @@ -12,13 +12,13 @@ var subList []*subscription type subscription struct { Keys map[string]bool - Channel chan<- models.Change + Channel chan<- *models.Change WildCard bool } // Subscribe subscribes to all changes. -func Subscribe(ctx context.Context, keys []models.ChangeKey, wildcard bool) <-chan models.Change { - channel := make(chan models.Change, 64) +func Subscribe(ctx context.Context, keys []models.ChangeKey, wildcard bool) <-chan *models.Change { + channel := make(chan *models.Change, 64) sub := &subscription{ Keys: make(map[string]bool, len(keys)), Channel: channel, @@ -58,16 +58,18 @@ func pushToSubscribers(change models.Change) { subMutex.Lock() SubLoop: for _, sub := range subList { + changeCopy := change + if sub.WildCard && change.Listed { select { - case sub.Channel <- change: + case sub.Channel <- &changeCopy: default: } } else { for _, key := range keys { if sub.Keys[key] { select { - case sub.Channel <- change: + case sub.Channel <- &changeCopy: default: } diff --git a/models/channel.go b/models/channel.go index d65e23f..dc8a019 100644 --- a/models/channel.go +++ b/models/channel.go @@ -8,3 +8,9 @@ type Channel struct { EventName string `bson:"eventName,omitempty"` LocationName string `bson:"locationName,omitempty"` } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Channel) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/chapter.go b/models/chapter.go index e795af7..23698bd 100644 --- a/models/chapter.go +++ b/models/chapter.go @@ -20,3 +20,9 @@ type Chapter struct { func (chapter *Chapter) CanComment() bool { return !chapter.CommentsLocked && chapter.CommentMode.IsEnabled() } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Chapter) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/character.go b/models/character.go index 37e819a..a8c7d0b 100644 --- a/models/character.go +++ b/models/character.go @@ -29,3 +29,9 @@ func (character *Character) HasNick(nick string) bool { return false } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Character) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/comment.go b/models/comment.go index f1ccd31..2634ad7 100644 --- a/models/comment.go +++ b/models/comment.go @@ -15,3 +15,9 @@ type Comment struct { EditedDate time.Time `bson:"editeddDate"` Source string `bson:"source"` } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Comment) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/file.go b/models/file.go index 97a9013..4da021b 100644 --- a/models/file.go +++ b/models/file.go @@ -14,3 +14,9 @@ type File struct { Author string `bson:"author" json:"author"` URL string `bson:"url,omitempty" json:"url,omitempty"` } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*File) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/log.go b/models/log.go index 5171ea8..f1cfc3a 100644 --- a/models/log.go +++ b/models/log.go @@ -14,3 +14,9 @@ type Log struct { Open bool `bson:"open"` CharacterIDs []string `bson:"characterIds"` } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Log) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/post.go b/models/post.go index 4769ca4..fb14508 100644 --- a/models/post.go +++ b/models/post.go @@ -12,3 +12,9 @@ type Post struct { Text string `bson:"text"` Position int `bson:"position"` } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Post) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/story.go b/models/story.go index 8910b39..3a1d02b 100644 --- a/models/story.go +++ b/models/story.go @@ -16,3 +16,9 @@ type Story struct { FictionalDate time.Time `bson:"fictionalDate,omitempty"` UpdatedDate time.Time `bson:"updatedDate"` } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Story) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file diff --git a/models/tag.go b/models/tag.go index a9f37c3..d848e20 100644 --- a/models/tag.go +++ b/models/tag.go @@ -10,3 +10,9 @@ type Tag struct { func (tag *Tag) Equal(other Tag) bool { return tag.Kind == other.Kind && tag.Name == other.Name } + +// IsChangeObject is an interface implementation to identify it as a valid +// ChangeObject in GQL. +func (*Tag) IsChangeObject() { + panic("this method is a dummy, and so is its caller") +} \ No newline at end of file