Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GODRIVER-2696 Use an options pattern that doesn't require merging structs #1570

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3276282
Update InsertOneOptions.
qingyang-hu Nov 27, 2023
7b6858d
Update FindOptions.
qingyang-hu Nov 27, 2023
7ac2261
Add a generic merging function.
qingyang-hu Dec 27, 2023
6605798
Merge branch 'master' into godriver2696POC
qingyang-hu Dec 28, 2023
0608e50
Merge InsertOneArgs by the generic function.
qingyang-hu Dec 28, 2023
b20327f
Move `Options` to `mongo`.
qingyang-hu Jan 8, 2024
dbbc305
WIP
qingyang-hu Jan 10, 2024
ff03ed5
Merge branch 'master' into godriver2696POC
qingyang-hu Jan 11, 2024
f311172
GODRIVER-2696 Resolve mege conflicts
prestonvasquez Feb 23, 2024
0325efb
GODRIVER-2696 Update Aggregate and BulkWrite
prestonvasquez Feb 28, 2024
a71f378
GODRIVER-2696 Update clientoptions
prestonvasquez Mar 8, 2024
a50e6ee
GODRIVER-2696 Check for nil args in GetURI
prestonvasquez Mar 8, 2024
3d506f0
GODRIVER-2696 Update collection/createCollection/count
prestonvasquez Mar 12, 2024
f53f8d0
GODRIVER-2696 Update up to gridfs
prestonvasquez Mar 13, 2024
0a9fbc6
GODRIVER-2696 Finish converting options
prestonvasquez Mar 14, 2024
faebbbc
Merge branch 'master' into GODRIVER-2696
prestonvasquez Mar 14, 2024
760bc3e
GODRIVER-2696 Clean up comments and add mongoutil tests
prestonvasquez Mar 15, 2024
6e87836
Merge branch 'GODRIVER-2696' of github.com:prestonvasquez/mongo-go-dr…
prestonvasquez Mar 15, 2024
937c941
Merge branch 'master' into GODRIVER-2696
prestonvasquez Mar 19, 2024
b81dbc0
Merge branch 'master' into GODRIVER-2696
prestonvasquez Apr 15, 2024
1edea67
GODRIVER-2696 Fix typos
prestonvasquez Apr 15, 2024
d6497aa
GODRIVER-2696 Resolve merge conflicts
prestonvasquez May 8, 2024
c94c4e6
GODRIVER-2696 Resolve PR issues
prestonvasquez May 9, 2024
49e44f8
GODRIER-2696 Use default revision for skip num
prestonvasquez May 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 9 additions & 9 deletions internal/integration/client_side_encryption_prose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2642,7 +2642,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetAlgorithm("RangePreview").
SetKeyID(key1ID).
SetContentionFactor(0).
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)
// Insert 0.
insertPayloadZero, err := clientEncryption.Encrypt(context.Background(), test.zero, eo)
assert.Nil(mt, err, "error in Encrypt: %v", err)
Expand Down Expand Up @@ -2689,7 +2689,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetAlgorithm("RangePreview").
SetKeyID(key1ID).
SetContentionFactor(0).
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)
insertPayloadSix, err := clientEncryption.Encrypt(context.Background(), test.six, eo)
assert.Nil(mt, err, "error in Encrypt: %v", err)
got, err := clientEncryption.Decrypt(context.Background(), insertPayloadSix)
Expand All @@ -2706,7 +2706,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetKeyID(key1ID).
SetContentionFactor(0).
SetQueryType("rangePreview").
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)

