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

Conversation

prestonvasquez
Copy link
Collaborator

@prestonvasquez prestonvasquez commented Feb 29, 2024

GODRIVER-2696

Summary

This PR implements the proposed solution described here in the POC PR #1480 with the intention of updating the options pattern to abstract merging.

Background & Motivation

To avoid creating merge function for each option type, the goal of 2696 is to find and implement an optional arguments pattern that doesn't require maintaining dedicated code for merging each type of option struct together when multiple options structs are provided.

Copy link

mongodb-drivers-pr-bot bot commented Feb 29, 2024

API Change Report

##! second, different message for obj type ./mongo/options.NameOptions struct{Revision *int32}, isNew false, part ""
first: old is comparable, new is not
second: removed
##! second, different message for obj type ./mongo/options.UploadOptions struct{ChunkSizeBytes *int32; Metadata interface{}; Registry *./bson.Registry}, isNew false, part ""
first: old is comparable, new is not
second: removed

./mongo

incompatible changes

##(Client).Database: changed from func(string, ..../mongo/options.DatabaseOptions) *Database to func(string, ...Options[./mongo/options.DatabaseArgs]) *Database
##(Client).ListDatabaseNames: changed from func(context.Context, interface{}, ..../mongo/options.ListDatabasesOptions) ([]string, error) to func(context.Context, interface{}, ...Options[./mongo/options.ListDatabasesArgs]) ([]string, error)
##(Client).ListDatabases: changed from func(context.Context, interface{}, ..../mongo/options.ListDatabasesOptions) (ListDatabasesResult, error) to func(context.Context, interface{}, ...Options[./mongo/options.ListDatabasesArgs]) (ListDatabasesResult, error)
##(Client).StartSession: changed from func(..../mongo/options.SessionOptions) (*Session, error) to func(...Options[./mongo/options.SessionArgs]) (*Session, error)
##(Client).Watch: changed from func(context.Context, interface{}, ..../mongo/options.ChangeStreamOptions) (*ChangeStream, error) to func(context.Context, interface{}, ...Options[./mongo/options.ChangeStreamArgs]) (*ChangeStream, error)
##(ClientEncryption).CreateDataKey: changed from func(context.Context, string, ..../mongo/options.DataKeyOptions) (./bson.Binary, error) to func(context.Context, string, ...Options[./mongo/options.DataKeyArgs]) (./bson.Binary, error)
##(*ClientEncryption).CreateEncryptedCollection: changed from func(context.Context, *Database, string, *./mongo/options.CreateCollectionOptions, string, interface{}) (*Collection, ./bson.M, error) to func(context.Context, *Database, string, Options[./mongo/options.CreateCollectionArgs], string, interface{}) (*Collection, ./bson.M, error)
##(ClientEncryption).Encrypt: changed from func(context.Context, ./bson.RawValue, ..../mongo/options.EncryptOptions) (./bson.Binary, error) to func(context.Context, ./bson.RawValue, ...Options[./mongo/options.EncryptArgs]) (./bson.Binary, error)
##(ClientEncryption).EncryptExpression: changed from func(context.Context, interface{}, interface{}, ..../mongo/options.EncryptOptions) error to func(context.Context, interface{}, interface{}, ...Options[./mongo/options.EncryptArgs]) error
##(ClientEncryption).RewrapManyDataKey: changed from func(context.Context, interface{}, ..../mongo/options.RewrapManyDataKeyOptions) (*RewrapManyDataKeyResult, error) to func(context.Context, interface{}, ...Options[./mongo/options.RewrapManyDataKeyArgs]) (*RewrapManyDataKeyResult, error)
##(Collection).Aggregate: changed from func(context.Context, interface{}, ..../mongo/options.AggregateOptions) (*Cursor, error) to func(context.Context, interface{}, ...Options[./mongo/options.AggregateArgs]) (*Cursor, error)
##(Collection).BulkWrite: changed from func(context.Context, []WriteModel, ..../mongo/options.BulkWriteOptions) (*BulkWriteResult, error) to func(context.Context, []WriteModel, ...Options[./mongo/options.BulkWriteArgs]) (*BulkWriteResult, error)
##(Collection).Clone: changed from func(..../mongo/options.CollectionOptions) (*Collection, error) to func(...Options[./mongo/options.CollectionArgs]) (*Collection, error)
##(Collection).CountDocuments: changed from func(context.Context, interface{}, ..../mongo/options.CountOptions) (int64, error) to func(context.Context, interface{}, ...Options[./mongo/options.CountArgs]) (int64, error)
##(Collection).DeleteMany: changed from func(context.Context, interface{}, ..../mongo/options.DeleteOptions) (*DeleteResult, error) to func(context.Context, interface{}, ...Options[./mongo/options.DeleteArgs]) (*DeleteResult, error)
##(Collection).DeleteOne: changed from func(context.Context, interface{}, ..../mongo/options.DeleteOptions) (*DeleteResult, error) to func(context.Context, interface{}, ...Options[./mongo/options.DeleteArgs]) (*DeleteResult, error)
##(Collection).Distinct: changed from func(context.Context, string, interface{}, ..../mongo/options.DistinctOptions) *DistinctResult to func(context.Context, string, interface{}, ...Options[./mongo/options.DistinctArgs]) *DistinctResult
##(Collection).Drop: changed from func(context.Context, ..../mongo/options.DropCollectionOptions) error to func(context.Context, ...Options[./mongo/options.DropCollectionArgs]) error
##(Collection).EstimatedDocumentCount: changed from func(context.Context, ..../mongo/options.EstimatedDocumentCountOptions) (int64, error) to func(context.Context, ...Options[./mongo/options.EstimatedDocumentCountArgs]) (int64, error)
##(Collection).Find: changed from func(context.Context, interface{}, ..../mongo/options.FindOptions) (*Cursor, error) to func(context.Context, interface{}, ...Options[./mongo/options.FindArgs]) (*Cursor, error)
##(Collection).FindOne: changed from func(context.Context, interface{}, ..../mongo/options.FindOneOptions) *SingleResult to func(context.Context, interface{}, ...Options[./mongo/options.FindOneArgs]) *SingleResult
##(Collection).FindOneAndDelete: changed from func(context.Context, interface{}, ..../mongo/options.FindOneAndDeleteOptions) *SingleResult to func(context.Context, interface{}, ...Options[./mongo/options.FindOneAndDeleteArgs]) *SingleResult
##(Collection).FindOneAndReplace: changed from func(context.Context, interface{}, interface{}, ..../mongo/options.FindOneAndReplaceOptions) *SingleResult to func(context.Context, interface{}, interface{}, ...Options[./mongo/options.FindOneAndReplaceArgs]) *SingleResult
##(Collection).FindOneAndUpdate: changed from func(context.Context, interface{}, interface{}, ..../mongo/options.FindOneAndUpdateOptions) *SingleResult to func(context.Context, interface{}, interface{}, ...Options[./mongo/options.FindOneAndUpdateArgs]) *SingleResult
##(Collection).InsertMany: changed from func(context.Context, interface{}, ..../mongo/options.InsertManyOptions) (*InsertManyResult, error) to func(context.Context, interface{}, ...Options[./mongo/options.InsertManyArgs]) (*InsertManyResult, error)
##(Collection).InsertOne: changed from func(context.Context, interface{}, ..../mongo/options.InsertOneOptions) (*InsertOneResult, error) to func(context.Context, interface{}, ...Options[./mongo/options.InsertOneArgs]) (*InsertOneResult, error)
##(Collection).ReplaceOne: changed from func(context.Context, interface{}, interface{}, ..../mongo/options.ReplaceOptions) (*UpdateResult, error) to func(context.Context, interface{}, interface{}, ...Options[./mongo/options.ReplaceArgs]) (*UpdateResult, error)
##(Collection).UpdateByID: changed from func(context.Context, interface{}, interface{}, ..../mongo/options.UpdateOptions) (*UpdateResult, error) to func(context.Context, interface{}, interface{}, ...Options[./mongo/options.UpdateArgs]) (*UpdateResult, error)
##(Collection).UpdateMany: changed from func(context.Context, interface{}, interface{}, ..../mongo/options.UpdateOptions) (*UpdateResult, error) to func(context.Context, interface{}, interface{}, ...Options[./mongo/options.UpdateArgs]) (*UpdateResult, error)
##(Collection).UpdateOne: changed from func(context.Context, interface{}, interface{}, ..../mongo/options.UpdateOptions) (*UpdateResult, error) to func(context.Context, interface{}, interface{}, ...Options[./mongo/options.UpdateArgs]) (*UpdateResult, error)
##(Collection).Watch: changed from func(context.Context, interface{}, ..../mongo/options.ChangeStreamOptions) (*ChangeStream, error) to func(context.Context, interface{}, ...Options[./mongo/options.ChangeStreamArgs]) (*ChangeStream, error)
##(Database).Aggregate: changed from func(context.Context, interface{}, ..../mongo/options.AggregateOptions) (*Cursor, error) to func(context.Context, interface{}, ...Options[./mongo/options.AggregateArgs]) (*Cursor, error)
##(Database).Collection: changed from func(string, ..../mongo/options.CollectionOptions) *Collection to func(string, ...Options[./mongo/options.CollectionArgs]) *Collection
##(Database).CreateCollection: changed from func(context.Context, string, ..../mongo/options.CreateCollectionOptions) error to func(context.Context, string, ...Options[./mongo/options.CreateCollectionArgs]) error
##(Database).CreateView: changed from func(context.Context, string, string, interface{}, ..../mongo/options.CreateViewOptions) error to func(context.Context, string, string, interface{}, ...Options[./mongo/options.CreateViewArgs]) error
##(Database).GridFSBucket: changed from func(..../mongo/options.BucketOptions) *GridFSBucket to func(...Options[./mongo/options.BucketArgs]) *GridFSBucket
##(Database).ListCollectionNames: changed from func(context.Context, interface{}, ..../mongo/options.ListCollectionsOptions) ([]string, error) to func(context.Context, interface{}, ...Options[./mongo/options.ListCollectionsArgs]) ([]string, error)
##(Database).ListCollectionSpecifications: changed from func(context.Context, interface{}, ..../mongo/options.ListCollectionsOptions) ([]*CollectionSpecification, error) to func(context.Context, interface{}, ...Options[./mongo/options.ListCollectionsArgs]) ([]*CollectionSpecification, error)
##(Database).ListCollections: changed from func(context.Context, interface{}, ..../mongo/options.ListCollectionsOptions) (*Cursor, error) to func(context.Context, interface{}, ...Options[./mongo/options.ListCollectionsArgs]) (*Cursor, error)
##(Database).RunCommand: changed from func(context.Context, interface{}, ..../mongo/options.RunCmdOptions) *SingleResult to func(context.Context, interface{}, ...Options[./mongo/options.RunCmdArgs]) *SingleResult
##(Database).RunCommandCursor: changed from func(context.Context, interface{}, ..../mongo/options.RunCmdOptions) (*Cursor, error) to func(context.Context, interface{}, ...Options[./mongo/options.RunCmdArgs]) (*Cursor, error)
##(Database).Watch: changed from func(context.Context, interface{}, ..../mongo/options.ChangeStreamOptions) (*ChangeStream, error) to func(context.Context, interface{}, ...Options[./mongo/options.ChangeStreamArgs]) (*ChangeStream, error)
##(GridFSBucket).DownloadToStreamByName: changed from func(context.Context, string, io.Writer, ..../mongo/options.NameOptions) (int64, error) to func(context.Context, string, io.Writer, ...Options[./mongo/options.GridFSNameArgs]) (int64, error)
##(GridFSBucket).Find: changed from func(context.Context, interface{}, ..../mongo/options.GridFSFindOptions) (*Cursor, error) to func(context.Context, interface{}, ...Options[./mongo/options.GridFSFindArgs]) (*Cursor, error)
##(GridFSBucket).OpenDownloadStreamByName: changed from func(context.Context, string, ..../mongo/options.NameOptions) (*GridFSDownloadStream, error) to func(context.Context, string, ...Options[./mongo/options.GridFSNameArgs]) (*GridFSDownloadStream, error)
##(GridFSBucket).OpenUploadStream: changed from func(context.Context, string, ..../mongo/options.UploadOptions) (GridFSUploadStream, error) to func(context.Context, string, ...Options[./mongo/options.GridFSUploadArgs]) (GridFSUploadStream, error)
##(GridFSBucket).OpenUploadStreamWithID: changed from func(context.Context, interface{}, string, ..../mongo/options.UploadOptions) (GridFSUploadStream, error) to func(context.Context, interface{}, string, ...Options[./mongo/options.GridFSUploadArgs]) (GridFSUploadStream, error)
##(GridFSBucket).UploadFromStream: changed from func(context.Context, string, io.Reader, ..../mongo/options.UploadOptions) (./bson.ObjectID, error) to func(context.Context, string, io.Reader, ...Options[./mongo/options.GridFSUploadArgs]) (./bson.ObjectID, error)
##(GridFSBucket).UploadFromStreamWithID: changed from func(context.Context, interface{}, string, io.Reader, ..../mongo/options.UploadOptions) error to func(context.Context, interface{}, string, io.Reader, ...Options[./mongo/options.GridFSUploadArgs]) error
##(Session).StartTransaction: changed from func(..../mongo/options.TransactionOptions) error to func(...Options[./mongo/options.TransactionArgs]) error
##(Session).WithTransaction: changed from func(context.Context, func(ctx context.Context) (interface{}, error), ..../mongo/options.TransactionOptions) (interface{}, error) to func(context.Context, func(ctx context.Context) (interface{}, error), ...Options[./mongo/options.TransactionArgs]) (interface{}, error)
##Connect: changed from func(...
./mongo/options.ClientOptions) (Client, error) to func(...Options[./mongo/options.ClientArgs]) (Client, error)
##IndexView.CreateMany: changed from func(context.Context, []IndexModel, ...
./mongo/options.CreateIndexesOptions) ([]string, error) to func(context.Context, []IndexModel, ...Options[./mongo/options.CreateIndexesArgs]) ([]string, error)
##IndexView.CreateOne: changed from func(context.Context, IndexModel, ...
./mongo/options.CreateIndexesOptions) (string, error) to func(context.Context, IndexModel, ...Options[./mongo/options.CreateIndexesArgs]) (string, error)
##IndexView.DropAll: changed from func(context.Context, ...
./mongo/options.DropIndexesOptions) error to func(context.Context, ...Options[./mongo/options.DropIndexesArgs]) error
##IndexView.DropOne: changed from func(context.Context, string, ...
./mongo/options.DropIndexesOptions) (./bson.Raw, error) to func(context.Context, string, ...Options[./mongo/options.DropIndexesArgs]) (./bson.Raw, error)
##IndexView.List: changed from func(context.Context, ...
./mongo/options.ListIndexesOptions) (*Cursor, error) to func(context.Context, ...Options[./mongo/options.ListIndexesArgs]) (Cursor, error)
##IndexView.ListSpecifications: changed from func(context.Context, ...
./mongo/options.ListIndexesOptions) ([]*IndexSpecification, error) to func(context.Context, ...Options[./mongo/options.ListIndexesArgs]) ([]*IndexSpecification, error)
##NewClientEncryption: changed from func(Client, ..../mongo/options.ClientEncryptionOptions) (ClientEncryption, error) to func(Client, ...Options[./mongo/options.ClientEncryptionArgs]) (ClientEncryption, error)
##SearchIndexView.CreateMany: changed from func(context.Context, []SearchIndexModel, ...
./mongo/options.CreateSearchIndexesOptions) ([]string, error) to func(context.Context, []SearchIndexModel, ...Options[./mongo/options.CreateSearchIndexesArgs]) ([]string, error)
##SearchIndexView.CreateOne: changed from func(context.Context, SearchIndexModel, ...
./mongo/options.CreateSearchIndexesOptions) (string, error) to func(context.Context, SearchIndexModel, ...Options[./mongo/options.CreateSearchIndexesArgs]) (string, error)
##SearchIndexView.DropOne: changed from func(context.Context, string, ...
./mongo/options.DropSearchIndexOptions) error to func(context.Context, string, ...Options[./mongo/options.DropSearchIndexArgs]) error
##SearchIndexView.List: changed from func(context.Context, ./mongo/options.SearchIndexesOptions, ..../mongo/options.ListSearchIndexesOptions) (*Cursor, error) to func(context.Context, Options[./mongo/options.SearchIndexesArgs], ...Options[./mongo/options.ListSearchIndexesArgs]) (Cursor, error)
##SearchIndexView.UpdateOne: changed from func(context.Context, string, interface{}, ...
./mongo/options.UpdateSearchIndexOptions) error to func(context.Context, string, interface{}, ...Options[./mongo/options.UpdateSearchIndexArgs]) error

