Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions Sources/MongoSwift/MongoError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,16 @@ public enum MongoError {
/// A description of the error.
public let message: String

/// A document providing more information about the write error (e.g. details pertaining to document
/// validation).
public let details: BSONDocument?

// swiftlint:disable:next nesting
private enum CodingKeys: String, CodingKey {
case code
case codeName
case message = "errmsg"
case details = "errInfo"
}

// TODO: can remove this once SERVER-36755 is resolved
Expand All @@ -175,13 +180,15 @@ public enum MongoError {
self.code = try container.decode(ServerErrorCode.self, forKey: .code)
self.message = try container.decode(String.self, forKey: .message)
self.codeName = try container.decodeIfPresent(String.self, forKey: .codeName) ?? ""
self.details = try container.decodeIfPresent(BSONDocument.self, forKey: .details)
}

// TODO: can remove this once SERVER-36755 is resolved
internal init(code: ServerErrorCode, codeName: String, message: String) {
internal init(code: ServerErrorCode, codeName: String, message: String, details: BSONDocument?) {
self.code = code
self.codeName = codeName
self.message = message
self.details = details
}
}

Expand Down Expand Up @@ -251,12 +258,17 @@ public enum MongoError {
/// The index of the request that errored.
public let index: Int

/// A document providing more information about the write error (e.g. details pertaining to document
/// validation).
public let details: BSONDocument?

// swiftlint:disable:next nesting
private enum CodingKeys: String, CodingKey {
case code
case codeName
case message = "errmsg"
case index
case details = "errInfo"
}

// TODO: can remove this once SERVER-36755 is resolved
Expand All @@ -266,14 +278,16 @@ public enum MongoError {
self.message = try container.decode(String.self, forKey: .message)
self.index = try container.decode(Int.self, forKey: .index)
self.codeName = try container.decodeIfPresent(String.self, forKey: .codeName) ?? ""
self.details = try container.decodeIfPresent(BSONDocument.self, forKey: .details)
}

// TODO: can remove this once SERVER-36755 is resolved
internal init(code: ServerErrorCode, codeName: String, message: String, index: Int) {
internal init(code: ServerErrorCode, codeName: String, message: String, index: Int, details: BSONDocument?) {
self.code = code
self.codeName = codeName
self.message = message
self.index = index
self.details = details
}
}
}
Expand Down Expand Up @@ -496,7 +510,8 @@ internal func convertBulkWriteError(_ error: Error) -> Error {
return MongoError.WriteFailure(
code: firstFailure.code,
codeName: firstFailure.codeName,
message: firstFailure.message
message: firstFailure.message,
details: firstFailure.details
)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/MongoSwiftSync/Exports.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated using Sourcery 1.3.4 — https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 1.6.0 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

// Re-export the BSON library
Expand Down
10 changes: 6 additions & 4 deletions Sources/TestsCommon/CommonTestUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -637,9 +637,10 @@ extension MongoError.WriteFailure {
public static func new(
code: MongoError.ServerErrorCode,
codeName: String,
message: String
message: String,
details: BSONDocument?
) -> MongoError.WriteFailure {
MongoError.WriteFailure(code: code, codeName: codeName, message: message)
MongoError.WriteFailure(code: code, codeName: codeName, message: message, details: details)
}
}

Expand Down Expand Up @@ -684,9 +685,10 @@ extension MongoError.BulkWriteFailure {
code: MongoError.ServerErrorCode,
codeName: String,
message: String,
index: Int
index: Int,
details: BSONDocument?
) -> MongoError.BulkWriteFailure {
MongoError.BulkWriteFailure(code: code, codeName: codeName, message: message, index: index)
MongoError.BulkWriteFailure(code: code, codeName: codeName, message: message, index: index, details: details)
}
}

Expand Down
10 changes: 9 additions & 1 deletion Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Generated using Sourcery 1.3.4 — https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 1.6.0 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT

@testable import BSONTests
Expand Down Expand Up @@ -248,6 +248,13 @@ extension OptionsTests {
]
}

extension ProseTests {
static var allTests = [
("testWriteConcernErrorDetailsExposed", testWriteConcernErrorDetailsExposed),
("testWriteErrorDetailsExposed", testWriteErrorDetailsExposed),
]
}

