diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1368738..390d4c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v3 with: - go-version: '1.23.2' + go-version: '1.25.5' cache: true - name: Run build @@ -31,7 +31,7 @@ jobs: mongodb-version: ${{ matrix.mongodb-version }} - name: Run tests - run: go test `go list ./... | grep -v ./cmd` -v -covermode=count -coverprofile=coverage.out + run: GOTOOLCHAIN=go1.25.5+auto go test `go list ./... | grep -v ./cmd` -v -covermode=count -coverprofile=coverage.out - name: Convert coverage.out to coverage.lcov uses: jandelgado/gcov2lcov-action@v1 diff --git a/cmd/tracing/example.go b/cmd/tracing/example.go new file mode 100644 index 0000000..5bc1fef --- /dev/null +++ b/cmd/tracing/example.go @@ -0,0 +1,105 @@ +package main + +import ( + "context" + "fmt" + "log" + + "github.com/jensteichert/colt" + "go.mongodb.org/mongo-driver/bson" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +func initTracer() *sdktrace.TracerProvider { + exporter, err := stdouttrace.New( + stdouttrace.WithPrettyPrint()) + if err != nil { + log.Fatal(err) + } + + tp := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithSyncer(exporter), + sdktrace.WithResource( + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("colt-example"), + semconv.ServiceVersionKey.String("0.0.42"), + semconv.DeploymentEnvironmentKey.String("local"), + )), + ) + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{})) + return tp +} + +var tracer = initTracer().Tracer("colt-example") + +type Database struct { + Todos *colt.Collection[*Todo] +} + +type Todo struct { + colt.DocWithTimestamps `bson:",inline"` + Title string `bson:"title" json:"title"` +} + +func (t *Todo) BeforeInsert() error { + t.DocWithTimestamps.BeforeInsert() + fmt.Println("BeforeInsert executed") + return nil +} + +func initAndConnectToDbWithTracing() colt.Database { + connectTraceCtx, span := tracer.Start(context.Background(), "coltInitDB") + defer span.End() + + db := colt.NewDatabase().WithContext(connectTraceCtx) + db.Connect("mongodb://localhost:27017/colt?readPreference=primary&directConnection=true&ssl=false", "colt") + return db +} + +func handleTodos(ctx context.Context) []*Todo { + traceCtx, span := tracer.Start(ctx, "handleTodos") + defer span.End() + + newTodo := Todo{Title: "Hello"} + + contextAwareTodoCollection := database.Todos.WithContext(traceCtx) + + todo, _ := contextAwareTodoCollection.Insert(&newTodo) // Will return a Todo + insertedTodo, _ := contextAwareTodoCollection.FindById(todo.ID) + + fmt.Println(todo) + + contextAwareTodoCollection.UpdateById(todo.ID, todo) + + if insertedTodo != nil { + fmt.Println(insertedTodo.Title) + } + + allTodos, _ := contextAwareTodoCollection.Find(bson.M{"title": "Hello"}) + return allTodos +} + +var database Database + +func main() { + db := initAndConnectToDbWithTracing() + + database = Database{ + Todos: colt.GetCollection[*Todo](&db, "todos"), + } + + // This would usually be a request handler with its own request context + allTodos := handleTodos(context.Background()) + + for _, todo := range allTodos { + fmt.Println(todo.ID) + } +} diff --git a/collection.go b/collection.go index 45763ca..b73404d 100644 --- a/collection.go +++ b/collection.go @@ -1,7 +1,9 @@ package colt import ( + "context" "errors" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" @@ -10,6 +12,23 @@ import ( type Collection[T Document] struct { collection *mongo.Collection + ctx context.Context // optional context +} + +func (repo Collection[T]) WithContext(ctx context.Context) *Collection[T] { + repo.ctx = ctx + return &repo +} + +func (repo *Collection[T]) ctxOrDefault() (context.Context, context.CancelFunc) { + if ctx := repo.ctx; ctx == nil { + return defaultContext() + } + if _, hasDeadline := repo.ctx.Deadline(); !hasDeadline { + ctxWithTimeout, cf := context.WithTimeout(repo.ctx, defaultTimeout) + return ctxWithTimeout, cf + } + return repo.ctx, nil } func (repo *Collection[T]) Insert(model T) (T, error) { @@ -23,7 +42,11 @@ func (repo *Collection[T]) Insert(model T) (T, error) { } } - res, err := repo.collection.InsertOne(DefaultContext(), model) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + res, err := repo.collection.InsertOne(ctx, model) if err != nil && res != nil { model.SetID(res.InsertedID.(string)) } @@ -42,17 +65,29 @@ func (repo *Collection[T]) UpdateOne(filter interface{}, model T) error { } } - _, err := repo.collection.UpdateOne(DefaultContext(), filter, bson.M{"$set": model}) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + _, err := repo.collection.UpdateOne(ctx, filter, bson.M{"$set": model}) return err } func (repo *Collection[T]) UpdateMany(filter interface{}, doc bson.M) error { - _, err := repo.collection.UpdateMany(DefaultContext(), filter, doc) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + _, err := repo.collection.UpdateMany(ctx, filter, doc) return err } func (repo *Collection[T]) DeleteById(id string) error { - res, err := repo.collection.DeleteOne(DefaultContext(), bson.M{"_id": id}) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + res, err := repo.collection.DeleteOne(ctx, bson.M{"_id": id}) if err != nil { return err @@ -70,19 +105,27 @@ func (repo *Collection[T]) FindById(id interface{}) (T, error) { } func (repo *Collection[T]) FindOne(filter interface{}) (T, error) { + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } var target T - err := repo.collection.FindOne(DefaultContext(), filter).Decode(&target) + err := repo.collection.FindOne(ctx, filter).Decode(&target) return target, err } func (repo *Collection[T]) Find(filter interface{}, opts ...*options.FindOptions) ([]T, error) { - csr, err := repo.collection.Find(DefaultContext(), filter, opts...) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + csr, err := repo.collection.Find(ctx, filter, opts...) if err != nil { return nil, err } var result = []T{} - if err = csr.All(DefaultContext(), &result); err != nil { + if err = csr.All(ctx, &result); err != nil { return nil, err } @@ -90,15 +133,23 @@ func (repo *Collection[T]) Find(filter interface{}, opts ...*options.FindOptions } func (repo *Collection[T]) CountDocuments(filter interface{}) (int64, error) { - count, err := repo.collection.CountDocuments(DefaultContext(), filter) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + count, err := repo.collection.CountDocuments(ctx, filter) return count, err } func (repo *Collection[T]) Aggregate(pipeline mongo.Pipeline, opts ...*options.AggregateOptions) ([]bson.M, error) { - csr, err := repo.collection.Aggregate(DefaultContext(), pipeline, opts...) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + csr, err := repo.collection.Aggregate(ctx, pipeline, opts...) var result = []bson.M{} - if err = csr.All(DefaultContext(), &result); err != nil { + if err = csr.All(ctx, &result); err != nil { return nil, err } @@ -106,7 +157,11 @@ func (repo *Collection[T]) Aggregate(pipeline mongo.Pipeline, opts ...*options.A } func (repo *Collection[T]) Drop() error { - err := repo.collection.Drop(DefaultContext()) + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } + err := repo.collection.Drop(ctx) return err } diff --git a/collection_test.go b/collection_test.go index dadfe25..cf7298b 100644 --- a/collection_test.go +++ b/collection_test.go @@ -1,15 +1,17 @@ package colt import ( + "context" "fmt" - "github.com/stretchr/testify/assert" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/bson/primitive" - "go.mongodb.org/mongo-driver/mongo" "math/rand" "strings" "testing" "time" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" + "go.mongodb.org/mongo-driver/mongo" ) type testdoc struct { @@ -306,3 +308,71 @@ func TestCollection_Aggregate(t *testing.T) { collection.Drop() mockDb.Disconnect() } + +func TestCollection_WithContext(t *testing.T) { + mockDb.Connect("mongodb://localhost:27017/colt?readPreference=primary&directConnection=true&ssl=false", "colt") + + collection := GetCollection[*testdoc](&mockDb, "testdocs") + // Assert that the original collection has nil ctx (default) + assert.Nil(t, collection.ctx) + + // Uses with explicit ctx + ctxCollection := collection.WithContext(testCtx) + + // Assert that the underlying mongo collection pointer is identical + assert.Equal(t, collection.collection, ctxCollection.collection) + + // Assert that the ctx is different + assert.NotEqual(t, collection.ctx, ctxCollection.ctx) + + // Assert that the new collection has the context set + assert.NotNil(t, ctxCollection.ctx) + assert.Equal(t, testCtx, ctxCollection.ctx) + + secondCtx := context.WithValue(context.Background(), "test", "secondCtx") + derivedCollection := ctxCollection.WithContext(secondCtx) + + // Assert that the underlying mongo collection pointer is identical + assert.Equal(t, collection.collection, derivedCollection.collection) + + // Assert that the ctx is different + assert.NotEqual(t, ctxCollection.ctx, derivedCollection.ctx) + + mockDb.Disconnect() +} + +func TestCollection_ctxOrDefault(t *testing.T) { + + mockDb.Connect("mongodb://localhost:27017/colt?readPreference=primary&directConnection=true&ssl=false", "colt") + + // Assert that the original collection has nil ctx (default) + staticCollection := GetCollection[*testdoc](&mockDb, "testdocs") + defaultCtx, defaultCancelFunc := staticCollection.ctxOrDefault() + assert.NotNil(t, defaultCtx) + assert.NotNil(t, defaultCancelFunc) + + // Uses with explicit ctx + ctxCollection := staticCollection.WithContext(testCtx) + + // Always has a deadline set + defaultCtx, defaultCancelFunc = ctxCollection.ctxOrDefault() + _, ok := defaultCtx.Deadline() + assert.True(t, ok) + // Enriches ctx with deadline and returns cancelFunc + assert.NotNil(t, defaultCancelFunc) + + // Uses given ctx deadline if set + deadline := time.Now().Add(42 * time.Second) + deadlineCtx, cancel := context.WithDeadline(testCtx, deadline) + + dbWithManualCtxDeadline := staticCollection.WithContext(deadlineCtx) + defaultCtx, defaultCancelFunc = dbWithManualCtxDeadline.ctxOrDefault() + + ctxDeadline, ok := deadlineCtx.Deadline() + assert.True(t, ok) + assert.Equal(t, ctxDeadline, deadline) + // Doesnt return cancelFunc, as its managed by caller + assert.Nil(t, defaultCancelFunc) + + cancel() +} diff --git a/database.go b/database.go index 9e2517d..d9c3716 100644 --- a/database.go +++ b/database.go @@ -8,23 +8,38 @@ import ( "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" + "go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo" ) +var defaultTimeout = 5 * time.Second + type Database struct { db *mongo.Database client *mongo.Client + ctx context.Context // optional context +} + +func NewDatabase() *Database { + return &Database{} +} + +func (db Database) WithContext(ctx context.Context) Database { + db.ctx = ctx + return db } func (db *Database) connect(options *options.ClientOptions, dbName string) error { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() + ctx, cancel := db.ctxOrDefault() + if cancel != nil { + defer cancel() + } client, err := mongo.Connect(ctx, options) if err != nil { log.Fatal(err) } db.client = client - err = db.client.Ping(context.Background(), readpref.Primary()) + err = db.client.Ping(ctx, readpref.Primary()) if err == nil { log.Print("Connected to MongoDB!") } else { @@ -37,25 +52,45 @@ func (db *Database) connect(options *options.ClientOptions, dbName string) error func (db *Database) Connect(connectionString string, dbName string) error { options := options.Client().ApplyURI(connectionString) + options.Monitor = otelmongo.NewMonitor() err := db.connect(options, dbName) return err } +func (db *Database) ctxOrDefault() (context.Context, context.CancelFunc) { + if ctx := db.ctx; ctx == nil { + return defaultContext() + } + if _, hasDeadline := db.ctx.Deadline(); !hasDeadline { + ctxWithTimeout, cf := context.WithTimeout(db.ctx, defaultTimeout) + return ctxWithTimeout, cf + } + return db.ctx, nil +} + func (db *Database) Ping() error { - return db.client.Ping(DefaultContext(), nil) + ctx, cancel := db.ctxOrDefault() + if cancel != nil { + defer cancel() + } + return db.client.Ping(ctx, nil) } func (db *Database) Disconnect() error { - err := db.client.Disconnect(DefaultContext()) + ctx, cancel := db.ctxOrDefault() + if cancel != nil { + defer cancel() + } + err := db.client.Disconnect(ctx) db.db = nil return err } -func DefaultContext() context.Context { - ctx, _ := context.WithTimeout(context.Background(), 5*time.Second) - return ctx +func defaultContext() (context.Context, context.CancelFunc) { + ctx, cf := context.WithTimeout(context.Background(), defaultTimeout) + return ctx, cf } func GetCollection[T Document](db *Database, collectionName string) *Collection[T] { - return &Collection[T]{db.db.Collection(collectionName)} + return &Collection[T]{db.db.Collection(collectionName), nil} } diff --git a/database_test.go b/database_test.go new file mode 100644 index 0000000..780568d --- /dev/null +++ b/database_test.go @@ -0,0 +1,55 @@ +package colt + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestDatabase_WithContext(t *testing.T) { + staticDb := NewDatabase() + + assert.Nil(t, staticDb.ctx) + + ctxAwareDb := staticDb.WithContext(testCtx) + + assert.NotEqual(t, staticDb, ctxAwareDb) + assert.Equal(t, staticDb.db, ctxAwareDb.db) + assert.Equal(t, staticDb.client, ctxAwareDb.client) + assert.NotEqual(t, staticDb.ctx, ctxAwareDb.ctx) +} + +func TestDatabase_ctxOrDefault(t *testing.T) { + + // Static, non explicit ctx use + staticDb := NewDatabase() + defaultCtx, defaultCancelFunc := staticDb.ctxOrDefault() + assert.NotNil(t, defaultCtx) + assert.NotNil(t, defaultCancelFunc) + + // Manuall, explicit ctx uses + ctxDb := NewDatabase().WithContext(testCtx) + + // Always has a deadline set + defaultCtx, defaultCancelFunc = ctxDb.ctxOrDefault() + _, ok := defaultCtx.Deadline() + assert.True(t, ok) + // Enriches ctx with deadline and returns cancelFunc + assert.NotNil(t, defaultCancelFunc) + + // Uses given ctx deadline if set + deadline := time.Now().Add(42 * time.Second) + deadlineCtx, cancel := context.WithDeadline(testCtx, deadline) + dbWithManualCtxDeadline := NewDatabase().WithContext(deadlineCtx) + defaultCtx, defaultCancelFunc = dbWithManualCtxDeadline.ctxOrDefault() + + ctxDeadline, ok := deadlineCtx.Deadline() + assert.True(t, ok) + assert.Equal(t, ctxDeadline, deadline) + // Doesnt return cancelFunc, as its managed by caller + assert.Nil(t, defaultCancelFunc) + + cancel() +} diff --git a/go.mod b/go.mod index c991b15..0a78199 100644 --- a/go.mod +++ b/go.mod @@ -1,25 +1,40 @@ module github.com/jensteichert/colt -go 1.18 +go 1.25.5 require ( - github.com/stretchr/testify v1.10.0 - go.mongodb.org/mongo-driver v1.17.3 + github.com/stretchr/testify v1.11.1 + go.mongodb.org/mongo-driver v1.17.6 + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.64.0 ) require ( + github.com/google/uuid v1.6.0 // indirect + golang.org/x/sys v0.39.0 // indirect +) + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/snappy v1.0.0 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect - github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/scram v1.2.0 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect - golang.org/x/crypto v0.31.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/text v0.21.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.39.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/sdk v1.39.0 + go.opentelemetry.io/otel/trace v1.39.0 // indirect + golang.org/x/crypto v0.45.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/text v0.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3e31c08..3e3cee5 100644 --- a/go.sum +++ b/go.sum @@ -1,59 +1,96 @@ +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= -github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs= +github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= -go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= +go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.64.0 h1:/jNnYHxei43Rn6d6B4BCjhvYtL3UmhfMBVlfPruddxg= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.64.0/go.mod h1:fCwr528Fsk2KnKBk5khdhlLWKSLPMkOQtum/MRTgks0= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hooks_test.go b/hooks_test.go index be10d13..4d0541e 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -1,9 +1,10 @@ package colt import ( + "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - "testing" ) type testDoc struct { diff --git a/indexes.go b/indexes.go index 8a1cbbe..4b9ce8d 100644 --- a/indexes.go +++ b/indexes.go @@ -6,11 +6,15 @@ import ( ) func (repo *Collection[T]) CreateIndex(keys bson.D) error { + ctx, cancel := repo.ctxOrDefault() + if cancel != nil { + defer cancel() + } mod := mongo.IndexModel{ Keys: keys, Options: nil, } - _, err := repo.collection.Indexes().CreateOne(DefaultContext(), mod) + _, err := repo.collection.Indexes().CreateOne(ctx, mod) return err } diff --git a/indexes_test.go b/indexes_test.go index f3ab3d9..f0a5a0d 100644 --- a/indexes_test.go +++ b/indexes_test.go @@ -1,14 +1,18 @@ package colt import ( + "context" "fmt" - "github.com/stretchr/testify/assert" - "go.mongodb.org/mongo-driver/bson" "math/rand" "testing" "time" + + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" ) +var testCtx = context.WithValue(context.Background(), "test", "ctx") + func TestCollection_CreateIndex(t *testing.T) { rand.Seed(time.Now().UnixNano()) mockDb.Connect("mongodb://localhost:27017/colt?readPreference=primary&directConnection=true&ssl=false", "colt") @@ -16,8 +20,8 @@ func TestCollection_CreateIndex(t *testing.T) { collection := GetCollection[*testdoc](&mockDb, "testdocs") var indxs = []interface{}{} - indexCursor, _ := collection.collection.Indexes().List(DefaultContext()) - indexCursor.All(DefaultContext(), &indxs) + indexCursor, _ := collection.collection.Indexes().List(testCtx) + indexCursor.All(testCtx, &indxs) indexCountBefore := len(indxs) @@ -27,8 +31,8 @@ func TestCollection_CreateIndex(t *testing.T) { assert.Nil(t, err) - indexCursor2, _ := collection.collection.Indexes().List(DefaultContext()) - indexCursor2.All(DefaultContext(), &indxs) + indexCursor2, _ := collection.collection.Indexes().List(testCtx) + indexCursor2.All(testCtx, &indxs) // new index assert.Equal(t, len(indxs), indexCountBefore+1) @@ -41,8 +45,8 @@ func TestCollection_CreateMultiKeyIndex(t *testing.T) { collection := GetCollection[*testdoc](&mockDb, "testdocs") var indxs = []interface{}{} - indexCursor, _ := collection.collection.Indexes().List(DefaultContext()) - indexCursor.All(DefaultContext(), &indxs) + indexCursor, _ := collection.collection.Indexes().List(testCtx) + indexCursor.All(testCtx, &indxs) indexCountBefore := len(indxs) @@ -53,8 +57,8 @@ func TestCollection_CreateMultiKeyIndex(t *testing.T) { assert.Nil(t, err) - indexCursor2, _ := collection.collection.Indexes().List(DefaultContext()) - indexCursor2.All(DefaultContext(), &indxs) + indexCursor2, _ := collection.collection.Indexes().List(testCtx) + indexCursor2.All(testCtx, &indxs) // new index assert.Equal(t, len(indxs), indexCountBefore+1)