compatible changes

Options: added

./mongo/options

incompatible changes

(*EncryptOptions).SetRangeOptions: changed from func(RangeOptions) *EncryptOptions to func(*RangeOptions) *EncryptOptions
AggregateOptions.AllowDiskUse: removed
AggregateOptions.BatchSize: removed
AggregateOptions.BypassDocumentValidation: removed
AggregateOptions.Collation: removed
AggregateOptions.Comment: removed
AggregateOptions.Custom: removed
AggregateOptions.Hint: removed
AggregateOptions.Let: removed
AggregateOptions.MaxAwaitTime: removed
AggregateOptions.MaxTime: removed
AutoEncryptionOptions.BypassAutoEncryption: removed
AutoEncryptionOptions.BypassQueryAnalysis: removed
AutoEncryptionOptions.EncryptedFieldsMap: removed
AutoEncryptionOptions.ExtraOptions: removed
AutoEncryptionOptions.HTTPClient: removed
AutoEncryptionOptions.KeyVaultClientOptions: removed
AutoEncryptionOptions.KeyVaultNamespace: removed
AutoEncryptionOptions.KmsProviders: removed
AutoEncryptionOptions.SchemaMap: removed
AutoEncryptionOptions.TLSConfig: removed
BucketOptions.ChunkSizeBytes: removed
BucketOptions.Name: removed
BucketOptions.ReadConcern: removed
BucketOptions.ReadPreference: removed
BucketOptions.WriteConcern: removed
BucketOptions: old is comparable, new is not
BulkWriteOptions.BypassDocumentValidation: removed
BulkWriteOptions.Comment: removed
BulkWriteOptions.Let: removed
BulkWriteOptions.Ordered: removed
BulkWriteOptions: old is comparable, new is not
ChangeStreamOptions.BatchSize: removed
ChangeStreamOptions.Collation: removed
ChangeStreamOptions.Comment: removed
ChangeStreamOptions.Custom: removed
ChangeStreamOptions.CustomPipeline: removed
ChangeStreamOptions.FullDocument: removed
ChangeStreamOptions.FullDocumentBeforeChange: removed
ChangeStreamOptions.MaxAwaitTime: removed
ChangeStreamOptions.ResumeAfter: removed
ChangeStreamOptions.ShowExpandedEvents: removed
ChangeStreamOptions.StartAfter: removed
ChangeStreamOptions.StartAtOperationTime: removed
ClientEncryptionOptions.HTTPClient: removed
ClientEncryptionOptions.KeyVaultNamespace: removed
ClientEncryptionOptions.KmsProviders: removed
ClientEncryptionOptions.TLSConfig: removed
ClientOptions.AppName: removed
ClientOptions.Auth: removed
ClientOptions.AutoEncryptionOptions: removed
ClientOptions.BSONOptions: removed
ClientOptions.Compressors: removed
ClientOptions.ConnectTimeout: removed
ClientOptions.Crypt: removed
ClientOptions.Deployment: removed
ClientOptions.Dialer: removed
ClientOptions.Direct: removed
ClientOptions.DisableOCSPEndpointCheck: removed
ClientOptions.HTTPClient: removed
ClientOptions.HeartbeatInterval: removed
ClientOptions.Hosts: removed
ClientOptions.LoadBalanced: removed
ClientOptions.LocalThreshold: removed
ClientOptions.LoggerOptions: removed
ClientOptions.MaxConnIdleTime: removed
ClientOptions.MaxConnecting: removed
ClientOptions.MaxPoolSize: removed
ClientOptions.MinPoolSize: removed
ClientOptions.Monitor: removed
ClientOptions.PoolMonitor: removed
ClientOptions.ReadConcern: removed
ClientOptions.ReadPreference: removed
ClientOptions.Registry: removed
ClientOptions.ReplicaSet: removed
ClientOptions.RetryReads: removed
ClientOptions.RetryWrites: removed
ClientOptions.SRVMaxHosts: removed
ClientOptions.SRVServiceName: removed
ClientOptions.ServerAPIOptions: removed
ClientOptions.ServerMonitor: removed
ClientOptions.ServerMonitoringMode: removed
ClientOptions.ServerSelectionTimeout: removed
ClientOptions.SocketTimeout: removed
ClientOptions.TLSConfig: removed
ClientOptions.Timeout: removed
ClientOptions.WriteConcern: removed
ClientOptions.ZlibLevel: removed
ClientOptions.ZstdLevel: removed
CollectionOptions.BSONOptions: removed
CollectionOptions.ReadConcern: removed
CollectionOptions.ReadPreference: removed
CollectionOptions.Registry: removed
CollectionOptions.WriteConcern: removed
CollectionOptions: old is comparable, new is not
CountOptions.Collation: removed
CountOptions.Comment: removed
CountOptions.Hint: removed
CountOptions.Limit: removed
CountOptions.MaxTime: removed
CountOptions.Skip: removed
CountOptions: old is comparable, new is not
CreateCollectionOptions.Capped: removed
CreateCollectionOptions.ChangeStreamPreAndPostImages: removed
CreateCollectionOptions.ClusteredIndex: removed
CreateCollectionOptions.Collation: removed
CreateCollectionOptions.DefaultIndexOptions: removed
CreateCollectionOptions.EncryptedFields: removed
CreateCollectionOptions.ExpireAfterSeconds: removed
CreateCollectionOptions.MaxDocuments: removed
CreateCollectionOptions.SizeInBytes: removed
CreateCollectionOptions.StorageEngine: removed
CreateCollectionOptions.TimeSeriesOptions: removed
CreateCollectionOptions.ValidationAction: removed
CreateCollectionOptions.ValidationLevel: removed
CreateCollectionOptions.Validator: removed
CreateCollectionOptions: old is comparable, new is not
CreateIndexesOptions.CommitQuorum: removed
CreateIndexesOptions.MaxTime: removed
CreateIndexesOptions: old is comparable, new is not
CreateSearchIndexesOptions: old is comparable, new is not
CreateViewOptions.Collation: removed
CreateViewOptions: old is comparable, new is not
DataKeyOptions.KeyAltNames: removed
DataKeyOptions.KeyMaterial: removed
DataKeyOptions.MasterKey: removed
DatabaseOptions.BSONOptions: removed
DatabaseOptions.ReadConcern: removed
DatabaseOptions.ReadPreference: removed
DatabaseOptions.Registry: removed
DatabaseOptions.WriteConcern: removed
DatabaseOptions: old is comparable, new is not
DefaultIndexOptions.StorageEngine: removed
DefaultIndexOptions: old is comparable, new is not
DeleteOptions.Collation: removed
DeleteOptions.Comment: removed
DeleteOptions.Hint: removed
DeleteOptions.Let: removed
DeleteOptions: old is comparable, new is not
DistinctOptions.Collation: removed
DistinctOptions.Comment: removed
DistinctOptions.MaxTime: removed
DistinctOptions: old is comparable, new is not
DropCollectionOptions.EncryptedFields: removed
DropCollectionOptions: old is comparable, new is not
DropIndexesOptions.MaxTime: removed
DropIndexesOptions: old is comparable, new is not
DropSearchIndexOptions: old is comparable, new is not
EncryptOptions.Algorithm: removed
EncryptOptions.ContentionFactor: removed
EncryptOptions.KeyAltName: removed
EncryptOptions.KeyID: removed
EncryptOptions.QueryType: removed
EncryptOptions.RangeOptions: removed
EncryptOptions: old is comparable, new is not
EstimatedDocumentCountOptions.Comment: removed
EstimatedDocumentCountOptions.MaxTime: removed
EstimatedDocumentCountOptions: old is comparable, new is not
FindOneAndDeleteOptions.Collation: removed
FindOneAndDeleteOptions.Comment: removed
FindOneAndDeleteOptions.Hint: removed
FindOneAndDeleteOptions.Let: removed
FindOneAndDeleteOptions.MaxTime: removed
FindOneAndDeleteOptions.Projection: removed
FindOneAndDeleteOptions.Sort: removed
FindOneAndDeleteOptions: old is comparable, new is not
FindOneAndReplaceOptions.BypassDocumentValidation: removed
FindOneAndReplaceOptions.Collation: removed
FindOneAndReplaceOptions.Comment: removed
FindOneAndReplaceOptions.Hint: removed
FindOneAndReplaceOptions.Let: removed
FindOneAndReplaceOptions.MaxTime: removed
FindOneAndReplaceOptions.Projection: removed
FindOneAndReplaceOptions.ReturnDocument: removed
FindOneAndReplaceOptions.Sort: removed
FindOneAndReplaceOptions.Upsert: removed
FindOneAndReplaceOptions: old is comparable, new is not
FindOneAndUpdateOptions.ArrayFilters: removed
FindOneAndUpdateOptions.BypassDocumentValidation: removed
FindOneAndUpdateOptions.Collation: removed
FindOneAndUpdateOptions.Comment: removed
FindOneAndUpdateOptions.Hint: removed
FindOneAndUpdateOptions.Let: removed
FindOneAndUpdateOptions.MaxTime: removed
FindOneAndUpdateOptions.Projection: removed
FindOneAndUpdateOptions.ReturnDocument: removed
FindOneAndUpdateOptions.Sort: removed
FindOneAndUpdateOptions.Upsert: removed
FindOneAndUpdateOptions: old is comparable, new is not
FindOneOptions.AllowPartialResults: removed
FindOneOptions.Collation: removed
FindOneOptions.Comment: removed
FindOneOptions.Hint: removed
FindOneOptions.Max: removed
FindOneOptions.MaxTime: removed
FindOneOptions.Min: removed
FindOneOptions.Projection: removed
FindOneOptions.ReturnKey: removed
FindOneOptions.ShowRecordID: removed
FindOneOptions.Skip: removed
FindOneOptions.Sort: removed
FindOneOptions: old is comparable, new is not
FindOptions.AllowDiskUse: removed
FindOptions.AllowPartialResults: removed
FindOptions.BatchSize: removed
FindOptions.Collation: removed
FindOptions.Comment: removed
FindOptions.CursorType: removed
FindOptions.Hint: removed
FindOptions.Let: removed
FindOptions.Limit: removed
FindOptions.Max: removed
FindOptions.MaxAwaitTime: removed
FindOptions.MaxTime: removed
FindOptions.Min: removed
FindOptions.NoCursorTimeout: removed
FindOptions.Projection: removed
FindOptions.ReturnKey: removed
FindOptions.ShowRecordID: removed
FindOptions.Skip: removed
FindOptions.Sort: removed
FindOptions: old is comparable, new is not
GridFSFindOptions.AllowDiskUse: removed
GridFSFindOptions.BatchSize: removed
GridFSFindOptions.Limit: removed
GridFSFindOptions.MaxTime: removed
GridFSFindOptions.NoCursorTimeout: removed
GridFSFindOptions.Skip: removed
GridFSFindOptions.Sort: removed
GridFSFindOptions: old is comparable, new is not
IndexOptions.Bits: removed
IndexOptions.BucketSize: removed
IndexOptions.Collation: removed
IndexOptions.DefaultLanguage: removed
IndexOptions.ExpireAfterSeconds: removed
IndexOptions.Hidden: removed
IndexOptions.LanguageOverride: removed
IndexOptions.Max: removed
IndexOptions.Min: removed
IndexOptions.Name: removed
IndexOptions.PartialFilterExpression: removed
IndexOptions.Sparse: removed
IndexOptions.SphereVersion: removed
IndexOptions.StorageEngine: removed
IndexOptions.TextVersion: removed
IndexOptions.Unique: removed
IndexOptions.Version: removed
IndexOptions.Weights: removed
IndexOptions.WildcardProjection: removed
IndexOptions: old is comparable, new is not
InsertManyOptions.BypassDocumentValidation: removed
InsertManyOptions.Comment: removed
InsertManyOptions.Ordered: removed
InsertManyOptions: old is comparable, new is not
InsertOneOptions.BypassDocumentValidation: removed
InsertOneOptions.Comment: removed
InsertOneOptions: old is comparable, new is not
ListCollectionsOptions.AuthorizedCollections: removed
ListCollectionsOptions.BatchSize: removed
ListCollectionsOptions.NameOnly: removed
ListCollectionsOptions: old is comparable, new is not
ListDatabasesOptions.AuthorizedDatabases: removed
ListDatabasesOptions.NameOnly: removed
ListDatabasesOptions: old is comparable, new is not
ListIndexesOptions.BatchSize: removed
ListIndexesOptions.MaxTime: removed
ListIndexesOptions: old is comparable, new is not
ListSearchIndexesOptions.AggregateOpts: removed
ListSearchIndexesOptions: old is comparable, new is not
LoggerOptions.ComponentLevels: removed
LoggerOptions.MaxDocumentLength: removed
LoggerOptions.Sink: removed
MergeClientOptions: removed
MergeDropCollectionOptions: removed
NameOptions.Revision: removed
NameOptions: removed
RangeOptions.Max: removed
RangeOptions.Min: removed
RangeOptions.Precision: removed
RangeOptions.Sparsity: removed
RangeOptions: old is comparable, new is not
ReplaceOptions.BypassDocumentValidation: removed
ReplaceOptions.Collation: removed
ReplaceOptions.Comment: removed
ReplaceOptions.Hint: removed
ReplaceOptions.Let: removed
ReplaceOptions.Upsert: removed
ReplaceOptions: old is comparable, new is not
RewrapManyDataKeyOptions.MasterKey: removed
RewrapManyDataKeyOptions.Provider: removed
RewrapManyDataKeyOptions: old is comparable, new is not
RunCmdOptions.ReadPreference: removed
RunCmdOptions: old is comparable, new is not
SearchIndexesOptions.Name: removed
SearchIndexesOptions: old is comparable, new is not
ServerAPIOptions.DeprecationErrors: removed
ServerAPIOptions.ServerAPIVersion: removed
ServerAPIOptions.Strict: removed
ServerAPIOptions: old is comparable, new is not
SessionOptions.CausalConsistency: removed
SessionOptions.DefaultMaxCommitTime: removed
SessionOptions.DefaultReadConcern: removed
SessionOptions.DefaultReadPreference: removed
SessionOptions.DefaultWriteConcern: removed
SessionOptions.Snapshot: removed
SessionOptions: old is comparable, new is not
TimeSeriesOptions.BucketMaxSpan: removed
TimeSeriesOptions.BucketRounding: removed
TimeSeriesOptions.Granularity: removed
TimeSeriesOptions.MetaField: removed
TimeSeriesOptions.TimeField: removed
TimeSeriesOptions: old is comparable, new is not
TransactionOptions.MaxCommitTime: removed
TransactionOptions.ReadConcern: removed
TransactionOptions.ReadPreference: removed
TransactionOptions.WriteConcern: removed
TransactionOptions: old is comparable, new is not
UpdateOptions.ArrayFilters: removed
UpdateOptions.BypassDocumentValidation: removed
UpdateOptions.Collation: removed
UpdateOptions.Comment: removed
UpdateOptions.Hint: removed
UpdateOptions.Let: removed
UpdateOptions.Upsert: removed
UpdateOptions: old is comparable, new is not
UpdateSearchIndexOptions: old is comparable, new is not
UploadOptions.ChunkSizeBytes: removed
UploadOptions.Metadata: removed
UploadOptions.Registry: removed
UploadOptions: removed

