Skip to content

Commit ea257f6

Browse files
SWIFT-1163 MongoConnectionString options support (#707)
1 parent f22e1fd commit ea257f6

File tree

8 files changed

+735
-91
lines changed

8 files changed

+735
-91
lines changed

Sources/MongoSwift/MongoConnectionString.swift

Lines changed: 567 additions & 52 deletions
Large diffs are not rendered by default.

Sources/MongoSwift/ReadConcern.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public struct ReadConcern: Codable {
4848
}
4949

5050
/// Initialize a new `ReadConcern` with a `String`.
51-
fileprivate init(_ level: String?) {
51+
internal init(_ level: String?) {
5252
self.level = level
5353
}
5454

Sources/MongoSwift/ReadPreference.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public struct ReadPreference: Equatable {
193193
self.maxStalenessSeconds = nil
194194
}
195195

196-
private init(_ mode: Mode, tagSets: [BSONDocument]?, maxStalenessSeconds: Int?) throws {
196+
internal init(_ mode: Mode, tagSets: [BSONDocument]?, maxStalenessSeconds: Int?) throws {
197197
if let maxStaleness = maxStalenessSeconds {
198198
guard maxStaleness >= MONGOC_SMALLEST_MAX_STALENESS_SECONDS else {
199199
throw MongoError.InvalidArgumentError(

Sources/MongoSwift/WriteConcern.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@ public struct WriteConcern: Codable {
4242
case custom(String)
4343
// swiftlint:enable line_length
4444

45+
internal init(_ string: String) throws {
46+
switch string {
47+
case "majority":
48+
self = .majority
49+
case let other:
50+
if let n = Int(string) {
51+
self = .number(n)
52+
} else {
53+
self = .custom(other)
54+
}
55+
}
56+
}
57+
4558
public init(from decoder: Decoder) throws {
4659
let container = try decoder.singleValueContainer()
4760
if let string = try? container.decode(String.self) {

Tests/MongoSwiftTests/ConnectionStringTests.swift

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -63,37 +63,31 @@ private struct TestCredential: Decodable {
6363
// Tests we skip because we don't support the specified behavior.
6464
let skipUnsupported: [String: [String]] = [
6565
"valid-auth.json": ["mongodb-cr"], // we don't support MONGODB-CR authentication
66+
"compression-options.json": ["multiple compressors are parsed correctly"], // requires Snappy, see SWIFT-894
6667
"connection-pool-options.json": [
67-
// we don't support maxIdleTimeMS.
68+
// we don't support maxIdleTimeMS
6869
"too low maxidletimems causes a warning",
6970
"non-numeric maxidletimems causes a warning",
7071
"valid connection pool options are parsed correctly",
71-
// We don't support minPoolSize.
72+
// we don't support minPoolSize
7273
"minpoolsize=0 does not error",
73-
// We don't allow maxPoolSize=0, see SWIFT-1339.
74+
// we don't allow maxPoolSize=0, see SWIFT-1339
7475
"maxpoolsize=0 does not error"
7576
],
76-
"connection-options.json": ["*"], // requires maxIdleTimeMS
77-
"compression-options.json": ["*"], // requires Snappy, see SWIFT-894
78-
"valid-db-with-dotted-name.json": ["*"] // libmongoc doesn't allow db names in dotted form in the URI
77+
"connection-options.json": [
78+
"valid connection and timeout options are parsed correctly" // we don't support maxIdleTimeMS
79+
],
80+
"single-threaded-options.json": ["*"], // we don't support single threaded options
81+
"valid-options.json": ["option names are normalized to lowercase"] // we don't support MONGODB-CR authentication
7982
]
8083

8184
// Tests for options that are not yet supported on MongoConnectionString.
82-
// TODO: SWIFT-1163: remove this.
85+
// TODO: delete this when MongoConnectionString is fully implemented
8386
let skipMongoConnectionStringUnsupported: [String: [String]] = [
84-
// connection string tests
85-
"invalid-uris.json": ["option"],
86-
"valid-auth.json": ["at-signs in options aren't part of the userinfo"],
87-
"valid-options.json": ["*"],
87+
// TODO: SWIFT-1174: unskip these tests
88+
"valid-db-with-dotted-name.json": ["*"],
8889
"valid-unix_socket-absolute.json": ["*"],
89-
"valid-unix_socket-relative.json": ["*"],
90-
"valid-warnings.json": ["*"],
91-
92-
// URI options tests
93-
"concern-options.json": ["*"],
94-
"read-preference-options.json": ["*"],
95-
"single-threaded-options.json": ["*"],
96-
"srv-options.json": ["*"]
90+
"valid-unix_socket-relative.json": ["*"]
9791
]
9892

9993
func shouldSkip(file: String, test: String) -> Bool {
@@ -189,7 +183,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
189183
expect(connStr.description).to(equal("mongodb://localhost:27017"))
190184
}
191185

192-
// TODO: Test string conversion behavior after changing to MongoConnectionString
186+
// TODO: SWIFT-1416 Test string conversion behavior after changing to MongoConnectionString
193187
func testAppNameOption() throws {
194188
// option is set correctly from options struct
195189
let opts1 = MongoClientOptions(appName: "MyApp")
@@ -205,7 +199,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
205199
expect(connStr3.appName).to(equal("MyApp"))
206200
}
207201

208-
// TODO: Test string conversion behavior after changing to MongoConnectionString
202+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
209203
func testReplSetOption() throws {
210204
// option is set correctly from options struct
211205
var opts = MongoClientOptions(replicaSet: "rs0")
@@ -255,7 +249,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
255249
}
256250
}
257251

258-
// TODO: Test string conversion behavior after changing to MongoConnectionString
252+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
259253
func testHeartbeatFrequencyMSOption() throws {
260254
// option is set correctly from options struct
261255
let opts = MongoClientOptions(heartbeatFrequencyMS: 50000)
@@ -313,7 +307,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
313307
fileprivate init() {}
314308
}
315309

316-
// TODO: Test string conversion behavior after changing to MongoConnectionString
310+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
317311
func testHeartbeatFrequencyMSWithMonitoring() throws {
318312
guard MongoSwiftTestCase.topologyType == .single else {
319313
print(unsupportedTopologyMessage(testName: self.name))
@@ -341,7 +335,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
341335
expect(difference).to(beCloseTo(2.0, within: 0.2))
342336
}
343337

344-
// TODO: Test string conversion behavior after changing to MongoConnectionString
338+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
345339
func testServerSelectionTimeoutMS() throws {
346340
// option is set correctly from options struct
347341
let opts = MongoClientOptions(serverSelectionTimeoutMS: 10000)
@@ -381,7 +375,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
381375
)).to(throwError(errorType: MongoError.InvalidArgumentError.self))
382376
}
383377

384-
// TODO: Test string conversion behavior after changing to MongoConnectionString
378+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
385379
func testServerSelectionTimeoutMSWithCommand() throws {
386380
let opts = MongoClientOptions(serverSelectionTimeoutMS: 1000)
387381
try self.withTestClient("mongodb://localhost:27099", options: opts) { client in
@@ -394,7 +388,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
394388
}
395389
}
396390

397-
// TODO: Test string conversion behavior after changing to MongoConnectionString
391+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
398392
func testLocalThresholdMSOption() throws {
399393
// option is set correctly from options struct
400394
let opts = MongoClientOptions(localThresholdMS: 100)
@@ -433,7 +427,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
433427
)).to(throwError(errorType: MongoError.InvalidArgumentError.self))
434428
}
435429

436-
// TODO: Test string conversion behavior after changing to MongoConnectionString
430+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
437431
func testConnectTimeoutMSOption() throws {
438432
// option is set correctly from options struct
439433
let opts = MongoClientOptions(connectTimeoutMS: 100)
@@ -482,23 +476,24 @@ final class ConnectionStringTests: MongoSwiftTestCase {
482476
)).to(throwError(errorType: MongoError.InvalidArgumentError.self))
483477
}
484478

485-
// TODO: Test string conversion behavior after changing to MongoConnectionString
479+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
486480
func testUnsupportedOptions() throws {
487481
// options we know of but don't support yet should throw errors
488-
expect(try ConnectionString("mongodb://localhost:27017/?minPoolSize=10"))
482+
expect(try MongoConnectionString(throwsIfInvalid: "mongodb://localhost:27017/?minPoolSize=10"))
489483
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
490-
expect(try ConnectionString("mongodb://localhost:27017/?maxIdleTimeMS=10"))
484+
expect(try MongoConnectionString(throwsIfInvalid: "mongodb://localhost:27017/?maxIdleTimeMS=10"))
491485
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
492-
expect(try ConnectionString("mongodb://localhost:27017/?waitQueueMultiple=10"))
486+
expect(try MongoConnectionString(throwsIfInvalid: "mongodb://localhost:27017/?waitQueueMultiple=10"))
493487
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
494-
expect(try ConnectionString("mongodb://localhost:27017/?waitQueueTimeoutMS=10"))
488+
expect(try MongoConnectionString(throwsIfInvalid: "mongodb://localhost:27017/?waitQueueTimeoutMS=10"))
495489
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
496490

497-
// options we don't know of should be ignored
498-
expect(try ConnectionString("mongodb://localhost:27017/?blah=10")).toNot(throwError())
491+
// options we don't know of should throw errors
492+
expect(try MongoConnectionString(throwsIfInvalid: "mongodb://localhost:27017/?blah=10"))
493+
.to(throwError(errorType: MongoError.InvalidArgumentError.self))
499494
}
500495

501-
// TODO: Test string conversion behavior after changing to MongoConnectionString
496+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionString
502497
func testCompressionOptions() throws {
503498
// zlib level validation
504499
expect(try Compressor.zlib(level: -2)).to(throwError(errorType: MongoError.InvalidArgumentError.self))
@@ -544,7 +539,7 @@ final class ConnectionStringTests: MongoSwiftTestCase {
544539
// warning but does not provide us access to the see the full specified list.
545540
}
546541

547-
// TODO: Test string conversion behavior after changing to MongoConnectionString
542+
// TODO: SWIFT-1416: Test string conversion behavior after changing to MongoConnectionStringtes
548543
func testInvalidOptionsCombinations() throws {
549544
// tlsInsecure and conflicting options
550545
var opts = MongoClientOptions(tlsAllowInvalidCertificates: true, tlsInsecure: true)

Tests/Specs/connection-string/tests/valid-warnings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,4 @@
9595
"options": null
9696
}
9797
]
98-
}
98+
}

Tests/Specs/uri-options/tests/README.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ The ``valid`` and ``warning`` fields are boolean in order to keep the tests
3838
flexible. We are not concerned with asserting the format of specific error or
3939
warnings messages strings.
4040

41+
Under normal circumstances, it should not be necessary to specify both
42+
``valid: false`` and ``warning: true``. Typically, a URI test case will either
43+
yield an error (e.g. options conflict) or a warning (e.g. invalid type or value
44+
for an option), but not both.
45+
4146
Use as unit tests
4247
=================
4348

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
{
2+
"tests": [
3+
{
4+
"description": "SRV URI with custom srvServiceName",
5+
"uri": "mongodb+srv://test22.test.build.10gen.cc/?srvServiceName=customname",
6+
"valid": true,
7+
"warning": false,
8+
"hosts": null,
9+
"auth": null,
10+
"options": {
11+
"srvServiceName": "customname"
12+
}
13+
},
14+
{
15+
"description": "Non-SRV URI with custom srvServiceName",
16+
"uri": "mongodb://example.com/?srvServiceName=customname",
17+
"valid": false,
18+
"warning": false,
19+
"hosts": null,
20+
"auth": null,
21+
"options": {}
22+
},
23+
{
24+
"description": "SRV URI with srvMaxHosts",
25+
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2",
26+
"valid": true,
27+
"warning": false,
28+
"hosts": null,
29+
"auth": null,
30+
"options": {
31+
"srvMaxHosts": 2
32+
}
33+
},
34+
{
35+
"description": "SRV URI with negative integer for srvMaxHosts",
36+
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=-1",
37+
"valid": true,
38+
"warning": true,
39+
"hosts": null,
40+
"auth": null,
41+
"options": {}
42+
},
43+
{
44+
"description": "SRV URI with invalid type for srvMaxHosts",
45+
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=foo",
46+
"valid": true,
47+
"warning": true,
48+
"hosts": null,
49+
"auth": null,
50+
"options": {}
51+
},
52+
{
53+
"description": "Non-SRV URI with srvMaxHosts",
54+
"uri": "mongodb://example.com/?srvMaxHosts=2",
55+
"valid": false,
56+
"warning": false,
57+
"hosts": null,
58+
"auth": null,
59+
"options": {}
60+
},
61+
{
62+
"description": "SRV URI with positive srvMaxHosts and replicaSet",
63+
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&replicaSet=foo",
64+
"valid": false,
65+
"warning": false,
66+
"hosts": null,
67+
"auth": null,
68+
"options": {}
69+
},
70+
{
71+
"description": "SRV URI with positive srvMaxHosts and loadBalanced=true",
72+
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=true",
73+
"valid": false,
74+
"warning": false,
75+
"hosts": null,
76+
"auth": null,
77+
"options": {}
78+
},
79+
{
80+
"description": "SRV URI with positive srvMaxHosts and loadBalanced=false",
81+
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=2&loadBalanced=false",
82+
"valid": true,
83+
"warning": false,
84+
"hosts": null,
85+
"auth": null,
86+
"options": {
87+
"loadBalanced": false,
88+
"srvMaxHosts": 2
89+
}
90+
},
91+
{
92+
"description": "SRV URI with srvMaxHosts=0 and replicaSet",
93+
"uri": "mongodb+srv://test1.test.build.10gen.cc/?srvMaxHosts=0&replicaSet=foo",
94+
"valid": true,
95+
"warning": false,
96+
"hosts": null,
97+
"auth": null,
98+
"options": {
99+
"replicaSet": "foo",
100+
"srvMaxHosts": 0
101+
}
102+
},
103+
{
104+
"description": "SRV URI with srvMaxHosts=0 and loadBalanced=true",
105+
"uri": "mongodb+srv://test3.test.build.10gen.cc/?srvMaxHosts=0&loadBalanced=true",
106+
"valid": true,
107+
"warning": false,
108+
"hosts": null,
109+
"auth": null,
110+
"options": {
111+
"loadBalanced": true,
112+
"srvMaxHosts": 0
113+
}
114+
}
115+
]
116+
}

0 commit comments

Comments
 (0)