Browse Source
models: Painstakingly converted half the model package to a models & repostiry stucture. All binaries are probably dead at this point.
1.0
models: Painstakingly converted half the model package to a models & repostiry stucture. All binaries are probably dead at this point.
1.0
Gisle Aune
6 years ago
38 changed files with 878 additions and 64 deletions
-
22graph2/gqlgen.yml
-
6graph2/graph.go
-
11graph2/queries/channel.go
-
15graph2/queries/character.go
-
28graph2/queries/log.go
-
29graph2/queries/post.go
-
7graph2/queries/tags.go
-
5graph2/schema/root.gql
-
2graph2/schema/types/Log.gql
-
8graph2/schema/types/Post.gql
-
40graph2/types/log.go
-
17internal/loader/channel.go
-
24internal/loader/character.go
-
6internal/loader/loader.go
-
10models/channel.go
-
33models/channels/add.go
-
19models/channels/db.go
-
11models/channels/find.go
-
46models/channels/list.go
-
20models/character.go
-
48models/characters/add.go
-
55models/characters/db.go
-
16models/characters/find.go
-
61models/characters/list.go
-
16models/log.go
-
65models/logs/db.go
-
16models/logs/find.go
-
70models/logs/list.go
-
14models/post.go
-
56models/posts/db.go
-
14models/posts/find.go
-
57models/posts/list.go
-
1models/posts/search.go
-
0models/scalars/date.go
-
47models/tag-kind.go
-
7models/tag.go
-
14models/tags/db.go
-
26models/tags/list.go
@ -0,0 +1,40 @@ |
|||||
|
package types |
||||
|
|
||||
|
import ( |
||||
|
"context" |
||||
|
"errors" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/internal/loader" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"git.aiterp.net/rpdata/api/models/posts" |
||||
|
) |
||||
|
|
||||
|
type logResolver struct{} |
||||
|
|
||||
|
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 loader.Channel("name", log.ChannelName) |
||||
|
} |
||||
|
|
||||
|
func (r *logResolver) Characters(ctx context.Context, log *models.Log) ([]models.Character, error) { |
||||
|
loader := loader.FromContext(ctx) |
||||
|
if loader == nil { |
||||
|
return nil, errors.New("no loader") |
||||
|
} |
||||
|
|
||||
|
return loader.Characters("id", log.CharacterIDs...) |
||||
|
} |
||||
|
|
||||
|
func (r *logResolver) Posts(ctx context.Context, log *models.Log, kinds []string) ([]models.Post, error) { |
||||
|
return posts.List(&posts.Filter{ |
||||
|
LogID: &log.ShortID, |
||||
|
Kind: kinds, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// LogResolver is a resolver
|
||||
|
var LogResolver logResolver |
@ -0,0 +1,10 @@ |
|||||
|
package models |
||||
|
|
||||
|
// A Channel represents information abount an IRC RP channel, and whether it should be logged
|
||||
|
type Channel struct { |
||||
|
Name string `bson:"_id"` |
||||
|
Logged bool `bson:"logged"` |
||||
|
Hub bool `bson:"hub"` |
||||
|
EventName string `bson:"event,omitempty"` |
||||
|
LocationName string `bson:"location,omitempty"` |
||||
|
} |
@ -0,0 +1,33 @@ |
|||||
|
package channels |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"strings" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
) |
||||
|
|
||||
|
// ErrInvalidName is an error for an invalid channel name.
|
||||
|
var ErrInvalidName = errors.New("Invalid channel name") |
||||
|
|
||||
|
// Add creates a new channel.
|
||||
|
func Add(name string, logged, hub bool, event, location string) (models.Channel, error) { |
||||
|
if len(name) < 3 && !strings.HasPrefix(name, "#") { |
||||
|
return models.Channel{}, ErrInvalidName |
||||
|
} |
||||
|
|
||||
|
channel := models.Channel{ |
||||
|
Name: name, |
||||
|
Logged: logged, |
||||
|
Hub: hub, |
||||
|
EventName: event, |
||||
|
LocationName: location, |
||||
|
} |
||||
|
|
||||
|
err := collection.Insert(channel) |
||||
|
if err != nil { |
||||
|
return models.Channel{}, err |
||||
|
} |
||||
|
|
||||
|
return channel, nil |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
package channels |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/internal/store" |
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
var collection *mgo.Collection |
||||
|
|
||||
|
func init() { |
||||
|
store.HandleInit(func(db *mgo.Database) { |
||||
|
collection = db.C("common.channels") |
||||
|
|
||||
|
collection.EnsureIndexKey("logged") |
||||
|
collection.EnsureIndexKey("hub") |
||||
|
collection.EnsureIndexKey("event") |
||||
|
collection.EnsureIndexKey("location") |
||||
|
}) |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
package channels |
||||
|
|
||||
|
import "git.aiterp.net/rpdata/api/models" |
||||
|
|
||||
|
// FindName finds a channel by its id (its name).
|
||||
|
func FindName(name string) (models.Channel, error) { |
||||
|
channel := models.Channel{} |
||||
|
err := collection.FindId(name).One(&channel) |
||||
|
|
||||
|
return channel, err |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
package channels |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// Filter for searching
|
||||
|
type Filter struct { |
||||
|
Logged *bool `json:"logged"` |
||||
|
EventName string `json:"eventName"` |
||||
|
LocationName string `json:"locationName"` |
||||
|
} |
||||
|
|
||||
|
// List finds channels, if logged is true it will be limited to logged
|
||||
|
// channels
|
||||
|
func List(filter *Filter) ([]models.Channel, error) { |
||||
|
query := bson.M{} |
||||
|
|
||||
|
if filter != nil { |
||||
|
if filter.Logged != nil { |
||||
|
query["logged"] = *filter.Logged |
||||
|
} |
||||
|
if filter.EventName != "" { |
||||
|
query["eventName"] = filter.EventName |
||||
|
} |
||||
|
if filter.LocationName != "" { |
||||
|
query["locationName"] = filter.LocationName |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
channels := make([]models.Channel, 0, 128) |
||||
|
err := collection.Find(query).All(&channels) |
||||
|
|
||||
|
return channels, err |
||||
|
} |
||||
|
|
||||
|
// ListNames finds channels by the names provided
|
||||
|
func ListNames(names ...string) ([]models.Channel, error) { |
||||
|
query := bson.M{"_id": bson.M{"$in": names}} |
||||
|
|
||||
|
channels := make([]models.Channel, 0, 32) |
||||
|
err := collection.Find(query).All(&channels) |
||||
|
|
||||
|
return channels, err |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
package models |
||||
|
|
||||
|
// Character is a common data model representing an RP character or NPC.
|
||||
|
type Character struct { |
||||
|
ID string `json:"id" bson:"_id"` |
||||
|
Nicks []string `json:"nicks" bson:"nicks"` |
||||
|
Name string `json:"name" bson:"name"` |
||||
|
ShortName string `json:"shortName" bson:"shortName"` |
||||
|
Author string `json:"author" bson:"author"` |
||||
|
Description string `json:"description" bson:"description"` |
||||
|
} |
||||
|
|
||||
|
// Nick gets the character's nick.
|
||||
|
func (character *Character) Nick() *string { |
||||
|
if len(character.Nicks[0]) == 0 { |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
return &character.Nicks[0] |
||||
|
} |
@ -0,0 +1,48 @@ |
|||||
|
package characters |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/model/counter" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
) |
||||
|
|
||||
|
// Add creates a Character and pushes it to the database. It does some validation
|
||||
|
// on nick, name, shortName and author. It will generate a shortname from the first
|
||||
|
// name if a blank one is provided.
|
||||
|
func Add(nick, name, shortName, author, description string) (models.Character, error) { |
||||
|
if len(nick) < 1 || len(name) < 1 || len(author) < 1 { |
||||
|
return models.Character{}, errors.New("Nick, name, or author name too short or empty") |
||||
|
} |
||||
|
if shortName == "" { |
||||
|
shortName = strings.SplitN(name, " ", 2)[0] |
||||
|
} |
||||
|
|
||||
|
char, err := FindNick(nick) |
||||
|
if err == nil && char.ID != "" { |
||||
|
return models.Character{}, errors.New("Nick is occupied") |
||||
|
} |
||||
|
|
||||
|
nextID, err := counter.Next("auto_increment", "models.Character") |
||||
|
if err != nil { |
||||
|
return models.Character{}, err |
||||
|
} |
||||
|
|
||||
|
character := models.Character{ |
||||
|
ID: "C" + strconv.Itoa(nextID), |
||||
|
Nicks: []string{nick}, |
||||
|
Name: name, |
||||
|
ShortName: shortName, |
||||
|
Author: author, |
||||
|
Description: description, |
||||
|
} |
||||
|
|
||||
|
err = collection.Insert(character) |
||||
|
if err != nil { |
||||
|
return models.Character{}, err |
||||
|
} |
||||
|
|
||||
|
return character, nil |
||||
|
} |
@ -0,0 +1,55 @@ |
|||||
|
package characters |
||||
|
|
||||
|
import ( |
||||
|
"log" |
||||
|
|
||||
|
"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.Character, error) { |
||||
|
character := models.Character{} |
||||
|
err := collection.Find(query).One(&character) |
||||
|
if err != nil { |
||||
|
return models.Character{}, err |
||||
|
} |
||||
|
|
||||
|
return character, nil |
||||
|
} |
||||
|
|
||||
|
func list(query interface{}) ([]models.Character, error) { |
||||
|
characters := make([]models.Character, 0, 64) |
||||
|
err := collection.Find(query).All(&characters) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return characters, nil |
||||
|
} |
||||
|
|
||||
|
func init() { |
||||
|
store.HandleInit(func(db *mgo.Database) { |
||||
|
collection = db.C("common.characters") |
||||
|
|
||||
|
collection.EnsureIndexKey("name") |
||||
|
collection.EnsureIndexKey("shortName") |
||||
|
collection.EnsureIndexKey("author") |
||||
|
err := collection.EnsureIndex(mgo.Index{ |
||||
|
Key: []string{"nicks"}, |
||||
|
Unique: true, |
||||
|
DropDups: true, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
log.Fatalln("init common.characters:", err) |
||||
|
} |
||||
|
err = collection.EnsureIndex(mgo.Index{ |
||||
|
Key: []string{"$text:description"}, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
log.Fatalln("init common.characters:", err) |
||||
|
} |
||||
|
}) |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package characters |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// FindID finds a character by id.
|
||||
|
func FindID(id string) (models.Character, error) { |
||||
|
return find(bson.M{"_id": id}) |
||||
|
} |
||||
|
|
||||
|
// FindNick finds a character by nick
|
||||
|
func FindNick(nick string) (models.Character, error) { |
||||
|
return find(bson.M{"nicks": nick}) |
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
package characters |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// Filter is used to filter the list of characters
|
||||
|
type Filter struct { |
||||
|
IDs []string `json:"ids"` |
||||
|
Nicks []string `json:"nicks"` |
||||
|
Names []string `json:"names"` |
||||
|
Author *string `json:"author"` |
||||
|
Search *string `json:"search"` |
||||
|
Logged *bool `json:"logged"` |
||||
|
} |
||||
|
|
||||
|
// List lists all characters
|
||||
|
func List(filter *Filter) ([]models.Character, error) { |
||||
|
query := bson.M{} |
||||
|
|
||||
|
if filter != nil { |
||||
|
if len(filter.IDs) > 1 { |
||||
|
query["_id"] = bson.M{"$in": filter.IDs} |
||||
|
} else if len(filter.IDs) == 1 { |
||||
|
query["_id"] = filter.IDs[0] |
||||
|
} |
||||
|
|
||||
|
if len(filter.Nicks) > 1 { |
||||
|
query["nicks"] = bson.M{"$in": filter.Nicks} |
||||
|
} else if len(filter.Nicks) == 1 { |
||||
|
query["nicks"] = filter.Nicks[0] |
||||
|
} |
||||
|
|
||||
|
if len(filter.Names) > 1 { |
||||
|
query["$or"] = bson.M{ |
||||
|
"name": bson.M{"$in": filter.Names}, |
||||
|
"shortName": bson.M{"$in": filter.Names}, |
||||
|
} |
||||
|
} else if len(filter.Names) == 1 { |
||||
|
query["$or"] = bson.M{ |
||||
|
"name": filter.Names[0], |
||||
|
"shortName": filter.Names[0], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if filter.Logged != nil { |
||||
|
query["logged"] = *filter.Logged |
||||
|
} |
||||
|
|
||||
|
if filter.Author != nil { |
||||
|
query["author"] = *filter.Author |
||||
|
} |
||||
|
|
||||
|
if filter.Search != nil { |
||||
|
query["$text"] = bson.M{"$search": *filter.Search} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return list(query) |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package models |
||||
|
|
||||
|
import "time" |
||||
|
|
||||
|
// Log is the header/session for a log file.
|
||||
|
type Log struct { |
||||
|
ID string `bson:"_id"` |
||||
|
ShortID string `bson:"shortId"` |
||||
|
Date time.Time `bson:"date"` |
||||
|
ChannelName string `bson:"channel"` |
||||
|
EventName string `bson:"event,omitempty"` |
||||
|
Title string `bson:"title,omitempty"` |
||||
|
Description string `bson:"description,omitempty"` |
||||
|
Open bool `bson:"open"` |
||||
|
CharacterIDs []string `bson:"characterIds"` |
||||
|
} |
@ -0,0 +1,65 @@ |
|||||
|
package logs |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"log" |
||||
|
"time" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/internal/store" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
var collection *mgo.Collection |
||||
|
var postCollection *mgo.Collection |
||||
|
|
||||
|
func find(query interface{}) (models.Log, error) { |
||||
|
log := models.Log{} |
||||
|
err := collection.Find(query).One(&log) |
||||
|
if err != nil { |
||||
|
return models.Log{}, err |
||||
|
} |
||||
|
|
||||
|
return log, nil |
||||
|
} |
||||
|
|
||||
|
func list(query interface{}, limit int) ([]models.Log, error) { |
||||
|
logs := make([]models.Log, 0, 64) |
||||
|
err := collection.Find(query).Limit(limit).Sort("-date").All(&logs) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return logs, nil |
||||
|
} |
||||
|
|
||||
|
func iter(query interface{}, limit int) *mgo.Iter { |
||||
|
return collection.Find(query).Sort("-date").Limit(limit).Batch(8).Iter() |
||||
|
} |
||||
|
|
||||
|
func makeLogID(date time.Time, channel string) string { |
||||
|
return fmt.Sprintf("%s%03d_%s", date.UTC().Format("2006-01-02_150405"), (date.Nanosecond() / int(time.Millisecond/time.Nanosecond)), channel[1:]) |
||||
|
} |
||||
|
|
||||
|
func init() { |
||||
|
store.HandleInit(func(db *mgo.Database) { |
||||
|
collection = db.C("logbot3.logs") |
||||
|
postCollection = db.C("logbo3.posts") |
||||
|
|
||||
|
collection.EnsureIndexKey("date") |
||||
|
collection.EnsureIndexKey("channel") |
||||
|
collection.EnsureIndexKey("characterIds") |
||||
|
collection.EnsureIndexKey("event") |
||||
|
collection.EnsureIndex(mgo.Index{ |
||||
|
Key: []string{"channel", "open"}, |
||||
|
}) |
||||
|
err := collection.EnsureIndex(mgo.Index{ |
||||
|
Key: []string{"shortId"}, |
||||
|
Unique: true, |
||||
|
DropDups: true, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
log.Fatalln("init logbot3.logs:", err) |
||||
|
} |
||||
|
}) |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package logs |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// FindID finds a log either by it's ID or short ID.
|
||||
|
func FindID(id string) (models.Log, error) { |
||||
|
return find(bson.M{ |
||||
|
"$or": []bson.M{ |
||||
|
bson.M{"_id": id}, |
||||
|
bson.M{"shortId": id}, |
||||
|
}, |
||||
|
}) |
||||
|
} |
@ -0,0 +1,70 @@ |
|||||
|
package logs |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// Filter for the List() function
|
||||
|
type Filter struct { |
||||
|
Search *string |
||||
|
Characters *[]string |
||||
|
Channels *[]string |
||||
|
Events *[]string |
||||
|
Open *bool |
||||
|
Limit int |
||||
|
} |
||||
|
|
||||
|
// List lists all logs
|
||||
|
func List(filter *Filter) ([]models.Log, error) { |
||||
|
query := bson.M{} |
||||
|
limit := 0 |
||||
|
|
||||
|
if filter != nil { |
||||
|
// Run a text search
|
||||
|
if filter.Search != nil { |
||||
|
searchResults, err := search(*filter.Search) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
// Posts always use shortId to refer to the log
|
||||
|
query["shortId"] = bson.M{"$in": searchResults} |
||||
|
} |
||||
|
|
||||
|
// Find logs including any of the specified events and channels
|
||||
|
if filter.Channels != nil { |
||||
|
query["channel"] = bson.M{"$in": *filter.Channels} |
||||
|
} |
||||
|
if filter.Events != nil { |
||||
|
query["event"] = bson.M{"$in": *filter.Events} |
||||
|
} |
||||
|
|
||||
|
// Find logs including all of the specified character IDs.
|
||||
|
if filter.Characters != nil { |
||||
|
query["characterIds"] = bson.M{"$all": *filter.Characters} |
||||
|
} |
||||
|
|
||||
|
// Limit to only open logs
|
||||
|
if filter.Open != nil { |
||||
|
query["open"] = *filter.Open |
||||
|
} |
||||
|
|
||||
|
// Set the limit from the filter
|
||||
|
limit = filter.Limit |
||||
|
} |
||||
|
|
||||
|
return list(query, limit) |
||||
|
} |
||||
|
|
||||
|
func search(text string) ([]string, error) { |
||||
|
query := bson.M{ |
||||
|
"$text": bson.M{"$search": text}, |
||||
|
"logId": bson.M{"$ne": nil}, |
||||
|
} |
||||
|
|
||||
|
ids := make([]string, 0, 64) |
||||
|
err := postCollection.Find(query).Distinct("logId", ids) |
||||
|
|
||||
|
return ids, err |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package models |
||||
|
|
||||
|
import "time" |
||||
|
|
||||
|
// A Post is a part of a log file.
|
||||
|
type Post struct { |
||||
|
ID string `bson:"_id"` |
||||
|
LogID string `bson:"logId"` |
||||
|
Time time.Time `bson:"time"` |
||||
|
Kind string `bson:"kind"` |
||||
|
Nick string `bson:"nick"` |
||||
|
Text string `bson:"text"` |
||||
|
Position int `bson:"position"` |
||||
|
} |
@ -0,0 +1,56 @@ |
|||||
|
package posts |
||||
|
|
||||
|
import ( |
||||
|
"log" |
||||
|
"sync" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/internal/store" |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
var collection *mgo.Collection |
||||
|
var mutex sync.RWMutex |
||||
|
|
||||
|
func find(query interface{}) (models.Post, error) { |
||||
|
post := models.Post{} |
||||
|
err := collection.Find(query).One(&post) |
||||
|
if err != nil { |
||||
|
return models.Post{}, err |
||||
|
} |
||||
|
|
||||
|
return post, nil |
||||
|
} |
||||
|
|
||||
|
func list(query interface{}, limit int, sort ...string) ([]models.Post, error) { |
||||
|
size := 64 |
||||
|
if limit > 0 { |
||||
|
size = limit |
||||
|
} |
||||
|
posts := make([]models.Post, 0, size) |
||||
|
|
||||
|
err := collection.Find(query).Limit(limit).Sort(sort...).All(&posts) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return posts, nil |
||||
|
} |
||||
|
|
||||
|
func init() { |
||||
|
store.HandleInit(func(db *mgo.Database) { |
||||
|
collection = db.C("logbot3.posts") |
||||
|
|
||||
|
collection.EnsureIndexKey("logId") |
||||
|
collection.EnsureIndexKey("time") |
||||
|
collection.EnsureIndexKey("kind") |
||||
|
collection.EnsureIndexKey("position") |
||||
|
|
||||
|
err := collection.EnsureIndex(mgo.Index{ |
||||
|
Key: []string{"$text:text"}, |
||||
|
}) |
||||
|
if err != nil { |
||||
|
log.Fatalln("init logbot3.logs:", err) |
||||
|
} |
||||
|
}) |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package posts |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// FindID finds a log post by ID.
|
||||
|
func FindID(id string) (models.Post, error) { |
||||
|
mutex.RLock() |
||||
|
defer mutex.RUnlock() |
||||
|
|
||||
|
return find(bson.M{"_id": id}) |
||||
|
} |
@ -0,0 +1,57 @@ |
|||||
|
package posts |
||||
|
|
||||
|
import ( |
||||
|
"strings" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// Filter is used to generate a query to the database.
|
||||
|
type Filter struct { |
||||
|
ID []string |
||||
|
Kind []string |
||||
|
LogID *string |
||||
|
Search *string |
||||
|
Limit int |
||||
|
} |
||||
|
|
||||
|
// List lists the posts according to the filter
|
||||
|
func List(filter *Filter) ([]models.Post, error) { |
||||
|
mutex.RLock() |
||||
|
defer mutex.RUnlock() |
||||
|
|
||||
|
limit := 256 |
||||
|
query := bson.M{} |
||||
|
|
||||
|
if filter != nil { |
||||
|
if filter.LogID != nil { |
||||
|
query["logId"] = filter.LogID |
||||
|
} |
||||
|
|
||||
|
if len(filter.ID) > 1 { |
||||
|
query["_id"] = bson.M{"$in": filter.ID} |
||||
|
} else if len(filter.ID) == 1 { |
||||
|
query["_id"] = filter.ID[0] |
||||
|
} |
||||
|
|
||||
|
if len(filter.Kind) > 1 { |
||||
|
for i := range filter.Kind { |
||||
|
filter.Kind[i] = strings.ToLower(filter.Kind[i]) |
||||
|
} |
||||
|
|
||||
|
query["kind"] = bson.M{"$in": filter.Kind} |
||||
|
} else if len(filter.Kind) == 1 { |
||||
|
query["kind"] = strings.ToLower(filter.Kind[0]) |
||||
|
} |
||||
|
|
||||
|
limit = filter.Limit |
||||
|
} |
||||
|
|
||||
|
posts, err := list(query, limit, "position") |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
|
||||
|
return posts, nil |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
package posts |
@ -0,0 +1,47 @@ |
|||||
|
package models |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"io" |
||||
|
) |
||||
|
|
||||
|
// TagKind represents the kind of tags.
|
||||
|
type TagKind string |
||||
|
|
||||
|
const ( |
||||
|
// TagKindOrganization is a tag kind, see GraphQL documentation.
|
||||
|
TagKindOrganization TagKind = "Organization" |
||||
|
|
||||
|
// TagKindCharacter is a tag kind, see GraphQL documentation.
|
||||
|
TagKindCharacter TagKind = "Character" |
||||
|
|
||||
|
// TagKindLocation is a tag kind, see GraphQL documentation.
|
||||
|
TagKindLocation TagKind = "Location" |
||||
|
|
||||
|
// TagKindEvent is a tag kind, see GraphQL documentation.
|
||||
|
TagKindEvent TagKind = "Event" |
||||
|
|
||||
|
// TagKindSeries is a tag kind, see GraphQL documentation.
|
||||
|
TagKindSeries TagKind = "Series" |
||||
|
) |
||||
|
|
||||
|
// UnmarshalGQL unmarshals
|
||||
|
func (e *TagKind) UnmarshalGQL(v interface{}) error { |
||||
|
str, ok := v.(string) |
||||
|
if !ok { |
||||
|
return fmt.Errorf("enums must be strings") |
||||
|
} |
||||
|
|
||||
|
*e = TagKind(str) |
||||
|
switch *e { |
||||
|
case TagKindOrganization, TagKindCharacter, TagKindLocation, TagKindEvent, TagKindSeries: |
||||
|
return nil |
||||
|
default: |
||||
|
return fmt.Errorf("%s is not a valid TagKind", str) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// MarshalGQL turns it into a JSON string
|
||||
|
func (e TagKind) MarshalGQL(w io.Writer) { |
||||
|
fmt.Fprint(w, "\""+string(e), "\"") |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
package models |
||||
|
|
||||
|
// A Tag associates a story with other content, like other stories, logs and more.
|
||||
|
type Tag struct { |
||||
|
Kind TagKind `bson:"kind"` |
||||
|
Name string `bson:"name"` |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
package tags |
||||
|
|
||||
|
import ( |
||||
|
"git.aiterp.net/rpdata/api/internal/store" |
||||
|
"github.com/globalsign/mgo" |
||||
|
) |
||||
|
|
||||
|
var storyCollection *mgo.Collection |
||||
|
|
||||
|
func init() { |
||||
|
store.HandleInit(func(db *mgo.Database) { |
||||
|
storyCollection = db.C("story.stories") |
||||
|
}) |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
package tags |
||||
|
|
||||
|
import ( |
||||
|
"sort" |
||||
|
"strings" |
||||
|
|
||||
|
"git.aiterp.net/rpdata/api/models" |
||||
|
"github.com/globalsign/mgo/bson" |
||||
|
) |
||||
|
|
||||
|
// List lists all tags
|
||||
|
func List() ([]models.Tag, error) { |
||||
|
tags := make([]models.Tag, 0, 64) |
||||
|
err := storyCollection.Find(bson.M{"listed": true, "tags": bson.M{"$ne": nil}}).Distinct("tags", &tags) |
||||
|
|
||||
|
sort.Slice(tags, func(i, j int) bool { |
||||
|
kindCmp := strings.Compare(string(tags[i].Kind), string(tags[j].Kind)) |
||||
|
if kindCmp != 0 { |
||||
|
return kindCmp < 0 |
||||
|
} |
||||
|
|
||||
|
return strings.Compare(tags[i].Name, tags[j].Name) < 0 |
||||
|
}) |
||||
|
|
||||
|
return tags, err |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue