Browse Source

models, graph2: Added Comment type and queries, and hooked it up to chapter.

module-madness-pointers
Gisle Aune 6 years ago
parent
commit
d22ab387dd
  1. 4
      graph2/gqlgen.yml
  2. 4
      graph2/graph.go
  3. 15
      graph2/queries/comment.go
  4. 4
      graph2/schema/root.gql
  5. 29
      graph2/schema/types/Chapter.gql
  6. 31
      graph2/schema/types/Comment.gql
  7. 15
      graph2/types/chapter.go
  8. 27
      graph2/types/comment.go
  9. 57
      models/chapter-comment-mode.go
  10. 7
      models/chapter.go
  11. 17
      models/comment.go
  12. 66
      models/comments/db.go
  13. 11
      models/comments/find.go
  14. 11
      models/comments/list.go

4
graph2/gqlgen.yml

@ -35,6 +35,10 @@ models:
model: git.aiterp.net/rpdata/api/models/logs.Filter model: git.aiterp.net/rpdata/api/models/logs.Filter
LogImporter: LogImporter:
model: git.aiterp.net/rpdata/api/models.LogImporter model: git.aiterp.net/rpdata/api/models.LogImporter
Comment:
model: git.aiterp.net/rpdata/api/models.Comment
ChapterCommentMode:
model: git.aiterp.net/rpdata/api/models.ChapterCommentMode
Chapter: Chapter:
model: git.aiterp.net/rpdata/api/models.Chapter model: git.aiterp.net/rpdata/api/models.Chapter
fields: fields:

4
graph2/graph.go

@ -34,6 +34,10 @@ func (r *rootResolver) Log() LogResolver {
return &types.LogResolver return &types.LogResolver
} }
func (r *rootResolver) Comment() CommentResolver {
return &types.CommentResolver
}
func (r *rootResolver) Chapter() ChapterResolver { func (r *rootResolver) Chapter() ChapterResolver {
return &types.ChapterResolver return &types.ChapterResolver
} }

15
graph2/queries/comment.go

@ -0,0 +1,15 @@
package queries
import (
"context"
"git.aiterp.net/rpdata/api/models/comments"
"git.aiterp.net/rpdata/api/models"
)
// Queries
func (r *resolver) Comment(ctx context.Context, id string) (models.Comment, error) {
return comments.Find(id)
}

4
graph2/schema/root.gql

@ -41,6 +41,10 @@ type Query {
chapter(id: String!): Chapter! chapter(id: String!): Chapter!
# Find comment by ID
comment(id: String!): Comment!
# Find all distinct tags used in stories # Find all distinct tags used in stories
tags: [Tag!]! tags: [Tag!]!

29
graph2/schema/types/Chapter.gql

@ -20,6 +20,35 @@ type Chapter {
# The date of edit. # The date of edit.
editedDate: Date! editedDate: Date!
"The comment mode."
commentMode: ChapterCommentMode!
"Whether the author has locket comments."
commentsLocked: Boolean!
"A shorthand for checking whether a logged-in user can comment on this chapter."
canComment: Boolean!
"Get all chapter comments."
comments(limit: Int): [Comment!]!
}
"""
The possibles modes of comments for a chapter.
"""
enum ChapterCommentMode {
"Comments are disabled and hidden."
Disabled
"Comments should be shown as typical news article comments."
Article
"Comments should be shown in a compact form suitable for many small messages."
Chat
"Comments are going to be shown as omni-tool messages."
Message
} }
# Input for addChapter mutation # Input for addChapter mutation

31
graph2/schema/types/Comment.gql

@ -0,0 +1,31 @@
"""
A Comment represents a comment to a story chapter.
"""
type Comment {
"A unique ID of the change."
id: String!
"subject"
subject: String!
"The comment's author."
author: String!
"The displayed name of the character. This may be the same as character.name, but it does not need to be."
characterName: String!
"The character associated with the comment."
character: Character
"The fictional (IC) date of the comment."
fictionalDate: Date
"The date of creation."
createdDate: Date!
"The date of the last edit."
editedDate: Date!
"The markdown source of the comment."
source: String!
}

15
graph2/types/chapter.go

@ -2,9 +2,11 @@ package types
import ( import (
"context" "context"
"errors"
"time" "time"
"git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/comments"
) )
type chapterResolver struct{} type chapterResolver struct{}
@ -17,5 +19,18 @@ func (r *chapterResolver) FictionalDate(ctx context.Context, chapter *models.Cha
return &chapter.FictionalDate, nil return &chapter.FictionalDate, nil
} }
func (r *chapterResolver) Comments(ctx context.Context, chapter *models.Chapter, limit *int) ([]models.Comment, error) {
limitValue := 0
if limit != nil {
if *limit < 0 {
return nil, errors.New("Limit cannot be negative")
}
limitValue = *limit
}
return comments.ListChapterID(chapter.ID, limitValue)
}
// ChapterResolver is a resolver // ChapterResolver is a resolver
var ChapterResolver chapterResolver var ChapterResolver chapterResolver

27
graph2/types/comment.go

