Browse Source

models: Changed change logging system to be allow for better querying in the future.

1.1
Gisle Aune 6 years ago
parent
commit
cee370c2ac
  1. 5
      graph2/queries/channel.go
  2. 11
      graph2/queries/character.go
  3. 7
      graph2/queries/log.go
  4. 25
      graph2/queries/post.go
  5. 8
      models/change.go
  6. 8
      models/changekeys/all.go
  7. 10
      models/changekeys/listed.go
  8. 27
      models/changekeys/many.go
  9. 52
      models/changekeys/many_test.go
  10. 29
      models/changekeys/one.go
  11. 42
      models/changekeys/one_test.go
  12. 1
      models/changes/db.go
  13. 12
      models/changes/submit.go

5
graph2/queries/channel.go

@ -7,6 +7,7 @@ import (
"git.aiterp.net/rpdata/api/graph2/input" "git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changekeys"
"git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/channels" "git.aiterp.net/rpdata/api/models/channels"
) )
@ -51,7 +52,7 @@ func (r *mutationResolver) AddChannel(ctx context.Context, input input.ChannelAd
return models.Channel{}, errors.New("Failed to add channel: " + err.Error()) return models.Channel{}, errors.New("Failed to add channel: " + err.Error())
} }
go changes.Submit("Channel", "add", token.UserID, channel)
go changes.Submit("Channel", "add", token.UserID, true, changekeys.Listed(channel), channel)
return channel, nil return channel, nil
} }
@ -72,7 +73,7 @@ func (r *mutationResolver) EditChannel(ctx context.Context, input input.ChannelE
return models.Channel{}, errors.New("Failed to edit channel: " + err.Error()) return models.Channel{}, errors.New("Failed to edit channel: " + err.Error())
} }
go changes.Submit("Channel", "edit", token.UserID, channel)
go changes.Submit("Channel", "edit", token.UserID, true, changekeys.Listed(channel), channel)
return channel, nil return channel, nil
} }

11
graph2/queries/character.go

@ -8,6 +8,7 @@ import (
"git.aiterp.net/rpdata/api/graph2/input" "git.aiterp.net/rpdata/api/graph2/input"
"git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changekeys"
"git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/characters" "git.aiterp.net/rpdata/api/models/characters"
"git.aiterp.net/rpdata/api/models/logs" "git.aiterp.net/rpdata/api/models/logs"
@ -68,7 +69,7 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact
return models.Character{}, errors.New("Adding character failed: " + err.Error()) return models.Character{}, errors.New("Adding character failed: " + err.Error())
} }
go changes.Submit("Character", "add", token.UserID, character)
go changes.Submit("Character", "add", token.UserID, true, changekeys.Listed(character), character)
return character, nil return character, nil
} }
@ -96,7 +97,7 @@ func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.Cha
} }
go logs.ScheduleFullUpdate() go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, character)
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return character, nil return character, nil
} }
@ -118,7 +119,7 @@ func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.
} }
go logs.ScheduleFullUpdate() go logs.ScheduleFullUpdate()
go changes.Submit("Character", "edit", token.UserID, character)
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return character, nil return character, nil
} }
@ -146,7 +147,7 @@ func (r *mutationResolver) EditCharacter(ctx context.Context, input input.Charac
return models.Character{}, errors.New("Failed to edit character: " + err.Error()) return models.Character{}, errors.New("Failed to edit character: " + err.Error())
} }
go changes.Submit("Character", "edit", token.UserID, character)
go changes.Submit("Character", "edit", token.UserID, true, changekeys.Listed(character), character)
return character, nil return character, nil
} }
@ -167,7 +168,7 @@ func (r *mutationResolver) RemoveCharacter(ctx context.Context, input input.Char
return models.Character{}, errors.New("Failed to remove character: " + err.Error()) return models.Character{}, errors.New("Failed to remove character: " + err.Error())
} }
go changes.Submit("Character", "remove", token.UserID, character)
go changes.Submit("Character", "remove", token.UserID, true, changekeys.Listed(character), character)
return character, nil return character, nil
} }

7
graph2/queries/log.go

@ -9,6 +9,7 @@ import (
"git.aiterp.net/rpdata/api/internal/auth" "git.aiterp.net/rpdata/api/internal/auth"
"git.aiterp.net/rpdata/api/internal/loader" "git.aiterp.net/rpdata/api/internal/loader"
"git.aiterp.net/rpdata/api/models" "git.aiterp.net/rpdata/api/models"
"git.aiterp.net/rpdata/api/models/changekeys"
"git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/logs" "git.aiterp.net/rpdata/api/models/logs"
"github.com/99designs/gqlgen/graphql" "github.com/99designs/gqlgen/graphql"
@ -77,7 +78,7 @@ func (r *mutationResolver) AddLog(ctx context.Context, input input.LogAddInput)
return models.Log{}, errors.New("Failed to create log: " + err.Error()) return models.Log{}, errors.New("Failed to create log: " + err.Error())
} }
go changes.Submit("Log", "add", token.UserID, log)
go changes.Submit("Log", "add", token.UserID, true, changekeys.Listed(log), log)
return log, nil return log, nil
} }
@ -98,7 +99,7 @@ func (r *mutationResolver) EditLog(ctx context.Context, input input.LogEditInput
return models.Log{}, errors.New("Failed to edit log: " + err.Error()) return models.Log{}, errors.New("Failed to edit log: " + err.Error())
} }
go changes.Submit("Log", "edit", token.UserID, log)
go changes.Submit("Log", "edit", token.UserID, true, changekeys.Listed(log), log)
return log, nil return log, nil
} }
@ -119,7 +120,7 @@ func (r *mutationResolver) RemoveLog(ctx context.Context, input input.LogRemoveI
return models.Log{}, errors.New("Failed to remove log: " + err.Error()) return models.Log{}, errors.New("Failed to remove log: " + err.Error())
} }
go changes.Submit("Log", "remove", token.UserID, log)
go changes.Submit("Log", "remove", token.UserID, true, changekeys.Listed(log), log)
return log, nil return log, nil
} }

