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
			
			
		
				 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