diff --git a/delete.go b/delete.go index 9d527e9..664822d 100644 --- a/delete.go +++ b/delete.go @@ -6,12 +6,11 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" - "git.gsuntres.com/general/mongo/options" "git.gsuntres.com/general/commons" ) // 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 // 1. Prepare query. diff --git a/delete_test.go b/delete_test.go index 31004ec..92554b8 100644 --- a/delete_test.go +++ b/delete_test.go @@ -10,7 +10,6 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "git.gsuntres.com/general/commons" - "git.gsuntres.com/general/events" ) func TestDeleteOne(t *testing.T) { @@ -124,21 +123,23 @@ func TestDeleteOne_WithAudit(t *testing.T) { onAudit_before any onAudit_context any ) - - events.Subscribe(func(audit *AuditResult) error { + + cancel := client.Subscribe(func(audit *AuditResult) error { onAudit_calls++ onAudit_before = audit.Before onAudit_context = audit.Context - + return nil }) - + 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) } - + + cancel() + // raw query var results bson.M filter := bson.M{ "name": "MyName" } @@ -149,7 +150,7 @@ func TestDeleteOne_WithAudit(t *testing.T) { 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) } @@ -173,7 +174,7 @@ func TestDeleteOne_WithAudit(t *testing.T) { func TestDeleteOne_WithAuditButIgnored(t *testing.T) { client := GetMongoClient() - client.IgnoredAudit = []string{"mycollection"} + client.IgnoreAudit = []string{"mycollection"} data := map[string]any { "_id": "su_123458", @@ -189,22 +190,23 @@ func TestDeleteOne_WithAuditButIgnored(t *testing.T) { var ( onAudit_calls int onAudit_before any - onAudit_context any ) - events.Subscribe(func(audit *AuditResult) error { + cancel := client.Subscribe(func(audit *AuditResult) error { onAudit_calls++ onAudit_before = audit.Before - onAudit_context = audit.Context return nil }) - + 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) } + + cancel() + client.IgnoreAudit = []string{"event"} // raw query var results bson.M @@ -217,8 +219,8 @@ func TestDeleteOne_WithAuditButIgnored(t *testing.T) { 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_calls != 0 { + t.Fatalf("ondelete should have not been called. [%d]", onAudit_calls) } if onAudit_before != nil { diff --git a/filter.go b/filter.go index d60f0d9..3c96579 100644 --- a/filter.go +++ b/filter.go @@ -100,7 +100,7 @@ func mapToOp(v string) string { case "includes": return "$regex" default: - log.Printf("WARN: no conversion") + log.Printf("WARN: no conversion for %v", v) return v } } \ No newline at end of file diff --git a/go.mod b/go.mod index 7cc837f..244cc74 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module git.gsuntres.com/general/mongo go 1.25.0 require ( - git.gsuntres.com/general/commons v0.0.0-20260423171748-0ce3f3b5eb8c - git.gsuntres.com/general/events v0.0.0-20260423140000-1435849fb2c0 + git.gsuntres.com/general/commons v0.0.0-20260423193720-28dcfb9e4ce9 + git.gsuntres.com/general/events v0.0.0-20260424194951-506e91ff46a2 git.gsuntres.com/general/sys v0.0.1 github.com/go-viper/mapstructure/v2 v2.5.0 github.com/google/go-cmp v0.7.0 @@ -78,3 +78,5 @@ require ( google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace git.gsuntres.com/general/events => /home/master/Workspace/_general/events diff --git a/go.sum b/go.sum index 76114ab..e84b775 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,13 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= 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-20260422214453-1e9e43668f5e/go.mod h1:s774W5vN/53DLYKeY4iwFnwPOIHfSg8/V6Ft7sEdl9M= -git.gsuntres.com/general/commons v0.0.0-20260423171748-0ce3f3b5eb8c h1:YZJWYDqqUC0x687yYrcCYzMIvSO40H25IsInBCl+g6A= -git.gsuntres.com/general/commons v0.0.0-20260423171748-0ce3f3b5eb8c/go.mod h1:S93xcBczrgN+gZU0JWkPRnTcAMvQuTp1ChyKkOd/I50= -git.gsuntres.com/general/events v0.0.0-20260423140000-1435849fb2c0 h1:j4qqM1K6BMIG9ydzbuAljcAeaR/XbImEfs7GF6jtHlY= -git.gsuntres.com/general/events v0.0.0-20260423140000-1435849fb2c0/go.mod h1:IQEt0/YT7vYHLTmRhjpqdHgHhagIHZNaay83fndui7s= +git.gsuntres.com/general/commons v0.0.0-20260423193720-28dcfb9e4ce9 h1:wbyyIEMTQqnfE/AfYlzpSwtqbfUW5RP9F6Tt84S7RMI= +git.gsuntres.com/general/commons v0.0.0-20260423193720-28dcfb9e4ce9/go.mod h1:S93xcBczrgN+gZU0JWkPRnTcAMvQuTp1ChyKkOd/I50= +git.gsuntres.com/general/events v0.0.0-20260424123722-6d275bca7319 h1:HB3q3hrUOrfREV6eJLIZa++QEyw9i0l3FAGkhRqb5Xs= +git.gsuntres.com/general/events v0.0.0-20260424123722-6d275bca7319/go.mod h1:IQEt0/YT7vYHLTmRhjpqdHgHhagIHZNaay83fndui7s= +git.gsuntres.com/general/events v0.0.0-20260424131043-d6c514c3c113 h1:snNU8UgQ50E1Ud3+hGSJzMZG+Y2ILbcfyF+DEdKDVP0= +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/go.mod h1:OVs7w4/tJO1GT7cLIeEsb90LuZqH2xYIVQODI5P1GJs= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= diff --git a/insert.go b/insert.go index 8f37740..b4f2086 100644 --- a/insert.go +++ b/insert.go @@ -2,8 +2,11 @@ package mongo import ( "context" + "slices" "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. -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) + data := commons.BsonClone(original) + prepareForInsert(data, c.GetIdPrefix(name)) 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 { 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 } @@ -42,5 +62,3 @@ func prepareForInsert(data bson.M, idPrefix string) { ensureCreatedAt(data) ensureUpdatedAt(data) } - - diff --git a/insert_test.go b/insert_test.go index 8440f5e..5eef7fc 100644 --- a/insert_test.go +++ b/insert_test.go @@ -2,6 +2,7 @@ package mongo import ( "os" + "fmt" "context" "strings" "testing" @@ -235,4 +236,72 @@ func TestInsertOne_Discriminate_EnsureStore(t *testing.T) { if saved["store"] != "str_1234" { 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") + } } \ No newline at end of file diff --git a/main.go b/main.go index 972c5bb..e6f5f16 100644 --- a/main.go +++ b/main.go @@ -94,6 +94,8 @@ type MongoClient struct { WithAudit bool // OnAudit callback called when audit is enabled. OnAudit *OnAudit + // EventBus + EventBus *events.TypedEventBus // IgnoreAudit a list of entities to ignore IgnoreAudit []string } @@ -364,11 +366,11 @@ func Start(props *MongoStartProps) error { client.WithAudit = props.WithAudit - client.OnAudit = props.OnAudit + client.EventBus = events.NewTypedEventBus() if client.WithAudit { var onAudit OnAudit = func(audit *AuditResult) error { - return events.Publish(audit) + return events.Publish(client.EventBus, audit) } client.OnAudit = &onAudit } @@ -402,6 +404,10 @@ func Start(props *MongoStartProps) error { return nil } +func (c *MongoClient) Subscribe(h events.Handler[AuditResult]) (cancel func()) { + return events.Subscribe(c.EventBus, h) +} + func GetClient() *mongo.Client { return client.Client }