Skip to content

Commit

Permalink
Merge pull request #1208 from WalletConnect/feature/notify-sqlite
Browse files Browse the repository at this point in the history
[Notify] SQLite database for subscriptions and messages
  • Loading branch information
flypaper0 authored Nov 6, 2023
2 parents cea55e2 + bd40f8e commit cedc0dc
Show file tree
Hide file tree
Showing 28 changed files with 641 additions and 67 deletions.
66 changes: 66 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/Database.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Database"
BuildableName = "Database"
BlueprintName = "Database"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Database"
BuildableName = "Database"
BlueprintName = "Database"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
4 changes: 3 additions & 1 deletion Example/IntegrationTests/Push/NotifyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@ final class NotifyTests: XCTestCase {
keychainStorage: keychain,
environment: .sandbox)
let keyserverURL = URL(string: "https://keys.walletconnect.com")!
let sqlite = try! MemorySqlite()
// Note:- prod project_id do not exists on staging, we can use gmDappProjectId
let client = NotifyClientFactory.create(projectId: InputConfig.gmDappProjectId,
keyserverURL: keyserverURL,
keyserverURL: keyserverURL,
sqlite: sqlite,
logger: notifyLogger,
keyValueStorage: keyValueStorage,
keychainStorage: keychain,
Expand Down
2 changes: 1 addition & 1 deletion Example/WalletApp/ApplicationLayer/LoggingService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ final class LoggingService {
SentrySDK.capture(error: LoggingError.networking(log.aggregated))
case .warn(let log):
// Example of setting level to warning
var event = Event(level: .warning)
let event = Event(level: .warning)
event.message = SentryMessage(formatted: log.aggregated)
SentrySDK.capture(event: event)
default:
Expand Down
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ let package = Package(
path: "Sources/Web3Wallet"),
.target(
name: "WalletConnectNotify",
dependencies: ["WalletConnectPairing", "WalletConnectPush", "WalletConnectIdentity", "WalletConnectSigner"],
dependencies: ["WalletConnectPairing", "WalletConnectPush", "WalletConnectIdentity", "WalletConnectSigner", "Database"],
path: "Sources/WalletConnectNotify"),
.target(
name: "WalletConnectPush",
Expand Down Expand Up @@ -130,6 +130,9 @@ let package = Package(
.target(
name: "WalletConnectVerify",
dependencies: ["WalletConnectUtils", "WalletConnectNetworking"]),
.target(
name: "Database",
dependencies: []),
.target(
name: "WalletConnectModal",
dependencies: ["QRCode", "WalletConnectSign"],
Expand Down
46 changes: 46 additions & 0 deletions Sources/Database/DiskSqlite.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Foundation
import SQLite3

public final class DiskSqlite: Sqlite {

private let path: String

private var db: OpaquePointer?

public init(path: String) {
self.path = path
}

public func openDatabase() throws {
guard sqlite3_open_v2(path, &db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_FULLMUTEX, nil) == SQLITE_OK else {
throw SQLiteError.openDatabase(path: path)
}
}

public func query<Row: SqliteRow>(sql: String) throws -> [Row] {
var queryStatement: OpaquePointer?
guard sqlite3_prepare_v2(db, sql, -1, &queryStatement, nil) == SQLITE_OK else {
throw SQLiteError.queryPrepare(statement: sql)
}
var rows: [Row] = []
while sqlite3_step(queryStatement) == SQLITE_ROW {
let decoder = SqliteRowDecoder(statement: queryStatement)
guard let row = try? Row(decoder: decoder) else { continue }
rows.append(row)
}
sqlite3_finalize(queryStatement)
return rows
}

public func execute(sql: String) throws {
var error: UnsafeMutablePointer<CChar>?
guard sqlite3_exec(db, sql, nil, nil, &error) == SQLITE_OK else {
let message = error.map { String(cString: $0) }
throw SQLiteError.exec(error: message)
}
}

public func closeConnection() {
sqlite3_close(db)
}
}
44 changes: 44 additions & 0 deletions Sources/Database/MemorySqlite.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import Foundation
import SQLite3

public final class MemorySqlite: Sqlite {

private var db: OpaquePointer?

public init() throws {
guard sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_FULLMUTEX, nil) == SQLITE_OK else {
throw SQLiteError.openDatabaseMemory
}
}

public func openDatabase() throws {
// No op
}

public func query<Row: SqliteRow>(sql: String) throws -> [Row] {
var queryStatement: OpaquePointer?
guard sqlite3_prepare_v2(db, sql, -1, &queryStatement, nil) == SQLITE_OK else {
throw SQLiteError.queryPrepare(statement: sql)
}
var rows: [Row] = []
while sqlite3_step(queryStatement) == SQLITE_ROW {
let decoder = SqliteRowDecoder(statement: queryStatement)
guard let row = try? Row(decoder: decoder) else { continue }
rows.append(row)
}
sqlite3_finalize(queryStatement)
return rows
}

public func execute(sql: String) throws {
var error: UnsafeMutablePointer<CChar>?
guard sqlite3_exec(db, sql, nil, nil, &error) == SQLITE_OK else {
let message = error.map { String(cString: $0) }
throw SQLiteError.exec(error: message)
}
}

public func closeConnection() {
// No op
}
}
50 changes: 50 additions & 0 deletions Sources/Database/SQLiteQuery.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Foundation

public struct SqliteQuery {

public static func replace(table: String, rows: [SqliteRow]) throws -> String {
var values: [String] = []

for row in rows {
values.append(row.encode().values
.map { "'\($0.value)'" }
.joined(separator: ", "))
}

guard let first = rows.first else {
throw Errors.rowsNotFound
}

let formattedArguments = first.encode().values
.map { $0.argument }
.joined(separator: ", ")

let formattedValues = values
.map { "(\($0))" }
.joined(separator: ",\n")

return """
REPLACE INTO \(table) (\(formattedArguments)) VALUES
\(formattedValues);
"""
}

public static func select(table: String) -> String {
return "SELECT * FROM \(table);"
}

public static func select(table: String, where argument: String, equals value: String) -> String {
return "SELECT * FROM \(table) WHERE \(argument) = '\(value)';"
}

public static func delete(table: String, where argument: String, equals value: String) -> String {
return "DELETE FROM \(table) WHERE \(argument) = '\(value)';"
}
}

extension SqliteQuery {

enum Errors: Error {
case rowsNotFound
}
}
20 changes: 20 additions & 0 deletions Sources/Database/Sqlite.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation
import SQLite3

public protocol Sqlite {

/// Opening A New Database Connection
func openDatabase() throws

/// Evaluate an SQL Statement
/// - Parameter sql: SQL query
/// - Returns: Table rows array
func query<Row: SqliteRow>(sql: String) throws -> [Row]

/// One-Step query execution
/// - Parameter sql: SQL query
func execute(sql: String) throws

/// Closing A Database Connection
func closeConnection()
}
11 changes: 11 additions & 0 deletions Sources/Database/SqliteError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

public enum SQLiteError: Error {
case openDatabase(path: String)
case openDatabaseMemory
case queryPrepare(statement: String)
case exec(error: String?)
case decodeString(index: Int32)
case stringIsNotBase64
case stringIsNotTimestamp
}
12 changes: 12 additions & 0 deletions Sources/Database/SqliteRow.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation

public protocol SqliteRow {

/// SqliteRow initialization
/// - Parameter decoder: SqliteRowDecoder instance
init(decoder: SqliteRowDecoder) throws

/// SqliteRow encoding
/// - Returns: SqliteRowEncoder instance
func encode() -> SqliteRowEncoder
}
51 changes: 51 additions & 0 deletions Sources/Database/SqliteRowDecoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import Foundation
import SQLite3

public class SqliteRowDecoder {

private let statement: OpaquePointer?

init(statement: OpaquePointer?) {
self.statement = statement
}

/// Decode string from column at index
/// - Parameter index: Column index
/// - Returns: Decoded string
public func decodeString(at index: Int32) throws -> String {
guard let raw = sqlite3_column_text(statement, index) else {
throw SQLiteError.decodeString(index: index)
}
return String(cString: raw)
}

/// Decode bool from column at index
/// - Parameter index: Column index
/// - Returns: Decoded bool
public func decodeBool(at index: Int32) throws -> Bool {
let string = try decodeString(at: index)
return (string as NSString).boolValue
}

/// Decode codable object from column at index
/// - Parameter index: Column index
/// - Returns: Decoded codable object
public func decodeCodable<T: Codable>(at index: Int32) throws -> T {
let string = try decodeString(at: index)
guard let data = Data(base64Encoded: string) else {
throw SQLiteError.stringIsNotBase64
}
return try JSONDecoder().decode(T.self, from: data)
}

/// Decode date from column at index
/// - Parameter index: Column index
/// - Returns: Decoded date
public func decodeDate(at index: Int32) throws -> Date {
let string = try decodeString(at: index)
guard let interval = TimeInterval(string) else {
throw SQLiteError.stringIsNotTimestamp
}
return Date(timeIntervalSince1970: interval)
}
}
33 changes: 33 additions & 0 deletions Sources/Database/SqliteRowEncoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Foundation

public struct SqliteRowEncoder {
struct Value {
let argument: String
let value: String
}

var values: [Value] = []

public init() { }

public mutating func encodeString(_ value: String, for argument: String) {
let value = Value(argument: argument, value: value)
values.append(value)
}

public mutating func encodeDate(_ value: Date, for argument: String) {
let value = Value(argument: argument, value: String(value.timeIntervalSince1970))
values.append(value)
}

public mutating func encodeCodable<T: Codable>(_ value: T, for argument: String) {
let data = try! JSONEncoder().encode(value)
let value = Value(argument: argument, value: data.base64EncodedString())
values.append(value)
}

public mutating func encodeBool(_ value: Bool, for argument: String) {
let value = Value(argument: argument, value: String(value))
values.append(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ final class NotifyAccountProvider {
case currentAccountNotFound
}

private var currentAccount: Account?
private(set) var currentAccount: Account?

func setAccount(_ account: Account) {
self.currentAccount = account
Expand Down
Loading

0 comments on commit cedc0dc

Please sign in to comment.