compatible changes

(*AggregateOptions).ArgsSetters: added
(*AutoEncryptionOptions).ArgsSetters: added
(*BucketOptions).ArgsSetters: added
(*BulkWriteOptions).ArgsSetters: added
(*ChangeStreamOptions).ArgsSetters: added
(*ClientEncryptionOptions).ArgsSetters: added
(*ClientOptions).ArgsSetters: added
(*CollectionOptions).ArgsSetters: added
(*CountOptions).ArgsSetters: added
(*CreateCollectionOptions).ArgsSetters: added
(*CreateIndexesOptions).ArgsSetters: added
(*CreateSearchIndexesOptions).ArgsSetters: added
(*CreateViewOptions).ArgsSetters: added
(*DataKeyOptions).ArgsSetters: added
(*DatabaseOptions).ArgsSetters: added
(*DefaultIndexOptions).ArgsSetters: added
(*DeleteOptions).ArgsSetters: added
(*DistinctOptions).ArgsSetters: added
(*DropCollectionOptions).ArgsSetters: added
(*DropIndexesOptions).ArgsSetters: added
(*DropSearchIndexOptions).ArgsSetters: added
(*EncryptOptions).ArgsSetters: added
(*EstimatedDocumentCountOptions).ArgsSetters: added
(*FindOneAndDeleteOptions).ArgsSetters: added
(*FindOneAndReplaceOptions).ArgsSetters: added
(*FindOneAndUpdateOptions).ArgsSetters: added
(*FindOneOptions).ArgsSetters: added
(*FindOptions).ArgsSetters: added
(*GridFSFindOptions).ArgsSetters: added
(*GridFSNameOptions).ArgsSetters: added
(*GridFSUploadOptions).ArgsSetters: added
(*GridFSUploadOptions).SetRegistry: added
(*IndexOptions).ArgsSetters: added
(*InsertManyOptions).ArgsSetters: added
(*InsertOneOptions).ArgsSetters: added
(*ListCollectionsOptions).ArgsSetters: added
(*ListDatabasesOptions).ArgsSetters: added
(*ListIndexesOptions).ArgsSetters: added
(*ListSearchIndexesOptions).ArgsSetters: added
(*LoggerOptions).ArgsSetters: added
(*RangeOptions).ArgsSetters: added
(*ReplaceOptions).ArgsSetters: added
(*RewrapManyDataKeyOptions).ArgsSetters: added
(*RunCmdOptions).ArgsSetters: added
(*SearchIndexesOptions).ArgsSetters: added
(*ServerAPIOptions).ArgsSetters: added
(*SessionOptions).ArgsSetters: added
(*TimeSeriesOptions).ArgsSetters: added
(*TransactionOptions).ArgsSetters: added
(*UpdateOptions).ArgsSetters: added
(*UpdateSearchIndexOptions).ArgsSetters: added
AggregateArgs: added
AggregateOptions.Opts: added
AutoEncryptionArgs: added
AutoEncryptionOptions.Opts: added
BucketArgs: added
BucketOptions.Opts: added
BulkWriteArgs: added
BulkWriteOptions.Opts: added
ChangeStreamArgs: added
ChangeStreamOptions.Opts: added
ClientArgs: added
ClientEncryptionArgs: added
ClientEncryptionOptions.Opts: added
ClientOptions.Opts: added
CollectionArgs: added
CollectionOptions.Opts: added
CountArgs: added
CountOptions.Opts: added
CreateCollectionArgs: added
CreateCollectionOptions.Opts: added
CreateIndexesArgs: added
CreateIndexesOptions.Opts: added
CreateSearchIndexesArgs: added
CreateSearchIndexesOptions.Opts: added
CreateViewArgs: added
CreateViewOptions.Opts: added
DataKeyArgs: added
DataKeyOptions.Opts: added
DatabaseArgs: added
DatabaseOptions.Opts: added
DefaultIndexArgs: added
DefaultIndexOptions.Opts: added
DeleteArgs: added
DeleteOptions.Opts: added
DistinctArgs: added
DistinctOptions.Opts: added
DropCollectionArgs: added
DropCollectionOptions.Opts: added
DropIndexesArgs: added
DropIndexesOptions.Opts: added
DropSearchIndexArgs: added
DropSearchIndexOptions.Opts: added
EncryptArgs: added
EncryptOptions.Opts: added
EstimatedDocumentCountArgs: added
EstimatedDocumentCountOptions.Opts: added
FindArgs: added
FindOneAndDeleteArgs: added
FindOneAndDeleteOptions.Opts: added
FindOneAndReplaceArgs: added
FindOneAndReplaceOptions.Opts: added
FindOneAndUpdateArgs: added
FindOneAndUpdateOptions.Opts: added
FindOneArgs: added
FindOneOptions.Opts: added
FindOptions.Opts: added
GridFSFindArgs: added
GridFSFindOptions.Opts: added
GridFSNameArgs: added
GridFSNameOptions: added
GridFSUploadArgs: added
GridFSUploadOptions: added
IndexArgs: added
IndexOptions.Opts: added
InsertManyArgs: added
InsertManyOptions.Opts: added
InsertOneArgs: added
InsertOneOptions.Opts: added
ListCollectionsArgs: added
ListCollectionsOptions.Opts: added
ListDatabasesArgs: added
ListDatabasesOptions.Opts: added
ListIndexesArgs: added
ListIndexesOptions.Opts: added
ListSearchIndexesArgs: added
ListSearchIndexesOptions.Opts: added
LoggerArgs: added
LoggerOptions.Opts: added
NameOptions.Opts: added
Range: added
RangeArgs: added
RangeOptions.Opts: added
ReplaceArgs: added
ReplaceOptions.Opts: added
RewrapManyDataKeyArgs: added
RewrapManyDataKeyOptions.Opts: added
RunCmdArgs: added
RunCmdOptions.Opts: added
SearchIndexesArgs: added
SearchIndexesOptions.Opts: added
ServerAPIArgs: added
ServerAPIOptions.Opts: added
SessionArgs: added
SessionOptions.Opts: added
TimeSeriesArgs: added
TimeSeriesOptions.Opts: added
TransactionArgs: added
TransactionOptions.Opts: added
UpdateArgs: added
UpdateOptions.Opts: added
UpdateSearchIndexArgs: added
UpdateSearchIndexOptions.Opts: added
UploadOptions.Opts: added

