Move audit subscribe to client, Fix tests
This commit is contained in:
@@ -6,12 +6,11 @@ import (
|
|||||||
|
|
||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
|
||||||
"git.gsuntres.com/general/mongo/options"
|
|
||||||
"git.gsuntres.com/general/commons"
|
"git.gsuntres.com/general/commons"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DeleteOne will delete the first document that matches the filter.
|
// DeleteOne will delete the first document that matches the filter.
|
||||||
func (c *MongoClient) DeleteOne(ctx context.Context, database, name string, filter bson.M, opts ...options.Lister[options.DeleteOneOptions]) error {
|
func (c *MongoClient) DeleteOne(ctx context.Context, database, name string, filter bson.M) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// 1. Prepare query.
|
// 1. Prepare query.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"go.mongodb.org/mongo-driver/v2/bson"
|
"go.mongodb.org/mongo-driver/v2/bson"
|
||||||
|
|
||||||
"git.gsuntres.com/general/commons"
|
"git.gsuntres.com/general/commons"
|
||||||
"git.gsuntres.com/general/events"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDeleteOne(t *testing.T) {
|
func TestDeleteOne(t *testing.T) {
|
||||||
@@ -124,21 +123,23 @@ func TestDeleteOne_WithAudit(t *testing.T) {
|
|||||||
onAudit_before any
|
onAudit_before any
|
||||||
onAudit_context any
|
onAudit_context any
|
||||||
)
|
)
|
||||||
|
|
||||||
events.Subscribe(func(audit *AuditResult) error {
|
cancel := client.Subscribe(func(audit *AuditResult) error {
|
||||||
onAudit_calls++
|
onAudit_calls++
|
||||||
onAudit_before = audit.Before
|
onAudit_before = audit.Before
|
||||||
onAudit_context = audit.Context
|
onAudit_context = audit.Context
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "account", "xxxxxx")
|
ctx = context.WithValue(ctx, "account", "xxxxxx")
|
||||||
ctx = context.WithValue(ctx, "store", "str_4321")
|
ctx = context.WithValue(ctx, "store", "str_4321")
|
||||||
err = client.DeleteOne(ctx, "mydb", "mycollection", bson.M{"_id": o["_id"].(string)})
|
err = client.DeleteOne(ctx, "mydb", "mycollection", bson.M{"_id": o["_id"].(string)})
|
||||||
if err != nil { t.Fatalf("Failed to deleteOne %#v", err) }
|
if err != nil { t.Fatalf("Failed to deleteOne %#v", err) }
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
// raw query
|
// raw query
|
||||||
var results bson.M
|
var results bson.M
|
||||||
filter := bson.M{ "name": "MyName" }
|
filter := bson.M{ "name": "MyName" }
|
||||||
@@ -149,7 +150,7 @@ func TestDeleteOne_WithAudit(t *testing.T) {
|
|||||||
if results != nil {
|
if results != nil {
|
||||||
t.Fatalf("Should have no results not %v", results)
|
t.Fatalf("Should have no results not %v", results)
|
||||||
}
|
}
|
||||||
|
|
||||||
if onAudit_calls != 1 {
|
if onAudit_calls != 1 {
|
||||||
t.Fatalf("ondelete should have been called once, not %d", onAudit_calls)
|
t.Fatalf("ondelete should have been called once, not %d", onAudit_calls)
|
||||||
}
|
}
|
||||||
@@ -173,7 +174,7 @@ func TestDeleteOne_WithAudit(t *testing.T) {
|
|||||||
|
|
||||||
func TestDeleteOne_WithAuditButIgnored(t *testing.T) {
|
func TestDeleteOne_WithAuditButIgnored(t *testing.T) {
|
||||||
client := GetMongoClient()
|
client := GetMongoClient()
|
||||||
client.IgnoredAudit = []string{"mycollection"}
|
client.IgnoreAudit = []string{"mycollection"}
|
||||||
|
|
||||||
data := map[string]any {
|
data := map[string]any {
|
||||||
"_id": "su_123458",
|
"_id": "su_123458",
|
||||||
@@ -189,22 +190,23 @@ func TestDeleteOne_WithAuditButIgnored(t *testing.T) {
|
|||||||
var (
|
var (
|
||||||
onAudit_calls int
|
onAudit_calls int
|
||||||
onAudit_before any
|
onAudit_before any
|
||||||
onAudit_context any
|
|
||||||
)
|
)
|
||||||
|
|
||||||
events.Subscribe(func(audit *AuditResult) error {
|
cancel := client.Subscribe(func(audit *AuditResult) error {
|
||||||
onAudit_calls++
|
onAudit_calls++
|
||||||
onAudit_before = audit.Before
|
onAudit_before = audit.Before
|
||||||
onAudit_context = audit.Context
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
ctx = context.WithValue(ctx, "account", "xxxxxx")
|
ctx = context.WithValue(ctx, "account", "xxxxxx")
|
||||||
ctx = context.WithValue(ctx, "store", "str_4321")
|
ctx = context.WithValue(ctx, "store", "str_4321")
|
||||||
err = client.DeleteOne(ctx, "mydb", "mycollection", bson.M{"_id": o["_id"].(string)})
|
err = client.DeleteOne(ctx, "mydb", "mycollection", bson.M{"_id": o["_id"].(string)})
|
||||||
if err != nil { t.Fatalf("Failed to deleteOne %#v", err) }
|
if err != nil { t.Fatalf("Failed to deleteOne %#v", err) }
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
client.IgnoreAudit = []string{"event"}
|
||||||
|
|
||||||
// raw query
|
// raw query
|
||||||
var results bson.M
|
var results bson.M
|
||||||
@@ -217,8 +219,8 @@ func TestDeleteOne_WithAuditButIgnored(t *testing.T) {
|
|||||||
t.Fatalf("Should have no results not %v", results)
|
t.Fatalf("Should have no results not %v", results)
|
||||||
}
|
}
|
||||||
|
|
||||||
if onAudit_calls != 1 {
|
if onAudit_calls != 0 {
|
||||||
t.Fatalf("ondelete should have been called once, not %d", onAudit_calls)
|
t.Fatalf("ondelete should have not been called. [%d]", onAudit_calls)
|
||||||
}
|
}
|
||||||
|
|
||||||
if onAudit_before != nil {
|
if onAudit_before != nil {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ func mapToOp(v string) string {
|
|||||||
case "includes":
|
case "includes":
|
||||||
return "$regex"
|
return "$regex"
|
||||||
default:
|
default:
|
||||||
log.Printf("WARN: no conversion")
|
log.Printf("WARN: no conversion for %v", v)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6
go.mod
6
go.mod
@@ -3,8 +3,8 @@ module git.gsuntres.com/general/mongo
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.gsuntres.com/general/commons v0.0.0-20260423171748-0ce3f3b5eb8c
|
git.gsuntres.com/general/commons v0.0.0-20260423193720-28dcfb9e4ce9
|
||||||
git.gsuntres.com/general/events v0.0.0-20260423140000-1435849fb2c0
|
git.gsuntres.com/general/events v0.0.0-20260424194951-506e91ff46a2
|
||||||
git.gsuntres.com/general/sys v0.0.1
|
git.gsuntres.com/general/sys v0.0.1
|
||||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||||
github.com/google/go-cmp v0.7.0
|
github.com/google/go-cmp v0.7.0
|
||||||
@@ -78,3 +78,5 @@ require (
|
|||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace git.gsuntres.com/general/events => /home/master/Workspace/_general/events
|
||||||
|
|||||||
14
go.sum
14
go.sum
@@ -1,11 +1,13 @@
|
|||||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||||
git.gsuntres.com/general/commons v0.0.0-20260422214453-1e9e43668f5e h1:UFlnWT0u8ywStd7VRPNHmooZw3bSdXqhe/MwOpgWmvI=
|
git.gsuntres.com/general/commons v0.0.0-20260423193720-28dcfb9e4ce9 h1:wbyyIEMTQqnfE/AfYlzpSwtqbfUW5RP9F6Tt84S7RMI=
|
||||||
git.gsuntres.com/general/commons v0.0.0-20260422214453-1e9e43668f5e/go.mod h1:s774W5vN/53DLYKeY4iwFnwPOIHfSg8/V6Ft7sEdl9M=
|
git.gsuntres.com/general/commons v0.0.0-20260423193720-28dcfb9e4ce9/go.mod h1:S93xcBczrgN+gZU0JWkPRnTcAMvQuTp1ChyKkOd/I50=
|
||||||
git.gsuntres.com/general/commons v0.0.0-20260423171748-0ce3f3b5eb8c h1:YZJWYDqqUC0x687yYrcCYzMIvSO40H25IsInBCl+g6A=
|
git.gsuntres.com/general/events v0.0.0-20260424123722-6d275bca7319 h1:HB3q3hrUOrfREV6eJLIZa++QEyw9i0l3FAGkhRqb5Xs=
|
||||||
git.gsuntres.com/general/commons v0.0.0-20260423171748-0ce3f3b5eb8c/go.mod h1:S93xcBczrgN+gZU0JWkPRnTcAMvQuTp1ChyKkOd/I50=
|
git.gsuntres.com/general/events v0.0.0-20260424123722-6d275bca7319/go.mod h1:IQEt0/YT7vYHLTmRhjpqdHgHhagIHZNaay83fndui7s=
|
||||||
git.gsuntres.com/general/events v0.0.0-20260423140000-1435849fb2c0 h1:j4qqM1K6BMIG9ydzbuAljcAeaR/XbImEfs7GF6jtHlY=
|
git.gsuntres.com/general/events v0.0.0-20260424131043-d6c514c3c113 h1:snNU8UgQ50E1Ud3+hGSJzMZG+Y2ILbcfyF+DEdKDVP0=
|
||||||
git.gsuntres.com/general/events v0.0.0-20260423140000-1435849fb2c0/go.mod h1:IQEt0/YT7vYHLTmRhjpqdHgHhagIHZNaay83fndui7s=
|
git.gsuntres.com/general/events v0.0.0-20260424131043-d6c514c3c113/go.mod h1:IQEt0/YT7vYHLTmRhjpqdHgHhagIHZNaay83fndui7s=
|
||||||
|
git.gsuntres.com/general/events v0.0.0-20260424131248-c1c5eac835df h1:Ouu8cSqVKJAiJTmyvxeJm6gRjQ7IAhgdbkXDx0SnnHM=
|
||||||
|
git.gsuntres.com/general/events v0.0.0-20260424131248-c1c5eac835df/go.mod h1:IQEt0/YT7vYHLTmRhjpqdHgHhagIHZNaay83fndui7s=
|
||||||
git.gsuntres.com/general/sys v0.0.1 h1:JpGG6HCkJrTaCICR09kURhMTIc+/s8yb0lHQjo/TDVI=
|
git.gsuntres.com/general/sys v0.0.1 h1:JpGG6HCkJrTaCICR09kURhMTIc+/s8yb0lHQjo/TDVI=
|
||||||
git.gsuntres.com/general/sys v0.0.1/go.mod h1:OVs7w4/tJO1GT7cLIeEsb90LuZqH2xYIVQODI5P1GJs=
|
git.gsuntres.com/general/sys v0.0.1/go.mod h1:OVs7w4/tJO1GT7cLIeEsb90LuZqH2xYIVQODI5P1GJs=
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||||
|
|||||||
24
insert.go
24
insert.go
@@ -2,8 +2,11 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -18,9 +21,11 @@ func (c *MongoClient) InsertOneFromStruct(ctx context.Context, database, name st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InsertOne will add missing ids and the created date before saving to the database.
|
// InsertOne will add missing ids and the created date before saving to the database.
|
||||||
func (c *MongoClient) InsertOne(ctx context.Context, database, name string, data bson.M) (bson.M, error) {
|
func (c *MongoClient) InsertOne(ctx context.Context, database, name string, original bson.M) (bson.M, error) {
|
||||||
collection := c.GetCollection(database, name)
|
collection := c.GetCollection(database, name)
|
||||||
|
|
||||||
|
data := commons.BsonClone(original)
|
||||||
|
|
||||||
prepareForInsert(data, c.GetIdPrefix(name))
|
prepareForInsert(data, c.GetIdPrefix(name))
|
||||||
|
|
||||||
if err := c.DiscriminatorCheckAndApplyToData(ctx, name, data); err != nil {
|
if err := c.DiscriminatorCheckAndApplyToData(ctx, name, data); err != nil {
|
||||||
@@ -30,6 +35,21 @@ func (c *MongoClient) InsertOne(ctx context.Context, database, name string, data
|
|||||||
if _, err := collection.InsertOne(ctx, data); err != nil {
|
if _, err := collection.InsertOne(ctx, data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ignoreAudit := slices.Contains(c.IgnoreAudit, name)
|
||||||
|
|
||||||
|
if c.WithAudit && !ignoreAudit {
|
||||||
|
contx := commons.ContextSerialize(ctx, c.ContextFields)
|
||||||
|
audit := &AuditResult {
|
||||||
|
Entity: name,
|
||||||
|
Op: OpInsert,
|
||||||
|
Data: original,
|
||||||
|
After: data,
|
||||||
|
Context: contx,
|
||||||
|
}
|
||||||
|
|
||||||
|
(*c.OnAudit)(audit)
|
||||||
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
@@ -42,5 +62,3 @@ func prepareForInsert(data bson.M, idPrefix string) {
|
|||||||
ensureCreatedAt(data)
|
ensureCreatedAt(data)
|
||||||
ensureUpdatedAt(data)
|
ensureUpdatedAt(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mongo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"fmt"
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -235,4 +236,72 @@ func TestInsertOne_Discriminate_EnsureStore(t *testing.T) {
|
|||||||
if saved["store"] != "str_1234" {
|
if saved["store"] != "str_1234" {
|
||||||
t.Fatal("Wrong store")
|
t.Fatal("Wrong store")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertOne_WithAudit(t *testing.T) {
|
||||||
|
client := GetMongoClient()
|
||||||
|
|
||||||
|
var (
|
||||||
|
onAudit_calls int
|
||||||
|
onAudit_after any
|
||||||
|
onAudit_context any
|
||||||
|
)
|
||||||
|
|
||||||
|
cancel := client.Subscribe(func(audit *AuditResult) error {
|
||||||
|
onAudit_calls++
|
||||||
|
onAudit_after = audit.After
|
||||||
|
onAudit_context = audit.Context
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
data := map[string]any {
|
||||||
|
"_id": "su_123458",
|
||||||
|
"name": "MyNameTODelete",
|
||||||
|
"age": int32(25),
|
||||||
|
}
|
||||||
|
|
||||||
|
o, err := client.InsertOne(context.Background(), "mydb", "mycollection", data)
|
||||||
|
if err != nil { t.Fatalf("Failed to insertOne %#v", err) }
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = context.WithValue(ctx, "account", "xxxxxx")
|
||||||
|
ctx = context.WithValue(ctx, "store", "str_4321")
|
||||||
|
err = client.DeleteOne(ctx, "mydb", "mycollection", bson.M{"_id": o["_id"].(string)})
|
||||||
|
if err != nil { t.Fatalf("Failed to deleteOne %#v", err) }
|
||||||
|
|
||||||
|
// raw query
|
||||||
|
var results bson.M
|
||||||
|
filter := bson.M{ "name": "MyNameTODelete" }
|
||||||
|
|
||||||
|
c := client.Client.Database("mydb").Collection("mycollection")
|
||||||
|
c.FindOne(context.Background(), filter).Decode(&results)
|
||||||
|
|
||||||
|
if results != nil {
|
||||||
|
t.Fatalf("Should have no results not %v", results)
|
||||||
|
}
|
||||||
|
|
||||||
|
if onAudit_calls != 1 {
|
||||||
|
t.Fatalf("ondelete should have been called once, not %d", onAudit_calls)
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
10
main.go
10
main.go
@@ -94,6 +94,8 @@ type MongoClient struct {
|
|||||||
WithAudit bool
|
WithAudit bool
|
||||||
// OnAudit callback called when audit is enabled.
|
// OnAudit callback called when audit is enabled.
|
||||||
OnAudit *OnAudit
|
OnAudit *OnAudit
|
||||||
|
// EventBus
|
||||||
|
EventBus *events.TypedEventBus
|
||||||
// IgnoreAudit a list of entities to ignore
|
// IgnoreAudit a list of entities to ignore
|
||||||
IgnoreAudit []string
|
IgnoreAudit []string
|
||||||
}
|
}
|
||||||
@@ -364,11 +366,11 @@ func Start(props *MongoStartProps) error {
|
|||||||
|
|
||||||
client.WithAudit = props.WithAudit
|
client.WithAudit = props.WithAudit
|
||||||
|
|
||||||
client.OnAudit = props.OnAudit
|
client.EventBus = events.NewTypedEventBus()
|
||||||
|
|
||||||
if client.WithAudit {
|
if client.WithAudit {
|
||||||
var onAudit OnAudit = func(audit *AuditResult) error {
|
var onAudit OnAudit = func(audit *AuditResult) error {
|
||||||
return events.Publish(audit)
|
return events.Publish(client.EventBus, audit)
|
||||||
}
|
}
|
||||||
client.OnAudit = &onAudit
|
client.OnAudit = &onAudit
|
||||||
}
|
}
|
||||||
@@ -402,6 +404,10 @@ func Start(props *MongoStartProps) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *MongoClient) Subscribe(h events.Handler[AuditResult]) (cancel func()) {
|
||||||
|
return events.Subscribe(c.EventBus, h)
|
||||||
|
}
|
||||||
|
|
||||||
func GetClient() *mongo.Client {
|
func GetClient() *mongo.Client {
|
||||||
return client.Client
|
return client.Client
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user