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 Shared
7
import XCGLogger
8
9
private let log = Logger.syncLogger
10
11
let ONE_YEAR_IN_SECONDS = 365 * 24 * 60 * 60
12
13
/**
14
* Immutable representation for Sync records.
15
*
16
* Envelopes consist of:
17
* Required: "id", "collection", "payload".
18
* Optional: "modified", "sortindex", "ttl".
19
*
20
* Deletedness is a property of the payload.
21
*/
22
open class Record<T: CleartextPayloadJSON> {
23
public let id: String
24
public let payload: T
25
26
public let modified: Timestamp
27
public let sortindex: Int
28
public let ttl: Int? // Seconds. Can be null, which means 'don't expire'.
29
30
// This is a hook for decryption.
31
// Right now it only parses the string. In subclasses, it'll parse the
32
// string, decrypt the contents, and return the data as a JSON object.
33
// From the docs:
34
//
35
// payload none string 256k
36
// A string containing a JSON structure encapsulating the data of the record.
37
// This structure is defined separately for each WBO type.
38
// Parts of the structure may be encrypted, in which case the structure
39
// should also specify a record for decryption.
40
//
41
// @seealso EncryptedRecord.
42
open class func payloadFromPayloadString(_ envelope: EnvelopeJSON, payload: String) -> T? {
43
return T(payload)
44
}
45
46
// TODO: consider using error tuples.
47
open class func fromEnvelope(_ envelope: EnvelopeJSON, payloadFactory: (String) -> T?) -> Record<T>? {
48
if !(envelope.isValid()) {
49
log.error("Invalid envelope.")
50
return nil
51
}
52
guard let payload = payloadFactory(envelope.payload) else {
53
log.error("Unable to parse payload.")
54
return nil
55
}
56
57
if !payload.isValid() {
58
log.error("Invalid payload \(envelope.payload).")
59
return nil
60
}
61
62
return Record<T>(envelope: envelope, payload: payload)
63
}
64
65
/**
66
* Accepts an envelope and a decrypted payload.
67
* Inputs are not validated. Use `fromEnvelope` above.
68
*/
69
convenience init(envelope: EnvelopeJSON, payload: T) {
70
// TODO: modified, sortindex, ttl
71
self.init(id: envelope.id, payload: payload, modified: envelope.modified, sortindex: envelope.sortindex)
72
}
73
74
init(id: GUID, payload: T, modified: Timestamp = Timestamp(time(nil)), sortindex: Int = 0, ttl: Int? = nil) {
75
self.id = id
76
77
self.payload = payload
78
79
self.modified = modified
80
self.sortindex = sortindex
81
self.ttl = ttl
82
}
83
84
func equalIdentifiers(_ rec: Record) -> Bool {
85
return rec.id == self.id
86
}
87
88
// Override me.
89
func equalPayloads(_ rec: Record) -> Bool {
90
return equalIdentifiers(rec) && rec.payload.deleted == self.payload.deleted
91
}
92
93
func equals(_ rec: Record) -> Bool {
94
return rec.sortindex == self.sortindex &&
95
rec.modified == self.modified &&
96
equalPayloads(rec)
97
}
98
}