25
graph2/queries/post.go

@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"git.aiterp.net/rpdata/api/models/changekeys"
"git.aiterp.net/rpdata/api/models/changes" "git.aiterp.net/rpdata/api/models/changes"
"git.aiterp.net/rpdata/api/models/logs" "git.aiterp.net/rpdata/api/models/logs"
@ -59,7 +60,7 @@ func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput
} }
go logs.UpdateCharacters(log) go logs.UpdateCharacters(log)
go changes.Submit("Post", "add", token.UserID, post)
go changes.Submit("Post", "add", token.UserID, true, changekeys.Many(log, post), post)
return post, nil return post, nil
} }
@ -91,7 +92,14 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp
return models.Post{}, errors.New("Adding post failed: " + err.Error()) return models.Post{}, errors.New("Adding post failed: " + err.Error())
} }
go changes.Submit("Post", "edit", token.UserID, post)
go func() {
log, err := logs.FindID(post.LogID)
if err != nil {
log = models.Log{ID: post.LogID}
}
changes.Submit("Post", "edit", token.UserID, true, changekeys.Many(log, post), post)
}()
return post, nil return post, nil
} }
@ -112,11 +120,14 @@ func (r *mutationResolver) MovePost(ctx context.Context, input input.PostMoveInp
return nil, errors.New("Moving posts failed: " + err.Error()) return nil, errors.New("Moving posts failed: " + err.Error())
} }
posts2 := make([]interface{}, len(posts))
for i := range posts {
posts2[i] = posts[i]
go func() {
log, err := logs.FindID(post.LogID)
if err != nil {
log = models.Log{ID: post.LogID}
} }
go changes.Submit("Post", "move", token.UserID, posts2...)
changes.Submit("Post", "move", token.UserID, true, changekeys.Many(log, posts), post)
}()
return posts, nil return posts, nil
} }
@ -144,8 +155,8 @@ func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemov
} }
logs.UpdateCharacters(log) logs.UpdateCharacters(log)
changes.Submit("Post", "remove", token.UserID, true, changekeys.Many(log, post), post)
}() }()
go changes.Submit("Post", "remove", token.UserID, post)
return post, nil return post, nil
} }

8
models/change.go

@ -8,6 +8,8 @@ type Change struct {
Model string `bson:"model"` Model string `bson:"model"`
Op string `bson:"op"` Op string `bson:"op"`
Author string `bson:"author"` Author string `bson:"author"`
Listed bool `bson:"listed"`
Keys []ChangeKey `bson:"keys"`
Date time.Time `bson:"date"` Date time.Time `bson:"date"`
Logs []Log `bson:"logs"` Logs []Log `bson:"logs"`
@ -15,6 +17,12 @@ type Change struct {
Posts []Post `bson:"posts"` 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. // Data makes a combined, mixed array of all the models stored in this change.
func (change *Change) Data() []interface{} { func (change *Change) Data() []interface{} {
data := make([]interface{}, 0, len(change.Logs)+len(change.Characters)+len(change.Posts)) data := make([]interface{}, 0, len(change.Logs)+len(change.Characters)+len(change.Posts))

8
models/changekeys/all.go

@ -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: "*"}
}

10
models/changekeys/listed.go

@ -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)}
}

27
models/changekeys/many.go

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

52
models/changekeys/many_test.go

@ -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)
}
}
}

29
models/changekeys/one.go

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

42
models/changekeys/one_test.go

@ -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)
}
})
}
}

1
models/changes/db.go

@ -18,6 +18,7 @@ func init() {
collection.EnsureIndexKey("date") collection.EnsureIndexKey("date")
collection.EnsureIndexKey("author") collection.EnsureIndexKey("author")
collection.EnsureIndexKey("keys")
err := collection.EnsureIndex(mgo.Index{ err := collection.EnsureIndex(mgo.Index{
Name: "expiry", Name: "expiry",

12
models/changes/submit.go

@ -8,8 +8,8 @@ import (
"git.aiterp.net/rpdata/api/models" "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() submitMutex.Lock()
defer submitMutex.Unlock() defer submitMutex.Unlock()
@ -24,16 +24,24 @@ func Submit(model, op, author string, objects ...interface{}) (models.Change, er
Date: time.Now(), Date: time.Now(),
Op: op, Op: op,
Author: author, Author: author,
Keys: keys,
Listed: listed,
} }
for _, object := range objects { for _, object := range objects {
switch object := object.(type) { switch object := object.(type) {
case models.Log: case models.Log:
change.Logs = append(change.Logs, object) change.Logs = append(change.Logs, object)
case []models.Log:
change.Logs = append(change.Logs, object...)
case models.Character: case models.Character:
change.Characters = append(change.Characters, object) change.Characters = append(change.Characters, object)
case []models.Character:
change.Characters = append(change.Characters, object...)
case models.Post: case models.Post:
change.Posts = append(change.Posts, object) change.Posts = append(change.Posts, object)
case []models.Post:
change.Posts = append(change.Posts, object...)
} }
} }

Loading…
Cancel
Save