@trullock/firebase-common 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/automapper.js +218 -0
- package/src/index.js +2 -1
package/package.json
CHANGED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
let classes = {}
|
|
2
|
+
|
|
3
|
+
let mappings = [];
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {function} toFirestore A function of the form (key: string, value: object/primitive) => object/primitive. Return a mapped value or undefined to fallback to default mapping
|
|
7
|
+
* @param {funciton} fromFirestore A function of the form (key: string, value: object/primitive) => object/primitive. Return a mapped value or undefined to fallback to default mapping
|
|
8
|
+
*/
|
|
9
|
+
export function registerMapping(toFirestore, fromFirestore)
|
|
10
|
+
{
|
|
11
|
+
mappings.push({
|
|
12
|
+
toFirestore,
|
|
13
|
+
fromFirestore
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let migrations = []
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {function} shouldApply A function of the form (key: string, parent: object, value: object/primitive) => true/false. Return if the migration should apply or not
|
|
21
|
+
* @param {function} apply A function of the form key: string, parent: object, value: object/primitive) => object/primitive. Migrate the value. Return undefined to disregard the value.
|
|
22
|
+
*/
|
|
23
|
+
export function registerMigration(shouldApply, apply)
|
|
24
|
+
{
|
|
25
|
+
migrations.push({
|
|
26
|
+
shouldApply,
|
|
27
|
+
apply
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function serialiseClassName(type)
|
|
32
|
+
{
|
|
33
|
+
for(let [key, value] of Object.entries(classes))
|
|
34
|
+
{
|
|
35
|
+
if(type == value)
|
|
36
|
+
return key;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new Error(`Could not serialize type ${type.name}. Did you call registerClassForPersistence() or registerMapping()?`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function deserialiseClassName(name)
|
|
43
|
+
{
|
|
44
|
+
return classes[name] || null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Register a class with AutoMapper for persistence
|
|
49
|
+
* @param {Class} type The type of the class
|
|
50
|
+
* @param {string} name Optional: The name of the class. Leave blank to use the class' name+namespace. Beware minifiers can mangle class names and create dev/prod inconsistencies/issues
|
|
51
|
+
* @param {string} parent Optional: The name of the parent class, if present
|
|
52
|
+
*/
|
|
53
|
+
export function registerClassForPersistence(type, name, parent)
|
|
54
|
+
{
|
|
55
|
+
let key = name ? name : type.name;
|
|
56
|
+
if(parent)
|
|
57
|
+
key = parent + '.' + name;
|
|
58
|
+
|
|
59
|
+
classes[key] = type;
|
|
60
|
+
|
|
61
|
+
// static nested classes
|
|
62
|
+
for(let [k, value] of Object.entries(type))
|
|
63
|
+
{
|
|
64
|
+
if(value?.toString()?.substr(0, 5) == 'class')
|
|
65
|
+
registerClassForPersistence(value, k, key)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Converts an object into a firestore-storable format
|
|
71
|
+
* @param {object} obj
|
|
72
|
+
* @param {function} converter Optional: conversion function
|
|
73
|
+
* @returns
|
|
74
|
+
*/
|
|
75
|
+
export function convertToFirestore(obj, converter)
|
|
76
|
+
{
|
|
77
|
+
let converted = converter ? converter(obj) : autoMapToFirestore(obj);
|
|
78
|
+
return converted;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Converts an object from a firestore-storable format into an in-memory format
|
|
83
|
+
* @param {object} obj A firestore object
|
|
84
|
+
* @param {*} converter Optional: conversion function
|
|
85
|
+
* @returns
|
|
86
|
+
*/
|
|
87
|
+
export function convertFromFirestore(obj, converter)
|
|
88
|
+
{
|
|
89
|
+
if(converter)
|
|
90
|
+
{
|
|
91
|
+
let converted = converter(obj);
|
|
92
|
+
return converted;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let converted = autoMapFromFirestore(obj);
|
|
96
|
+
return converted;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Automatically maps an object into firestore format
|
|
101
|
+
* @param {object} obj The object to map
|
|
102
|
+
* @returns Firestore-friendly object
|
|
103
|
+
*/
|
|
104
|
+
export function autoMapToFirestore(obj)
|
|
105
|
+
{
|
|
106
|
+
if(obj == null)
|
|
107
|
+
return null;
|
|
108
|
+
|
|
109
|
+
if(Array.isArray(obj))
|
|
110
|
+
return obj.map(o => autoMapToFirestore(o));
|
|
111
|
+
|
|
112
|
+
if(obj?.constructor == Object || typeof obj == 'object')
|
|
113
|
+
{
|
|
114
|
+
let retval = {};
|
|
115
|
+
|
|
116
|
+
if(obj.constructor.toString()?.substr(0, 5) == 'class')
|
|
117
|
+
retval._type = serialiseClassName(obj.constructor)
|
|
118
|
+
|
|
119
|
+
for(let [key, value] of Object.entries(obj))
|
|
120
|
+
{
|
|
121
|
+
let mapped = false;
|
|
122
|
+
for(let mapping of mappings)
|
|
123
|
+
{
|
|
124
|
+
let result = mapping.toFirestore(key, value)
|
|
125
|
+
if(result !== undefined)
|
|
126
|
+
{
|
|
127
|
+
retval[key] = result;
|
|
128
|
+
mapped = true;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if(!mapped)
|
|
134
|
+
retval[key] = autoMapToFirestore(value);
|
|
135
|
+
}
|
|
136
|
+
return retval;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return obj;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Automatically maps an object from firestore format
|
|
144
|
+
* @param {object} obj
|
|
145
|
+
* @returns In-memory clean object
|
|
146
|
+
*/
|
|
147
|
+
export function autoMapFromFirestore(obj)
|
|
148
|
+
{
|
|
149
|
+
if(obj == undefined)
|
|
150
|
+
return undefined;
|
|
151
|
+
|
|
152
|
+
if(obj == null)
|
|
153
|
+
return null;
|
|
154
|
+
|
|
155
|
+
// Handle Objects
|
|
156
|
+
if(obj?.constructor == Object)
|
|
157
|
+
{
|
|
158
|
+
let dest = {};
|
|
159
|
+
|
|
160
|
+
let type = null;
|
|
161
|
+
|
|
162
|
+
if(obj._type)
|
|
163
|
+
{
|
|
164
|
+
type = deserialiseClassName(obj._type)
|
|
165
|
+
|
|
166
|
+
try
|
|
167
|
+
{
|
|
168
|
+
dest = new type();
|
|
169
|
+
}
|
|
170
|
+
catch(e)
|
|
171
|
+
{
|
|
172
|
+
dest = {};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for(let [key, value] of Object.entries(obj))
|
|
177
|
+
{
|
|
178
|
+
if(key == '_type')
|
|
179
|
+
continue;
|
|
180
|
+
|
|
181
|
+
for(let migration of migrations)
|
|
182
|
+
{
|
|
183
|
+
if(!migration.shouldApply(key, obj, value, dest, type))
|
|
184
|
+
continue;
|
|
185
|
+
|
|
186
|
+
value = migration.apply(key, obj, value, dest, type);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let handled = false;
|
|
190
|
+
for(let mapping of mappings)
|
|
191
|
+
{
|
|
192
|
+
let result = mapping.fromFirestore(key, value)
|
|
193
|
+
if(result !== undefined)
|
|
194
|
+
{
|
|
195
|
+
dest[key] = result;
|
|
196
|
+
handled = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if(!handled)
|
|
202
|
+
{
|
|
203
|
+
let result = autoMapFromFirestore(value);
|
|
204
|
+
if(result != undefined)
|
|
205
|
+
dest[key] = result;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return dest;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Handle Arrays
|
|
213
|
+
if(Array.isArray(obj))
|
|
214
|
+
return obj.map(autoMapFromFirestore);
|
|
215
|
+
|
|
216
|
+
// Handle Primitives
|
|
217
|
+
return obj;
|
|
218
|
+
}
|
package/src/index.js
CHANGED