Revision control

Copy as Markdown

// 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
public class RustSyncTelemetryPing {
public let version: Int
public let uid: String
public let events: [EventInfo]
public let syncs: [SyncInfo]
private static let EMPTY_UID = String(repeating: "0", count: 32)
init(version: Int, uid: String, events: [EventInfo], syncs: [SyncInfo]) {
self.version = version
self.uid = uid
self.events = events
self.syncs = syncs
}
static func empty() -> RustSyncTelemetryPing {
return RustSyncTelemetryPing(version: 1,
uid: EMPTY_UID,
events: [EventInfo](),
syncs: [SyncInfo]())
}
static func fromJSON(jsonObject: [String: Any]) throws -> RustSyncTelemetryPing {
guard let version = jsonObject["version"] as? Int else {
throw TelemetryJSONError.intValueNotFound(
message: "RustSyncTelemetryPing `version` property not found")
}
let events = unwrapFromJSON(jsonObject: jsonObject) { obj in
try EventInfo.fromJSONArray(
jsonArray: obj["events"] as? [[String: Any]] ?? [[String: Any]]())
} ?? [EventInfo]()
let syncs = unwrapFromJSON(jsonObject: jsonObject) { obj in
try SyncInfo.fromJSONArray(
jsonArray: obj["syncs"] as? [[String: Any]] ?? [[String: Any]]())
} ?? [SyncInfo]()
return try RustSyncTelemetryPing(version: version,
uid: stringOrNull(jsonObject: jsonObject,
key: "uid") ?? EMPTY_UID,
events: events, syncs: syncs)
}
public static func fromJSONString(jsonObjectText: String) throws -> RustSyncTelemetryPing {
guard let data = jsonObjectText.data(using: .utf8) else {
throw TelemetryJSONError.invalidJSONString
}
let jsonObject = try JSONSerialization.jsonObject(with: data) as? [String: Any] ?? [String: Any]()
return try fromJSON(jsonObject: jsonObject)
}
}
public class SyncInfo {
public let at: Int64
public let took: Int64
public let engines: [EngineInfo]
public let failureReason: FailureReason?
init(at: Int64, took: Int64, engines: [EngineInfo], failureReason: FailureReason?) {
self.at = at
self.took = took
self.engines = engines
self.failureReason = failureReason
}
static func fromJSON(jsonObject: [String: Any]) throws -> SyncInfo {
guard let at = jsonObject["when"] as? Int64 else {
throw TelemetryJSONError.intValueNotFound(
message: "SyncInfo `when` property not found")
}
let engines = unwrapFromJSON(jsonObject: jsonObject) { obj in
try EngineInfo.fromJSONArray(
jsonArray: obj["engines"] as? [[String: Any]] ?? [[String: Any]]())
} ?? [EngineInfo]()
let failureReason = unwrapFromJSON(jsonObject: jsonObject) { obj in
FailureReason.fromJSON(
jsonObject: obj["failureReason"] as? [String: Any] ?? [String: Any]())
} as? FailureReason
return SyncInfo(at: at,
took: int64OrZero(jsonObject: jsonObject, key: "took"),
engines: engines,
failureReason: failureReason)
}
static func fromJSONArray(jsonArray: [[String: Any]]) throws -> [SyncInfo] {
var result = [SyncInfo]()
for item in jsonArray {
try result.append(fromJSON(jsonObject: item))
}
return result
}
}
public class EngineInfo {
public let name: String
public let at: Int64
public let took: Int64
public let incoming: IncomingInfo?
public let outgoing: [OutgoingInfo]
public let failureReason: FailureReason?
public let validation: ValidationInfo?
init(
name: String,
at: Int64,
took: Int64,
incoming: IncomingInfo?,
outgoing: [OutgoingInfo],
failureReason: FailureReason?,
validation: ValidationInfo?
) {
self.name = name
self.at = at
self.took = took
self.incoming = incoming
self.outgoing = outgoing
self.failureReason = failureReason
self.validation = validation
}
static func fromJSON(jsonObject: [String: Any]) throws -> EngineInfo {
guard let name = jsonObject["name"] as? String else {
throw TelemetryJSONError.stringValueNotFound
}
guard let at = jsonObject["when"] as? Int64 else {
throw TelemetryJSONError.intValueNotFound(
message: "EngineInfo `at` property not found")
}
guard let took = jsonObject["took"] as? Int64 else {
throw TelemetryJSONError.intValueNotFound(
message: "EngineInfo `took` property not found")
}
let incoming = unwrapFromJSON(jsonObject: jsonObject) { obj in
IncomingInfo.fromJSON(
jsonObject: obj["incoming"] as? [String: Any] ?? [String: Any]())
}
let outgoing = unwrapFromJSON(jsonObject: jsonObject) { obj in
OutgoingInfo.fromJSONArray(
jsonArray: obj["outgoing"] as? [[String: Any]] ?? [[String: Any]]())
} ?? [OutgoingInfo]()
let failureReason = unwrapFromJSON(jsonObject: jsonObject) { obj in
FailureReason.fromJSON(
jsonObject: obj["failureReason"] as? [String: Any] ?? [String: Any]())
} as? FailureReason
let validation = unwrapFromJSON(jsonObject: jsonObject) { obj in
try ValidationInfo.fromJSON(
jsonObject: obj["validation"] as? [String: Any] ?? [String: Any]())
}
return EngineInfo(name: name,
at: at,
took: took,
incoming: incoming,
outgoing: outgoing,
failureReason: failureReason,
validation: validation)
}
static func fromJSONArray(jsonArray: [[String: Any]]) throws -> [EngineInfo] {
var result = [EngineInfo]()
for item in jsonArray {
try result.append(fromJSON(jsonObject: item))
}
return result
}
}
public class IncomingInfo {
public let applied: Int
public let failed: Int
public let newFailed: Int
public let reconciled: Int
init(applied: Int, failed: Int, newFailed: Int, reconciled: Int) {
self.applied = applied
self.failed = failed
self.newFailed = newFailed
self.reconciled = reconciled
}
static func fromJSON(jsonObject: [String: Any]) -> IncomingInfo {
return IncomingInfo(applied: intOrZero(jsonObject: jsonObject, key: "applied"),
failed: intOrZero(jsonObject: jsonObject, key: "failed"),
newFailed: intOrZero(jsonObject: jsonObject, key: "newFailed"),
reconciled: intOrZero(jsonObject: jsonObject, key: "reconciled"))
}
}
public class OutgoingInfo {
public let sent: Int
public let failed: Int
init(sent: Int, failed: Int) {
self.sent = sent
self.failed = failed
}
static func fromJSON(jsonObject: [String: Any]) -> OutgoingInfo {
return OutgoingInfo(sent: intOrZero(jsonObject: jsonObject, key: "sent"),
failed: intOrZero(jsonObject: jsonObject, key: "failed"))
}
static func fromJSONArray(jsonArray: [[String: Any]]) -> [OutgoingInfo] {
var result = [OutgoingInfo]()
for (_, item) in jsonArray.enumerated() {
result.append(fromJSON(jsonObject: item))
}
return result
}
}
public class ValidationInfo {
public let version: Int
public let problems: [ProblemInfo]
public let failureReason: FailureReason?
init(version: Int, problems: [ProblemInfo], failureReason: FailureReason?) {
self.version = version
self.problems = problems
self.failureReason = failureReason
}
static func fromJSON(jsonObject: [String: Any]) throws -> ValidationInfo {
guard let version = jsonObject["version"] as? Int else {
throw TelemetryJSONError.intValueNotFound(
message: "ValidationInfo `version` property not found")
}
let problems = unwrapFromJSON(jsonObject: jsonObject) { obj in
guard let problemJSON = obj["outgoing"] as? [[String: Any]] else {
return [ProblemInfo]()
}
return try ProblemInfo.fromJSONArray(jsonArray: problemJSON)
} ?? [ProblemInfo]()
let failureReason = unwrapFromJSON(jsonObject: jsonObject) { obj in
FailureReason.fromJSON(
jsonObject: obj["failureReason"] as? [String: Any] ?? [String: Any]())
} as? FailureReason
return ValidationInfo(version: version,
problems: problems,
failureReason: failureReason)
}
}
public class ProblemInfo {
public let name: String
public let count: Int
public init(name: String, count: Int) {
self.name = name
self.count = count
}
static func fromJSON(jsonObject: [String: Any]) throws -> ProblemInfo {
guard let name = jsonObject["name"] as? String else {
throw TelemetryJSONError.stringValueNotFound
}
return ProblemInfo(name: name,
count: intOrZero(jsonObject: jsonObject, key: "count"))
}
static func fromJSONArray(jsonArray: [[String: Any]]) throws -> [ProblemInfo] {
var result = [ProblemInfo]()
for (_, item) in jsonArray.enumerated() {
try result.append(fromJSON(jsonObject: item))
}
return result
}
}
public enum FailureName {
case shutdown
case other
case unexpected
case auth
case http
case unknown
}
public struct FailureReason {
public let name: FailureName
public let message: String?
public let code: Int
public init(name: FailureName, message: String? = nil, code: Int = -1) {
self.name = name
self.message = message
self.code = code
}
static func fromJSON(jsonObject: [String: Any]) -> FailureReason? {
guard let name = jsonObject["name"] as? String else {
return nil
}
switch name {
case "shutdownerror":
return FailureReason(name: FailureName.shutdown)
case "othererror":
return FailureReason(name: FailureName.other,
message: jsonObject["error"] as? String)
case "unexpectederror":
return FailureReason(name: FailureName.unexpected,
message: jsonObject["error"] as? String)
case "autherror":
return FailureReason(name: FailureName.auth,
message: jsonObject["from"] as? String)
case "httperror":
return FailureReason(name: FailureName.http,
code: jsonObject["code"] as? Int ?? -1)
default:
return FailureReason(name: FailureName.unknown)
}
}
}
public class EventInfo {
public let obj: String
public let method: String
public let value: String?
public let extra: [String: String]
public init(obj: String, method: String, value: String?, extra: [String: String]) {
self.obj = obj
self.method = method
self.value = value
self.extra = extra
}
static func fromJSON(jsonObject: [String: Any]) throws -> EventInfo {
let extra = unwrapFromJSON(jsonObject: jsonObject) { (json: [String: Any]) -> [String: String] in
if json["extra"] as? [String: Any] == nil {
return [String: String]()
} else {
var extraValues = [String: String]()
for key in json.keys {
extraValues[key] = extraValues[key]
}
return extraValues
}
}
return try EventInfo(obj: jsonObject["object"] as? String ?? "",
method: jsonObject["method"] as? String ?? "",
value: stringOrNull(jsonObject: jsonObject, key: "value"),
extra: extra ?? [String: String]())
}
static func fromJSONArray(jsonArray: [[String: Any]]) throws -> [EventInfo] {
var result = [EventInfo]()
for (_, item) in jsonArray.enumerated() {
try result.append(fromJSON(jsonObject: item))
}
return result
}
}
func unwrapFromJSON<T>(
jsonObject: [String: Any],
f: @escaping ([String: Any]) throws -> T
) -> T? {
do {
return try f(jsonObject)
} catch {
return nil
}
}
enum TelemetryJSONError: Error {
case stringValueNotFound
case intValueNotFound(message: String)
case invalidJSONString
}
func stringOrNull(jsonObject: [String: Any], key: String) throws -> String? {
return unwrapFromJSON(jsonObject: jsonObject) { data in
guard let value = data[key] as? String else {
throw TelemetryJSONError.stringValueNotFound
}
return value
}
}
func int64OrZero(jsonObject: [String: Any], key: String) -> Int64 {
return unwrapFromJSON(jsonObject: jsonObject) { data in
guard let value = data[key] as? Int64 else {
return 0
}
return value
} ?? 0
}
func intOrZero(jsonObject: [String: Any], key: String) -> Int {
return unwrapFromJSON(jsonObject: jsonObject) { data in
guard let value = data[key] as? Int else {
return 0
}
return value
} ?? 0
}