models: Painstakingly converted half the model package to a models & repostiry stucture. All binaries are probably dead at this point.
the build failed

This commit is contained in:
2018-09-16 00:13:12 +02:00
parent 2b05d90f4a
commit 0768cfd0bd
38 changed files with 878 additions and 64 deletions
+10
View File
@@ -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"`
}
+33
View File
@@ -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
}
+19
View File
@@ -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")
})
}
+11
View File
@@ -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
}
+46
View File
@@ -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
}
+20
View File
@@ -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]
}
+48
View File
@@ -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
}
+55
View File
@@ -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)
}
})
}
+16
View File
@@ -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})
}
+61
View File
@@ -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)
}
+16
View File
@@ -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"`
}
+65
View File
@@ -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)
}
})
}
+16
View File
@@ -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},
},
})
}
+70
View File
@@ -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
}
+14
View File
@@ -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"`
}
+56
View File
@@ -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)
}
})
}
+14
View File
@@ -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})
}
+57
View File
@@ -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
}
+1
View File
@@ -0,0 +1 @@
package posts
+28
View File
@@ -0,0 +1,28 @@
package scalars
import (
"fmt"
"io"
"time"
"github.com/99designs/gqlgen/graphql"
)
// MarshalDate marshals time into gql Date type
func MarshalDate(t time.Time) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
w.Write([]byte(`"` + t.Format(time.RFC3339Nano) + `"`))
})
}
// UnmarshalDate unmarshals time from gql Date type
func UnmarshalDate(v interface{}) (time.Time, error) {
switch v := v.(type) {
case string:
return time.Parse(time.RFC3339Nano, v)
case int:
return time.Unix(0, int64(v)*1000000), nil
default:
return time.Time{}, fmt.Errorf("%T is not a valid time", v)
}
}
+47
View File
@@ -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), "\"")
}
+7
View File
@@ -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"`
}
+14
View File
@@ -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")
})
}
+26
View File
@@ -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
}