models: Changed change logging system to be allow for better querying in the future.
the build was successful
the build was successful
This commit is contained in:
+13
-5
@@ -4,17 +4,25 @@ import "time"
|
||||
|
||||
// Change represents a change in the rpdata history through the API.
|
||||
type Change struct {
|
||||
ID string `bson:"_id"`
|
||||
Model string `bson:"model"`
|
||||
Op string `bson:"op"`
|
||||
Author string `bson:"author"`
|
||||
Date time.Time `bson:"date"`
|
||||
ID string `bson:"_id"`
|
||||
Model string `bson:"model"`
|
||||
Op string `bson:"op"`
|
||||
Author string `bson:"author"`
|
||||
Listed bool `bson:"listed"`
|
||||
Keys []ChangeKey `bson:"keys"`
|
||||
Date time.Time `bson:"date"`
|
||||
|
||||
Logs []Log `bson:"logs"`
|
||||
Characters []Character `bson:"characters"`
|
||||
Posts []Post `bson:"posts"`
|
||||
}
|
||||
|
||||
// ChangeKey is a key for a change that can be used when subscribing to them.
|
||||
type ChangeKey struct {
|
||||
Model string `bson:"model"`
|
||||
ID string `bson:"id"`
|
||||
}
|
||||
|
||||
// Data makes a combined, mixed array of all the models stored in this change.
|
||||
func (change *Change) Data() []interface{} {
|
||||
data := make([]interface{}, 0, len(change.Logs)+len(change.Characters)+len(change.Posts))
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package changekeys
|
||||
|
||||
import "git.aiterp.net/rpdata/api/models"
|
||||
|
||||
// All makes a changelog that's (model, "*"). It's a helper to standardize it.
|
||||
func All(model string) models.ChangeKey {
|
||||
return models.ChangeKey{Model: model, ID: "*"}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package changekeys
|
||||
|
||||
import "git.aiterp.net/rpdata/api/models"
|
||||
|
||||
// Listed is a helper for cases like []models.ChangeKey{changekeys.All("Logs"), changekeys.One(log)}
|
||||
func Listed(object interface{}) []models.ChangeKey {
|
||||
key := One(object)
|
||||
|
||||
return []models.ChangeKey{key, All(key.Model)}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package changekeys
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"git.aiterp.net/rpdata/api/models"
|
||||
)
|
||||
|
||||
// Many returns a key set matching the input objects, but without the asterisk key. You may pass
|
||||
// a slice as an argument, for example (log, posts) and it will be flattened
|
||||
func Many(objects ...interface{}) []models.ChangeKey {
|
||||
keys := make([]models.ChangeKey, 0, len(objects))
|
||||
for _, object := range objects {
|
||||
if v := reflect.ValueOf(object); v.Kind() == reflect.Slice {
|
||||
elems := make([]interface{}, 0, v.Len())
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
elems = append(elems, v.Index(i).Interface())
|
||||
}
|
||||
|
||||
keys = append(keys, Many(elems...)...)
|
||||
} else {
|
||||
keys = append(keys, One(object))
|
||||
}
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package changekeys_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/rpdata/api/models/changekeys"
|
||||
|
||||
"git.aiterp.net/rpdata/api/models"
|
||||
)
|
||||
|
||||
func TestMany(t *testing.T) {
|
||||
data := []interface{}{
|
||||
models.Log{ID: "Stuff"},
|
||||
[]interface{}{
|
||||
models.Post{ID: "P1"},
|
||||
models.Post{ID: "P2"},
|
||||
models.Post{ID: "P3"},
|
||||
[]interface{}{
|
||||
models.Post{ID: "P4"},
|
||||
models.Character{ID: "C17"},
|
||||
models.Channel{Name: "#Stuff"},
|
||||
},
|
||||
},
|
||||
models.Post{ID: "P5"},
|
||||
}
|
||||
|
||||
expectations := []models.ChangeKey{
|
||||
{Model: "Log", ID: "Stuff"},
|
||||
{Model: "Post", ID: "P1"},
|
||||
{Model: "Post", ID: "P2"},
|
||||
{Model: "Post", ID: "P3"},
|
||||
{Model: "Post", ID: "P4"},
|
||||
{Model: "Character", ID: "C17"},
|
||||
{Model: "Channel", ID: "#Stuff"},
|
||||
{Model: "Post", ID: "P5"},
|
||||
}
|
||||
|
||||
results := changekeys.Many(data...)
|
||||
|
||||
if len(results) != len(expectations) {
|
||||
t.Fatal("Incorrect result length")
|
||||
}
|
||||
|
||||
for i := range expectations {
|
||||
result := results[i]
|
||||
expectation := expectations[i]
|
||||
|
||||
if result.ID != expectation.ID || result.Model != expectation.Model {
|
||||
t.Errorf("Incorrect: actual(%#+v, %#+v) != expected(%#+v, %#+v)", result.Model, result.ID, expectation.Model, expectation.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package changekeys
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"git.aiterp.net/rpdata/api/models"
|
||||
)
|
||||
|
||||
// One makes a ChangeKey for a model, or panics if it's not supported.
|
||||
func One(object interface{}) models.ChangeKey {
|
||||
model := ""
|
||||
if t := reflect.TypeOf(object); t.Kind() == reflect.Ptr {
|
||||
model = t.Elem().Name()
|
||||
} else {
|
||||
model = t.Name()
|
||||
}
|
||||
|
||||
id := ""
|
||||
v := reflect.ValueOf(object)
|
||||
if f := v.FieldByName("ID"); f.Kind() == reflect.String {
|
||||
id = f.String()
|
||||
} else if f = v.FieldByName("Name"); f.Kind() == reflect.String {
|
||||
id = f.String()
|
||||
} else {
|
||||
panic("Unsupported model")
|
||||
}
|
||||
|
||||
return models.ChangeKey{Model: model, ID: id}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package changekeys_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.aiterp.net/rpdata/api/models"
|
||||
"git.aiterp.net/rpdata/api/models/changekeys"
|
||||
)
|
||||
|
||||
func TestOne(t *testing.T) {
|
||||
table := []struct {
|
||||
Label string
|
||||
Input interface{}
|
||||
Output models.ChangeKey
|
||||
}{
|
||||
{
|
||||
"Character_C17",
|
||||
models.Character{ID: "C17"},
|
||||
models.ChangeKey{Model: "Character", ID: "C17"},
|
||||
},
|
||||
{
|
||||
"Channel_#Miner'sRespite",
|
||||
models.Channel{Name: "#Miner'sRespite"},
|
||||
models.ChangeKey{Model: "Channel", ID: "#Miner'sRespite"},
|
||||
},
|
||||
{
|
||||
"Log_2018-10-23_210303325_RedrockAgency",
|
||||
models.Log{ID: "2018-10-23_210303325_RedrockAgency", ShortID: "L807"},
|
||||
models.ChangeKey{Model: "Log", ID: "2018-10-23_210303325_RedrockAgency"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, row := range table {
|
||||
t.Run(row.Label, func(t *testing.T) {
|
||||
key := changekeys.One(row.Input)
|
||||
|
||||
if key.ID != row.Output.ID || key.Model != row.Output.Model {
|
||||
t.Errorf("Incorrect: actual(%#+v, %#+v) != expected(%#+v, %#+v)", key.Model, key.ID, row.Output.Model, row.Output.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ func init() {
|
||||
|
||||
collection.EnsureIndexKey("date")
|
||||
collection.EnsureIndexKey("author")
|
||||
collection.EnsureIndexKey("keys")
|
||||
|
||||
err := collection.EnsureIndex(mgo.Index{
|
||||
Name: "expiry",
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"git.aiterp.net/rpdata/api/models"
|
||||
)
|
||||
|
||||
// Submit a change to the database
|
||||
func Submit(model, op, author string, objects ...interface{}) (models.Change, error) {
|
||||
// Submit a change to the database. The objects may be any supported model, or arrays.
|
||||
func Submit(model, op, author string, listed bool, keys []models.ChangeKey, objects ...interface{}) (models.Change, error) {
|
||||
submitMutex.Lock()
|
||||
defer submitMutex.Unlock()
|
||||
|
||||
@@ -24,16 +24,24 @@ func Submit(model, op, author string, objects ...interface{}) (models.Change, er
|
||||
Date: time.Now(),
|
||||
Op: op,
|
||||
Author: author,
|
||||
Keys: keys,
|
||||
Listed: listed,
|
||||
}
|
||||
|
||||
for _, object := range objects {
|
||||
switch object := object.(type) {
|
||||
case models.Log:
|
||||
change.Logs = append(change.Logs, object)
|
||||
case []models.Log:
|
||||
change.Logs = append(change.Logs, object...)
|
||||
case models.Character:
|
||||
change.Characters = append(change.Characters, object)
|
||||
case []models.Character:
|
||||
change.Characters = append(change.Characters, object...)
|
||||
case models.Post:
|
||||
change.Posts = append(change.Posts, object)
|
||||
case []models.Post:
|
||||
change.Posts = append(change.Posts, object...)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user