/
SyncMeta.swift
149 lines (128 loc) · 4.86 KB
/
SyncMeta.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import Shared
import SwiftyJSON
// Our engine choices need to persist across server changes.
// Note that EngineConfiguration is not enough to evolve an existing meta/global:
// a meta/global generated from this will have different syncIDs and will
// always use this device's engine versions.
open class EngineConfiguration: Equatable {
let enabled: [String]
let declined: [String]
public init(enabled: [String], declined: [String]) {
self.enabled = enabled
self.declined = declined
}
open class func fromJSON(_ json: JSON) -> EngineConfiguration? {
if json.isError() {
return nil
}
if let enabled = jsonsToStrings(json["enabled"].array) {
if let declined = jsonsToStrings(json["declined"].array) {
return EngineConfiguration(enabled: enabled, declined: declined)
}
}
return nil
}
open func toJSON() -> JSON {
let json: [String: AnyObject] = ["enabled": self.enabled as AnyObject, "declined": self.declined as AnyObject]
return JSON(json)
}
}
public func ==(lhs: EngineConfiguration, rhs: EngineConfiguration) -> Bool {
return Set(lhs.enabled) == Set(rhs.enabled)
}
extension EngineConfiguration: CustomStringConvertible {
public var description: String {
return "EngineConfiguration(enabled: \(self.enabled.sorted()), declined: \(self.declined.sorted()))"
}
}
// Equivalent to Android Sync's EngineSettings, but here
// we use them for meta/global itself.
public struct EngineMeta: Equatable {
let version: Int
let syncID: String
public static func fromJSON(_ json: JSON) -> EngineMeta? {
if let syncID = json["syncID"].string {
if let version = json["version"].int {
return EngineMeta(version: version, syncID: syncID)
}
}
return nil
}
public static func mapFromJSON(_ map: [String: JSON]?) -> [String: EngineMeta]? {
if let map = map {
return optFilter(mapValues(map, f: EngineMeta.fromJSON))
}
return nil
}
public func toJSON() -> JSON {
let json: [String: AnyObject] = ["version": self.version as AnyObject, "syncID": self.syncID as AnyObject]
return JSON(json)
}
}
public func ==(lhs: EngineMeta, rhs: EngineMeta) -> Bool {
return (lhs.version == rhs.version) && (lhs.syncID == rhs.syncID)
}
public struct MetaGlobal: Equatable {
let syncID: String
let storageVersion: Int
let engines: [String: EngineMeta]
let declined: [String]
// TODO: is it more useful to support partial globals?
// TODO: how do we return error states here?
public static func fromJSON(_ json: JSON) -> MetaGlobal? {
if json.isError() {
return nil
}
if let syncID = json["syncID"].string {
if let storageVersion = json["storageVersion"].int {
let engines = EngineMeta.mapFromJSON(json["engines"].dictionary) ?? [:]
let declined = json["declined"].array ?? []
return MetaGlobal(syncID: syncID,
storageVersion: storageVersion,
engines: engines,
declined: jsonsToStrings(declined) ?? [])
}
}
return nil
}
public func enginesPayload() -> JSON {
return JSON(mapValues(engines, f: { $0.toJSON() }))
}
// TODO: make a whole record JSON for this.
public func asPayload() -> CleartextPayloadJSON {
let json: JSON = JSON([
"syncID": self.syncID,
"storageVersion": self.storageVersion,
"engines": enginesPayload().dictionaryObject as Any,
"declined": self.declined
])
return CleartextPayloadJSON(json)
}
public func withSyncID(_ syncID: String) -> MetaGlobal {
return MetaGlobal(syncID: syncID, storageVersion: self.storageVersion, engines: self.engines, declined: self.declined)
}
public func engineConfiguration() -> EngineConfiguration {
return EngineConfiguration(enabled: Array(engines.keys), declined: declined)
}
}
public func ==(lhs: MetaGlobal, rhs: MetaGlobal) -> Bool {
return (lhs.syncID == rhs.syncID) &&
(lhs.storageVersion == rhs.storageVersion) &&
optArrayEqual(lhs.declined, rhs: rhs.declined) &&
optDictionaryEqual(lhs.engines, rhs: rhs.engines)
}
/**
* Encapsulates a meta/global, identity-derived keys, and keys.
*/
open class SyncMeta {
let syncKey: KeyBundle
var keys: Keys?
var global: MetaGlobal?
public init(syncKey: KeyBundle) {
self.syncKey = syncKey
}
}