Skip to content

Commit 7a08c6c

Browse files
committed
updated for logging and replication
1 parent 2f77604 commit 7a08c6c

File tree

6 files changed

+257
-28
lines changed

6 files changed

+257
-28
lines changed

CbliteSwiftJsLib/Classes/CollectionManager.swift

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77

88
import Foundation
9-
109
import CouchbaseLiteSwift
1110

1211
enum CollectionError: Error {
@@ -39,7 +38,7 @@ public class CollectionManager {
3938

4039
// MARK: - Helper Functions
4140

42-
private func getCollectionKey(_ collectionName: String,
41+
public func getCollectionKey(_ collectionName: String,
4342
scopeName: String,
4443
databaseName: String) -> String {
4544
return "\(databaseName).\(scopeName).\(collectionName)"
@@ -409,11 +408,4 @@ public class CollectionManager {
409408
}
410409

411410
}
412-
413-
// MARK: Replicator Functions
414-
415-
func replicator(_ replicatorId: String, replicatorConfig: [String: Any], collectionName: String, scopeName: String, databaseName: String) throws {
416-
417-
}
418-
419411
}

CbliteSwiftJsLib/Classes/DatabaseManager.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,8 @@ public class DatabaseManager {
2525

2626
/* change listeners */
2727
var databaseChangeListeners = [String: Any]()
28-
var documentChangeListeners = [String: Any]()
2928
var queryChangeListeners = [String: Any]()
3029

31-
/* replicators tracking */
32-
var replicators = [String: Replicator]()
33-
var replicatorChangeListeners = [String: Any]()
34-
var replicatorDocumentListeners = [String: Any]()
35-
3630
var queryCount: Int = 0
3731
var replicatorCount: Int = 0
3832
var allResultsChunkSize: Int = 0
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//
2+
// LoggingManager.swift
3+
// CbliteSwiftJsLib
4+
//
5+
// Created by Aaron LaBeau on 09/04/24.
6+
//
7+
8+
import Foundation
9+
import CouchbaseLiteSwift
10+
11+
enum LoggingError: Error {
12+
case invalidDomain(message: String)
13+
case invalidLogLevel(message: String)
14+
case invalidConfig(message: String)
15+
}
16+
17+
public class LoggingManager {
18+
19+
// MARK: - Singleton
20+
static let shared = LoggingManager()
21+
22+
// MARK: - Private initializer to prevent external instatiation
23+
private init() {
24+
25+
}
26+
27+
public func setLogLevel(_ logDomain: String, logLevel: NSNumber) throws {
28+
switch logDomain {
29+
case "ALL": Database.log.console.domains = .all
30+
case "DATABASE": Database.log.console.domains = .database
31+
case "NETWORK": Database.log.console.domains = .network
32+
case "QUERY": Database.log.console.domains = .query
33+
case "REPLICATOR": Database.log.console.domains = .replicator
34+
default:
35+
throw LoggingError.invalidDomain(message: "Invalid domain value \(logDomain)")
36+
}
37+
if let logLevelValue = LogLevel(rawValue: UInt8(truncating: logLevel)) {
38+
Database.log.console.level = logLevelValue
39+
} else {
40+
throw LoggingError.invalidLogLevel(message: "Invalid level value \(logLevel)")
41+
}
42+
}
43+
44+
public func setFileLogging(_ databaseName: String,
45+
config: [String: Any]) throws {
46+
guard let database = DatabaseManager.shared.getDatabase(databaseName) else {
47+
throw DatabaseError.invalidDatabaseName(databaseName: databaseName)
48+
}
49+
guard let logLevel = config["level"] as? Int,
50+
let directory = config["directory"] as? String,
51+
!directory.isEmpty else {
52+
throw LoggingError.invalidConfig(message: "Invalid configuration of level or directory")
53+
}
54+
55+
let rawDir = directory.replacingOccurrences(of: "file://", with: "")
56+
let fileLoggingConfig = LogFileConfiguration(directory: rawDir)
57+
58+
if let maxRotateCount = config["maxRotateCount"] as? Int {
59+
fileLoggingConfig.maxRotateCount = maxRotateCount
60+
}
61+
62+
if let maxSize = config["maxSize"] as? UInt64, maxSize > 0 {
63+
fileLoggingConfig.maxSize = maxSize
64+
}
65+
66+
if let usePlaintext = config["usePlaintext"] as? Bool {
67+
fileLoggingConfig.usePlainText = usePlaintext
68+
}
69+
if let logLevelValue = LogLevel(rawValue: UInt8(logLevel)) {
70+
Database.log.file.level = logLevelValue
71+
} else {
72+
throw LoggingError.invalidLogLevel(message: "Invalid level value \(logLevel)")
73+
}
74+
75+
Database.log.file.config = fileLoggingConfig
76+
}
77+
}

CbliteSwiftJsLib/Classes/ReplicatorHelper.swift

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ import CouchbaseLiteSwift
1010

1111
public struct ReplicatorHelper {
1212

13-
public static func replicatorConfigFromJson(_ database: Database, data: [String: Any]) -> ReplicatorConfiguration {
13+
public static func replicatorConfigFromJson(_ data: [String: Any]) throws -> ReplicatorConfiguration {
1414
guard let authenticatorData = data["authenticator"] as? [String: Any],
1515
let target = data["target"] as? [String: Any],
1616
let url = target["url"] as? String,
1717
let replicatorType = data["replicatorType"] as? String,
18-
let continuous = data["continuous"] as? Bool else {
19-
fatalError("Invalid JSON data")
18+
let continuous = data["continuous"] as? Bool,
19+
let collectionConfig = data["collectionConfig"] as? [String: Any] else {
20+
throw ReplicatorError.fatalError(message: "Invalid JSON data")
2021
}
2122

2223
let endpoint = URLEndpoint(url: URL(string: url)!)
23-
var replConfig = ReplicatorConfiguration(database: database, target: endpoint)
24+
var replConfig = ReplicatorConfiguration(target: endpoint)
2425

2526
switch replicatorType {
2627
case "PUSH_AND_PULL":
@@ -30,11 +31,7 @@ public struct ReplicatorHelper {
3031
case "PUSH":
3132
replConfig.replicatorType = .push
3233
default:
33-
fatalError("Invalid replicatorType")
34-
}
35-
36-
if let channels = data["channels"] as? [String] {
37-
replConfig.channels = channels
34+
throw ReplicatorError.fatalError(message: "Invalid replicatorType")
3835
}
3936

4037
replConfig.continuous = continuous
@@ -46,10 +43,42 @@ public struct ReplicatorHelper {
4643
return replConfig
4744
}
4845

49-
private static func replicatorCollectionConfigFromJson(_ data: [String: Any]) -> CollectionConfiguration {
50-
let config = CollectionConfiguration()
51-
52-
return config
46+
private static func replicatorCollectionConfigFromJson(_ data: [String: Any]) throws -> (Set<Collection>, CollectionConfiguration) {
47+
48+
//work on the collections sent in as part of the configuration with an array of collectionName, scopeName, and databaseName
49+
guard let collectionData = data["collections"] as? [[String: String]] else {
50+
throw ReplicatorError.configurationError(message: "collections doesn't include collections in the proper format")
51+
}
52+
guard let config = data["config"] as? [String: Any] else {
53+
throw ReplicatorError.configurationError(message: "ReplicationConfig collection config is incorrect format")
54+
}
55+
56+
var collections: Set<Collection> = []
57+
58+
for collectionItem in collectionData {
59+
guard let collectionName = collectionItem["collectionName"],
60+
let scopeName = collectionItem["scopeName"],
61+
let databaseName = collectionItem["databaseName"] else {
62+
// Handle the case where any required key is missing
63+
throw ReplicatorError.configurationError(message: "Error: collections missing required key in collection data - collectionName, scopeName, or databaseName")
64+
}
65+
guard let collection = try CollectionManager.shared.getCollection(collectionName, scopeName: scopeName, databaseName: databaseName) else {
66+
throw CollectionError.unableToFindCollection(collectionName: collectionName, scopeName: scopeName, databaseName: databaseName)
67+
}
68+
collections.insert(collection)
69+
}
70+
//process the config part of the data
71+
var collectionConfig = CollectionConfiguration()
72+
73+
//get the channels and documentIds to filter for the collections
74+
//these are optional
75+
if let channels = config["channels"] as? [String] {
76+
collectionConfig.channels = channels
77+
}
78+
if let documentIds = config["documentIds"] as? [String] {
79+
collectionConfig.documentIDs = documentIds
80+
}
81+
return (collections, collectionConfig)
5382
}
5483

5584
private static func replicatorAuthenticatorFromConfig(_ config: [String: Any]?) -> Authenticator? {
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//
2+
// ReplicatorManager.swift
3+
// CbliteSwiftJsLib
4+
//
5+
// Created by Aaron LaBeau on 09/04/24.
6+
//
7+
8+
import Foundation
9+
import CouchbaseLiteSwift
10+
11+
enum ReplicatorError: Error {
12+
case configurationError(message: String)
13+
case unableToFindReplicator(replicatorId: String)
14+
case unknownError(message: String)
15+
case fatalError(message: String)
16+
case invalidState(message: String)
17+
}
18+
19+
public class ReplicatorManager {
20+
21+
/* replicators tracking */
22+
var replicators = [String: Replicator]()
23+
var replicatorChangeListeners = [String: Any]()
24+
var replicatorDocumentListeners = [String: Any]()
25+
26+
// MARK: - Singleton
27+
static let shared = ReplicatorManager()
28+
29+
// MARK: - Private initializer to prevent external instatiation
30+
private init() { }
31+
32+
// MARK: - Helper Functions
33+
public func getReplicator(replicatorId: String) -> Replicator? {
34+
return self.replicators[replicatorId]
35+
}
36+
37+
// MARK: Replicator Functions
38+
39+
func replicator(replicatorConfig: [String: Any]) throws -> String {
40+
do {
41+
let id = UUID().uuidString
42+
let config = try ReplicatorHelper.replicatorConfigFromJson(replicatorConfig)
43+
let replicator = Replicator(config: config)
44+
replicators[id] = replicator
45+
return id
46+
} catch {
47+
throw ReplicatorError.fatalError(message: error.localizedDescription)
48+
}
49+
}
50+
51+
func start(replicatorId: String) throws {
52+
if let replicator = getReplicator(replicatorId: replicatorId) {
53+
replicator.start()
54+
} else {
55+
throw ReplicatorError.unableToFindReplicator(replicatorId: replicatorId)
56+
}
57+
}
58+
59+
func stop(replicatorId: String) throws {
60+
if let replicator = getReplicator(replicatorId: replicatorId) {
61+
replicator.stop()
62+
} else {
63+
throw ReplicatorError.unableToFindReplicator(replicatorId: replicatorId)
64+
}
65+
}
66+
67+
func resetCheckpoint(replicatorId: String) throws {
68+
if let replicator = getReplicator(replicatorId: replicatorId) {
69+
let status = replicator.status
70+
let activity = status.activity
71+
if activity == .stopped || activity == .idle {
72+
replicator.start(reset: true)
73+
} else {
74+
throw ReplicatorError.invalidState(message: "replicator is in an invalid state to reset checkpoint: \(activity)")
75+
}
76+
} else {
77+
throw ReplicatorError.unableToFindReplicator(replicatorId: replicatorId)
78+
}
79+
}
80+
81+
func getStatus(replicatorId: String) throws -> [String: Any] {
82+
if let replicator = getReplicator(replicatorId: replicatorId) {
83+
let status = replicator.status
84+
let statusJson = ReplicatorHelper.generateReplicatorStatusJson(status)
85+
return statusJson
86+
} else {
87+
throw ReplicatorError.unableToFindReplicator(replicatorId: replicatorId)
88+
}
89+
}
90+
91+
func cleanUp(replicatorId: String) throws {
92+
if let replicator = getReplicator(replicatorId: replicatorId) {
93+
replicator.stop()
94+
self.replicators.removeValue(forKey: replicatorId)
95+
} else {
96+
throw ReplicatorError.unableToFindReplicator(replicatorId: replicatorId)
97+
}
98+
}
99+
100+
func pendingDocIds(_ replicatorId: String, collectionName: String, scopeName: String, databaseName: String) throws -> Set<String> {
101+
guard let collection = try CollectionManager.shared.getCollection(collectionName, scopeName: scopeName, databaseName: databaseName) else {
102+
throw CollectionError.unableToFindCollection(collectionName: collectionName, scopeName: scopeName, databaseName: databaseName)
103+
}
104+
guard let replicator = self.getReplicator(replicatorId: replicatorId) else {
105+
throw ReplicatorError.unableToFindReplicator(replicatorId: replicatorId)
106+
}
107+
do {
108+
let docIds = try replicator.pendingDocumentIds(collection: collection)
109+
return docIds
110+
} catch {
111+
throw error
112+
}
113+
}
114+
115+
func isDocumentPending(_ documentId: String, replicatorId: String, collectionName: String, scopeName: String, databaseName: String) throws -> Bool {
116+
guard let collection = try CollectionManager.shared.getCollection(collectionName, scopeName: scopeName, databaseName: databaseName) else {
117+
throw CollectionError.unableToFindCollection(collectionName: collectionName, scopeName: scopeName, databaseName: databaseName)
118+
}
119+
guard let replicator = self.getReplicator(replicatorId: replicatorId) else {
120+
throw ReplicatorError.unableToFindReplicator(replicatorId: replicatorId)
121+
}
122+
do {
123+
let isPending = try replicator.isDocumentPending(documentId, collection: collection)
124+
return isPending
125+
} catch {
126+
throw error
127+
}
128+
}
129+
}

Example/Pods/Pods.xcodeproj/project.pbxproj

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

0 commit comments

Comments
 (0)