Add audit to replace, update set
This commit is contained in:
@@ -9,7 +9,6 @@ import (
|
|||||||
"git.gsuntres.com/general/commons"
|
"git.gsuntres.com/general/commons"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// InsertOneWithStruct can be used to insert defined structs.
|
// InsertOneWithStruct can be used to insert defined structs.
|
||||||
func (c *MongoClient) InsertOneFromStruct(ctx context.Context, database, name string, data any) (bson.M, error) {
|
func (c *MongoClient) InsertOneFromStruct(ctx context.Context, database, name string, data any) (bson.M, error) {
|
||||||
o, err := ToMap(data)
|
o, err := ToMap(data)
|
||||||
|
|||||||
23
replace.go
23
replace.go
@@ -2,14 +2,19 @@ package mongo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||||
|
|
||||||
|
"git.gsuntres.com/general/commons"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *MongoClient) Replace(ctx context.Context, database, name string, id string, data bson.M) (bson.M, error) {
|
func (c *MongoClient) Replace(ctx context.Context, database, name string, id string, original bson.M) (bson.M, error) {
|
||||||
collection := c.GetCollection(database, name)
|
collection := c.GetCollection(database, name)
|
||||||
|
|
||||||
|
data := commons.BsonClone(original)
|
||||||
|
|
||||||
filter := map[string]any { "_id": id }
|
filter := map[string]any { "_id": id }
|
||||||
|
|
||||||
var found bson.M
|
var found bson.M
|
||||||
@@ -31,6 +36,22 @@ func (c *MongoClient) Replace(ctx context.Context, database, name string, id str
|
|||||||
|
|
||||||
PostReplace(updateResult, data, id)
|
PostReplace(updateResult, data, id)
|
||||||
|
|
||||||
|
ignoreAudit := slices.Contains(c.IgnoreAudit, name)
|
||||||
|
|
||||||
|
if c.WithAudit && !ignoreAudit {
|
||||||
|
contx := commons.ContextSerialize(ctx, c.ContextFields)
|
||||||
|
audit := &AuditResult {
|
||||||
|
Entity: name,
|
||||||
|
Op: OpUpdate,
|
||||||
|
Data: original,
|
||||||
|
Before: found,
|
||||||
|
After: data,
|
||||||
|
Context: contx,
|
||||||
|
}
|
||||||
|
|
||||||
|
(*c.OnAudit)(audit)
|
||||||
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mongo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"fmt"
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -18,21 +19,23 @@ func TestReplace(t *testing.T) {
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Insert to mycollection
|
||||||
o, err := client.InsertOne(ctx, "mydb", "mycollection", data)
|
o, err := client.InsertOne(ctx, "mydb", "mycollection", data)
|
||||||
if err != nil { t.Fatalf("Failed to insertOne %#v", err) }
|
if err != nil { t.Fatalf("Failed to insertOne %#v", err) }
|
||||||
|
|
||||||
id := o["_id"].(string)
|
id := o["_id"].(string)
|
||||||
|
|
||||||
// first we retrieve the entity
|
// Retrieve from mycollection
|
||||||
fetched, err := client.GetOne(ctx, "mydb", "mycollection", id)
|
fetched, err := client.GetOne(ctx, "mydb", "mycollection", id)
|
||||||
if err != nil { t.Fatalf("Failed to fetch %#v", err) }
|
if err != nil { t.Fatalf("Failed to fetch %#v", err) }
|
||||||
|
|
||||||
fetched["name"] = "Noah Patel"
|
fetched["name"] = "Noah Patel"
|
||||||
|
|
||||||
|
|
||||||
|
// Replace in mycollection
|
||||||
replaced, err := client.Replace(ctx, "mydb", "mycollection", id, fetched)
|
replaced, err := client.Replace(ctx, "mydb", "mycollection", id, fetched)
|
||||||
if err != nil { t.Fatalf("Failed to replace %#v", err) }
|
if err != nil { t.Fatalf("Failed to replace %#v", err) }
|
||||||
|
|
||||||
// t.Fatalf("-> %v", replaced)
|
|
||||||
if replaced["_id"] != fetched["_id"] {
|
if replaced["_id"] != fetched["_id"] {
|
||||||
t.Fatalf("Not the same entity")
|
t.Fatalf("Not the same entity")
|
||||||
}
|
}
|
||||||
@@ -177,5 +180,86 @@ func TestReplace_Discrimination_EnsureStore(t *testing.T) {
|
|||||||
if changed["store"] != "str_1234" {
|
if changed["store"] != "str_1234" {
|
||||||
t.Fatal("Should have ensured store")
|
t.Fatal("Should have ensured store")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplace_WithAudit(t *testing.T) {
|
||||||
|
client := GetMongoClient()
|
||||||
|
|
||||||
|
// Insert sample
|
||||||
|
data := map[string]any {
|
||||||
|
"_id": "su_123458",
|
||||||
|
"name": "MyNameTODelete",
|
||||||
|
"age": int32(25),
|
||||||
|
}
|
||||||
|
|
||||||
|
before, err := client.InsertOne(context.Background(), "mydb", "mycollection", data)
|
||||||
|
if err != nil { t.Fatalf("Failed to insertOne %#v", err) }
|
||||||
|
|
||||||
|
var (
|
||||||
|
onAudit_calls int
|
||||||
|
onAudit_data any
|
||||||
|
onAudit_before any
|
||||||
|
onAudit_after any
|
||||||
|
onAudit_context any
|
||||||
|
)
|
||||||
|
|
||||||
|
cancel := client.Subscribe(func(audit *AuditResult) error {
|
||||||
|
onAudit_calls++
|
||||||
|
onAudit_data = audit.Data
|
||||||
|
onAudit_before = audit.Before
|
||||||
|
onAudit_after = audit.After
|
||||||
|
onAudit_context = audit.Context
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
toupdate := map[string]any { "name": "** CHANGED NAME **" }
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = context.WithValue(ctx, "account", "xxxxxx")
|
||||||
|
ctx = context.WithValue(ctx, "store", "str_4321")
|
||||||
|
o, err := client.Replace(ctx, "mydb", "mycollection", "su_123458", toupdate)
|
||||||
|
if err != nil { t.Fatalf("Failed to replace %#v", err) }
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if onAudit_calls != 1 {
|
||||||
|
t.Fatalf("ondelete should have been called once, not %d", onAudit_calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_data != nil {
|
||||||
|
dta := onAudit_data.(bson.M)
|
||||||
|
AssertSubset(t, dta, toupdate, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_before != nil {
|
||||||
|
bf := onAudit_before.(bson.M)
|
||||||
|
AssertSubset(t, bf, before, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have before")
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_after != nil {
|
||||||
|
tp := fmt.Sprintf("%T", onAudit_after)
|
||||||
|
|
||||||
|
expectedType := fmt.Sprintf("%T", map[string]any{})
|
||||||
|
if tp != expectedType {
|
||||||
|
t.Fatalf("after has the wrong type %s", tp)
|
||||||
|
}
|
||||||
|
|
||||||
|
after := onAudit_after.(map[string]any)
|
||||||
|
|
||||||
|
AssertSubset(t, after, o, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have after")
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_context != nil {
|
||||||
|
ctx := onAudit_context.(map[string]any)
|
||||||
|
AssertSubset(t, ctx, map[string]any{"account": "xxxxxx", "store": "str_4321"}, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have context")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,32 +2,66 @@ package mongo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
"git.gsuntres.com/general/commons"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdateSet search documents using filter and updates the first it finds using the $set operator.
|
// UpdateSet search documents using filter and updates the first it finds using the $set operator.
|
||||||
func (c *MongoClient) UpdateSet(ctx context.Context, database, name string, filter, data bson.M) (bool, error) {
|
func (c *MongoClient) UpdateSet(ctx context.Context, database, name string, filter, original bson.M) (bool, error) {
|
||||||
collection := c.GetCollection(database, name)
|
collection := c.GetCollection(database, name)
|
||||||
|
|
||||||
|
data := commons.BsonClone(original)
|
||||||
|
|
||||||
prepareForUpdateSet(data)
|
prepareForUpdateSet(data)
|
||||||
|
|
||||||
if err := c.DiscriminatorCheckAndApplyToFilter(ctx, name, filter); err != nil {
|
if err := c.DiscriminatorCheckAndApplyToFilter(ctx, name, filter); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f := Mongofy(&Query{ Filter: filter })
|
||||||
|
|
||||||
if err := c.DiscriminatorOmitInData(name, data); err != nil {
|
if err := c.DiscriminatorOmitInData(name, data); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var found bson.M
|
||||||
|
if c.WithAudit {
|
||||||
|
if err := collection.FindOne(ctx, f).Decode(&found); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
update := bson.M{ "$set": data }
|
update := bson.M{ "$set": data }
|
||||||
updateResult, err := collection.UpdateOne(ctx, filter, update)
|
updateResult, err := collection.UpdateOne(ctx, f, update)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
changed := updateResult.ModifiedCount != 0
|
changed := updateResult.ModifiedCount != 0
|
||||||
|
|
||||||
|
ignoreAudit := slices.Contains(c.IgnoreAudit, name)
|
||||||
|
|
||||||
|
if changed && c.WithAudit && !ignoreAudit {
|
||||||
|
var after bson.M
|
||||||
|
if err := collection.FindOne(ctx, f).Decode(&after); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
contx := commons.ContextSerialize(ctx, c.ContextFields)
|
||||||
|
audit := &AuditResult {
|
||||||
|
Entity: name,
|
||||||
|
Op: OpUpdate,
|
||||||
|
Data: original,
|
||||||
|
Before: found,
|
||||||
|
After: after,
|
||||||
|
Context: contx,
|
||||||
|
}
|
||||||
|
|
||||||
|
(*c.OnAudit)(audit)
|
||||||
|
}
|
||||||
|
|
||||||
return changed, nil
|
return changed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
95
update_set_test.go
Normal file
95
update_set_test.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package mongo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUpdateSet_WithAudit(t *testing.T) {
|
||||||
|
client := GetMongoClient()
|
||||||
|
|
||||||
|
// Insert sample
|
||||||
|
data := map[string]any {
|
||||||
|
"_id": "su_122258",
|
||||||
|
"name": "MyNameToUpdateSet",
|
||||||
|
"age": int32(25),
|
||||||
|
}
|
||||||
|
|
||||||
|
before, err := client.InsertOne(context.Background(), "mydb", "mycollection", data)
|
||||||
|
if err != nil { t.Fatalf("Failed to insertOne %#v", err) }
|
||||||
|
|
||||||
|
var (
|
||||||
|
onAudit_calls int
|
||||||
|
onAudit_data any
|
||||||
|
onAudit_before any
|
||||||
|
onAudit_after any
|
||||||
|
onAudit_context any
|
||||||
|
)
|
||||||
|
|
||||||
|
cancel := client.Subscribe(func(audit *AuditResult) error {
|
||||||
|
onAudit_calls++
|
||||||
|
onAudit_data = audit.Data
|
||||||
|
onAudit_before = audit.Before
|
||||||
|
onAudit_after = audit.After
|
||||||
|
onAudit_context = audit.Context
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
toupdate := map[string]any { "name": "** CHANGED USING UPDATE SET **" }
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = context.WithValue(ctx, "account", "xxxxxx")
|
||||||
|
ctx = context.WithValue(ctx, "store", "str_4321")
|
||||||
|
ok, err := client.UpdateSet(ctx, "mydb", "mycollection", bson.M{"_id": "su_122258"}, toupdate)
|
||||||
|
if err != nil { t.Fatalf("Failed to update %#v", err) }
|
||||||
|
|
||||||
|
if !ok { t.Fatal("Should have updated") }
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if onAudit_calls != 1 {
|
||||||
|
t.Fatalf("ondelete should have been called once, not %d", onAudit_calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_data != nil {
|
||||||
|
dta := onAudit_data.(bson.M)
|
||||||
|
AssertSubset(t, dta, toupdate, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_before != nil {
|
||||||
|
bf := onAudit_before.(bson.M)
|
||||||
|
AssertSubset(t, bf, before, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have before")
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_after != nil {
|
||||||
|
tp := fmt.Sprintf("%T", onAudit_after)
|
||||||
|
|
||||||
|
expectedType := fmt.Sprintf("%T", bson.M{})
|
||||||
|
if tp != expectedType {
|
||||||
|
t.Fatalf("after has the wrong type %s", tp)
|
||||||
|
}
|
||||||
|
|
||||||
|
after := onAudit_after.(bson.M)
|
||||||
|
|
||||||
|
before["name"] = "** CHANGED USING UPDATE SET **"
|
||||||
|
|
||||||
|
AssertSubset(t, after, before, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have after")
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_context != nil {
|
||||||
|
ctx := onAudit_context.(map[string]any)
|
||||||
|
AssertSubset(t, ctx, map[string]any{"account": "xxxxxx", "store": "str_4321"}, "Should have been equal")
|
||||||
|
} else {
|
||||||
|
t.Fatal("should have context")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user