Skip to content

Commit 8836684

Browse files
committed
feat: Added Api Key support and new tests.
1 parent 1a8cd7f commit 8836684

File tree

15 files changed

+178
-29
lines changed

15 files changed

+178
-29
lines changed

Sources/SQLiteCloud/Core/SQLiteCloudConfig+Internal.swift

+8-2
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ extension SQLiteCloudConfig {
3232
var sqConfig: SQCloudConfig {
3333
var sqConfig = SQCloudConfig()
3434
sqConfig.family = family.rawValue
35-
sqConfig.username = (username as NSString).utf8String
36-
sqConfig.password = (password as NSString).utf8String
35+
if let username,
36+
let password {
37+
sqConfig.username = (username as NSString).utf8String
38+
sqConfig.password = (password as NSString).utf8String
39+
}
40+
if let apiKey {
41+
sqConfig.api_key = (apiKey as NSString).utf8String
42+
}
3743
sqConfig.password_hashed = passwordHashed
3844
sqConfig.non_linearizable = nonlinearizable
3945
sqConfig.timeout = Int32(timeout)

Sources/SQLiteCloud/Core/SQLiteCloudConfig.swift

+127-11
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ import Foundation
2828
public struct SQLiteCloudConfig: Sendable {
2929
public let hostname: String
3030
public let port: Port
31-
public let username: String
32-
public let password: String
31+
public let username: String?
32+
public let password: String?
33+
public let apiKey: String?
3334
public let family: Family
3435
public let passwordHashed: Bool
3536
public let nonlinearizable: Bool
@@ -49,6 +50,52 @@ public struct SQLiteCloudConfig: Sendable {
4950
public let clientCertificate: String?
5051
public let clientCertificateKey: String?
5152

53+
public init(hostname: String,
54+
apiKey: String,
55+
port: Port = .default,
56+
family: Family = .ipv4,
57+
passwordHashed: Bool = false,
58+
nonlinearizable: Bool = false,
59+
timeout: Int = 0,
60+
compression: Bool = false,
61+
zerotext: Bool = false,
62+
memory: Bool = false,
63+
dbCreate: Bool = false,
64+
insecure: Bool = false,
65+
noblob: Bool = false,
66+
isReadonlyConnection: Bool = false,
67+
maxData: Int = 0,
68+
maxRows: Int = 0,
69+
maxRowset: Int = 0,
70+
dbname: String? = nil,
71+
rootCertificate: String? = nil,
72+
clientCertificate: String? = nil,
73+
clientCertificateKey: String? = nil) {
74+
self.init(hostname: hostname,
75+
username: nil,
76+
password: nil,
77+
apiKey: apiKey,
78+
port: port,
79+
family: family,
80+
passwordHashed: passwordHashed,
81+
nonlinearizable: nonlinearizable,
82+
timeout: timeout,
83+
compression: compression,
84+
zerotext: zerotext,
85+
memory: memory,
86+
dbCreate: dbCreate,
87+
insecure: insecure,
88+
noblob: noblob,
89+
isReadonlyConnection: isReadonlyConnection,
90+
maxData: maxData,
91+
maxRows: maxRows,
92+
maxRowset: maxRowset,
93+
dbname: dbname,
94+
rootCertificate: rootCertificate,
95+
clientCertificate: clientCertificate,
96+
clientCertificateKey: clientCertificateKey)
97+
}
98+
5299
public init(hostname: String,
53100
username: String,
54101
password: String,
@@ -71,10 +118,59 @@ public struct SQLiteCloudConfig: Sendable {
71118
rootCertificate: String? = nil,
72119
clientCertificate: String? = nil,
73120
clientCertificateKey: String? = nil) {
121+
self.init(hostname: hostname,
122+
username: username,
123+
password: password,
124+
apiKey: nil,
125+
port: port,
126+
family: family,
127+
passwordHashed: passwordHashed,
128+
nonlinearizable: nonlinearizable,
129+
timeout: timeout,
130+
compression: compression,
131+
zerotext: zerotext,
132+
memory: memory,
133+
dbCreate: dbCreate,
134+
insecure: insecure,
135+
noblob: noblob,
136+
isReadonlyConnection: isReadonlyConnection,
137+
maxData: maxData,
138+
maxRows: maxRows,
139+
maxRowset: maxRowset,
140+
dbname: dbname,
141+
rootCertificate: rootCertificate,
142+
clientCertificate: clientCertificate,
143+
clientCertificateKey: clientCertificateKey)
144+
}
145+
146+
private init(hostname: String,
147+
username: String?,
148+
password: String?,
149+
apiKey: String?,
150+
port: Port = .default,
151+
family: Family = .ipv4,
152+
passwordHashed: Bool = false,
153+
nonlinearizable: Bool = false,
154+
timeout: Int = 0,
155+
compression: Bool = false,
156+
zerotext: Bool = false,
157+
memory: Bool = false,
158+
dbCreate: Bool = false,
159+
insecure: Bool = false,
160+
noblob: Bool = false,
161+
isReadonlyConnection: Bool = false,
162+
maxData: Int = 0,
163+
maxRows: Int = 0,
164+
maxRowset: Int = 0,
165+
dbname: String? = nil,
166+
rootCertificate: String? = nil,
167+
clientCertificate: String? = nil,
168+
clientCertificateKey: String? = nil) {
74169
self.hostname = hostname
75170
self.port = port
76171
self.username = username
77172
self.password = password
173+
self.apiKey = apiKey
78174
self.family = family
79175
self.passwordHashed = passwordHashed
80176
self.nonlinearizable = nonlinearizable
@@ -94,7 +190,7 @@ public struct SQLiteCloudConfig: Sendable {
94190
self.clientCertificate = clientCertificate
95191
self.clientCertificateKey = clientCertificateKey
96192
}
97-
193+
98194
public init?(connectionString: String) {
99195
guard let url = URL(string: connectionString) else { return nil }
100196

@@ -103,22 +199,38 @@ public struct SQLiteCloudConfig: Sendable {
103199

104200
/// sqlitecloud://user:[email protected]:port/dbname?timeout=10&key2=value2&key3=value3.
105201
public init?(connectionURL: URL) {
106-
guard let username = connectionURL.user else { return nil }
107-
guard let password = connectionURL.password else { return nil }
108202
guard let hostname = connectionURL.host else { return nil }
109-
let port = connectionURL.port.map { Port.custom(portNumber: $0) } ?? .default
110-
203+
111204
let urlComponents = URLComponents(string: connectionURL.absoluteString)
112205
let queryItems = urlComponents?.queryItems
113-
206+
207+
// There are 2 kind of possibile credentials types
208+
// - based on an apikey
209+
// - based on the username and the password combo
210+
// We need to search for this credential info in the connection string.
211+
// First we check for apikey, if fails we fallback on the user/pass combo.
212+
213+
if let apiKey = UrlParser.parse(items: queryItems, name: "apikey") {
214+
self.username = nil
215+
self.password = nil
216+
self.apiKey = apiKey
217+
} else {
218+
guard let username = connectionURL.user else { return nil }
219+
guard let password = connectionURL.password else { return nil }
220+
221+
self.username = username
222+
self.password = password
223+
self.apiKey = nil
224+
}
225+
226+
let port = connectionURL.port.map { Port.custom(portNumber: $0) } ?? .default
227+
114228
// external
115229
self.hostname = hostname
116230
self.port = port
117231
self.isReadonlyConnection = UrlParser.parse(items: queryItems, name: "readonly")
118232

119233
// in config
120-
self.username = username
121-
self.password = password
122234
self.dbname = urlComponents?.path.replacingOccurrences(of: "/", with: "")
123235
self.family = Family(rawValue: UrlParser.parse(items: queryItems, name: "family")) ?? .ipv4
124236
self.passwordHashed = UrlParser.parse(items: queryItems, name: "passwordHashed")
@@ -143,7 +255,11 @@ public struct SQLiteCloudConfig: Sendable {
143255

144256
extension SQLiteCloudConfig {
145257
var connectionString: String {
146-
"sqlitecloud://\(username):****@\(hostname):\(port.number)/\(dbname ?? .empty)"
258+
if let apiKey {
259+
"sqlitecloud://\(hostname):\(port.number)/\(dbname ?? .empty)?apikey=\(apiKey)"
260+
} else {
261+
"sqlitecloud://\(username ?? ""):****@\(hostname):\(port.number)/\(dbname ?? .empty)"
262+
}
147263
}
148264
}
149265

Sources/libsqcloud.xcframework/Info.plist

+8-8
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,32 @@
1010
<key>HeadersPath</key>
1111
<string>Headers</string>
1212
<key>LibraryIdentifier</key>
13-
<string>ios-arm64_x86_64-simulator</string>
13+
<string>ios-arm64</string>
1414
<key>LibraryPath</key>
1515
<string>libsqcloud.a</string>
1616
<key>SupportedArchitectures</key>
1717
<array>
1818
<string>arm64</string>
19-
<string>x86_64</string>
2019
</array>
2120
<key>SupportedPlatform</key>
2221
<string>ios</string>
23-
<key>SupportedPlatformVariant</key>
24-
<string>simulator</string>
2522
</dict>
2623
<dict>
2724
<key>BinaryPath</key>
2825
<string>libsqcloud.a</string>
2926
<key>HeadersPath</key>
3027
<string>Headers</string>
3128
<key>LibraryIdentifier</key>
32-
<string>ios-arm64</string>
29+
<string>macos-arm64_x86_64</string>
3330
<key>LibraryPath</key>
3431
<string>libsqcloud.a</string>
3532
<key>SupportedArchitectures</key>
3633
<array>
3734
<string>arm64</string>
35+
<string>x86_64</string>
3836
</array>
3937
<key>SupportedPlatform</key>
40-
<string>ios</string>
38+
<string>macos</string>
4139
</dict>
4240
<dict>
4341
<key>BinaryPath</key>
@@ -64,7 +62,7 @@
6462
<key>HeadersPath</key>
6563
<string>Headers</string>
6664
<key>LibraryIdentifier</key>
67-
<string>macos-arm64_x86_64</string>
65+
<string>ios-arm64_x86_64-simulator</string>
6866
<key>LibraryPath</key>
6967
<string>libsqcloud.a</string>
7068
<key>SupportedArchitectures</key>
@@ -73,7 +71,9 @@
7371
<string>x86_64</string>
7472
</array>
7573
<key>SupportedPlatform</key>
76-
<string>macos</string>
74+
<string>ios</string>
75+
<key>SupportedPlatformVariant</key>
76+
<string>simulator</string>
7777
</dict>
7878
</array>
7979
<key>CFBundlePackageType</key>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Sources/libtls.xcframework/Info.plist

+5-5
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,32 @@
2929
<key>HeadersPath</key>
3030
<string>Headers</string>
3131
<key>LibraryIdentifier</key>
32-
<string>ios-arm64</string>
32+
<string>macos-arm64_x86_64</string>
3333
<key>LibraryPath</key>
3434
<string>libtls.a</string>
3535
<key>SupportedArchitectures</key>
3636
<array>
3737
<string>arm64</string>
38+
<string>x86_64</string>
3839
</array>
3940
<key>SupportedPlatform</key>
40-
<string>ios</string>
41+
<string>macos</string>
4142
</dict>
4243
<dict>
4344
<key>BinaryPath</key>
4445
<string>libtls.a</string>
4546
<key>HeadersPath</key>
4647
<string>Headers</string>
4748
<key>LibraryIdentifier</key>
48-
<string>macos-arm64_x86_64</string>
49+
<string>ios-arm64</string>
4950
<key>LibraryPath</key>
5051
<string>libtls.a</string>
5152
<key>SupportedArchitectures</key>
5253
<array>
5354
<string>arm64</string>
54-
<string>x86_64</string>
5555
</array>
5656
<key>SupportedPlatform</key>
57-
<string>macos</string>
57+
<string>ios</string>
5858
</dict>
5959
<dict>
6060
<key>BinaryPath</key>
440 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Tests/SQLiteCloudTests/Integrations/SQLiteCloudTests+Connection.swift

+27-2
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ final class SQLiteCloudTests_Connection: XCTestCase {
3030
private var hostname: String = .empty
3131
private var username: String = .empty
3232
private var password: String = .empty
33+
private var apiKey: String = .empty
3334

3435
override func setUpWithError() throws {
3536
let secrets = try Secrets.load()
3637
hostname = secrets.hostname
3738
username = secrets.username
3839
password = secrets.password
40+
apiKey = secrets.apiKey
3941
}
4042

4143
func test_connect_withValidCredentials_shouldConnectWithoutError() async throws {
@@ -48,7 +50,18 @@ final class SQLiteCloudTests_Connection: XCTestCase {
4850
XCTFail("An error was thrown: \(error)")
4951
}
5052
}
51-
53+
54+
func test_connect_withValidCredentialAPIKey_shouldConnectWithoutError() async throws {
55+
let config = SQLiteCloudConfig(hostname: hostname, apiKey: apiKey)
56+
let cloud = SQLiteCloud(config: config)
57+
58+
do {
59+
try await cloud.connect()
60+
} catch {
61+
XCTFail("An error was thrown: \(error)")
62+
}
63+
}
64+
5265
func test_connect_withInvalidCredentials_shouldThrowError() async throws {
5366
let config = SQLiteCloudConfig(hostname: hostname, username: "!!invalid!!", password: "!!credentials!!")
5467
let cloud = SQLiteCloud(config: config)
@@ -60,7 +73,19 @@ final class SQLiteCloudTests_Connection: XCTestCase {
6073
XCTAssert(error is SQLiteCloudError)
6174
}
6275
}
63-
76+
77+
func test_connect_withInvalidCredentialAPIKey_shouldThrowError() async throws {
78+
let config = SQLiteCloudConfig(hostname: hostname, apiKey: "!!invalid!!")
79+
let cloud = SQLiteCloud(config: config)
80+
81+
do {
82+
try await cloud.connect()
83+
XCTFail("No errors were thrown.")
84+
} catch {
85+
XCTAssert(error is SQLiteCloudError)
86+
}
87+
}
88+
6489
func test_disconnect_withValidConnection_shouldDisconnectWithoutError() async throws {
6590
let config = SQLiteCloudConfig(hostname: hostname, username: username, password: password)
6691
let cloud = SQLiteCloud(config: config)

Tests/SQLiteCloudTests/Secrets/Secrets.swift

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct Secrets: Decodable {
2929
let hostname: String
3030
let username: String
3131
let password: String
32+
let apiKey: String
3233
}
3334

3435
extension Secrets {
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"hostname": "<your host>",
33
"username": "<your user>",
4-
"password": "<your pass>"
4+
"password": "<your pass>",
5+
"apiKey": "<your apikey>"
56
}

0 commit comments

Comments
 (0)