Revision control

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