@ -0,0 +1,27 @@
package types
import (
"context"
"git.aiterp.net/rpdata/api/models/characters"
"git.aiterp.net/rpdata/api/models"
)
type commentResolver struct{}
func (r *commentResolver) Character(ctx context.Context, obj *models.Comment) (*models.Character, error) {
if obj.CharacterID == "" {
return nil, nil
}
character, err := characters.FindID(obj.CharacterID)
if err != nil {
return nil, err
}
return &character, nil
}
// CommentResolver is a resolver
var CommentResolver commentResolver

57
models/chapter-comment-mode.go

@ -0,0 +1,57 @@
package models
import (
"fmt"
"io"
)
// ChapterCommentMode represents the kind of tags.
type ChapterCommentMode string
const (
// ChapterCommentModeDisabled is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeDisabled ChapterCommentMode = "Disabled"
// ChapterCommentModeArticle is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeArticle ChapterCommentMode = "Article"
// ChapterCommentModeChat is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeChat ChapterCommentMode = "Chat"
// ChapterCommentModeMessage is a chapter comment mode, see GraphQL documentation.
ChapterCommentModeMessage ChapterCommentMode = "Message"
)
// UnmarshalGQL unmarshals
func (e *ChapterCommentMode) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = ChapterCommentMode(str)
switch *e {
case ChapterCommentModeDisabled, ChapterCommentModeArticle, ChapterCommentModeChat, ChapterCommentModeMessage:
return nil
default:
return fmt.Errorf("%s is not a valid ChapterCommentMode", str)
}
}
// IsEnabled returns true if comments are enabled.
func (e ChapterCommentMode) IsEnabled() bool {
return e != ChapterCommentModeDisabled && len(e) != 0
}
// MarshalGQL turns it into a JSON string
func (e ChapterCommentMode) MarshalGQL(w io.Writer) {
// Backwards compatibility: Empty value means disabled.
if len(e) == 0 {
w.Write(disabledChapterCommentModeBytes)
return
}
fmt.Fprint(w, "\""+string(e)+"\"")
}
var disabledChapterCommentModeBytes = []byte(`"Disabled"`)

7
models/chapter.go

@ -12,4 +12,11 @@ type Chapter struct {
CreatedDate time.Time `bson:"createdDate"` CreatedDate time.Time `bson:"createdDate"`
FictionalDate time.Time `bson:"fictionalDate,omitempty"` FictionalDate time.Time `bson:"fictionalDate,omitempty"`
EditedDate time.Time `bson:"editedDate"` EditedDate time.Time `bson:"editedDate"`
CommentMode ChapterCommentMode `bson:"commentMode"`
CommentsLocked bool `bson:"commentsLocked"`
}
// CanComment returns true if the chapter can be commented to.
func (chapter *Chapter) CanComment() bool {
return !chapter.CommentsLocked && chapter.CommentMode.IsEnabled()
} }

17
models/comment.go

@ -0,0 +1,17 @@
package models
import "time"
// A Comment is a comment on a chapter.
type Comment struct {
ID string `bson:"id"`
ChapterID string `bson:"chapterId"`
Subject string `bson:"subject"`
Author string `bson:"author"`
CharacterName string `bson:"characterName"`
CharacterID string `bson:"characterId"`
FictionalDate time.Time `bson:"fictionalDate"`
CreatedDate time.Time `bson:"createdDate"`
EditedDate time.Time `bson:"editeddDate"`
Source string `bson:"sources"`
}

66
models/comments/db.go

@ -0,0 +1,66 @@
package comments
import (
"crypto/rand"
"encoding/binary"
"strconv"
"git.aiterp.net/rpdata/api/internal/store"
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo"
)
var collection *mgo.Collection
func find(query interface{}) (models.Comment, error) {
comment := models.Comment{}
err := collection.Find(query).One(&comment)
return comment, err
}
func list(query interface{}, limit int) ([]models.Comment, error) {
allocSize := 32
if limit >= 0 {
allocSize = limit
} else {
limit = 0
}
comments := make([]models.Comment, 0, allocSize)
err := collection.Find(query).Sort("createdDate").Limit(limit).All(&comments)
if err != nil {
return nil, err
}
return comments, nil
}
func makeCommentID() string {
result := "SCC"
offset := 0
data := make([]byte, 48)
rand.Read(data)
for len(result) < 32 {
result += strconv.FormatUint(binary.LittleEndian.Uint64(data[offset:]), 36)
offset += 8
if offset >= 48 {
rand.Read(data)
offset = 0
}
}
return result[:32]
}
func init() {
store.HandleInit(func(db *mgo.Database) {
collection = db.C("story.comments")
collection.EnsureIndexKey("chapterId")
collection.EnsureIndexKey("author")
collection.EnsureIndexKey("createdDate")
})
}

11
models/comments/find.go

@ -0,0 +1,11 @@
package comments
import (
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo/bson"
)
// Find finds a comment by ID.
func Find(id string) (models.Comment, error) {
return find(bson.M{"_id": id})
}

11
models/comments/list.go

@ -0,0 +1,11 @@
package comments
import (
"git.aiterp.net/rpdata/api/models"
"github.com/globalsign/mgo/bson"
)
// ListChapterID lists all comments by chapter-ID
func ListChapterID(chapterID string, limit int) ([]models.Comment, error) {
return list(bson.M{"chapterId": chapterID}, limit)
}
Loading…
Cancel
Save