Skip to content

Commit ecae6b9

Browse files
committed
Create AccountInfo interface
1 parent adb044d commit ecae6b9

File tree

6 files changed

+209
-14
lines changed

6 files changed

+209
-14
lines changed

Package.resolved

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import PackageDescription
55

66
let package = Package(
77
name: "MatrixSQLiteStore",
8+
platforms: [
9+
.iOS(.v15), .tvOS(.v15),
10+
.watchOS(.v8), .macOS(.v12)
11+
],
812
products: [
913
// Products define the executables and libraries a package produces, and make them visible to other packages.
1014
.library(
@@ -14,13 +18,17 @@ let package = Package(
1418
dependencies: [
1519
// Dependencies declare other packages that this package depends on.
1620
// .package(url: /* package url */, from: "1.0.0"),
21+
.package(name: "MatrixCore", path: "/Users/kloenk/Developer/Xcode/MatrixCore"),
22+
.package(url: "https://github.com/groue/GRDB.swift.git", from: "5.23.0")
1723
],
1824
targets: [
19-
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
20-
// Targets can depend on other targets in this package, and on products in packages this package depends on.
2125
.target(
2226
name: "MatrixSQLiteStore",
23-
dependencies: []),
27+
dependencies: [
28+
.product(name: "MatrixCore", package: "MatrixCore"),
29+
.product(name: "MatrixClient", package: "MatrixCore"),
30+
.product(name: "GRDB", package: "GRDB.swift")
31+
]),
2432
.testTarget(
2533
name: "MatrixSQLiteStoreTests",
2634
dependencies: ["MatrixSQLiteStore"]),
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Finn Behrens on 16.04.22.
6+
//
7+
8+
import Foundation
9+
import GRDB
10+
import MatrixClient
11+
import MatrixCore
12+
13+
public struct MatrixSQLAccountInfo: MatrixStoreAccountInfo {
14+
public var name: String
15+
16+
public var displayName: String?
17+
18+
public var mxID: MatrixUserIdentifier
19+
20+
public var homeServer: MatrixHomeserver
21+
22+
public var accessToken: String?
23+
}
24+
25+
extension MatrixSQLAccountInfo: Codable, FetchableRecord, PersistableRecord {
26+
public static var databaseTableName: String = "account"
27+
28+
enum CodingKeys: String, CodingKey {
29+
case name
30+
case displayName
31+
case mxID = "id"
32+
case homeServer = "homeserver"
33+
}
34+
}
Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,78 @@
1+
2+
import Foundation
3+
import GRDB
4+
import MatrixClient
5+
import MatrixCore
6+
17
public struct MatrixSQLiteStore {
2-
public private(set) var text = "Hello, World!"
8+
var dbWriter: DatabaseWriter
9+
10+
public init(_ dbWriter: DatabaseWriter) throws {
11+
self.dbWriter = dbWriter
12+
try migrator.migrate(dbWriter)
13+
}
14+
15+
// TODO: lazy instead of computed?
16+
private var migrator: DatabaseMigrator {
17+
var migrator = DatabaseMigrator()
18+
19+
#if DEBUG
20+
// Speed up development by nuking the database when migrations change
21+
// See https://github.com/groue/GRDB.swift/blob/master/Documentation/Migrations.md#the-erasedatabaseonschemachange-option
22+
migrator.eraseDatabaseOnSchemaChange = true
23+
#endif
24+
25+
migrator.registerMigration("createAccount") { db in
26+
try db.create(table: "account") { t in
27+
t.column("id", .text).notNull().indexed().primaryKey().unique()
28+
t.column("name", .text).notNull()
29+
t.column("displayName", .text)
30+
t.column("homeserver", .text).notNull()
31+
}
32+
}
33+
34+
return migrator
35+
}
36+
}
37+
38+
extension MatrixSQLiteStore: MatrixStore {
39+
public func saveAccountInfo(account: MatrixSQLAccountInfo) async throws {
40+
try await dbWriter.write { [account] db in
41+
try account.save(db)
42+
try account.saveToKeychain()
43+
}
44+
}
45+
46+
public func getAccountInfo(accountID: MatrixUserIdentifier) async throws -> MatrixSQLAccountInfo {
47+
let account = try await dbWriter.read { db in
48+
try MatrixSQLAccountInfo.fetchOne(db, key: accountID.FQMXID!)
49+
}
50+
51+
guard var account = account else {
52+
throw SQLError.missingData
53+
}
54+
55+
account.accessToken = try MatrixSQLAccountInfo.getFromKeychain(account: accountID)
56+
57+
return account
58+
}
59+
60+
public func getAccountInfos() async throws -> [MatrixSQLAccountInfo] {
61+
let accounts = try await dbWriter.read { db in
62+
try MatrixSQLAccountInfo.fetchAll(db)
63+
}
64+
65+
return try accounts.map { account in
66+
var account = account
67+
account.accessToken = try MatrixSQLAccountInfo.getFromKeychain(account: account.mxID)
68+
return account
69+
}
70+
}
71+
}
372

4-
public init() {
73+
public extension MatrixSQLiteStore {
74+
enum SQLError: Error {
75+
case invalidType
76+
case missingData
577
}
678
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Finn Behrens on 16.04.22.
6+
//
7+
8+
import GRDB
9+
import MatrixClient
10+
@testable import MatrixSQLiteStore
11+
import XCTest
12+
13+
class AccountInfoTests: XCTestCase {
14+
func testGet() async throws {
15+
let dbQueue = DatabaseQueue()
16+
let store = try MatrixSQLiteStore(dbQueue)
17+
18+
let id = MatrixUserIdentifier(rawValue: "@test:example.com")!
19+
let data = MatrixSQLAccountInfo(name: "test", mxID: id, homeServer: MatrixHomeserver(string: "https://example.com")!, accessToken: "secret")
20+
21+
defer {
22+
try! data.deleteFromKeychain()
23+
}
24+
25+
try await store.saveAccountInfo(account: data)
26+
27+
let account = try await store.getAccountInfo(accountID: id)
28+
29+
XCTAssertEqual(account.accessToken, data.accessToken)
30+
}
31+
32+
func testGetAll() async throws {
33+
let dbQueue = DatabaseQueue()
34+
35+
let store = try MatrixSQLiteStore(dbQueue)
36+
let homeserver = MatrixHomeserver(string: "https://example.com")!
37+
38+
let id1 = MatrixUserIdentifier(locapart: "test1", domain: "example.com")
39+
let data1 = MatrixSQLAccountInfo(name: "test1", mxID: id1, homeServer: homeserver, accessToken: "secret2")
40+
41+
let id2 = MatrixUserIdentifier(locapart: "test2", domain: "example.com")
42+
let data2 = MatrixSQLAccountInfo(name: "test2", mxID: id2, homeServer: homeserver, accessToken: "secret2")
43+
44+
defer {
45+
try! data1.deleteFromKeychain()
46+
try! data2.deleteFromKeychain()
47+
}
48+
49+
try await store.saveAccountInfo(account: data1)
50+
try await store.saveAccountInfo(account: data2)
51+
52+
let accounts = try await store.getAccountInfos()
53+
54+
XCTAssertEqual(accounts.count, 2)
55+
56+
XCTAssertEqual(accounts[0].name, data1.name)
57+
XCTAssertEqual(accounts[1].name, data2.name)
58+
59+
XCTAssertEqual(accounts[0].accessToken, data1.accessToken)
60+
XCTAssertEqual(accounts[1].accessToken, data2.accessToken)
61+
62+
XCTAssertEqual(accounts[0].mxID, data1.mxID)
63+
XCTAssertEqual(accounts[1].mxID, data2.mxID)
64+
}
65+
}
Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
import XCTest
21
@testable import MatrixSQLiteStore
2+
import XCTest
33

4-
final class MatrixSQLiteStoreTests: XCTestCase {
5-
func testExample() throws {
6-
// This is an example of a functional test case.
7-
// Use XCTAssert and related functions to verify your tests produce the correct
8-
// results.
9-
XCTAssertEqual(MatrixSQLiteStore().text, "Hello, World!")
10-
}
11-
}
4+
final class MatrixSQLiteStoreTests: XCTestCase {}

0 commit comments

Comments
 (0)