You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
5.1 KiB
211 lines
5.1 KiB
package models
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Change represents a change in the rpdata history through the API.
|
|
type Change struct {
|
|
ID string `bson:"_id"`
|
|
Model ChangeModel `bson:"model"`
|
|
Op string `bson:"op"`
|
|
Author string `bson:"author"`
|
|
Listed bool `bson:"listed"`
|
|
Keys []ChangeKey `bson:"keys"`
|
|
Date time.Time `bson:"date"`
|
|
|
|
ChangeObjectSet
|
|
}
|
|
|
|
type ChangeObjectSet struct {
|
|
Logs []*Log `bson:"logs" json:"logs,omitempty"`
|
|
Characters []*Character `bson:"characters" json:"characters,omitempty"`
|
|
Channels []*Channel `bson:"channels" json:"channels,omitempty"`
|
|
Posts []*Post `bson:"posts" json:"posts,omitempty"`
|
|
Stories []*Story `bson:"stories" json:"stories,omitempty"`
|
|
Tags []*Tag `bson:"tags" json:"tags,omitempty"`
|
|
Chapters []*Chapter `bson:"chapters" json:"chapters,omitempty"`
|
|
Comments []*Comment `bson:"comments" json:"comments,omitempty"`
|
|
Files []*File `bson:"files" json:"files,omitempty"`
|
|
}
|
|
|
|
// AddObject adds the model into the appropriate array.
|
|
func (change *ChangeObjectSet) AddObject(object interface{}) bool {
|
|
if v := reflect.ValueOf(object); v.Kind() == reflect.Struct {
|
|
ptr := reflect.PtrTo(v.Type())
|
|
ptrValue := reflect.New(ptr.Elem())
|
|
ptrValue.Elem().Set(v)
|
|
|
|
object = ptrValue.Interface()
|
|
}
|
|
|
|
switch object := object.(type) {
|
|
case *Log:
|
|
change.Logs = append(change.Logs, object)
|
|
case []*Log:
|
|
change.Logs = append(change.Logs, object...)
|
|
case *Character:
|
|
change.Characters = append(change.Characters, object)
|
|
case []*Character:
|
|
change.Characters = append(change.Characters, object...)
|
|
case *Channel:
|
|
change.Channels = append(change.Channels, object)
|
|
case []*Channel:
|
|
change.Channels = append(change.Channels, object...)
|
|
case *Post:
|
|
change.Posts = append(change.Posts, object)
|
|
case []*Post:
|
|
change.Posts = append(change.Posts, object...)
|
|
case *Story:
|
|
change.Stories = append(change.Stories, object)
|
|
case []*Story:
|
|
change.Stories = append(change.Stories, object...)
|
|
case *Tag:
|
|
change.Tags = append(change.Tags, object)
|
|
case []*Tag:
|
|
change.Tags = append(change.Tags, object...)
|
|
case *Chapter:
|
|
change.Chapters = append(change.Chapters, object)
|
|
case []*Chapter:
|
|
change.Chapters = append(change.Chapters, object...)
|
|
case *Comment:
|
|
change.Comments = append(change.Comments, object)
|
|
case []*Comment:
|
|
change.Comments = append(change.Comments, object...)
|
|
case *File:
|
|
change.Files = append(change.Files, object)
|
|
case []*File:
|
|
change.Files = append(change.Files, object...)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Objects makes a combined, mixed array of all the models stored in this change.
|
|
func (change *Change) Objects() []interface{} {
|
|
data := make([]interface{}, 0, 8)
|
|
|
|
for _, log := range change.Logs {
|
|
data = append(data, log)
|
|
}
|
|
for _, channel := range change.Channels {
|
|
data = append(data, channel)
|
|
}
|
|
for _, character := range change.Characters {
|
|
data = append(data, character)
|
|
}
|
|
for _, post := range change.Posts {
|
|
data = append(data, post)
|
|
}
|
|
for _, story := range change.Stories {
|
|
data = append(data, story)
|
|
}
|
|
for _, tag := range change.Tags {
|
|
data = append(data, tag)
|
|
}
|
|
for _, chapter := range change.Chapters {
|
|
data = append(data, chapter)
|
|
}
|
|
for _, comment := range change.Comments {
|
|
data = append(data, comment)
|
|
}
|
|
for _, file := range change.Files {
|
|
data = append(data, file)
|
|
}
|
|
|
|
return data
|
|
}
|
|
|
|
func (change *Change) PassesFilter(filter ChangeFilter) bool {
|
|
if filter.Author != nil && change.Author != *filter.Author {
|
|
return false
|
|
}
|
|
|
|
// For unlisted changes, pass it only if the filter refers to the specific index.
|
|
if !change.Listed {
|
|
hasSpecificKey := false
|
|
|
|
KeyFindLoop:
|
|
for _, key := range filter.Keys {
|
|
if key.ID == "*" {
|
|
continue
|
|
}
|
|
|
|
for _, changeKey := range change.Keys {
|
|
if changeKey.Model == key.Model && changeKey.ID == key.ID {
|
|
hasSpecificKey = true
|
|
break KeyFindLoop
|
|
}
|
|
}
|
|
}
|
|
|
|
if !hasSpecificKey {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if filter.EarliestDate != nil && filter.EarliestDate.Before(change.Date) {
|
|
return false
|
|
}
|
|
|
|
if len(filter.Keys) > 0 {
|
|
foundKey := false
|
|
|
|
KeyFindLoop2:
|
|
for _, key := range filter.Keys {
|
|
for _, changeKey := range change.Keys {
|
|
if changeKey == key {
|
|
foundKey = true
|
|
break KeyFindLoop2
|
|
}
|
|
}
|
|
}
|
|
|
|
if !foundKey {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// ChangeKey is a key for a change that can be used when subscribing to them.
|
|
type ChangeKey struct {
|
|
Model ChangeModel `bson:"model"`
|
|
ID string `bson:"id"`
|
|
}
|
|
|
|
func (ck *ChangeKey) String() string {
|
|
return ck.Model.String() + ":" + ck.ID
|
|
}
|
|
|
|
func (ck *ChangeKey) Decode(str string) error {
|
|
split := strings.Split(str, ":")
|
|
if len(split) != 2 {
|
|
return fmt.Errorf("invalid change key: %s", str)
|
|
}
|
|
|
|
model := ChangeModel(split[0])
|
|
if !model.IsValid() {
|
|
return fmt.Errorf("invalid change key model: %s", model)
|
|
}
|
|
|
|
ck.Model = model
|
|
ck.ID = split[1]
|
|
|
|
return nil
|
|
}
|
|
|
|
// ChangeFilter is a filter for listing changes.
|
|
type ChangeFilter struct {
|
|
Keys []ChangeKey
|
|
EarliestDate *time.Time
|
|
LatestDate *time.Time
|
|
Author *string
|
|
Limit *int
|
|
}
|