From da163390f192c2f82963b563e3758dc3489e58f3 Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Wed, 16 Apr 2025 18:30:05 -0400 Subject: [PATCH 1/4] GODRIVER-3037 Support internal-only options. --- mongo/options/internaloptions.go | 38 ++++++++++++++ mongo/options/internaloptions_test.go | 73 +++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 mongo/options/internaloptions.go create mode 100644 mongo/options/internaloptions_test.go diff --git a/mongo/options/internaloptions.go b/mongo/options/internaloptions.go new file mode 100644 index 0000000000..cb58536699 --- /dev/null +++ b/mongo/options/internaloptions.go @@ -0,0 +1,38 @@ +// Copyright (C) MongoDB, Inc. 2025-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +package options + +import ( + "fmt" + + "go.mongodb.org/mongo-driver/v2/x/mongo/driver" +) + +// Deprecated: SetInternalClientOptions sets internal only options for ClientOptions. It may be changed +// or removed in any release. +func SetInternalClientOptions(opts *ClientOptions, custom map[string]any) (*ClientOptions, error) { + const typeErr = "unexpected type for %s" + for k, v := range custom { + switch k { + case "crypt": + c, ok := v.(driver.Crypt) + if !ok { + return nil, fmt.Errorf(typeErr, k) + } + opts.Crypt = c + case "deployment": + d, ok := v.(driver.Deployment) + if !ok { + return nil, fmt.Errorf(typeErr, k) + } + opts.Deployment = d + default: + return nil, fmt.Errorf("unsupported option: %s", k) + } + } + return opts, nil +} diff --git a/mongo/options/internaloptions_test.go b/mongo/options/internaloptions_test.go new file mode 100644 index 0000000000..b0d23d0f46 --- /dev/null +++ b/mongo/options/internaloptions_test.go @@ -0,0 +1,73 @@ +// Copyright (C) MongoDB, Inc. 2025-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +package options + +import ( + "testing" + + "go.mongodb.org/mongo-driver/v2/internal/require" + "go.mongodb.org/mongo-driver/v2/x/mongo/driver" + "go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest" +) + +func TestSetClientOptions(t *testing.T) { + t.Parallel() + + t.Run("set Crypt with driver.Crypt", func(t *testing.T) { + t.Parallel() + + opts := &ClientOptions{} + c := driver.NewCrypt(&driver.CryptOptions{}) + opts, err := SetInternalClientOptions(opts, map[string]any{ + "crypt": c, + }) + require.NoError(t, err) + require.Equal(t, c, opts.Crypt) + }) + + t.Run("set Crypt with driver.Deployment", func(t *testing.T) { + t.Parallel() + + opts := &ClientOptions{} + _, err := SetInternalClientOptions(opts, map[string]any{ + "crypt": &drivertest.MockDeployment{}, + }) + require.EqualError(t, err, "unexpected type for crypt") + }) + + t.Run("set Deployment with driver.Deployment", func(t *testing.T) { + t.Parallel() + + opts := &ClientOptions{} + d := &drivertest.MockDeployment{} + opts, err := SetInternalClientOptions(opts, map[string]any{ + "deployment": d, + }) + require.NoError(t, err) + require.Equal(t, d, opts.Deployment) + }) + + t.Run("set Deployment with driver.Crypt", func(t *testing.T) { + t.Parallel() + + opts := &ClientOptions{} + _, err := SetInternalClientOptions(opts, map[string]any{ + "deployment": driver.NewCrypt(&driver.CryptOptions{}), + }) + require.EqualError(t, err, "unexpected type for deployment") + }) + + t.Run("set unsupported option", func(t *testing.T) { + t.Parallel() + + opts := &ClientOptions{} + _, err := SetInternalClientOptions(opts, map[string]any{ + "unsupported": "unsupported", + }) + require.EqualError(t, err, "unsupported option: unsupported") + }) +} From dbd783757d9b1c368089032fc6ff5928b293cd9f Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Wed, 16 Apr 2025 18:40:56 -0400 Subject: [PATCH 2/4] update comment --- mongo/options/internaloptions.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mongo/options/internaloptions.go b/mongo/options/internaloptions.go index cb58536699..d69f7e187f 100644 --- a/mongo/options/internaloptions.go +++ b/mongo/options/internaloptions.go @@ -12,8 +12,9 @@ import ( "go.mongodb.org/mongo-driver/v2/x/mongo/driver" ) -// Deprecated: SetInternalClientOptions sets internal only options for ClientOptions. It may be changed -// or removed in any release. +// SetInternalClientOptions sets internal options for ClientOptions. +// +// Deprecated: This function is for internal use only. It may be changed or removed in any release. func SetInternalClientOptions(opts *ClientOptions, custom map[string]any) (*ClientOptions, error) { const typeErr = "unexpected type for %s" for k, v := range custom { From e277ba37799ca6f71c11fa6fc82f499e6898d070 Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Fri, 25 Apr 2025 14:26:25 -0400 Subject: [PATCH 3/4] improve interface --- internal/options/options.go | 45 +++++++++++++++++++ mongo/options/clientoptions.go | 7 +++ mongo/options/clientoptions_test.go | 3 ++ .../mongo/driver/options/options.go | 35 +++++++-------- .../mongo/driver/options/options_test.go | 39 +++++++--------- 5 files changed, 87 insertions(+), 42 deletions(-) create mode 100644 internal/options/options.go rename mongo/options/internaloptions.go => x/mongo/driver/options/options.go (54%) rename mongo/options/internaloptions_test.go => x/mongo/driver/options/options_test.go (59%) diff --git a/internal/options/options.go b/internal/options/options.go new file mode 100644 index 0000000000..8d5f47f422 --- /dev/null +++ b/internal/options/options.go @@ -0,0 +1,45 @@ +// Copyright (C) MongoDB, Inc. 2025-present. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + +package options + +// Options stores internal options. +type Options struct { + values map[string]any +} + +// WithValue sets an option value with the associated key. +func WithValue(opts Options, key string, option any) Options { + if opts.values == nil { + opts.values = make(map[string]any) + } + opts.values[key] = option + return opts +} + +// Value returns the value associated with the options for key. +func Value(opts Options, key string) any { + if opts.values == nil { + return nil + } + if val, ok := opts.values[key]; ok { + return val + } + return nil +} + +// Equal compares two Options instances for equality. +func Equal(opts1, opts2 Options) bool { + if len(opts1.values) != len(opts2.values) { + return false + } + for key, val1 := range opts1.values { + if val2, ok := opts2.values[key]; !ok || val1 != val2 { + return false + } + } + return true +} diff --git a/mongo/options/clientoptions.go b/mongo/options/clientoptions.go index 925325c6c5..2aa94f8960 100644 --- a/mongo/options/clientoptions.go +++ b/mongo/options/clientoptions.go @@ -26,6 +26,7 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/httputil" + "go.mongodb.org/mongo-driver/v2/internal/options" "go.mongodb.org/mongo-driver/v2/mongo/readconcern" "go.mongodb.org/mongo-driver/v2/mongo/readpref" "go.mongodb.org/mongo-driver/v2/mongo/writeconcern" @@ -295,6 +296,12 @@ type ClientOptions struct { // release. Deployment driver.Deployment + // Custom specifies internal options for the new Client. + // + // Deprecated: This option is for internal use only and should not be set. It may be changed or removed in any + // release. + Custom options.Options + connString *connstring.ConnString err error } diff --git a/mongo/options/clientoptions_test.go b/mongo/options/clientoptions_test.go index 907584d5f0..a8a00122e0 100644 --- a/mongo/options/clientoptions_test.go +++ b/mongo/options/clientoptions_test.go @@ -27,6 +27,7 @@ import ( "go.mongodb.org/mongo-driver/v2/event" "go.mongodb.org/mongo-driver/v2/internal/assert" "go.mongodb.org/mongo-driver/v2/internal/httputil" + "go.mongodb.org/mongo-driver/v2/internal/options" "go.mongodb.org/mongo-driver/v2/internal/ptrutil" "go.mongodb.org/mongo-driver/v2/mongo/readconcern" "go.mongodb.org/mongo-driver/v2/mongo/readpref" @@ -156,6 +157,7 @@ func TestClientOptions(t *testing.T) { cmp.Comparer(func(r1, r2 *bson.Registry) bool { return r1 == r2 }), cmp.Comparer(func(cfg1, cfg2 *tls.Config) bool { return cfg1 == cfg2 }), cmp.Comparer(func(fp1, fp2 *event.PoolMonitor) bool { return fp1 == fp2 }), + cmp.Comparer(options.Equal), cmp.AllowUnexported(ClientOptions{}), cmpopts.IgnoreFields(http.Client{}, "Transport"), ); diff != "" { @@ -1253,6 +1255,7 @@ func TestApplyURI(t *testing.T) { cmp.Comparer(func(r1, r2 *bson.Registry) bool { return r1 == r2 }), cmp.Comparer(compareTLSConfig), cmp.Comparer(compareErrors), + cmp.Comparer(options.Equal), cmpopts.SortSlices(stringLess), cmpopts.IgnoreFields(connstring.ConnString{}, "SSLClientCertificateKeyPassword"), cmpopts.IgnoreFields(http.Client{}, "Transport"), diff --git a/mongo/options/internaloptions.go b/x/mongo/driver/options/options.go similarity index 54% rename from mongo/options/internaloptions.go rename to x/mongo/driver/options/options.go index d69f7e187f..bc18228f1d 100644 --- a/mongo/options/internaloptions.go +++ b/x/mongo/driver/options/options.go @@ -9,31 +9,30 @@ package options import ( "fmt" + "go.mongodb.org/mongo-driver/v2/mongo/options" "go.mongodb.org/mongo-driver/v2/x/mongo/driver" ) // SetInternalClientOptions sets internal options for ClientOptions. // // Deprecated: This function is for internal use only. It may be changed or removed in any release. -func SetInternalClientOptions(opts *ClientOptions, custom map[string]any) (*ClientOptions, error) { +func SetInternalClientOptions(opts *options.ClientOptions, key string, option any) error { const typeErr = "unexpected type for %s" - for k, v := range custom { - switch k { - case "crypt": - c, ok := v.(driver.Crypt) - if !ok { - return nil, fmt.Errorf(typeErr, k) - } - opts.Crypt = c - case "deployment": - d, ok := v.(driver.Deployment) - if !ok { - return nil, fmt.Errorf(typeErr, k) - } - opts.Deployment = d - default: - return nil, fmt.Errorf("unsupported option: %s", k) + switch key { + case "crypt": + c, ok := option.(driver.Crypt) + if !ok { + return fmt.Errorf(typeErr, key) } + opts.Crypt = c + case "deployment": + d, ok := option.(driver.Deployment) + if !ok { + return fmt.Errorf(typeErr, key) + } + opts.Deployment = d + default: + return fmt.Errorf("unsupported option: %s", key) } - return opts, nil + return nil } diff --git a/mongo/options/internaloptions_test.go b/x/mongo/driver/options/options_test.go similarity index 59% rename from mongo/options/internaloptions_test.go rename to x/mongo/driver/options/options_test.go index b0d23d0f46..bbeaca3dd7 100644 --- a/mongo/options/internaloptions_test.go +++ b/x/mongo/driver/options/options_test.go @@ -10,43 +10,38 @@ import ( "testing" "go.mongodb.org/mongo-driver/v2/internal/require" + "go.mongodb.org/mongo-driver/v2/mongo/options" "go.mongodb.org/mongo-driver/v2/x/mongo/driver" "go.mongodb.org/mongo-driver/v2/x/mongo/driver/drivertest" ) -func TestSetClientOptions(t *testing.T) { +func TestSetInternalClientOptions(t *testing.T) { t.Parallel() - t.Run("set Crypt with driver.Crypt", func(t *testing.T) { + t.Run("crypt with driver.Crypt", func(t *testing.T) { t.Parallel() - opts := &ClientOptions{} c := driver.NewCrypt(&driver.CryptOptions{}) - opts, err := SetInternalClientOptions(opts, map[string]any{ - "crypt": c, - }) - require.NoError(t, err) - require.Equal(t, c, opts.Crypt) + opts := options.Client() + err := SetInternalClientOptions(opts, "crypt", c) + require.NoError(t, err, "error setting crypt: %v", err) + require.Equal(t, c, opts.Crypt, "expected %v, got %v", c, opts.Crypt) }) t.Run("set Crypt with driver.Deployment", func(t *testing.T) { t.Parallel() - opts := &ClientOptions{} - _, err := SetInternalClientOptions(opts, map[string]any{ - "crypt": &drivertest.MockDeployment{}, - }) + opts := options.Client() + err := SetInternalClientOptions(opts, "crypt", &drivertest.MockDeployment{}) require.EqualError(t, err, "unexpected type for crypt") }) t.Run("set Deployment with driver.Deployment", func(t *testing.T) { t.Parallel() - opts := &ClientOptions{} d := &drivertest.MockDeployment{} - opts, err := SetInternalClientOptions(opts, map[string]any{ - "deployment": d, - }) + opts := options.Client() + err := SetInternalClientOptions(opts, "deployment", d) require.NoError(t, err) require.Equal(t, d, opts.Deployment) }) @@ -54,20 +49,16 @@ func TestSetClientOptions(t *testing.T) { t.Run("set Deployment with driver.Crypt", func(t *testing.T) { t.Parallel() - opts := &ClientOptions{} - _, err := SetInternalClientOptions(opts, map[string]any{ - "deployment": driver.NewCrypt(&driver.CryptOptions{}), - }) + opts := options.Client() + err := SetInternalClientOptions(opts, "deployment", driver.NewCrypt(&driver.CryptOptions{})) require.EqualError(t, err, "unexpected type for deployment") }) t.Run("set unsupported option", func(t *testing.T) { t.Parallel() - opts := &ClientOptions{} - _, err := SetInternalClientOptions(opts, map[string]any{ - "unsupported": "unsupported", - }) + opts := options.Client() + err := SetInternalClientOptions(opts, "unsupported", "unsupported") require.EqualError(t, err, "unsupported option: unsupported") }) } From 5fef97b2b8644b544ebb08fc0768cdc4e67ef8fd Mon Sep 17 00:00:00 2001 From: Qingyang Hu Date: Fri, 25 Apr 2025 14:43:34 -0400 Subject: [PATCH 4/4] minor updates --- x/mongo/driver/options/options_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/x/mongo/driver/options/options_test.go b/x/mongo/driver/options/options_test.go index bbeaca3dd7..a3cdc90600 100644 --- a/x/mongo/driver/options/options_test.go +++ b/x/mongo/driver/options/options_test.go @@ -18,7 +18,7 @@ import ( func TestSetInternalClientOptions(t *testing.T) { t.Parallel() - t.Run("crypt with driver.Crypt", func(t *testing.T) { + t.Run("set crypt", func(t *testing.T) { t.Parallel() c := driver.NewCrypt(&driver.CryptOptions{}) @@ -28,7 +28,7 @@ func TestSetInternalClientOptions(t *testing.T) { require.Equal(t, c, opts.Crypt, "expected %v, got %v", c, opts.Crypt) }) - t.Run("set Crypt with driver.Deployment", func(t *testing.T) { + t.Run("set crypt - wrong type", func(t *testing.T) { t.Parallel() opts := options.Client() @@ -36,17 +36,17 @@ func TestSetInternalClientOptions(t *testing.T) { require.EqualError(t, err, "unexpected type for crypt") }) - t.Run("set Deployment with driver.Deployment", func(t *testing.T) { + t.Run("set deployment", func(t *testing.T) { t.Parallel() d := &drivertest.MockDeployment{} opts := options.Client() err := SetInternalClientOptions(opts, "deployment", d) - require.NoError(t, err) - require.Equal(t, d, opts.Deployment) + require.NoError(t, err, "error setting deployment: %v", err) + require.Equal(t, d, opts.Deployment, "expected %v, got %v", d, opts.Deployment) }) - t.Run("set Deployment with driver.Crypt", func(t *testing.T) { + t.Run("set deployment - wrong type", func(t *testing.T) { t.Parallel() opts := options.Client()