1// Copyright (C) MongoDB, Inc. 2017-present. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 7package unified 8 9import ( 10 "context" 11 "fmt" 12 13 "go.mongodb.org/mongo-driver/bson" 14 "go.mongodb.org/mongo-driver/mongo" 15) 16 17type Operation struct { 18 Name string `bson:"name"` 19 Object string `bson:"object"` 20 Arguments bson.Raw `bson:"arguments"` 21 ExpectedError *ExpectedError `bson:"expectError"` 22 ExpectedResult *bson.RawValue `bson:"expectResult"` 23 ResultEntityID *string `bson:"saveResultAsEntity"` 24} 25 26// Execute runs the operation and verifies the returned result and/or error. If the result needs to be saved as 27// an entity, it also updates the EntityMap associated with ctx to do so. 28func (op *Operation) Execute(ctx context.Context) error { 29 res, err := op.run(ctx) 30 if err != nil { 31 return fmt.Errorf("execution failed: %v", err) 32 } 33 34 if err := VerifyOperationError(ctx, op.ExpectedError, res); err != nil { 35 return fmt.Errorf("error verification failed: %v", err) 36 } 37 38 if op.ExpectedResult != nil { 39 if err := VerifyOperationResult(ctx, *op.ExpectedResult, res); err != nil { 40 return fmt.Errorf("result verification failed: %v", err) 41 } 42 } 43 return nil 44} 45 46func (op *Operation) run(ctx context.Context) (*OperationResult, error) { 47 if op.Object == "testRunner" { 48 // testRunner operations don't have results or expected errors, so we use NewEmptyResult to fake a result. 49 return NewEmptyResult(), executeTestRunnerOperation(ctx, op) 50 } 51 52 // Special handling for the "session" field because it applies to all operations. 53 if id, ok := op.Arguments.Lookup("session").StringValueOK(); ok { 54 sess, err := Entities(ctx).Session(id) 55 if err != nil { 56 return nil, err 57 } 58 ctx = mongo.NewSessionContext(ctx, sess) 59 60 // Set op.Arguments to a new document that has the "session" field removed so individual operations do 61 // not have to account for it. 62 op.Arguments = RemoveFieldsFromDocument(op.Arguments, "session") 63 } 64 65 switch op.Name { 66 // Session operations 67 case "abortTransaction": 68 return executeAbortTransaction(ctx, op) 69 case "commitTransaction": 70 return executeCommitTransaction(ctx, op) 71 case "endSession": 72 // The EndSession() method doesn't return a result, so we return a non-nil empty result. 73 return NewEmptyResult(), executeEndSession(ctx, op) 74 case "startTransaction": 75 return executeStartTransaction(ctx, op) 76 case "withTransaction": 77 // executeWithTransaction internally verifies results/errors for each operation, so it doesn't return a result. 78 return NewEmptyResult(), executeWithTransaction(ctx, op) 79 80 // Client operations 81 case "createChangeStream": 82 return executeCreateChangeStream(ctx, op) 83 case "listDatabases": 84 return executeListDatabases(ctx, op) 85 86 // Database operations 87 case "createCollection": 88 return executeCreateCollection(ctx, op) 89 case "dropCollection": 90 return executeDropCollection(ctx, op) 91 case "listCollections": 92 return executeListCollections(ctx, op) 93 case "listCollectionNames": 94 return executeListCollectionNames(ctx, op) 95 case "runCommand": 96 return executeRunCommand(ctx, op) 97 98 // Collection operations 99 case "aggregate": 100 return executeAggregate(ctx, op) 101 case "bulkWrite": 102 return executeBulkWrite(ctx, op) 103 case "countDocuments": 104 return executeCountDocuments(ctx, op) 105 case "createIndex": 106 return executeCreateIndex(ctx, op) 107 case "deleteOne": 108 return executeDeleteOne(ctx, op) 109 case "deleteMany": 110 return executeDeleteMany(ctx, op) 111 case "distinct": 112 return executeDistinct(ctx, op) 113 case "estimatedDocumentCount": 114 return executeEstimatedDocumentCount(ctx, op) 115 case "find": 116 return executeFind(ctx, op) 117 case "findOneAndUpdate": 118 return executeFindOneAndUpdate(ctx, op) 119 case "insertMany": 120 return executeInsertMany(ctx, op) 121 case "insertOne": 122 return executeInsertOne(ctx, op) 123 case "replaceOne": 124 return executeReplaceOne(ctx, op) 125 case "updateOne": 126 return executeUpdateOne(ctx, op) 127 case "updateMany": 128 return executeUpdateMany(ctx, op) 129 130 // GridFS operations 131 case "delete": 132 return executeBucketDelete(ctx, op) 133 case "download": 134 return executeBucketDownload(ctx, op) 135 case "upload": 136 return executeBucketUpload(ctx, op) 137 138 // Change Stream operations 139 case "iterateUntilDocumentOrError": 140 return executeIterateUntilDocumentOrError(ctx, op) 141 default: 142 return nil, fmt.Errorf("unrecognized entity operation %q", op.Name) 143 } 144} 145