extension ReadConcernTests {
static var allTests = [
("testReadConcernType", testReadConcernType),
Expand Down Expand Up @@ -420,6 +427,7 @@ XCTMain([
testCase(MongoCursorTests.allTests),
testCase(MongoDatabaseTests.allTests),
testCase(OptionsTests.allTests),
testCase(ProseTests.allTests),
testCase(ReadConcernTests.allTests),
testCase(ReadPreferenceOperationTests.allTests),
testCase(ReadPreferenceTests.allTests),
Expand Down
85 changes: 85 additions & 0 deletions Tests/MongoSwiftSyncTests/CrudTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -550,3 +550,88 @@ private class UpdateTest: CrudTest {
try self.verifyUpdateResult(result)
}
}

final class ProseTests: MongoSwiftTestCase {
func testWriteConcernErrorDetailsExposed() throws {
try withTestNamespace { client, _, coll in
guard try client.supportsFailCommand() else {
print("Skipping WriteConcernError details test due to client not supporting fail command")
return
}

let errInfo: BSONDocument = [
"writeConcern": [
"w": 2,
"wtimeout": 0,
"provenance": "clientSupplied"
]
]
let wce: BSONDocument = [
"code": 100,
"codeName": "unsatisfiableWriteConcern",
"errmsg": "Not enough data-bearing nodes",
"errInfo": .document(errInfo)
]

let failPoint = FailPoint.failCommand(
failCommands: ["insert"],
mode: .times(1),
writeConcernError: wce
)
defer { failPoint.disable(using: client) }
try failPoint.enable(using: client)

let doc: BSONDocument = ["x": 1]
expect(try coll.insertOne(doc))
.to(throwError { (error: MongoError.WriteError) in
expect(error.writeConcernFailure?.details).to(equal(errInfo))
})
}
}

func testWriteErrorDetailsExposed() throws {
try withTestNamespace { client, db, collection in
guard try client.serverVersionIsInRange("5.0", nil) else {
print("Skipping WriteError details test due to unsupported server version")
return
}

try collection.drop()

let validator: BSONDocument = [
"create": "test",
"validator": [
"x": ["$type": "string"]
]
]
let options = CreateCollectionOptions(validator: validator)
_ = try db.createCollection(collection.name, options: options)

let monitor = client.addCommandMonitor()
try monitor.captureEvents {
expect(try collection.insertOne(["x": 1]))
.to(throwError { (error: MongoError.WriteError) in
expect(error.writeFailure?.code).to(equal(121))
expect(error.writeFailure?.details).toNot(beNil())

guard let errorDetails = error.writeFailure?.details else {
XCTFail("writeFailure did not include details field")
return
}
guard let eventDetails = monitor.commandSucceededEvents(withNames: ["insert"])
.first?
.reply["writeErrors"]?
.arrayValue?
.first?
.documentValue?["errInfo"]?
.documentValue
else {
XCTFail("commandSucceededEvent did not include errInfo document")
return
}
expect(errorDetails).to(equal(eventDetails))
})
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,13 @@ final class MongoCollection_BulkWriteTests: MongoSwiftTestCase {
// Expect a duplicate key error (11000)
let expectedError = MongoError.BulkWriteError.new(
writeFailures: [
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1)
MongoError.BulkWriteFailure.new(
code: 11000,
codeName: "DuplicateKey",
message: "",
index: 1,
details: nil
)
],
writeConcernFailure: nil,
otherError: nil,
Expand Down
8 changes: 4 additions & 4 deletions Tests/MongoSwiftSyncTests/MongoCollectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ final class MongoCollectionTests: MongoSwiftTestCase {

// error code 11000: DuplicateKey
let expectedError = MongoError.WriteError.new(
writeFailure: MongoError.WriteFailure.new(code: 11000, codeName: "DuplicateKey", message: ""),
writeFailure: MongoError.WriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", details: nil),
writeConcernFailure: nil,
errorLabels: nil
)
Expand Down Expand Up @@ -185,7 +185,7 @@ final class MongoCollectionTests: MongoSwiftTestCase {

let expectedResultOrdered = BulkWriteResult.new(insertedCount: 1, insertedIDs: [0: newDoc1["_id"]!])
let expectedErrorsOrdered = [
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1)
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1, details: nil)
]

let expectedErrorOrdered = MongoError.BulkWriteError.new(
Expand All @@ -199,8 +199,8 @@ final class MongoCollectionTests: MongoSwiftTestCase {
expect(try self.coll.insertMany([newDoc1, docId1, newDoc2, docId2])).to(throwError(expectedErrorOrdered))

let expectedErrors = [
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1),
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 3)
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1, details: nil),
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 3, details: nil)
]
let expectedResult = BulkWriteResult.new(
insertedCount: 2,
Expand Down