models, graph2: Added Comment type and queries, and hooked it up to chapter.
the build was successful
the build was successful
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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!]!
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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!
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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"`)
|
||||||
+15
-8
@@ -4,12 +4,19 @@ import "time"
|
|||||||
|
|
||||||
// A Chapter is a part of a story.
|
// A Chapter is a part of a story.
|
||||||
type Chapter struct {
|
type Chapter struct {
|
||||||
ID string `bson:"_id"`
|
ID string `bson:"_id"`
|
||||||
StoryID string `bson:"storyId"`
|
StoryID string `bson:"storyId"`
|
||||||
Title string `bson:"title"`
|
Title string `bson:"title"`
|
||||||
Author string `bson:"author"`
|
Author string `bson:"author"`
|
||||||
Source string `bson:"source"`
|
Source string `bson:"source"`
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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})
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user