./x/mongo/driver/topology

incompatible changes

##ConvertToDriverAPIOptions: changed from func(*./mongo/options.ServerAPIOptions) *./x/mongo/driver.ServerAPIOptions to func(./internal/mongoutil.MongoOptions[./mongo/options.ServerAPIArgs]) *./x/mongo/driver.ServerAPIOptions

compatible changes

NewConfigFromArgs: added

@prestonvasquez prestonvasquez changed the title Godriver 2696 GODRIVER-2696 Use an options pattern that doesn't require merging structs Mar 14, 2024
@mongodb-drivers-pr-bot mongodb-drivers-pr-bot bot added the priority-3-low Low Priority PR for Review label Mar 14, 2024
mongo/util.go Outdated
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can use ptrutil.Ptr instead.

qingyang-hu
qingyang-hu previously approved these changes Apr 16, 2024
Copy link
Collaborator

@qingyang-hu qingyang-hu left a comment

Choose a reason for hiding this comment

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

LGTM!

// 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
Copy link
Collaborator

Choose a reason for hiding this comment

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

Optional: It's not obvious that Clbk is an abbreviation for "Callback" because it's similar to other more common abbreviations (e.g. clk, blk). Consider renaming to Callback.


// Options is an interface that wraps a method to return a list of setter
// functions that can set a generic arguments type.
type Options[T mongoutil.Args] interface {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a risk to using T any instead of T mongoutil.Args?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Although, functionally, T any fits here as well, I think it's better to restrict the types. The mongoutil.Args type also implies the expected usage. The only issue is that we need to maintain the Args list.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm concerned about the cost of maintaining that exhaustive list and also of using something defined in an internal package in the options API. The usage of options.Options in the mongo and options APIs seems pretty clear already.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No matter what, we would need a compile-time check that options implement ArgsSetters. mongoutil.Args serves this purpose under the proposed paradigm, as well as providing type safety to the mongo.Options type.

In terms of maintaining the list, options are part of stable API so this list could only grow within the scope of 2.x.

Unfortunately, due to dependency cycles we can't define this list in the mongo package. However, it doesn't really matter: if an embedded type (WLOG consider options.ClientArgs) is removed from mongoutil.Args, then you will get a compile-time failure akin to the following:

"go.mongodb.org/mongo-driver/mongo/options".ClientArgs does not satisfy mongoutil.Args

The only way you wouldn't get this failure is if (1) the underlying options are not used [in which case removal doesn't matter], or (2) the options have been removed in all instance [a breaking change].

