-
Notifications
You must be signed in to change notification settings - Fork 110
/
Copy pathDocument.ts
207 lines (176 loc) · 5.56 KB
/
Document.ts
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/**
* Firestore Document
*/
class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
fields?: Record<string, FirestoreAPI.Value>;
createTime?: string;
updateTime?: string;
readTime?: string;
name?: string;
/**
*
*
* @param obj
* @param name
*/
constructor(obj: Value | FirestoreAPI.Document, name?: string | Document | FirestoreAPI.ReadOnly) {
//Treat parameters as existing Document with extra parameters to merge in
if (typeof name === 'object') {
Object.assign(this, obj);
Object.assign(this, name);
} else {
this.fields = Document.wrapMap(obj as ValueObject).fields;
if (name) {
this.name = name;
}
}
}
get created(): Date {
return this.createTime ? Document.unwrapDate(this.createTime) : new Date();
}
get updated(): Date {
return this.updateTime ? Document.unwrapDate(this.updateTime) : new Date();
}
get read(): Date {
return this.readTime ? Document.unwrapDate(this.readTime) : new Date();
}
get path() {
return this.name?.match(Util_.regexPath)![1];
}
/**
* Extract fields from a Firestore document.
*
* @param {object} firestoreDoc the Firestore document whose fields will be extracted
* @return {object} an object with the given document's fields and values
*/
get obj(): Record<string, Value> {
return Document.unwrapObject(this);
}
toString(): string {
return `Document (${Util_.getDocumentFromPath(this.name as string)[1]})`;
}
static unwrapValue(obj: FirestoreAPI.Value): Value {
// eslint-disable-next-line prefer-const
let [type, val]: [string, any] = Object.entries(obj)[0];
switch (type) {
case 'referenceValue':
case 'bytesValue':
case 'stringValue':
case 'booleanValue':
case 'geoPointValue':
return val;
case 'doubleValue':
return parseFloat(val as string);
case 'integerValue':
return parseInt(val as string);
case 'mapValue':
return this.unwrapObject(val as FirestoreAPI.MapValue);
case 'arrayValue':
return this.unwrapArray(val.values);
case 'timestampValue':
return this.unwrapDate(val as string);
case 'nullValue':
default:
return null;
}
}
static unwrapObject(obj: FirestoreAPI.MapValue): ValueObject {
return Object.entries(obj.fields || {}).reduce(
(o: Record<string, Value>, [key, val]: [string, FirestoreAPI.Value]) => {
o[key] = Document.unwrapValue(val);
return o;
},
{}
);
}
static unwrapArray(wrappedArray: FirestoreAPI.Value[] = []): Value[] {
return wrappedArray.map(this.unwrapValue, this);
}
static unwrapDate(wrappedDate: string): Date {
// Trim out extra microsecond precision
return new Date(wrappedDate.replace(Util_.regexDatePrecision, '$1'));
}
static wrapValue(val: Value): FirestoreAPI.Value {
const type = typeof val;
switch (type) {
case 'string':
return this.wrapString(val as string);
case 'object':
return this.wrapObject(val as ValueObject);
case 'number':
return this.wrapNumber(val as number);
case 'boolean':
return this.wrapBoolean(val as boolean);
default:
return this.wrapNull();
}
}
static wrapString(string: string): FirestoreAPI.Value {
// Test for root path reference inclusion (see Util.js)
if (Util_.regexPath.test(string)) {
return this.wrapRef(string);
}
// Test for binary data in string (see Util.js)
if (Util_.regexBinary.test(string)) {
return this.wrapBytes(string);
}
return { stringValue: string };
}
static wrapObject(obj: ValueObject): FirestoreAPI.Value {
if (!obj) {
return this.wrapNull();
}
if (Array.isArray(obj)) {
return this.wrapArray(obj);
}
// instanceof fails for code referencing this library
if (obj instanceof Date || obj.constructor.name === 'Date') {
return this.wrapDate((obj as any) as Date);
}
// Check if LatLng type
if (Object.keys(obj).length === 2 && 'latitude' in obj && 'longitude' in obj) {
return this.wrapLatLong(obj as FirestoreAPI.LatLng);
}
return { mapValue: this.wrapMap(obj) };
}
static wrapMap(obj: ValueObject): FirestoreAPI.MapValue {
return {
fields: Object.entries(obj).reduce((o: Record<string, FirestoreAPI.Value>, [key, val]: [string, Value]) => {
o[key] = Document.wrapValue(val);
return o;
}, {}),
};
}
static wrapNull(): FirestoreAPI.Value {
return { nullValue: null };
}
static wrapBytes(bytes: string): FirestoreAPI.Value {
return { bytesValue: bytes };
}
static wrapRef(ref: string): FirestoreAPI.Value {
return { referenceValue: ref };
}
static wrapNumber(num: number): FirestoreAPI.Value {
const func = Util_.isInt(num) ? this.wrapInt : this.wrapDouble;
return func(num);
}
static wrapInt(int: number): FirestoreAPI.Value {
return { integerValue: int.toString() };
}
static wrapDouble(double: number): FirestoreAPI.Value {
return { doubleValue: double };
}
static wrapBoolean(boolean: boolean): FirestoreAPI.Value {
return { booleanValue: boolean };
}
static wrapDate(date: Date): FirestoreAPI.Value {
return { timestampValue: date.toISOString() };
}
static wrapLatLong(latLong: FirestoreAPI.LatLng): FirestoreAPI.Value {
return { geoPointValue: latLong };
}
static wrapArray(array: any[]): FirestoreAPI.Value {
const wrappedArray = array.map(this.wrapValue, this);
return { arrayValue: { values: wrappedArray } };
}
}