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-2443 Make Distinct return a decodable struct #1603

Merged
2 changes: 1 addition & 1 deletion internal/integration/client_side_encryption_prose_test.go
Expand Up @@ -1901,7 +1901,7 @@ func TestClientSideEncryptionProse(t *testing.T) {
return defKeyID
}

var validateAddKeyAltName = func(mt *mtest.T, cse *cseProseTest, res *mongo.SingleResult, expected ...string) {
var validateAddKeyAltName = func(mt *mtest.T, cse *cseProseTest, res *mongo.SingleResult[bson.Raw], expected ...string) {
assert.Nil(mt, res.Err(), "error adding key alt name: %v", res.Err())

resbytes, err := res.Raw()
Expand Down
25 changes: 15 additions & 10 deletions internal/integration/collection_test.go
Expand Up @@ -874,24 +874,29 @@ func TestCollection(t *testing.T) {
}
})
mt.RunOpts("distinct", noClientOpts, func(mt *mtest.T) {
all := []interface{}{int32(1), int32(2), int32(3), int32(4), int32(5)}
last3 := []interface{}{int32(3), int32(4), int32(5)}
all := []int32{1, 2, 3, 4, 5}

testCases := []struct {
name string
filter bson.D
opts *options.DistinctOptions
expected []interface{}
name string
filter bson.D
opts *options.DistinctOptions
want []int32
}{
{"no options", bson.D{}, nil, all},
{"filter", bson.D{{"x", bson.D{{"$gt", 2}}}}, nil, last3},
{"filter", bson.D{{"x", bson.D{{"$gt", 2}}}}, nil, all[2:]},
{"options", bson.D{}, options.Distinct().SetMaxTime(5000000000), all},
}
for _, tc := range testCases {
mt.Run(tc.name, func(mt *mtest.T) {
initCollection(mt, mt.Coll)
res, err := mt.Coll.Distinct(context.Background(), "x", tc.filter, tc.opts)
assert.Nil(mt, err, "Distinct error: %v", err)
assert.Equal(mt, tc.expected, res, "expected result %v, got %v", tc.expected, res)
res := mt.Coll.Distinct(context.Background(), "x", tc.filter, tc.opts)
assert.Nil(mt, res.Err(), "Distinct error: %v", res.Err())

var got []int32
err := res.Decode(&got)
assert.NoError(t, err)

assert.EqualValues(mt, tc.want, got, "expected result %v, got %v", tc.want, res)
prestonvasquez marked this conversation as resolved.
Show resolved Hide resolved
})
}
})
Expand Down
72 changes: 43 additions & 29 deletions internal/integration/crud_helpers_test.go
Expand Up @@ -408,7 +408,7 @@ func executeFind(mt *mtest.T, sess mongo.Session, args bson.Raw) (*mongo.Cursor,
return mt.Coll.Find(context.Background(), filter, opts)
}

