Skip to content

Commit 7312f62

Browse files
SWIFT-1103 Expose details field in WriteFailure/BulkWriteFailure (#687)
1 parent a28aa3c commit 7312f62

File tree

6 files changed

+128
-12
lines changed

6 files changed

+128
-12
lines changed

Sources/MongoSwift/MongoError.swift

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,16 @@ public enum MongoError {
162162
/// A description of the error.
163163
public let message: String
164164

165+
/// A document providing more information about the write error (e.g. details pertaining to document
166+
/// validation).
167+
public let details: BSONDocument?
168+
165169
// swiftlint:disable:next nesting
166170
private enum CodingKeys: String, CodingKey {
167171
case code
168172
case codeName
169173
case message = "errmsg"
174+
case details = "errInfo"
170175
}
171176

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

180186
// TODO: can remove this once SERVER-36755 is resolved
181-
internal init(code: ServerErrorCode, codeName: String, message: String) {
187+
internal init(code: ServerErrorCode, codeName: String, message: String, details: BSONDocument?) {
182188
self.code = code
183189
self.codeName = codeName
184190
self.message = message
191+
self.details = details
185192
}
186193
}
187194

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

261+
/// A document providing more information about the write error (e.g. details pertaining to document
262+
/// validation).
263+
public let details: BSONDocument?
264+
254265
// swiftlint:disable:next nesting
255266
private enum CodingKeys: String, CodingKey {
256267
case code
257268
case codeName
258269
case message = "errmsg"
259270
case index
271+
case details = "errInfo"
260272
}
261273

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

271284
// TODO: can remove this once SERVER-36755 is resolved
272-
internal init(code: ServerErrorCode, codeName: String, message: String, index: Int) {
285+
internal init(code: ServerErrorCode, codeName: String, message: String, index: Int, details: BSONDocument?) {
273286
self.code = code
274287
self.codeName = codeName
275288
self.message = message
276289
self.index = index
290+
self.details = details
277291
}
278292
}
279293
}
@@ -496,7 +510,8 @@ internal func convertBulkWriteError(_ error: Error) -> Error {
496510
return MongoError.WriteFailure(
497511
code: firstFailure.code,
498512
codeName: firstFailure.codeName,
499-
message: firstFailure.message
513+
message: firstFailure.message,
514+
details: firstFailure.details
500515
)
501516
}
502517

Sources/TestsCommon/CommonTestUtils.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -637,9 +637,10 @@ extension MongoError.WriteFailure {
637637
public static func new(
638638
code: MongoError.ServerErrorCode,
639639
codeName: String,
640-
message: String
640+
message: String,
641+
details: BSONDocument?
641642
) -> MongoError.WriteFailure {
642-
MongoError.WriteFailure(code: code, codeName: codeName, message: message)
643+
MongoError.WriteFailure(code: code, codeName: codeName, message: message, details: details)
643644
}
644645
}
645646

@@ -684,9 +685,10 @@ extension MongoError.BulkWriteFailure {
684685
code: MongoError.ServerErrorCode,
685686
codeName: String,
686687
message: String,
687-
index: Int
688+
index: Int,
689+
details: BSONDocument?
688690
) -> MongoError.BulkWriteFailure {
689-
MongoError.BulkWriteFailure(code: code, codeName: codeName, message: message, index: index)
691+
MongoError.BulkWriteFailure(code: code, codeName: codeName, message: message, index: index, details: details)
690692
}
691693
}
692694

Tests/LinuxMain.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,13 @@ extension OptionsTests {
249249
]
250250
}
251251

