@entropic-bond/firebase 1.13.2 → 1.13.4

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.
Files changed (43) hide show
  1. package/README.md +0 -0
  2. package/lib/auth/firebase-auth.d.ts +20 -0
  3. package/lib/auth/firebase-auth.spec.d.ts +1 -0
  4. package/lib/cloud-functions/firebase-cloud-functions.d.ts +7 -0
  5. package/lib/cloud-functions/firebase-cloud-functions.spec.d.ts +6 -0
  6. package/lib/cloud-storage/firebase-cloud-storage.d.ts +10 -0
  7. package/lib/cloud-storage/firebase-cloud-storage.spec.d.ts +1 -0
  8. package/lib/entropic-bond-firebase.js +23778 -0
  9. package/lib/entropic-bond-firebase.js.map +1 -0
  10. package/lib/entropic-bond-firebase.umd.cjs +3678 -0
  11. package/lib/entropic-bond-firebase.umd.cjs.map +1 -0
  12. package/lib/firebase-helper.d.ts +38 -0
  13. package/lib/index.d.ts +5 -0
  14. package/lib/mocks/test-user.d.ts +49 -0
  15. package/lib/store/firebase-datasource.d.ts +19 -0
  16. package/lib/store/firebase-datasource.spec.d.ts +1 -0
  17. package/package.json +7 -3
  18. package/.firebaserc +0 -5
  19. package/.github/workflows/release.yml +0 -27
  20. package/CHANGELOG.md +0 -338
  21. package/firebase.json +0 -30
  22. package/firestore.indexes.json +0 -4
  23. package/firestore.rules +0 -8
  24. package/functions/package-lock.json +0 -2344
  25. package/functions/package.json +0 -26
  26. package/functions/src/index.ts +0 -33
  27. package/functions/tsconfig.json +0 -19
  28. package/src/auth/firebase-auth.spec.ts +0 -90
  29. package/src/auth/firebase-auth.ts +0 -212
  30. package/src/cloud-functions/firebase-cloud-functions.spec.ts +0 -47
  31. package/src/cloud-functions/firebase-cloud-functions.ts +0 -25
  32. package/src/cloud-storage/firebase-cloud-storage.spec.ts +0 -135
  33. package/src/cloud-storage/firebase-cloud-storage.ts +0 -67
  34. package/src/firebase-helper.ts +0 -92
  35. package/src/index.ts +0 -5
  36. package/src/mocks/mock-data.json +0 -148
  37. package/src/mocks/test-user.ts +0 -121
  38. package/src/store/firebase-datasource.spec.ts +0 -555
  39. package/src/store/firebase-datasource.ts +0 -146
  40. package/storage.rules +0 -8
  41. package/tsconfig-build.json +0 -7
  42. package/tsconfig.json +0 -30
  43. package/vite.config.ts +0 -23
