@trullock/firebase-common 0.4.0 → 1.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trullock/firebase-common",
3
- "version": "0.4.0",
3
+ "version": "1.1.0",
4
4
  "description": "Collection of common firebase helpers",
5
5
  "homepage": "https://github.com/trullock/firebase#readme",
6
6
  "bugs": {
@@ -21,7 +21,7 @@
21
21
  "access": "public"
22
22
  },
23
23
  "scripts": {
24
- "test": "echo \"Error: no test specified\" && exit 1"
24
+ "test": "node ./tests/getters.js"
25
25
  },
26
26
  "dependencies": {
27
27
  }
package/src/automapper.js CHANGED
@@ -28,18 +28,24 @@ export function registerMigration(shouldApply, apply)
28
28
  })
29
29
  }
30
30
 
31
- function serialiseClassName(type)
31
+ function getClassMappingByType(type)
32
32
  {
33
33
  for(let [key, value] of Object.entries(classes))
34
34
  {
35
- if(type == value)
36
- return key;
35
+ if(type == value.type)
36
+ {
37
+ return {
38
+ name: key,
39
+ type,
40
+ getters: value.getters
41
+ };
42
+ }
37
43
  }
38
44
 
39
45
  throw new Error(`Could not serialize type ${type.name}. Did you call registerClassForPersistence() or registerMapping()?`);
40
46
  }
41
47
 
42
- function deserialiseClassName(name)
48
+ function getClassMappingByName(name)
43
49
  {
44
50
  return classes[name] || null
45
51
  }
@@ -47,22 +53,29 @@ function deserialiseClassName(name)
47
53
  /**
48
54
  * Register a class with AutoMapper for persistence
49
55
  * @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
56
+ * @param {Array} name O
52
57
  */
53
- export function registerClassForPersistence(type, name, parent)
58
+ export function registerClassForPersistence(type, persistedGetters)
59
+ {
60
+ return doRegisterClassForPersistence(type, null, null, persistedGetters || [])
61
+ }
62
+
63
+ function doRegisterClassForPersistence(type, name, parent, persistedGetters)
54
64
  {
55
65
  let key = name ? name : type.name;
56
66
  if(parent)
57
67
  key = parent + '.' + name;
58
68
 
59
- classes[key] = type;
69
+ classes[key] = {
70
+ type,
71
+ getters: persistedGetters.filter(pg => pg.type == type).map(pg => pg.property)
72
+ };
60
73
 
61
74
  // static nested classes
62
75
  for(let [k, value] of Object.entries(type))
63
76
  {
64
77
  if(value?.toString()?.substr(0, 5) == 'class')
65
- registerClassForPersistence(value, k, key)
78
+ doRegisterClassForPersistence(value, k, key, persistedGetters)
66
79
  }
67
80
  }
68
81
 
@@ -114,7 +127,30 @@ export function autoMapToFirestore(obj)
114
127
  let retval = {};
115
128
 
116
129
  if(obj.constructor.toString()?.substr(0, 5) == 'class')
117
- retval._type = serialiseClassName(obj.constructor)
130
+ {
131
+ let mapping = getClassMappingByType(obj.constructor)
132
+ retval._type = mapping.name
133
+
134
+ for(let getter of mapping.getters)
135
+ {
136
+ let value = obj[getter];
137
+
138
+ let mapped = false;
139
+ for(let mapping of mappings)
140
+ {
141
+ let result = mapping.toFirestore(getter, value)
142
+ if(result !== undefined)
143
+ {
144
+ retval[getter] = result;
145
+ mapped = true;
146
+ break;
147
+ }
148
+ }
149
+
150
+ if(!mapped)
151
+ retval[getter] = autoMapToFirestore(value);
152
+ }
153
+ }
118
154
 
119
155
  for(let [key, value] of Object.entries(obj))
120
156
  {
@@ -156,21 +192,12 @@ export function autoMapFromFirestore(obj)
156
192
  if(obj?.constructor == Object)
157
193
  {
158
194
  let dest = {};
159
-
160
- let type = null;
195
+ let mapping = null;
161
196
 
162
197
  if(obj._type)
163
198
  {
164
- type = deserialiseClassName(obj._type)
165
-
166
- try
167
- {
168
- dest = new type();
169
- }
170
- catch(e)
171
- {
172
- dest = {};
173
- }
199
+ mapping = getClassMappingByName(obj._type)
200
+ dest = new mapping.type();
174
201
  }
175
202
 
176
203
  for(let [key, value] of Object.entries(obj))
@@ -178,12 +205,15 @@ export function autoMapFromFirestore(obj)
178
205
  if(key == '_type')
179
206
  continue;
180
207
 
208
+ if(mapping?.getters?.indexOf(key) > -1)
209
+ continue;
210
+
181
211
  for(let migration of migrations)
182
212
  {
183
- if(!migration.shouldApply(key, obj, value, dest, type))
213
+ if(!migration.shouldApply(key, obj, value, dest, mapping.type))
184
214
  continue;
185
215
 
186
- value = migration.apply(key, obj, value, dest, type);
216
+ value = migration.apply(key, obj, value, dest, mapping.type);
187
217
  }
188
218
 
189
219
  let handled = false;
@@ -0,0 +1,47 @@
1
+ import test from "node:test";
2
+ import { autoMapFromFirestore, autoMapToFirestore, registerClassForPersistence } from "../src/automapper.js";
3
+ import assert from "node:assert";
4
+
5
+ class MyObj {
6
+ get age() {
7
+ return 18;
8
+ }
9
+
10
+ name = 'Mr Test'
11
+ inner = new MyObj.MyInnerObj()
12
+
13
+
14
+ static MyInnerObj = class {
15
+ get height() {
16
+ return 180;
17
+ }
18
+
19
+ size = 'Large'
20
+ }
21
+ }
22
+
23
+
24
+ let myObj = new MyObj();
25
+
26
+ registerClassForPersistence(MyObj, [
27
+ {
28
+ type: MyObj.MyInnerObj,
29
+ property: 'height'
30
+ }
31
+ ]);
32
+
33
+ let mapped = autoMapToFirestore(myObj);
34
+
35
+ test('should automap defined getters to Firebase', t => {
36
+ assert.deepEqual(mapped, {
37
+ _type: 'MyObj',
38
+ name: 'Mr Test',
39
+ inner: { _type: 'MyObj.MyInnerObj', height: 180, size: 'Large' }
40
+ })
41
+ })
42
+
43
+ let unmapped = autoMapFromFirestore(mapped)
44
+
45
+ test('should ignore automap defined getters from Firebase', t => {
46
+ assert.deepEqual(unmapped, myObj)
47
+ })