252+
extension ProseTests {
253+
static var allTests = [
254+
("testWriteConcernErrorDetailsExposed", testWriteConcernErrorDetailsExposed),
255+
("testWriteErrorDetailsExposed", testWriteErrorDetailsExposed),
256+
]
257+
}
258+
252259
extension ReadConcernTests {
253260
static var allTests = [
254261
("testReadConcernType", testReadConcernType),
@@ -421,6 +428,7 @@ XCTMain([
421428
testCase(MongoCursorTests.allTests),
422429
testCase(MongoDatabaseTests.allTests),
423430
testCase(OptionsTests.allTests),
431+
testCase(ProseTests.allTests),
424432
testCase(ReadConcernTests.allTests),
425433
testCase(ReadPreferenceOperationTests.allTests),
426434
testCase(ReadPreferenceTests.allTests),

Tests/MongoSwiftSyncTests/CrudTests.swift

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,3 +550,88 @@ private class UpdateTest: CrudTest {
550550
try self.verifyUpdateResult(result)
551551
}
552552
}
553+
554+
final class ProseTests: MongoSwiftTestCase {
555+
func testWriteConcernErrorDetailsExposed() throws {
556+
try withTestNamespace { client, _, coll in
557+
guard try client.supportsFailCommand() else {
558+
print("Skipping WriteConcernError details test due to client not supporting fail command")
559+
return
560+
}
561+
562+
let errInfo: BSONDocument = [
563+
"writeConcern": [
564+
"w": 2,
565+
"wtimeout": 0,
566+
"provenance": "clientSupplied"
567+
]
568+
]
569+
let wce: BSONDocument = [
570+
"code": 100,
571+
"codeName": "unsatisfiableWriteConcern",
572+
"errmsg": "Not enough data-bearing nodes",
573+
"errInfo": .document(errInfo)
574+
]
575+
576+
let failPoint = FailPoint.failCommand(
577+
failCommands: ["insert"],
578+
mode: .times(1),
579+
writeConcernError: wce
580+
)
581+
defer { failPoint.disable(using: client) }
582+
try failPoint.enable(using: client)
583+
584+
let doc: BSONDocument = ["x": 1]
585+
expect(try coll.insertOne(doc))
586+
.to(throwError { (error: MongoError.WriteError) in
587+
expect(error.writeConcernFailure?.details).to(equal(errInfo))
588+
})
589+
}
590+
}
591+
592+
func testWriteErrorDetailsExposed() throws {
593+
try withTestNamespace { client, db, collection in
594+
guard try client.serverVersionIsInRange("5.0", nil) else {
595+
print("Skipping WriteError details test due to unsupported server version")
596+
return
597+
}
598+
599+
try collection.drop()
600+
601+
let validator: BSONDocument = [
602+
"create": "test",
603+
"validator": [
604+
"x": ["$type": "string"]
605+
]
606+
]
607+
let options = CreateCollectionOptions(validator: validator)
608+
_ = try db.createCollection(collection.name, options: options)
609+
610+
let monitor = client.addCommandMonitor()
611+
try monitor.captureEvents {
612+
expect(try collection.insertOne(["x": 1]))
613+
.to(throwError { (error: MongoError.WriteError) in
614+
expect(error.writeFailure?.code).to(equal(121))
615+
expect(error.writeFailure?.details).toNot(beNil())
616+
617+
guard let errorDetails = error.writeFailure?.details else {
618+
XCTFail("writeFailure did not include details field")
619+
return
620+
}
621+
guard let eventDetails = monitor.commandSucceededEvents(withNames: ["insert"])
622+
.first?
623+
.reply["writeErrors"]?
624+
.arrayValue?
625+
.first?
626+
.documentValue?["errInfo"]?
627+
.documentValue
628+
else {
629+
XCTFail("commandSucceededEvent did not include errInfo document")
630+
return
631+
}
632+
expect(errorDetails).to(equal(eventDetails))
633+
})
634+
}
635+
}
636+
}
637+
}

Tests/MongoSwiftSyncTests/MongoCollection+BulkWriteTests.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,13 @@ final class MongoCollection_BulkWriteTests: MongoSwiftTestCase {
106106
// Expect a duplicate key error (11000)
107107
let expectedError = MongoError.BulkWriteError.new(
108108
writeFailures: [
109-
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1)
109+
MongoError.BulkWriteFailure.new(
110+
code: 11000,
111+
codeName: "DuplicateKey",
112+
message: "",
113+
index: 1,
114+
details: nil
115+
)
110116
],
111117
writeConcernFailure: nil,
112118
otherError: nil,

Tests/MongoSwiftSyncTests/MongoCollectionTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ final class MongoCollectionTests: MongoSwiftTestCase {
8484

8585
// error code 11000: DuplicateKey
8686
let expectedError = MongoError.WriteError.new(
87-
writeFailure: MongoError.WriteFailure.new(code: 11000, codeName: "DuplicateKey", message: ""),
87+
writeFailure: MongoError.WriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", details: nil),
8888
writeConcernFailure: nil,
8989
errorLabels: nil
9090
)
@@ -185,7 +185,7 @@ final class MongoCollectionTests: MongoSwiftTestCase {
185185

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

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

201201
let expectedErrors = [
202-
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1),
203-
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 3)
202+
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 1, details: nil),
203+
MongoError.BulkWriteFailure.new(code: 11000, codeName: "DuplicateKey", message: "", index: 3, details: nil)
204204
]
205205
let expectedResult = BulkWriteResult.new(
206206
insertedCount: 2,

0 commit comments

Comments
 (0)