expr := bson.M{
"$and": bson.A{
Expand Down Expand Up @@ -2748,7 +2748,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetKeyID(key1ID).
SetContentionFactor(0).
SetQueryType("rangePreview").
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)

expr := bson.M{
"$and": bson.A{
Expand Down Expand Up @@ -2790,7 +2790,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetKeyID(key1ID).
SetContentionFactor(0).
SetQueryType("rangePreview").
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)

expr := bson.M{
"$and": bson.A{
Expand Down Expand Up @@ -2827,7 +2827,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetKeyID(key1ID).
SetContentionFactor(0).
SetQueryType("rangePreview").
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)

expr := bson.M{
"$and": bson.A{
Expand Down Expand Up @@ -2865,7 +2865,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetAlgorithm("RangePreview").
SetKeyID(key1ID).
SetContentionFactor(0).
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)

_, err := clientEncryption.Encrypt(context.Background(), test.twoHundredOne, eo)
assert.NotNil(mt, err, "expected error, but got none")
Expand All @@ -2879,7 +2879,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetAlgorithm("RangePreview").
SetKeyID(key1ID).
SetContentionFactor(0).
SetRangeOptions(*test.rangeOpts)
SetRangeOptions(test.rangeOpts)

var val bson.RawValue
if test.field == "encryptedInt" {
Expand All @@ -2906,7 +2906,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetAlgorithm("RangePreview").
SetKeyID(key1ID).
SetContentionFactor(0).
SetRangeOptions(*ro)
SetRangeOptions(ro)

_, err := clientEncryption.Encrypt(context.Background(), test.six, eo)
assert.NotNil(mt, err, "expected error, but got none")
Expand Down
2 changes: 1 addition & 1 deletion internal/integration/mtest/mongotest.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ func (t *T) createTestClient() {
t.mockDeployment = newMockDeployment()
args.Deployment = t.mockDeployment

opts := &mongoutil.Options[options.ClientArgs]{Args: args}
opts := &mongoutil.ArgOptions[options.ClientArgs]{Args: args}
t.Client, err = mongo.Connect(opts)
case Proxy:
t.proxyDialer = newProxyDialer()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1126,7 +1126,7 @@ func executeListSearchIndexes(ctx context.Context, operation *operation) (*opera
}

searchIdxOpts := options.SearchIndexes()
var opts []*mongoutil.Options[options.ListSearchIndexesArgs]
var opts []*mongoutil.ArgOptions[options.ListSearchIndexesArgs]

elems, err := operation.Arguments.Elements()
if err != nil {
Expand All @@ -1141,9 +1141,9 @@ func executeListSearchIndexes(ctx context.Context, operation *operation) (*opera
searchIdxOpts.SetName(val.StringValue())
case "aggregationOptions":
// Unmarshal the document into the AggregateArgs embedded object.
opt := &mongoutil.Options[options.ListSearchIndexesArgs]{
opt := &mongoutil.ArgOptions[options.ListSearchIndexesArgs]{
Args: &options.ListSearchIndexesArgs{},
Clbk: func(args *options.ListSearchIndexesArgs) error {
Callback: func(args *options.ListSearchIndexesArgs) error {
args.AggregateArgs = &options.AggregateArgs{}

return bson.Unmarshal(val.Document(), args.AggregateArgs)
Expand Down
18 changes: 9 additions & 9 deletions internal/mongoutil/mongoutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,25 +71,25 @@ func NewArgsFromOptions[T Args](opts ...MongoOptions[T]) (*T, error) {
return args, nil
}

// Options implements a mongo.Options object for an arbitrary arguments type.
// ArgOptions implements a mongo.ArgOptions object for an arbitrary arguments type.
// The intended use case is to create options from arguments.
type Options[T Args] struct {
Args *T // Arguments to set on the option type
Clbk func(*T) error // A callback for further modification
type ArgOptions[T Args] struct {
Args *T // Arguments to set on the option type
Callback func(*T) error // A callback for further modification
}

// ArgsSetters will re-assign the entire argument option to the Args field
// defined on opts. If a callback exists, that function will be executed to
// further modify the arguments.
func (opts *Options[T]) ArgsSetters() []func(*T) error {
func (opts *ArgOptions[T]) ArgsSetters() []func(*T) error {
return []func(*T) error{
func(args *T) error {
if opts.Args != nil {
*args = *opts.Args
}

if opts.Clbk != nil {
return opts.Clbk(args)
if opts.Callback != nil {
return opts.Callback(args)
}

return nil
Expand All @@ -99,8 +99,8 @@ func (opts *Options[T]) ArgsSetters() []func(*T) error {

// NewOptionsFromArgs will construct an Options object from the provided
// arguments object.
func NewOptionsFromArgs[T Args](args *T, clbk func(*T) error) *Options[T] {
return &Options[T]{Args: args, Clbk: clbk}
func NewOptionsFromArgs[T Args](args *T) *ArgOptions[T] {
return &ArgOptions[T]{Args: args}
}

// AuthFromURI will create a Credentials object given the provided URI.
Expand Down
13 changes: 1 addition & 12 deletions internal/mongoutil/mongoutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,8 @@ func TestNewOptionsFromArgs(t *testing.T) {
{
name: "no callback",
args: &options.ClientArgs{AppName: ptrutil.Ptr[string]("testApp")},
clbk: nil,
want: options.ClientArgs{AppName: ptrutil.Ptr[string]("testApp")},
},
{
name: "mutating callback",
args: &options.ClientArgs{AppName: ptrutil.Ptr[string]("testApp1")},
clbk: func(args *options.ClientArgs) error {
*args.AppName = "testApp2"

return nil
},
want: options.ClientArgs{AppName: ptrutil.Ptr[string]("testApp2")},
},
}

for _, test := range clientTests {
Expand All @@ -162,7 +151,7 @@ func TestNewOptionsFromArgs(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()

opts := NewOptionsFromArgs[options.ClientArgs](test.args, test.clbk)
opts := NewOptionsFromArgs[options.ClientArgs](test.args)

got, err := NewArgsFromOptions[options.ClientArgs](opts)
assert.NoError(t, err)
Expand Down
6 changes: 1 addition & 5 deletions mongo/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,6 @@ func newClient(opts ...Options[options.ClientArgs]) (*Client, error) {
args.MaxPoolSize = &defaultMaxPoolSize
}

if err != nil {
return nil, err
}

cfg, err := topology.NewConfigFromArgs(args, client.clock)
if err != nil {
return nil, err
Expand Down Expand Up @@ -501,7 +497,7 @@ func (c *Client) getOrCreateInternalClient(args *options.ClientArgs) (*Client, e
argsCopy.AutoEncryptionOptions = nil
argsCopy.MinPoolSize = ptrutil.Ptr[uint64](0)

opts := &mongoutil.Options[options.ClientArgs]{Args: &argsCopy}
opts := &mongoutil.ArgOptions[options.ClientArgs]{Args: &argsCopy}

var err error
c.internalClientFLE, err = newClient(opts)
Expand Down
2 changes: 1 addition & 1 deletion mongo/client_encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (ce *ClientEncryption) CreateEncryptedCollection(ctx context.Context,
}
}

updatedCreateOpts := mongoutil.NewOptionsFromArgs[options.CreateCollectionArgs](createArgs, nil)
updatedCreateOpts := mongoutil.NewOptionsFromArgs[options.CreateCollectionArgs](createArgs)
err = db.CreateCollection(ctx, coll, updatedCreateOpts)
if err != nil {
return nil, m, err
Expand Down
2 changes: 1 addition & 1 deletion mongo/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,7 @@ func (coll *Collection) ReplaceOne(
Comment: args.Comment,
}

updateOptions := mongoutil.NewOptionsFromArgs[options.UpdateArgs](updateArgs, nil)
updateOptions := mongoutil.NewOptionsFromArgs[options.UpdateArgs](updateArgs)

return coll.updateOrReplace(ctx, f, r, false, rrOne, false, updateOptions)
}
Expand Down
122 changes: 59 additions & 63 deletions mongo/options/clientoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,68 +280,6 @@ func (c *ClientOptions) ArgsSetters() []func(*ClientArgs) error {
return c.Opts
}

// ValidateClientArgs checks if the client arguments will create a valid
// connection.
func ValidateClientArgs(args *ClientArgs) error {
// Direct connections cannot be made if multiple hosts are specified or an SRV
// URI is used.
if args.Direct != nil && *args.Direct {
if len(args.Hosts) > 1 {
return errors.New("a direct connection cannot be made if multiple hosts are specified")
}
if args.connString != nil && args.connString.Scheme == connstring.SchemeMongoDBSRV {
return errors.New("a direct connection cannot be made if an SRV URI is used")
}
}

if args.MaxPoolSize != nil && args.MinPoolSize != nil && *args.MaxPoolSize != 0 &&
*args.MinPoolSize > *args.MaxPoolSize {
return fmt.Errorf("minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=%d maxPoolSize=%d",
*args.MinPoolSize, *args.MaxPoolSize)
}

// verify server API version if ServerAPIOptions are passed in.
if args.ServerAPIOptions != nil {
serverAPIArgs, err := getArgs[ServerAPIArgs](args.ServerAPIOptions)
if err != nil {
return fmt.Errorf("failed to construct arguments from options: %w", err)
}

if err := serverAPIArgs.ServerAPIVersion.Validate(); err != nil {
return err
}
}

// Validation for load-balanced mode.
if args.LoadBalanced != nil && *args.LoadBalanced {
if len(args.Hosts) > 1 {
return connstring.ErrLoadBalancedWithMultipleHosts
}
if args.ReplicaSet != nil {
return connstring.ErrLoadBalancedWithReplicaSet
}
if args.Direct != nil && *args.Direct {
return connstring.ErrLoadBalancedWithDirectConnection
}
}

// Validation for srvMaxHosts.
if args.SRVMaxHosts != nil && *args.SRVMaxHosts > 0 {
if args.ReplicaSet != nil {
return connstring.ErrSRVMaxHostsWithReplicaSet
}
if args.LoadBalanced != nil && *args.LoadBalanced {
return connstring.ErrSRVMaxHostsWithLoadBalanced
}
}

if mode := args.ServerMonitoringMode; mode != nil && !connstring.IsValidServerMonitoringMode(*mode) {
return fmt.Errorf("invalid server monitoring mode: %q", *mode)
}

return nil
}

// GetURI returns the original URI used to configure the ClientOptions instance.
// If ApplyURI was not called during construction, this returns "".
func (args *ClientArgs) GetURI() string {
Expand Down Expand Up @@ -579,11 +517,69 @@ func (c *ClientOptions) GetURI() string {
// error found.
func (c *ClientOptions) Validate() error {
args, err := getArgs[ClientArgs](c)
//return err

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//return err

if err != nil {
return err
}

return ValidateClientArgs(args)
// Direct connections cannot be made if multiple hosts are specified or an SRV
// URI is used.
if args.Direct != nil && *args.Direct {
if len(args.Hosts) > 1 {
return errors.New("a direct connection cannot be made if multiple hosts are specified")
}
if args.connString != nil && args.connString.Scheme == connstring.SchemeMongoDBSRV {
return errors.New("a direct connection cannot be made if an SRV URI is used")
}
}

if args.MaxPoolSize != nil && args.MinPoolSize != nil && *args.MaxPoolSize != 0 &&
*args.MinPoolSize > *args.MaxPoolSize {
return fmt.Errorf("minPoolSize must be less than or equal to maxPoolSize, got minPoolSize=%d maxPoolSize=%d",
*args.MinPoolSize, *args.MaxPoolSize)
}

// verify server API version if ServerAPIOptions are passed in.
if args.ServerAPIOptions != nil {
serverAPIArgs, err := getArgs[ServerAPIArgs](args.ServerAPIOptions)
if err != nil {
return fmt.Errorf("failed to construct arguments from options: %w", err)
}

if err := serverAPIArgs.ServerAPIVersion.Validate(); err != nil {
return err
}
}

// Validation for load-balanced mode.
if args.LoadBalanced != nil && *args.LoadBalanced {
if len(args.Hosts) > 1 {
return connstring.ErrLoadBalancedWithMultipleHosts
}
if args.ReplicaSet != nil {
return connstring.ErrLoadBalancedWithReplicaSet
}
if args.Direct != nil && *args.Direct {
return connstring.ErrLoadBalancedWithDirectConnection
}
}

// Validation for srvMaxHosts.
if args.SRVMaxHosts != nil && *args.SRVMaxHosts > 0 {
if args.ReplicaSet != nil {
return connstring.ErrSRVMaxHostsWithReplicaSet
}
if args.LoadBalanced != nil && *args.LoadBalanced {
return connstring.ErrSRVMaxHostsWithLoadBalanced
}
}

if mode := args.ServerMonitoringMode; mode != nil && !connstring.IsValidServerMonitoringMode(*mode) {
return fmt.Errorf("invalid server monitoring mode: %q", *mode)
}

return nil
}

// ApplyURI parses the given URI and sets options accordingly. The URI can contain host names, IPv4/IPv6 literals, or
Expand Down
4 changes: 2 additions & 2 deletions mongo/options/encryptoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ func (e *EncryptOptions) SetContentionFactor(contentionFactor int64) *EncryptOpt

// SetRangeOptions specifies the options to use for explicit encryption with range. It is only valid to set if algorithm is "rangePreview".
// Beta: The Range algorithm is experimental only. It is not intended for public use. It is subject to breaking changes.
func (e *EncryptOptions) SetRangeOptions(ro RangeOptions) *EncryptOptions {
func (e *EncryptOptions) SetRangeOptions(ro *RangeOptions) *EncryptOptions {
e.Opts = append(e.Opts, func(args *EncryptArgs) error {
args.RangeOptions = &ro
args.RangeOptions = ro

return nil
})
Expand Down
2 changes: 1 addition & 1 deletion mongo/search_index_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func (siv SearchIndexView) List(
return nil, err
}

aggregateOpts := &mongoutil.Options[options.AggregateArgs]{Args: args.AggregateArgs}
aggregateOpts := &mongoutil.ArgOptions[options.AggregateArgs]{Args: args.AggregateArgs}

return siv.coll.Aggregate(ctx, Pipeline{{{"$listSearchIndexes", index}}}, aggregateOpts)
}
Expand Down