@@ -1,555 +0,0 @@
1
- import dns from 'node:dns'
2
- import { Model, Persistent, Store } from 'entropic-bond'
3
- import { FirebaseDatasource } from './firebase-datasource'
4
- import { FirebaseHelper } from '../firebase-helper'
5
- import { TestUser, DerivedUser, SubClass } from '../mocks/test-user'
6
- import mockData from '../mocks/mock-data.json'
7
-
8
- dns.setDefaultResultOrder('ipv4first')
9
-
10
- async function loadTestData( model: Model<TestUser> ) {
11
- const users = Object.values( mockData.TestUser )
12
- await Promise.all(
13
- users.map( userObj => {
14
- const user = Persistent.createInstance<TestUser>( userObj as any )
15
- return model.save( user )
16
- })
17
- )
18
- }
19
-
20
- describe( 'Firestore Model', ()=>{
21
- let model: Model<TestUser>
22
- let testUser: TestUser
23
- const host = 'localhost'
24
- const firestorePort = 9080
25
-
26
- beforeAll(()=>{
27
- FirebaseHelper.setFirebaseConfig({
28
- projectId: "demo-test",
29
- })
30
- FirebaseHelper.useEmulator({ host, firestorePort })
31
- Store.useDataSource( new FirebaseDatasource() )
32
- })
33
-
34
- beforeEach( async ()=>{
35
-
36
- testUser = new TestUser()
37
- testUser.name = {
38
- firstName: 'testUserFirstName',
39
- lastName: 'testUserLastName'
40
- }
41
- testUser.age = 35
42
- testUser.skills = [ 'lazy', 'dirty' ]
43
-
44
- model = Store.getModel<TestUser>( 'TestUser' )
45
-
46
- await loadTestData( model )
47
- })
48
-
49
- afterEach( async ()=>{
50
- await fetch( `http://${ host }:${ firestorePort }/emulator/v1/projects/demo-test/databases/(default)/documents`, {
51
- method: 'DELETE'
52
- })
53
- })
54
-
55
- it( 'should find document by id', async ()=>{
56
- await model.save( testUser )
57
-
58
- const user = await model.findById( testUser.id )
59
-
60
- expect( user ).toBeInstanceOf( TestUser )
61
- expect( user?.id ).toEqual( testUser.id )
62
- expect( user?.name?.firstName ).toEqual( 'testUserFirstName' )
63
- })
64
-
65
- it( 'should write a document', async ()=>{
66
- await model.save( testUser )
67
- const newUser = await model.findById( testUser.id )
68
-
69
- expect( newUser?.name ).toEqual({
70
- firstName: 'testUserFirstName',
71
- lastName: 'testUserLastName'
72
- })
73
- })
74
-
75
- it( 'should delete a document by id', async ()=>{
76
- await model.save( testUser )
77
-
78
- const newUser = await model.findById( testUser.id )
79
- expect( newUser?.age ).toBe( 35 )
80
-
81
- await model.delete( testUser.id )
82
-
83
- const deletedUser = await model.findById( testUser.id )
84
- expect( deletedUser ).toBeUndefined()
85
- })
86
-
87
- it( 'should not throw if a document id doesn\'t exists', ()=>{
88
- expect( async ()=>{
89
- await model.findById( 'nonExistingId' )
90
- }).not.toThrow()
91
- })
92
-
93
- it( 'should return undefined if a document id doesn\'t exists', async ()=>{
94
- expect( await model.findById( 'nonExistingId' ) ).toBeUndefined()
95
- })
96
-
97
- it( 'should retrieve array fields', async ()=>{
98
- await model.save( testUser )
99
- const newUser = await model.findById( testUser.id )
100
-
101
- expect( Array.isArray( newUser?.skills ) ).toBeTruthy()
102
- expect( newUser?.skills ).toEqual( expect.arrayContaining([ 'lazy', 'dirty' ]) )
103
- })
104
-
105
- it( 'should retrieve object fields', async ()=>{
106
- await model.save( testUser )
107
- const newUser = await model.findById( testUser.id )
108
-
109
- expect( newUser?.name ).toEqual({
110
- firstName: 'testUserFirstName',
111
- lastName: 'testUserLastName'
112
- })
113
- })
114
-
115
- describe( 'Generic find', ()=>{
116
- it( 'should query all admins with query object', async ()=>{
117
- testUser.admin = true
118
- await model.save( testUser )
119
-
120
- const admins = await model.query({
121
- operations: [{
122
- property: 'admin',
123
- operator: '==',
124
- value: true
125
- }]
126
- })
127
-
128
- expect( admins.length ).toBeGreaterThanOrEqual( 1 )
129
- expect( admins[ 0 ] ).toBeInstanceOf( TestUser )
130
- })
131
-
132
- it( 'should find all admins with where methods', async ()=>{
133
- const admins = await model.find().where( 'admin', '==', true ).get()
134
-
135
- expect( admins.length ).toBeGreaterThanOrEqual( 1 )
136
- expect( admins[ 0 ] ).toBeInstanceOf( TestUser )
137
- })
138
-
139
- it( 'should find admins with age less than 56', async ()=>{
140
- const admins = await model.find()
141
- .where( 'admin', '==', true )
142
- .where( 'age', '<', 50 )
143
- .get()
144
-
145
- expect( admins.length ).toBeGreaterThanOrEqual( 1 )
146
- expect( admins[ 0 ]?.age ).toBeLessThan( 50 )
147
- })
148
-
149
- it( 'should query by subproperties', async ()=>{
150
- const users = await model.query({
151
- operations: [
152
- {
153
- property: 'name',
154
- operator: '==',
155
- value: { firstName: 'userFirstName3' }
156
- },
157
- { property: 'age', operator: '!=', value: 134 }
158
- ]
159
- })
160
-
161
- expect( users[0]?.id ).toBe( 'user3' )
162
- })
163
-
164
- it( 'should find by subproperties', async ()=>{
165
- const users = await model.find()
166
- .where( 'name', '==', { firstName: 'userFirstName3' })
167
- .get()
168
-
169
- expect( users[0]?.id ).toBe( 'user3' )
170
- })
171
-
172
- it( 'should find by property path', async ()=>{
173
- const users = await model.find()
174
- .whereDeepProp( 'name.firstName', '==', 'userFirstName3' )
175
- .get()
176
-
177
- expect( users[0]?.id ).toBe( 'user3' )
178
- })
179
-
180
- it( 'should find by superdeep property path', async ()=>{
181
- const users = await model.find()
182
- .whereDeepProp( 'name.ancestorName.father', '==', 'user3Father')
183
- .get()
184
-
185
- expect( users[0]?.id ).toEqual( 'user3' )
186
- })
187
-
188
- it( 'should find by swallow property path', async ()=>{
189
- const users = await model.find()
190
- .whereDeepProp( 'age', '==', 21 )
191
- .get()
192
-
193
- expect( users[0]?.id ).toEqual( 'user2' )
194
- })
195
- })
196
-
197
-
198
- describe( 'Compound queries', ()=>{
199
- it( 'should find documents using `AND` compound query', async ()=>{
200
- const admins = await model.find()
201
- .where( 'admin', '==', true )
202
- .where( 'age', '<', 50 )
203
- .get()
204
-
205
- expect( admins ).toHaveLength( 1 )
206
- expect( admins[0]?.age ).toBeLessThan( 50 )
207
- })
208
-
209
- it( 'should find using `OR` query', async ()=>{
210
- const docs = await model.find().or( 'age', '==', 23 ).or( 'age', '==', 41 ).get()
211
-
212
- expect( docs ).toHaveLength( 2 )
213
- expect( docs ).toEqual( expect.arrayContaining([
214
- expect.objectContaining({ id: 'user1', age: 23 }),
215
- expect.objectContaining({ id: 'user5', age: 41 })
216
- ]))
217
- })
218
-
219
- it( 'should find combining `OR` query and `where` query', async ()=>{
220
- const docs = await model.find().where( 'age', '>', 50 ).or( 'age', '==', 23 ).or( 'age', '==', 41 ).get()
221
-
222
- expect( docs ).toHaveLength( 3 )
223
- expect( docs ).toEqual( expect.arrayContaining([
224
- expect.objectContaining({ id: 'user1', age: 23 }),
225
- expect.objectContaining({ id: 'user5', age: 41 }),
226
- expect.objectContaining({ id: 'user3', age: 56 })
227
- ]))
228
- })
229
-
230
- it( 'should find combining `OR` query and `where` query in a range', async ()=>{
231
- const docs = await model.find().where( 'age', '<', 22 ).or( 'age', '>', 50 ).get()
232
-
233
- expect( docs ).toHaveLength( 2 )
234
- expect( docs ).toEqual( expect.arrayContaining([
235
- expect.objectContaining({ id: 'user2', age: 21 }),
236
- expect.objectContaining({ id: 'user3', age: 56 })
237
- ]))
238
- })
239
-
240
- it( 'should find combining `OR` query and `where` query on different fields', async ()=>{
241
- const docs = await model.find().where( 'age', '==', 23 ).or( 'admin', '!=', true ).get()
242
-
243
- expect( docs ).toHaveLength( 3 )
244
- expect( docs ).toEqual( expect.arrayContaining([
245
- expect.objectContaining({ id: 'user1', admin: true, age: 23 }),
246
- expect.objectContaining({ id: 'user2', admin: false, age: 21 }),
247
- expect.objectContaining({ id: 'user4', admin: false, age: 35 }),
248
- ]))
249
- })
250
-
251
- it( 'should throw if a `where` query is used after an `or` query', ()=>{
252
- expect(
253
- ()=> model.find().or( 'age', '==', 23 ).where( 'age', '>', 50 )
254
- ).toThrow( Model.error.invalidQueryOrder )
255
- })
256
-
257
- it( 'should evaluate mixing operands', async ()=>{
258
- const docs = await model.find().where( 'age', '>', 39 ).and( 'age', '<', 57 ).or( 'age', '==', 23 ).or( 'age', '==', 21 ).get()
259
- expect( docs ).toHaveLength( 5 )
260
- expect( docs ).toEqual( expect.arrayContaining([
261
- expect.objectContaining({ id: 'user1', age: 23 }),
262
- expect.objectContaining({ id: 'user2', age: 21 }),
263
- expect.objectContaining({ id: 'user3', age: 56 }),
264
- expect.objectContaining({ id: 'user5', age: 41 }),
265
- expect.objectContaining({ id: 'user6', age: 40 })
266
- ]))
267
-
268
- const docs1 = await model.find().where( 'age', '==', 41 ).and( 'age', '==', 56 ).or( 'age', '==', 23 ).or( 'age', '==', 21 ).get()
269
- expect( docs1 ).toHaveLength( 2 )
270
- expect( docs1 ).toEqual( expect.arrayContaining([
271
- expect.objectContaining({ id: 'user1', age: 23 }),
272
- expect.objectContaining({ id: 'user2', age: 21 })
273
- ]))
274
-
275
- const docs2 = await model.find().where( 'age', '==', 41 ).or( 'age', '==', 56 ).or( 'age', '==', 23 ).or( 'age', '==', 21 ).get()
276
- expect( docs2 ).toHaveLength( 4 )
277
- expect( docs2 ).toEqual( expect.arrayContaining([
278
- expect.objectContaining({ id: 'user1', age: 23 }),
279
- expect.objectContaining({ id: 'user2', age: 21 }),
280
- expect.objectContaining({ id: 'user3', age: 56 }),
281
- expect.objectContaining({ id: 'user5', age: 41 })
282
- ]))
283
- })
284
-
285
- })
286
-
287
- describe( 'Searchable array property', ()=>{
288
-
289
- it( 'should find documents using `containsAny` operator', async ()=>{
290
- const colleague1 = new TestUser( 'colleague1' )
291
- const colleague2 = new TestUser( 'colleague2' )
292
- const docs = await model.find().where( 'colleagues', 'containsAny', [ colleague1, colleague2 ]).get()
293
-
294
- expect( docs ).toHaveLength( 3 )
295
- expect( docs ).toEqual( expect.arrayContaining([
296
- expect.objectContaining({ id: 'user2' }),
297
- expect.objectContaining({ id: 'user4' }),
298
- expect.objectContaining({ id: 'user6' })
299
- ]))
300
- })
301
-
302
- it( 'should find documents using `contains` operator', async ()=>{
303
- const colleague2 = new TestUser( 'colleague2' )
304
- const docs = await model.find().where( 'colleagues', 'contains', colleague2 ).get()
305
-
306
- expect( docs ).toHaveLength( 2 )
307
- expect( docs ).toEqual([
308
- expect.objectContaining({ id: 'user4' }),
309
- expect.objectContaining({ id: 'user6' })
310
- ])
311
- })
312
-
313
- })
314
-
315
- describe( 'Derived classes should fit on parent collection', ()=>{
316
-
317
- it( 'should save derived object in parent collection', async ()=>{
318
- const derived = new DerivedUser()
319
- derived.name = { firstName: 'Fulanito', lastName: 'Derived' }
320
- derived.salary = 3900
321
-
322
- await model.save( derived )
323
-
324
- const newUser = await model.findById( derived.id ) as DerivedUser
325
- expect( newUser ).toBeInstanceOf( DerivedUser )
326
- expect( newUser.salary ).toBe( 3900 )
327
- expect( newUser.className ).toEqual( 'DerivedUser' )
328
- })
329
- })
330
-
331
- describe( 'References to documents', ()=>{
332
- let ref1: SubClass, ref2: SubClass
333
-
334
- beforeEach( async ()=>{
335
- testUser.documentRef = new SubClass()
336
- testUser.documentRef.year = 2045
337
- ref1 = new SubClass(); ref1.year = 2081
338
- ref2 = new SubClass(); ref2.year = 2082
339
- testUser.manyRefs.push( ref1 )
340
- testUser.manyRefs.push( ref2 )
341
- testUser.derived = new DerivedUser()
342
- testUser.derived!.salary = 1350
343
- testUser.manyDerived = [ new DerivedUser(), new DerivedUser() ]
344
- testUser.manyDerived![ 0 ]!.salary = 990
345
- testUser.manyDerived![ 1 ]!.salary = 1990
346
-
347
- await model.save( testUser )
348
- })
349
-
350
- it( 'should save a document as a reference', async ()=>{
351
- const subClassModel = Store.getModel( 'SubClass' )
352
- expect( subClassModel ).toBeDefined()
353
-
354
- const newDocument = await subClassModel.findById( testUser.documentRef!.id ) as SubClass
355
-
356
- expect( newDocument ).toBeInstanceOf( SubClass )
357
- expect( newDocument.year ).toBe( 2045 )
358
- })
359
-
360
- it( 'should read a swallow document reference', async ()=>{
361
- const loadedUser = await model.findById( testUser.id )
362
-
363
- expect( loadedUser?.documentRef ).toBeInstanceOf( SubClass )
364
- expect( loadedUser?.documentRef?.id ).toBeDefined()
365
- expect( loadedUser?.documentRef?.year ).toBeUndefined()
366
- })
367
-
368
- it( 'should fill data of swallow document reference', async ()=>{
369
- const loadedUser = await model.findById( testUser.id )
370
-
371
- await Store.populate( loadedUser!.documentRef! )
372
- expect( loadedUser?.documentRef?.id ).toBeDefined()
373
- expect( loadedUser?.documentRef?.year ).toBe( 2045 )
374
- })
375
-
376
-
377
- it( 'should save and array of references', async ()=>{
378
- const subClassModel = Store.getModel( 'SubClass' )
379
-
380
- const newDocument = await subClassModel.findById( testUser.documentRef!.id ) as SubClass
381
-
382
- expect( newDocument ).toBeInstanceOf( SubClass )
383
- expect( newDocument.year ).toBe( 2045 )
384
- })
385
-
386
- it( 'should read an array of references', async ()=>{
387
- const loadedUser = await model.findById( testUser.id )
388
-
389
- expect( loadedUser?.manyRefs ).toHaveLength( 2 )
390
- expect( loadedUser?.manyRefs[0] ).toBeInstanceOf( SubClass )
391
- expect( loadedUser?.manyRefs[0]?.id ).toEqual( testUser.manyRefs[0]!.id )
392
- expect( loadedUser?.manyRefs[0]?.year ).toBeUndefined()
393
- expect( loadedUser?.manyRefs[1] ).toBeInstanceOf( SubClass )
394
- expect( loadedUser?.manyRefs[1]?.id ).toEqual( testUser.manyRefs[1]!.id )
395
- expect( loadedUser?.manyRefs[1]?.year ).toBeUndefined()
396
- })
397
-
398
- it( 'should fill array of refs', async ()=>{
399
- const loadedUser = await model.findById( testUser.id )
400
- await Store.populate( loadedUser!.manyRefs )
401
-
402
- expect( loadedUser?.manyRefs[ 0 ]?.year ).toBe( 2081 )
403
- expect( loadedUser?.manyRefs[ 1 ]?.year ).toBe( 2082 )
404
- })
405
-
406
- it( 'should save a reference when declared @persistentAt', async ()=>{
407
- const loadedUser = await model.findById( testUser.id )
408
-
409
- expect( loadedUser?.derived?.id ).toEqual( testUser.derived!.id )
410
- expect( loadedUser?.derived?.salary ).toBeUndefined()
411
-
412
- await Store.populate( loadedUser!.derived! )
413
-
414
- expect( loadedUser?.derived?.salary ).toBe( 1350 )
415
- expect( loadedUser?.derived?.id ).toBe( testUser.derived!.id )
416
- })
417
-
418
- it( 'should populate from special collection when declared with @persistentRefAt', async ()=>{
419
- const loadedUser = await model.findById( 'user6' )
420
- await Store.populate( loadedUser!.derived! )
421
-
422
- expect( loadedUser?.derived?.salary ).toBe( 2800 )
423
- expect( loadedUser?.derived?.id ).toBe( 'user4' )
424
- })
425
-
426
- it( 'should save a reference when declared @persistentAt as array', async ()=>{
427
- const loadedUser = await model.findById( testUser.id )
428
-
429
- expect( loadedUser?.manyDerived?.[0]?.id ).toEqual( testUser.manyDerived![0]!.id )
430
- expect( loadedUser?.manyDerived?.[0]?.salary ).toBeUndefined()
431
- expect( loadedUser?.manyDerived?.[1]?.salary ).toBeUndefined()
432
-
433
- await Store.populate( loadedUser!.manyDerived! )
434
-
435
- expect( loadedUser?.manyDerived?.[0]?.salary ).toBe( 990 )
436
- expect( loadedUser?.manyDerived?.[0]?.id ).toBe( testUser.manyDerived![0]!.id )
437
- expect( loadedUser?.manyDerived?.[1]?.salary ).toBe( 1990 )
438
- expect( loadedUser?.manyDerived?.[1]?.id ).toBe( testUser.manyDerived?.[1]?.id )
439
- })
440
-
441
- it( 'should not overwrite not filled ref in collection', async ()=>{
442
- const loadedUser = await model.findById( 'user6' )
443
- await model.save( loadedUser! )
444
- const refInCollection = await model.findById<DerivedUser>( 'user4' )
445
-
446
- expect( refInCollection?.salary ).toBe( 2800 )
447
- })
448
-
449
- it( 'should save loaded ref with assigned new instance', async ()=>{
450
- const loadedUser = await model.findById( 'user6' )
451
- loadedUser!.derived = new DerivedUser()
452
- loadedUser!.derived!.salary = 345
453
- await model.save( loadedUser! )
454
-
455
- const refInCollection = await model.findById<DerivedUser>( loadedUser!.derived.id )
456
- expect( refInCollection?.salary ).toBe( 345 )
457
- })
458
-
459
- it( 'should save loaded ref with modified ref data', async ()=>{
460
- const loadedUser = await model.findById( 'user6' )
461
- await Store.populate( loadedUser!.derived! )
462
- loadedUser!.derived!.salary = 1623
463
- await model.save( loadedUser! )
464
-
465
- const refInCollection = await model.findById<DerivedUser>( 'user4' )
466
- expect( refInCollection?.salary ).toBe( 1623 )
467
- })
468
- })
469
-
470
- describe( 'Operations on queries', ()=>{
471
- it( 'should limit the result set', async ()=>{
472
- const unlimited = await model.find().get()
473
- const limited = await model.find().limit( 2 ).get()
474
-
475
- expect( unlimited.length ).not.toBe( limited.length )
476
- expect( limited ).toHaveLength( 2 )
477
- })
478
-
479
- it( 'should sort ascending the result set', async ()=>{
480
- const docs = await model.find().orderBy( 'age' ).get()
481
-
482
- expect( docs[ 0 ]?.id ).toEqual( 'user2' )
483
- expect( docs[ 1 ]?.id ).toEqual( 'user1' )
484
- })
485
-
486
- it( 'should sort descending the result set', async ()=>{
487
- const docs = await model.find().orderBy( 'age', 'desc' ).get()
488
-
489
- expect( docs[ 0 ]?.id ).toEqual( 'user3' )
490
- expect( docs[ 1 ]?.id ).toEqual( 'user5' )
491
- })
492
-
493
- it( 'should sort by deep property path', async ()=>{
494
- const docs = await model.find().orderByDeepProp( 'name.firstName', 'desc' ).get()
495
-
496
- expect( docs[ 0 ]?.id ).toEqual( 'user6' )
497
- expect( docs[ 1 ]?.id ).toEqual( 'user5' )
498
- })
499
-
500
- it( 'should sort by swallow property path', async ()=>{
501
- const docs = await model.find().orderByDeepProp( 'age' ).get()
502
-
503
- expect( docs[ 0 ]?.id ).toEqual( 'user2' )
504
- expect( docs[ 1 ]?.id ).toEqual( 'user1' )
505
- })
506
-
507
- it( 'should count documents in collection', async ()=>{
508
- expect( await model.find().count() ).toBe( 6 )
509
- })
510
-
511
-
512
- describe( 'Data Cursors', ()=>{
513
- beforeEach( async ()=>{
514
- await model.find().get( 2 )
515
- })
516
-
517
- it( 'should get next result set', async ()=>{
518
- const docs = await model.next()
519
- const mockDataArr = Object.values( mockData.TestUser )
520
-
521
- expect( docs ).toHaveLength( 2 )
522
- expect( docs[0]?.id ).toEqual( mockDataArr[2]!.id )
523
- expect( docs[ 0 ]?.id ).toEqual( 'user3' )
524
- })
525
-
526
- it( 'should not go beyond the end of result set', async ()=>{
527
- await model.next()
528
- await model.next()
529
- const docs = await model.next()
530
- expect( docs ).toHaveLength( 0 )
531
- })
532
-
533
- })
534
- })
535
-
536
- describe( 'SubCollections', ()=>{
537
- let model: Model<SubClass>
538
-
539
- beforeEach(()=>{
540
- model = Store.getModelForSubCollection( testUser, 'SubClass' )
541
- })
542
-
543
- it( 'should retrieve from subcollection', async ()=>{
544
- const subClass = new SubClass()
545
- subClass.year = 3452
546
-
547
- await model.save( subClass )
548
-
549
- const loaded = await model.findById( subClass.id )
550
-
551
- expect( loaded?.year ).toBe( 3452 )
552
- })
553
- })
554
-
555
- })
@@ -1,146 +0,0 @@
1
- import { and, collection, connectFirestoreEmulator, deleteDoc, doc, DocumentData, getCountFromServer, getDoc, getDocs, limit, or, orderBy, Query, query, QueryDocumentSnapshot, QueryFieldFilterConstraint, QueryNonFilterConstraint, startAfter, where, WhereFilterOp, writeBatch } from 'firebase/firestore'
2
- import { Collections, DataSource, DocumentObject, QueryObject, QueryOperator } from 'entropic-bond'
3
- import { EmulatorConfig, FirebaseHelper, FirebaseQuery } from '../firebase-helper'
4
-
5
- interface ConstraintsContainer {
6
- andConstraints: QueryFieldFilterConstraint[]
7
- orConstraints: QueryFieldFilterConstraint[]
8
- nonFilterConstraints: QueryNonFilterConstraint[]
9
- }
10
-
11
- export class FirebaseDatasource extends DataSource {
12
- constructor( emulator?: EmulatorConfig ) {
13
- super()
14
- if ( emulator ) FirebaseHelper.useEmulator( emulator )
15
-
16
- if ( FirebaseHelper.emulator?.emulate ) {
17
- const { host, firestorePort } = FirebaseHelper.emulator
18
- connectFirestoreEmulator( FirebaseHelper.instance.firestore(), host, firestorePort )
19
- }
20
- }
21
-
22
- findById( id: string, collectionName: string ): Promise< DocumentObject > {
23
- const db = FirebaseHelper.instance.firestore()
24
-
25
- return new Promise<DocumentObject>( async resolve => {
26
- try {
27
- const docSnap = await getDoc( doc( db, collectionName, id ) )
28
- resolve( docSnap.data() as DocumentObject )
29
- }
30
- catch( error ) {
31
- console.log( error )
32
- return null
33
- }
34
- })
35
- }
36
-
37
- save( collections: Collections ): Promise< void > {
38
- const db = FirebaseHelper.instance.firestore()
39
- const batch = writeBatch( db )
40
-
41
- Object.entries( collections ).forEach(([ collectionName, collection ]) => {
42
- collection?.forEach( document => {
43
- const ref = doc( db, collectionName, document.id )
44
- batch.set( ref, document )
45
- })
46
- })
47
-
48
- return batch.commit()
49
- }
50
-
51
- find( queryObject: QueryObject<DocumentObject>, collectionName: string ): Promise< DocumentObject[] > {
52
- const query = this.queryObjectToQueryConstraints( queryObject, collectionName )
53
- return this.getFromQuery( query )
54
- }
55
-
56
- async count( queryObject: QueryObject<DocumentObject>, collectionName: string ): Promise<number> {
57
- const query = this.queryObjectToQueryConstraints( queryObject, collectionName )
58
-
59
- const snapShot = await getCountFromServer( query )
60
- return snapShot.data().count
61
- }
62
-
63
- delete( id: string, collectionName: string ): Promise< void > {
64
- const db = FirebaseHelper.instance.firestore()
65
-
66
- return deleteDoc( doc( db, collectionName, id ) )
67
- }
68
-
69
- next( maxDocs?: number ): Promise< DocumentObject[] > {
70
- if( !this._lastConstraints || !this._lastCollectionName ) throw new Error('You should perform a query prior to using method next')
71
-
72
- const db = FirebaseHelper.instance.firestore()
73
- this._lastLimit = maxDocs || this._lastLimit
74
-
75
- const constraints = this._lastConstraints.nonFilterConstraints.concat(
76
- limit( this._lastLimit ),
77
- startAfter( this._lastDocRetrieved )
78
- )
79
-
80
- return this.getFromQuery( query( collection( db, this._lastCollectionName ), ...constraints ) )
81
- }
82
-
83
- // prev should be used with next in reverse order
84
- // prev( limit?: number ): Promise< DocumentObject[] > {
85
- // }
86
-
87
- private queryObjectToQueryConstraints( queryObject: QueryObject<DocumentObject>, collectionName: string ): Query {
88
- const db = FirebaseHelper.instance.firestore()
89
- const andConstraints: QueryFieldFilterConstraint[] = []
90
- const orConstraints: QueryFieldFilterConstraint[] = []
91
- const nonFilterConstraints: QueryNonFilterConstraint[] = []
92
-
93
- DataSource.toPropertyPathOperations( queryObject.operations as any ).forEach( operation => {
94
- const operator = this.toFirebaseOperator( operation.operator )
95
- if ( operation.aggregate ) orConstraints.push( where( operation.property, operator, operation.value ) )
96
- else andConstraints.push( where( operation.property, operator, operation.value ) )
97
- })
98
-
99
- if ( queryObject.sort?.propertyName ) {
100
- nonFilterConstraints.push( orderBy( queryObject.sort.propertyName, queryObject.sort.order ) )
101
- }
102
-
103
- this._lastConstraints = {
104
- orConstraints,
105
- andConstraints,
106
- nonFilterConstraints
107
- }
108
-
109
- this._lastCollectionName = collectionName
110
-
111
- if( queryObject.limit ) {
112
- this._lastLimit = queryObject.limit
113
- nonFilterConstraints.push( limit( queryObject.limit ) )
114
- }
115
-
116
- return query( collection( db, collectionName ), or( ...orConstraints, and( ...andConstraints ) ), ...nonFilterConstraints )
117
- }
118
-
119
- toFirebaseOperator( operator: QueryOperator ): WhereFilterOp {
120
- switch( operator ) {
121
- case '==':
122
- case '!=':
123
- case '<':
124
- case '<=':
125
- case '>':
126
- case '>=': return operator
127
- case 'contains': return 'array-contains'
128
- case 'containsAny': return 'array-contains-any'
129
- default: return operator
130
- }
131
- }
132
-
133
- private getFromQuery( query: FirebaseQuery ) {
134
- return new Promise< DocumentObject[] >( async resolve => {
135
- const doc = await getDocs( query )
136
- this._lastDocRetrieved = doc.docs[ doc.docs.length-1 ]
137
-
138
- resolve( doc.docs.map( doc => doc.data() as DocumentObject ) )
139
- })
140
- }
141
-
142
- private _lastDocRetrieved: QueryDocumentSnapshot<DocumentData> | undefined
143
- private _lastConstraints: ConstraintsContainer | undefined
144
- private _lastLimit: number = 0
145
- private _lastCollectionName: string | undefined
146
- }
package/storage.rules DELETED
@@ -1,8 +0,0 @@
1
- rules_version = '2';
2
- service firebase.storage {
3
- match /b/{bucket}/o {
4
- match /{allPaths=**} {
5
- allow read, write
6
- }
7
- }
8
- }