IMO the benefit of type-safety outweighs the need to extend this list when new option requirements arise.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was actually thinking about when we need to add new Options types, which would require adding a type to the mongoutil.Args interface. For example, if we add a new Collection method coll.Foo, we'd probably also add FooOptions and FooArgs, which will need to implement Options. We'd need to add FooArgs to mongoutil.Args. Could that change break any user code?

Another consideration is users who want to write functions that operate on Options. For example, if a user wanted to pass *Args types directly to collection methods (like we do in our spec tests), they might define a generic type like:

type argsAsOptions[T mongoutil.Args] struct {
	args *T
}

func (o *argsAsOptions[T]) ArgsSetters() []func(*T) error {
	return []func(*T) error{
		func(args *T) error {
			*args = *o.args
			return nil
		},
	}
}

func main() {
	coll.FindOne(
		context.Background(),
		bson.D{},
		&argsAsOptions[options.FindOneArgs]{
			args: &options.FindOneArgs{
				Comment: "blah",
			},
		},
	)
}

However, a user can't reference mongoutil.Args from outside the Go Driver module, so they would have to define a type constraint that is a copy of mongoutil.Args:

type myArgs interface {
	options.AggregateArgs | options.BucketArgs | options.BulkWriteArgs |
	// ...
}

type argsAsOptions[T myArgs] struct {
	// ...

That example is inspired by a public code search for users defining ClientOptions literals instead of using the builder API (see here). Is that a use case we want to support?

Concerning type safety, is there a problematic case that using the mongoutils.Args type constraint prevents?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was actually thinking about when we need to add new Options types, which would require adding a type to the mongoutil.Args interface.

We do this as a compile-time check, it's essentially this:

var _ mongoutil.Args = &FindOneArgs{}

Removing any arg struct (that is actually being used somewhere in the code) from mongoutil.Args will result in the following error [WLOG]:

"go.mongodb.org/mongo-driver/mongo/options".ClientArgs does not satisfy `mongoutil.Args`

Could that change break any user code?

Users don't need to be able to access or copy mongoutil.Args. Although it's unclear to me why you would want to do this, if a user is trying to genericize their own options they can create a type that implements a subset of mongoutil.Args, e.g.:

type opts interface {
	options.FindOneArgs | options.InsertOneArgs | SomeRandomStruct
}

type argsAsOptions[T opts] struct {
	args *T
}

In general, I would expect users to define custom types without generics:

type argsAsOptions struct {
	args *options.FindOneArgs
}

func (o *argsAsOptions) ArgsSetters() []func(*options.FindOneArgs) error {
	return []func(*options.FindOneArgs) error{
		func(args *options.FindOneArgs) error {
			*args = *o.args

			return nil
		},
	}
}

func main() {
	// ...

	coll.FindOne(
		context.Background(),
		bson.D{},
		&argsAsOptions{
			args: &options.FindOneArgs{
				Comment: "blah",
			},
		},
	)
}

That example is inspired by a public code search for users defining ClientOptions literals instead of using the builder API (see here). Is that a use case we want to support?

I would expect users to define struct literals by appending the Opts field:

opts := options.Client().ApplyURI("mongodb://localhost:27017")

opts.Opts = append(opts.Opts, func(args *options.ClientArgs) error {
	appName := "myApp"

	*args = options.ClientArgs{
		AppName: &appName,
	}

	return nil
})

client, err := mongo.Connect(opts)
if err != nil {
	panic(err)
}

Concerning type safety, is there a problematic case that using the mongoutils.Args type constraint prevents?

Probably not, since we define the expected options type in the function signature, preventing any external arguments being used in our code:

func (coll *Collection) InsertOne(ctx context.Context, document interface{},
	opts ...TOptions[options.InsertOneArgs]) (*InsertOneResult, error) {

I don't expect users to directly interact with mongo.Options, and copying mongoutil.Args should never be necessary, although it's safe to do so since that list can only be extended. As far as I understand, strongly typing the arg setter parameters won't cause issues and will behave the same as using any. If you strongly prefer using any, I'm open to making that change. It's worth noting that using any doesn't necessarily violate the Liskov Substitution Principle, as there's currently no way to create ambiguity with custom types. However, relying on any is more likely to lead to misuse in the future compared to using strong types.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Given the examples you provided which don't require generics or copying mongoutil.Args, I agree it doesn't block any known use cases. If a user did want to define a generic method for converting an *Args into an Options, defining a type constraint that is a subset of mongoutil.Args is somewhat inconvenient, but also not blocking. Ideally, if we think that's a use case that many users are interested in, we can provide that utility in the options package in the future.

Consider this resolved.

// Options is an interface that wraps a method to return a list of setter
// functions that can set a generic arguments type.
type Options[T mongoutil.Args] interface {
ArgsSetters() []func(*T) error
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason to return errors from the setters? Handling the errors adds a significant amount of code and is currently completely unused. Do we plan to use the ability to return errors in the future for something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The setter error is used on L605 of the client options, in the ApplyURI method.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, good point. Considering we're going to be stuck with this API for at least all of v2.x, and there's an example of it actually being used, the added flexibility of being able to use different setter validation patterns in the future seems worth the additional error handling code, even if it remains mostly unused.

Consider this resolved.

if len(c.Hosts) > 1 {
// ValidateClientArgs checks if the client arguments will create a valid
// connection.
func ValidateClientArgs(args *ClientArgs) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems like this is separate from ClientOptions.Validate so it can also be called in the topology package for a ClientArgs. Can we move the Validate method to a ClientArgs instead so we don't have to add this exported symbol?

Alternatively, we should move this validation to an internal package and remove ClientOptions.Validate.

@@ -2651,7 +2642,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
SetAlgorithm("RangePreview").
SetKeyID(key1ID).
SetContentionFactor(0).
SetRangeOptions(test.rangeOpts)
SetRangeOptions(*test.rangeOpts)
Copy link
Collaborator

Choose a reason for hiding this comment

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

options.Range() returns a *RangeOptions, but SetRangeOptions expects a RangeOptions. That creates an awkward mismatch where users will have to immediately dereference pointers to use them.

For example, the current code requires:

options.Encrypt().SetRangeOptions(*options.Range())

but we should make the following work:

options.Encrypt().SetRangeOptions(options.Range())

Either by changing SetRangeOptions to take a *RangeOptions or making options.Range() return a RangeOptions.

Comment on lines +32 to +34
type RangeOptions struct {
Opts []func(*RangeArgs) error
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm concerned this adds breaking changes and some code without a clear need because there are no APIs that accept multiple RangeOptions. Are there other motivations for changing RangeOptions to the functional options builder pattern?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If the specifications are ever extended to include an API that accepts RangeOptions as a parameter, then we would be forced to create a duplicate type that fits the functional pattern.

}
return driverOpts
}

func newLogger(opts *options.LoggerOptions) (*logger.Logger, error) {
func newLogger(opts mongoutil.MongoOptions[options.LoggerArgs]) (*logger.Logger, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Continuing to pass the Options list through the internal driver APIs makes it awkward set and read arg values and requires additional error handling in internal APIs. We should convert the the Options list to an *Args as soon as we have it and then only pass around *Args internally. We should treat the Options pattern as syntactic sugar for the public API and use it sparingly.

This suggestion applies to most non-exported or x/ package APIs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Much of the stable API function signatures used in the internal and x/ packages are defined with Options. Converting options to args too quickly can be equally awkward. Consider the use case with runTest here. Part of the logic for runTest is to create a mongo client:

func runTest(ctx context.Context, clientOpts *options.ClientOptions) error {
	client, err := mongo.Connect(clientOpts)
	if err != nil {
		return fmt.Errorf("Connect error: %w", err)
	}

	// ...
}

If we were to update the function signature to use args, then we would have to convert the args back into options:

func runTest(ctx context.Context, clientArgs *options.ClientArgs) error {
	clientOpts := mongoutil.NewOptionsFromArgs(clientArgs, nil)

	client, err := mongo.Connect(clientOpts)
	if err != nil {
		return fmt.Errorf("Connect error: %w", err)
	}
	
	// ...
}

The same is true for the integration functionexecuteAdminCommandWithRetry, which passes options to RunCommand. This is the only other instance of this pattern I could find in the proposal.

mongo/client.go Outdated
Comment on lines 214 to 216
if err != nil {
return nil, err
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This error handling block will never evaluate to true and can be removed.


// Options implements a mongo.Options object for an arbitrary arguments type.
// The intended use case is to create options from arguments.
type Options[T Args] struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Optional: It's a bit confusing that this type is called Options but does not serve the same purpose as the mongo.Options interface. Consider renaming this to something like ArgsOptions and renaming MongoOptions to Options.

Alternatively, you could un-export this type and have NewOptionsFromArgs could return a MongoOptions[T] instead of *Options[T] which would resolve the name conflict.

E.g.

type Options[T Args] interface {
	ArgsSetters() []func(*T) error
}

type options[T Args] struct {
	Args *T             // Arguments to set on the option type
	Clbk func(*T) error // A callback for further modification
}

func NewOptionsFromArgs[T Args](args *T, clbk func(*T) error) Options[T] {
	return &options[T]{Args: args, Clbk: clbk}
}

Returning an interface is often an anti-pattern, but I think it would be appropriate here because the return value is only intended for use as an Options.


// NewOptionsFromArgs will construct an Options object from the provided
// arguments object.
func NewOptionsFromArgs[T Args](args *T, clbk func(*T) error) *Options[T] {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Optional: The clbk argument seems unused and can be removed.

Comment on lines 520 to 521
//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


nameOpts := options.GridFSName()
nameOpts.Revision = &options.DefaultRevision
var numSkip int32 = -1
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shall we initialize numSkip with &options.DefaultRevision?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That makes sense. Looking at this method a bit closer, it's unclear why we set the Revision on the arguments. Suggest we also remove that code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
priority-3-low Low Priority PR for Review
Projects
None yet
3 participants