func executeRunCommand(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult {
func executeRunCommand(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult[bson.Raw] {
mt.Helper()

cmd := emptyDoc
Expand All @@ -431,7 +431,7 @@ func executeRunCommand(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.Si
}

if sess != nil {
var sr *mongo.SingleResult
var sr *mongo.SingleResult[bson.Raw]
_ = mongo.WithSession(context.Background(), sess, func(sc mongo.SessionContext) error {
sr = mt.DB.RunCommand(sc, cmd, opts)
return nil
Expand Down Expand Up @@ -557,7 +557,7 @@ func executeListDatabases(mt *mtest.T, sess mongo.Session, args bson.Raw) (mongo
return mt.Client.ListDatabases(context.Background(), filter)
}

func executeFindOne(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult {
func executeFindOne(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult[bson.Raw] {
mt.Helper()

filter := emptyDoc
Expand All @@ -575,7 +575,7 @@ func executeFindOne(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.Singl
}

if sess != nil {
var res *mongo.SingleResult
var res *mongo.SingleResult[bson.Raw]
_ = mongo.WithSession(context.Background(), sess, func(sc mongo.SessionContext) error {
res = mt.Coll.FindOne(sc, filter)
return nil
Expand All @@ -602,7 +602,7 @@ func executeListIndexes(mt *mtest.T, sess mongo.Session, args bson.Raw) (*mongo.
return mt.Coll.Indexes().List(context.Background())
}

func executeDistinct(mt *mtest.T, sess mongo.Session, args bson.Raw) ([]interface{}, error) {
func executeDistinct(mt *mtest.T, sess mongo.Session, args bson.Raw) (bson.RawArray, error) {
mt.Helper()

var fieldName string
Expand All @@ -627,19 +627,25 @@ func executeDistinct(mt *mtest.T, sess mongo.Session, args bson.Raw) ([]interfac
}
}

var res *mongo.SingleResult[bson.RawArray]
if sess != nil {
var res []interface{}
err := mongo.WithSession(context.Background(), sess, func(sc mongo.SessionContext) error {
var derr error
res, derr = mt.Coll.Distinct(sc, fieldName, filter, opts)
return derr
res = mt.Coll.Distinct(sc, fieldName, filter, opts)

return res.Err()
})
return res, err

if err != nil {
return nil, err
}
} else {
res = mt.Coll.Distinct(context.Background(), fieldName, filter, opts)
}
return mt.Coll.Distinct(context.Background(), fieldName, filter, opts)

return res.Raw()
}

func executeFindOneAndDelete(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult {
func executeFindOneAndDelete(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult[bson.Raw] {
mt.Helper()

filter := emptyDoc
Expand Down Expand Up @@ -668,7 +674,7 @@ func executeFindOneAndDelete(mt *mtest.T, sess mongo.Session, args bson.Raw) *mo
}

if sess != nil {
var res *mongo.SingleResult
var res *mongo.SingleResult[bson.Raw]
_ = mongo.WithSession(context.Background(), sess, func(sc mongo.SessionContext) error {
res = mt.Coll.FindOneAndDelete(sc, filter, opts)
return nil
Expand All @@ -678,7 +684,7 @@ func executeFindOneAndDelete(mt *mtest.T, sess mongo.Session, args bson.Raw) *mo
return mt.Coll.FindOneAndDelete(context.Background(), filter, opts)
}

func executeFindOneAndUpdate(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult {
func executeFindOneAndUpdate(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult[bson.Raw] {
mt.Helper()

filter := emptyDoc
Expand Down Expand Up @@ -725,7 +731,7 @@ func executeFindOneAndUpdate(mt *mtest.T, sess mongo.Session, args bson.Raw) *mo
}

if sess != nil {
var res *mongo.SingleResult
var res *mongo.SingleResult[bson.Raw]
_ = mongo.WithSession(context.Background(), sess, func(sc mongo.SessionContext) error {
res = mt.Coll.FindOneAndUpdate(sc, filter, update, opts)
return nil
Expand All @@ -735,7 +741,7 @@ func executeFindOneAndUpdate(mt *mtest.T, sess mongo.Session, args bson.Raw) *mo
return mt.Coll.FindOneAndUpdate(context.Background(), filter, update, opts)
}

func executeFindOneAndReplace(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult {
func executeFindOneAndReplace(mt *mtest.T, sess mongo.Session, args bson.Raw) *mongo.SingleResult[bson.Raw] {
mt.Helper()

filter := emptyDoc
Expand Down Expand Up @@ -778,7 +784,7 @@ func executeFindOneAndReplace(mt *mtest.T, sess mongo.Session, args bson.Raw) *m
}

if sess != nil {
var res *mongo.SingleResult
var res *mongo.SingleResult[bson.Raw]
_ = mongo.WithSession(context.Background(), sess, func(sc mongo.SessionContext) error {
res = mt.Coll.FindOneAndReplace(sc, filter, replacement, opts)
return nil
Expand Down Expand Up @@ -1529,25 +1535,29 @@ func verifyDeleteResult(mt *mtest.T, res *mongo.DeleteResult, result interface{}
"deleted count mismatch; expected %v, got %v", expected.DeletedCount, res.DeletedCount)
}

func verifyDistinctResult(mt *mtest.T, actualResult []interface{}, expectedResult interface{}) {
func verifyDistinctResult(
mt *mtest.T,
got bson.RawArray,
want interface{},
) {
mt.Helper()

if expectedResult == nil {
if got == nil {
prestonvasquez marked this conversation as resolved.
Show resolved Hide resolved
return
}

for i, expected := range expectedResult.(bson.A) {
actual := actualResult[i]
iExpected := getIntFromInterface(expected)
iActual := getIntFromInterface(actual)
for i, iwant := range want.(bson.A) {
gotRawValue := got.Index(uint(i))

if iExpected != nil {
assert.NotNil(mt, iActual, "expected nil but got %v", iActual)
assert.Equal(mt, *iExpected, *iActual, "expected value %v but got %v", *iExpected, *iActual)
continue
iwantType, iwantBytes, err := bson.MarshalValue(iwant)
assert.NoError(mt, err)

wantRawValue := bson.RawValue{
Type: iwantType,
Value: iwantBytes,
}

assert.Equal(mt, expected, actual, "expected value %v but got %v", expected, actual)
assert.EqualValues(mt, wantRawValue, gotRawValue, "expected value %v but got %v", wantRawValue, gotRawValue)
}
}

Expand Down Expand Up @@ -1636,7 +1646,11 @@ func verifyCursorResult(mt *mtest.T, cur *mongo.Cursor, result interface{}) {
}
}

func verifySingleResult(mt *mtest.T, actualResult *mongo.SingleResult, expectedResult interface{}) {
func verifySingleResult(
mt *mtest.T,
actualResult *mongo.SingleResult[bson.Raw],
expectedResult interface{},
) {
mt.Helper()

if expectedResult == nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/integration/mock_find_test.go
Expand Up @@ -19,7 +19,7 @@ import (

// finder is an object that implements FindOne and Find.
type finder interface {
FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult
FindOne(ctx context.Context, filter interface{}, opts ...*options.FindOneOptions) *mongo.SingleResult[bson.Raw]
Find(context.Context, interface{}, ...*options.FindOptions) (*mongo.Cursor, error)
}

Expand All @@ -31,7 +31,7 @@ type mockFinder struct {
}

// FindOne mocks a findOne operation using NewSingleResultFromDocument.
func (mf *mockFinder) FindOne(_ context.Context, _ interface{}, _ ...*options.FindOneOptions) *mongo.SingleResult {
func (mf *mockFinder) FindOne(_ context.Context, _ interface{}, _ ...*options.FindOneOptions) *mongo.SingleResult[bson.Raw] {
return mongo.NewSingleResultFromDocument(mf.docs[0], mf.err, mf.registry)
}

Expand Down
4 changes: 3 additions & 1 deletion internal/integration/sessions_test.go
Expand Up @@ -619,7 +619,9 @@ func extractReturnError(returnValues []reflect.Value) error {
switch converted := errVal.Interface().(type) {
case error:
return converted
case *mongo.SingleResult:
case *mongo.SingleResult[bson.Raw]:
return converted.Err()
case *mongo.SingleResult[bson.RawArray]:
return converted.Err()
default:
return nil
Expand Down
2 changes: 1 addition & 1 deletion internal/integration/unified/admin_helpers.go
Expand Up @@ -67,7 +67,7 @@ func performDistinctWorkaround(ctx context.Context) error {
commandFn := func(ctx context.Context, client *mongo.Client) error {
for _, coll := range entities(ctx).collections() {
newColl := client.Database(coll.Database().Name()).Collection(coll.Name())
_, err := newColl.Distinct(ctx, "x", bson.D{})
err := newColl.Distinct(ctx, "x", bson.D{}).Err()
if err != nil {
ns := fmt.Sprintf("%s.%s", coll.Database().Name(), coll.Name())
return fmt.Errorf("error running distinct for collection %q: %w", ns, err)
Expand Down
12 changes: 7 additions & 5 deletions internal/integration/unified/collection_operation_execution.go
Expand Up @@ -531,15 +531,17 @@ func executeDistinct(ctx context.Context, operation *operation) (*operationResul
return nil, newMissingArgumentError("filter")
}

res, err := coll.Distinct(ctx, fieldName, filter, opts)
if err != nil {
res := coll.Distinct(ctx, fieldName, filter, opts)
if err := res.Err(); err != nil {
return newErrorResult(err), nil
}
_, rawRes, err := bson.MarshalValue(res)

arr, err := res.Raw()
if err != nil {
return nil, fmt.Errorf("error converting Distinct result to raw BSON: %w", err)
return newErrorResult(err), nil
}
return newValueResult(bson.TypeArray, rawRes, nil), nil

return newValueResult(bson.TypeArray, arr, nil), nil
}

func executeDropIndex(ctx context.Context, operation *operation) (*operationResult, error) {
Expand Down
4 changes: 2 additions & 2 deletions internal/integration/unified_spec_test.go
Expand Up @@ -259,8 +259,8 @@ func runSpecTestCase(mt *mtest.T, test *testCase, testFile testFile) {
if mtest.ClusterTopologyKind() == mtest.Sharded && test.Description == "distinct" {
err := runCommandOnAllServers(func(mongosClient *mongo.Client) error {
coll := mongosClient.Database(mt.DB.Name()).Collection(mt.Coll.Name())
_, err := coll.Distinct(context.Background(), "x", bson.D{})
return err

return coll.Distinct(context.Background(), "x", bson.D{}).Err()
})
assert.Nil(mt, err, "error running distinct against all mongoses: %v", err)
}
Expand Down
8 changes: 4 additions & 4 deletions mongo/client_encryption.go
Expand Up @@ -144,7 +144,7 @@ func (ce *ClientEncryption) CreateEncryptedCollection(ctx context.Context,

// AddKeyAltName adds a keyAltName to the keyAltNames array of the key document in the key vault collection with the
// given UUID (BSON binary subtype 0x04). Returns the previous version of the key document.
func (ce *ClientEncryption) AddKeyAltName(ctx context.Context, id bson.Binary, keyAltName string) *SingleResult {
func (ce *ClientEncryption) AddKeyAltName(ctx context.Context, id bson.Binary, keyAltName string) *SingleResult[bson.Raw] {
filter := bsoncore.NewDocumentBuilder().AppendBinary("_id", id.Subtype, id.Data).Build()
keyAltNameDoc := bsoncore.NewDocumentBuilder().AppendString("keyAltNames", keyAltName).Build()
update := bsoncore.NewDocumentBuilder().AppendDocument("$addToSet", keyAltNameDoc).Build()
Expand Down Expand Up @@ -333,14 +333,14 @@ func (ce *ClientEncryption) DeleteKey(ctx context.Context, id bson.Binary) (*Del
}

// GetKeyByAltName returns a key document in the key vault collection with the given keyAltName.
func (ce *ClientEncryption) GetKeyByAltName(ctx context.Context, keyAltName string) *SingleResult {
func (ce *ClientEncryption) GetKeyByAltName(ctx context.Context, keyAltName string) *SingleResult[bson.Raw] {
filter := bsoncore.NewDocumentBuilder().AppendString("keyAltNames", keyAltName).Build()
return ce.keyVaultColl.FindOne(ctx, filter)
}

// GetKey finds a single key document with the given UUID (BSON binary subtype 0x04). Returns the result of the
// internal find() operation on the key vault collection.
func (ce *ClientEncryption) GetKey(ctx context.Context, id bson.Binary) *SingleResult {
func (ce *ClientEncryption) GetKey(ctx context.Context, id bson.Binary) *SingleResult[bson.Raw] {
filter := bsoncore.NewDocumentBuilder().AppendBinary("_id", id.Subtype, id.Data).Build()
return ce.keyVaultColl.FindOne(ctx, filter)
}
Expand All @@ -353,7 +353,7 @@ func (ce *ClientEncryption) GetKeys(ctx context.Context) (*Cursor, error) {

// RemoveKeyAltName removes a keyAltName from the keyAltNames array of the key document in the key vault collection with
// the given UUID (BSON binary subtype 0x04). Returns the previous version of the key document.
func (ce *ClientEncryption) RemoveKeyAltName(ctx context.Context, id bson.Binary, keyAltName string) *SingleResult {
func (ce *ClientEncryption) RemoveKeyAltName(ctx context.Context, id bson.Binary, keyAltName string) *SingleResult[bson.Raw] {
filter := bsoncore.NewDocumentBuilder().AppendBinary("_id", id.Subtype, id.Data).Build()
update := bson.A{bson.D{{"$set", bson.D{{"keyAltNames", bson.D{{"$cond", bson.A{bson.D{{"$eq",
bson.A{"$keyAltNames", bson.A{keyAltName}}}}, "$$REMOVE", bson.D{{"$filter",
Expand Down
2 changes: 1 addition & 1 deletion mongo/client_examples_test.go
Expand Up @@ -398,7 +398,7 @@ func ExampleConnect_stableAPI() {
coll := serverAPIStrictClient.Database("db").Collection("coll")
// Fails with error: (APIStrictError) Provided apiStrict:true, but the
// command distinct is not in API Version 1
_, err = coll.Distinct(context.TODO(), "distinct", bson.D{})
err = coll.Distinct(context.TODO(), "distinct", bson.D{}).Err()
log.Println(err)

// ServerAPIOptions can be declared with a DeprecationErrors option.
Expand Down