Revision control

/* 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 Account
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 {
public let enabled: [String]
public 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([
"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
}
}