@data-weave/backend-firestore 0.4.2 → 0.4.3
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/lib/FirestoreDataManager.d.ts +48 -0
- package/lib/FirestoreDataManager.js +223 -0
- package/lib/FirestoreList.d.ts +23 -0
- package/lib/FirestoreList.js +94 -0
- package/lib/FirestoreMetadata.d.ts +32 -0
- package/lib/FirestoreMetadata.js +39 -0
- package/lib/FirestoreReference.d.ts +27 -0
- package/lib/FirestoreReference.js +85 -0
- package/lib/firestoreTypes.d.ts +117 -0
- package/lib/firestoreTypes.js +130 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.js +22 -0
- package/lib/utils.d.ts +14 -0
- package/lib/utils.js +65 -0
- package/lib/version.js +1 -1
- package/package.json +8 -4
- package/CHANGELOG.md +0 -78
- package/lib/FirestoreDataManager.ts +0 -247
- package/lib/FirestoreList.ts +0 -96
- package/lib/FirestoreMetadata.ts +0 -59
- package/lib/FirestoreReference.ts +0 -92
- package/lib/firestoreTypes.ts +0 -251
- package/lib/index.ts +0 -6
- package/lib/utils.ts +0 -113
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
import { injectable } from 'inversify'
|
|
2
|
-
|
|
3
|
-
import { Cache, MapCache } from '@data-weave/datamanager/lib/Cache'
|
|
4
|
-
import { CreateOptions, DataManager, Metadata, WithMetadata } from '@data-weave/datamanager/lib/DataManager'
|
|
5
|
-
import { List, ListPaginationParams } from '@data-weave/datamanager/lib/List'
|
|
6
|
-
import { IdentifiableReference, WithoutId } from '@data-weave/datamanager/lib/Reference'
|
|
7
|
-
import { FirestoreList, FirestoreListContext } from './FirestoreList'
|
|
8
|
-
import {
|
|
9
|
-
FIRESTORE_KEYS,
|
|
10
|
-
FirestoreMetadataConverter,
|
|
11
|
-
FirestoreSerializedMetadata,
|
|
12
|
-
queryNotDeleted,
|
|
13
|
-
} from './FirestoreMetadata'
|
|
14
|
-
import { FirestoreReference, FirestoreReferenceContext } from './FirestoreReference'
|
|
15
|
-
import {
|
|
16
|
-
DocumentData,
|
|
17
|
-
FilterBy,
|
|
18
|
-
FirebaseCreateOptions,
|
|
19
|
-
Firestore,
|
|
20
|
-
FirestoreDataConverter,
|
|
21
|
-
FirestoreReadMode,
|
|
22
|
-
FirestoreReadOptions,
|
|
23
|
-
FirestoreTypes,
|
|
24
|
-
FirestoreWriteOptions,
|
|
25
|
-
InternalFirestoreDataConverter,
|
|
26
|
-
OrderBy,
|
|
27
|
-
WithFieldValue,
|
|
28
|
-
} from './firestoreTypes'
|
|
29
|
-
import { MergeConverters, checkIfReferenceExists } from './utils'
|
|
30
|
-
|
|
31
|
-
export type FirebaseDataManagerDeleteMode = 'soft' | 'hard'
|
|
32
|
-
|
|
33
|
-
export interface FirebaseDataManagerOptions {
|
|
34
|
-
readonly idResolver?: () => string
|
|
35
|
-
readonly deleteMode?: FirebaseDataManagerDeleteMode
|
|
36
|
-
readonly readMode?: FirestoreReadMode
|
|
37
|
-
readonly preventOverwriteOnCreate?: boolean
|
|
38
|
-
readonly errorInterceptor?: (error: unknown, ctx: FirestoreReferenceContext | FirestoreListContext) => void
|
|
39
|
-
readonly snapshotOptions?: FirestoreTypes.SnapshotOptions
|
|
40
|
-
// TODO: Add preventUpdateIfNotExists?
|
|
41
|
-
readonly Reference?: typeof FirestoreReference
|
|
42
|
-
readonly List?: typeof FirestoreList
|
|
43
|
-
readonly listCache?: Cache
|
|
44
|
-
readonly refCache?: Cache
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const defaultFirebaseDataManagerOptions: FirebaseDataManagerOptions = {
|
|
48
|
-
deleteMode: 'soft',
|
|
49
|
-
preventOverwriteOnCreate: true,
|
|
50
|
-
readMode: 'static',
|
|
51
|
-
Reference: FirestoreReference,
|
|
52
|
-
List: FirestoreList,
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface QueryParams<T extends FirestoreTypes.DocumentData> {
|
|
56
|
-
readonly filters?: Array<FilterBy<T & FirestoreSerializedMetadata>>
|
|
57
|
-
readonly orderBy?: Array<OrderBy<T & FirestoreSerializedMetadata>>
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
@injectable()
|
|
61
|
-
export class FirestoreDataManager<
|
|
62
|
-
T extends FirestoreTypes.DocumentData,
|
|
63
|
-
SerializedT extends FirestoreTypes.DocumentData = T,
|
|
64
|
-
> implements DataManager<T>
|
|
65
|
-
{
|
|
66
|
-
private mergedConverter: InternalFirestoreDataConverter<T & Metadata, SerializedT & FirestoreSerializedMetadata>
|
|
67
|
-
private collection: FirestoreTypes.CollectionReference<T & Metadata, SerializedT & FirestoreSerializedMetadata>
|
|
68
|
-
private collectionQuery: FirestoreTypes.Query<T & Metadata, SerializedT & FirestoreSerializedMetadata>
|
|
69
|
-
private managerOptions: FirebaseDataManagerOptions
|
|
70
|
-
|
|
71
|
-
private refCache: Cache<string, IdentifiableReference<WithMetadata<T>>>
|
|
72
|
-
private listCache: Cache<string, List<WithMetadata<T>>>
|
|
73
|
-
|
|
74
|
-
constructor(
|
|
75
|
-
private readonly firestore: Firestore,
|
|
76
|
-
private readonly collectionPath: string,
|
|
77
|
-
private readonly converter: FirestoreDataConverter<T, SerializedT>,
|
|
78
|
-
private readonly opts?: FirebaseDataManagerOptions
|
|
79
|
-
) {
|
|
80
|
-
// @ts-expect-error - Force merge FirestoreDataConverter and InternalFirestoreDataConverter
|
|
81
|
-
this.mergedConverter = new MergeConverters(this.converter, new FirestoreMetadataConverter())
|
|
82
|
-
this.managerOptions = Object.assign(defaultFirebaseDataManagerOptions, this.opts)
|
|
83
|
-
|
|
84
|
-
this.refCache = this.managerOptions.refCache || new MapCache(100)
|
|
85
|
-
this.listCache = this.managerOptions.listCache || new MapCache(100)
|
|
86
|
-
|
|
87
|
-
this.collection = this.firestore
|
|
88
|
-
.collection(this.firestore.app, this.collectionPath)
|
|
89
|
-
.withConverter(this.mergedConverter)
|
|
90
|
-
|
|
91
|
-
this.collectionQuery =
|
|
92
|
-
this.managerOptions.deleteMode === 'soft'
|
|
93
|
-
? queryNotDeleted(this.collection, this.firestore.query, this.firestore.where)
|
|
94
|
-
: this.collection
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
public async read(id: string, options?: FirestoreReadOptions): Promise<WithMetadata<T> | undefined> {
|
|
98
|
-
if (!this.managerOptions?.Reference) throw new Error('ReferenceClass not defined')
|
|
99
|
-
|
|
100
|
-
const ref = this.getRef(id)
|
|
101
|
-
|
|
102
|
-
if (options?.transaction) {
|
|
103
|
-
const snapshot = await options.transaction.get<WithMetadata<T>, DocumentData>(
|
|
104
|
-
this.firestore.doc(this.collection, id)
|
|
105
|
-
)
|
|
106
|
-
if (!checkIfReferenceExists(snapshot)) return undefined
|
|
107
|
-
return snapshot.data()
|
|
108
|
-
}
|
|
109
|
-
return await ref.resolve()
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public async create(data: WithFieldValue<WithoutId<T>>, options?: FirebaseCreateOptions) {
|
|
113
|
-
let id: string | undefined = undefined
|
|
114
|
-
if (options?.id) {
|
|
115
|
-
id = options?.id
|
|
116
|
-
} else if (this.managerOptions?.idResolver) {
|
|
117
|
-
id = this.managerOptions.idResolver()
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
let docRef: FirestoreTypes.DocumentReference
|
|
121
|
-
let docExists: boolean = false
|
|
122
|
-
|
|
123
|
-
if (id) {
|
|
124
|
-
docRef = this.firestore.doc(this.collection, id)
|
|
125
|
-
docExists = await this.preventOverwriteOnCreate(docRef, options)
|
|
126
|
-
} else {
|
|
127
|
-
docRef = this.firestore.doc(this.collection)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const docDataWithMetadata = {
|
|
131
|
-
...data,
|
|
132
|
-
[FIRESTORE_KEYS.CREATED_AT]: docExists ? undefined : this.firestore.serverTimestamp(),
|
|
133
|
-
[FIRESTORE_KEYS.UPDATED_AT]: this.firestore.serverTimestamp(),
|
|
134
|
-
[FIRESTORE_KEYS.DELETED]: false,
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const firebaseOptions = { merge: options?.merge }
|
|
138
|
-
|
|
139
|
-
if (options?.transaction) {
|
|
140
|
-
options?.transaction.set(docRef, docDataWithMetadata, firebaseOptions)
|
|
141
|
-
} else {
|
|
142
|
-
await this.firestore.setDoc(docRef, docDataWithMetadata, firebaseOptions)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return this.getRef(docRef.id)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private async _update(
|
|
149
|
-
id: string,
|
|
150
|
-
data: WithoutId<Partial<WithFieldValue<T & Metadata>>>,
|
|
151
|
-
options?: FirestoreWriteOptions
|
|
152
|
-
) {
|
|
153
|
-
const extendedData = {
|
|
154
|
-
...data,
|
|
155
|
-
[FIRESTORE_KEYS.UPDATED_AT]: this.firestore.serverTimestamp(),
|
|
156
|
-
}
|
|
157
|
-
// Firestore update method doesn't call converter like setDoc does, so we need to serialize the data manually.
|
|
158
|
-
const serializedData = this.mergedConverter.toFirestore(extendedData)
|
|
159
|
-
|
|
160
|
-
const ref = this.firestore.doc(this.collection, id)
|
|
161
|
-
|
|
162
|
-
if (options?.transaction) {
|
|
163
|
-
return options.transaction.update<DocumentData, DocumentData>(ref, serializedData)
|
|
164
|
-
}
|
|
165
|
-
return this.firestore.updateDoc(ref, serializedData)
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
public async update(id: string, data: WithoutId<Partial<WithFieldValue<T>>>, options?: FirestoreWriteOptions) {
|
|
169
|
-
await this._update(id, data, options)
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
public async upsert(id: string, data: WithFieldValue<WithoutId<T>>, options?: FirestoreWriteOptions) {
|
|
173
|
-
this.create(data, { ...options, id, merge: true })
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
public async delete(id: string, options?: FirestoreWriteOptions) {
|
|
177
|
-
if (this.managerOptions.deleteMode === 'soft') {
|
|
178
|
-
await this._update(
|
|
179
|
-
id,
|
|
180
|
-
{
|
|
181
|
-
...({} as Partial<T>),
|
|
182
|
-
[FIRESTORE_KEYS.DELETED]: true,
|
|
183
|
-
},
|
|
184
|
-
options
|
|
185
|
-
)
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
return this.firestore.deleteDoc(this.firestore.doc(this.collection, id))
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
public getRef(id: string) {
|
|
192
|
-
if (!this.managerOptions?.Reference) throw new Error('ReferenceClass not defined')
|
|
193
|
-
|
|
194
|
-
if (this.refCache.has(id)) {
|
|
195
|
-
return this.refCache.get(id)!
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const newRef = new this.managerOptions.Reference(this.firestore, this.firestore.doc(this.collection, id), {
|
|
199
|
-
readMode: this.managerOptions.readMode,
|
|
200
|
-
errorInterceptor: this.managerOptions.errorInterceptor,
|
|
201
|
-
snapshotOptions: this.managerOptions.snapshotOptions,
|
|
202
|
-
})
|
|
203
|
-
this.refCache.set(id, newRef)
|
|
204
|
-
return newRef
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
private _getFilteredQuery(params?: QueryParams<SerializedT>) {
|
|
208
|
-
let compoundQuery = this.collectionQuery
|
|
209
|
-
|
|
210
|
-
params?.filters?.forEach(filter => {
|
|
211
|
-
compoundQuery = this.firestore.query(compoundQuery, this.firestore.where(filter[0], filter[1], filter[2]))
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
params?.orderBy?.forEach(orderBy => {
|
|
215
|
-
compoundQuery = this.firestore.query(compoundQuery, this.firestore.orderBy(orderBy[0], orderBy[1]))
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
return compoundQuery
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
public getList(params?: QueryParams<SerializedT> & ListPaginationParams) {
|
|
222
|
-
if (!this.managerOptions.List) throw new Error('ListClass not defined')
|
|
223
|
-
|
|
224
|
-
const compoundQuery = this._getFilteredQuery(params)
|
|
225
|
-
|
|
226
|
-
const key = JSON.stringify(params || {})
|
|
227
|
-
if (this.listCache.has(key)) {
|
|
228
|
-
return this.listCache.get(key)!
|
|
229
|
-
}
|
|
230
|
-
const newList = new this.managerOptions.List(this.firestore, compoundQuery, {
|
|
231
|
-
readMode: this.managerOptions.readMode,
|
|
232
|
-
errorInterceptor: this.managerOptions.errorInterceptor,
|
|
233
|
-
...params,
|
|
234
|
-
})
|
|
235
|
-
this.listCache.set(key, newList)
|
|
236
|
-
return newList
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private async preventOverwriteOnCreate(docRef: FirestoreTypes.DocumentReference, createOptions?: CreateOptions) {
|
|
240
|
-
const doc = await this.firestore.getDoc(docRef)
|
|
241
|
-
const docExists = checkIfReferenceExists(doc)
|
|
242
|
-
if (docExists && this.managerOptions.preventOverwriteOnCreate && createOptions?.merge !== true) {
|
|
243
|
-
throw new Error(`Document already exists - ${doc.ref.path}`)
|
|
244
|
-
}
|
|
245
|
-
return docExists
|
|
246
|
-
}
|
|
247
|
-
}
|
package/lib/FirestoreList.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { ListPaginationParams, LiveList, LiveListOptions } from '@data-weave/datamanager'
|
|
2
|
-
import { DocumentData, Firestore, FirestoreReadMode, FirestoreTypes } from './firestoreTypes'
|
|
3
|
-
|
|
4
|
-
export interface FirestoreListContext {
|
|
5
|
-
query: FirestoreTypes.Query<unknown>
|
|
6
|
-
readMode?: FirestoreReadMode
|
|
7
|
-
type: 'list'
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface FirestoreListOptions<T> extends LiveListOptions<T> {
|
|
11
|
-
readMode?: FirestoreReadMode
|
|
12
|
-
errorInterceptor?: (error: unknown, ctx: FirestoreListContext) => void
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class FirestoreList<T extends DocumentData, S extends DocumentData> extends LiveList<T> {
|
|
16
|
-
private unsubscribeFromSnapshot: undefined | (() => void)
|
|
17
|
-
|
|
18
|
-
constructor(
|
|
19
|
-
private readonly firestore: Firestore,
|
|
20
|
-
private readonly query: FirestoreTypes.Query<T, S>,
|
|
21
|
-
private readonly options: FirestoreListOptions<T> & ListPaginationParams
|
|
22
|
-
) {
|
|
23
|
-
super(options)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
public async resolve() {
|
|
27
|
-
if (this.options?.readMode === 'realtime') {
|
|
28
|
-
let initialLoad = true
|
|
29
|
-
return new Promise<T[]>((resolve, reject) => {
|
|
30
|
-
this.unsubscribeFromSnapshot = this.firestore.onSnapshot(
|
|
31
|
-
this.query,
|
|
32
|
-
querySnapshot => {
|
|
33
|
-
try {
|
|
34
|
-
if (initialLoad) {
|
|
35
|
-
initialLoad = false
|
|
36
|
-
this.handleInitialDataChange(querySnapshot.docs)
|
|
37
|
-
} else {
|
|
38
|
-
this.handleSubsequentDataChanges(querySnapshot.docChanges())
|
|
39
|
-
}
|
|
40
|
-
// TODO: When calling ".resolve()" with realtime listener,
|
|
41
|
-
// the snapshot data might be stale from cache.
|
|
42
|
-
resolve(this.values)
|
|
43
|
-
} catch (error) {
|
|
44
|
-
this.onError(error)
|
|
45
|
-
reject(error)
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
error => {
|
|
49
|
-
this.onError(error)
|
|
50
|
-
reject(error)
|
|
51
|
-
}
|
|
52
|
-
)
|
|
53
|
-
})
|
|
54
|
-
} else {
|
|
55
|
-
const snapshot = await this.firestore.getDocs(this.query)
|
|
56
|
-
this.handleInitialDataChange(snapshot.docs)
|
|
57
|
-
return this.values
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
protected handleInitialDataChange(values: FirestoreTypes.QueryDocumentSnapshot<T, S>[]) {
|
|
62
|
-
const newValues = values.map(v => v.data())
|
|
63
|
-
this.onUpdateAll(newValues)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
protected handleSubsequentDataChanges(changes: FirestoreTypes.DocumentChange<T, S>[]) {
|
|
67
|
-
changes.forEach(change => {
|
|
68
|
-
if (change.type === 'added') {
|
|
69
|
-
this.onAddAtIndex(change.newIndex, change.doc.data())
|
|
70
|
-
} else if (change.type === 'modified') {
|
|
71
|
-
this.onUpdateAtIndex(change.newIndex, change.doc.data())
|
|
72
|
-
} else if (change.type === 'removed') {
|
|
73
|
-
this.onRemoveAtIndex(change.oldIndex)
|
|
74
|
-
}
|
|
75
|
-
})
|
|
76
|
-
// TODO: handle onUpdate in the parent class - make sure it's only called once after all changes are processed
|
|
77
|
-
this.onUpdate()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
public unSubscribe() {
|
|
81
|
-
this.unsubscribeFromSnapshot?.()
|
|
82
|
-
this.setStale()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
protected onError(error: unknown) {
|
|
86
|
-
// Try to provide useful collection details using internal properties
|
|
87
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
|
-
console.error(`FirestoreList Collection: ${(this.query as any)?._collectionPath?.id} error`, error)
|
|
89
|
-
this.options?.errorInterceptor?.(error, {
|
|
90
|
-
query: this.query,
|
|
91
|
-
readMode: this.options?.readMode,
|
|
92
|
-
type: 'list',
|
|
93
|
-
})
|
|
94
|
-
super.onError(error)
|
|
95
|
-
}
|
|
96
|
-
}
|
package/lib/FirestoreMetadata.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { Metadata, WithoutId } from '@data-weave/datamanager'
|
|
2
|
-
import {
|
|
3
|
-
DocumentData,
|
|
4
|
-
FirestoreQuery,
|
|
5
|
-
FirestoreTypes,
|
|
6
|
-
FirestoreWhere,
|
|
7
|
-
InternalFirestoreDataConverter,
|
|
8
|
-
WithFieldValue,
|
|
9
|
-
} from './firestoreTypes'
|
|
10
|
-
|
|
11
|
-
enum FIRESTORE_INTERAL_KEYS {
|
|
12
|
-
DELETED = '__deleted',
|
|
13
|
-
CREATED_AT = '__createdAt',
|
|
14
|
-
UPDATED_AT = '__updatedAt',
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export enum FIRESTORE_KEYS {
|
|
18
|
-
DELETED = 'deleted',
|
|
19
|
-
CREATED_AT = 'createdAt',
|
|
20
|
-
UPDATED_AT = 'updatedAt',
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type FirestoreSerializedMetadata = {
|
|
24
|
-
[FIRESTORE_INTERAL_KEYS.DELETED]: boolean
|
|
25
|
-
[FIRESTORE_INTERAL_KEYS.CREATED_AT]: FirestoreTypes.Timestamp
|
|
26
|
-
[FIRESTORE_INTERAL_KEYS.UPDATED_AT]: FirestoreTypes.Timestamp
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class FirestoreMetadataConverter
|
|
30
|
-
implements InternalFirestoreDataConverter<Metadata, FirestoreSerializedMetadata>
|
|
31
|
-
{
|
|
32
|
-
toFirestore(data: WithFieldValue<WithoutId<Metadata>>) {
|
|
33
|
-
return {
|
|
34
|
-
[FIRESTORE_INTERAL_KEYS.DELETED]: data[FIRESTORE_KEYS.DELETED],
|
|
35
|
-
// Cast: Firestore handles conversion from Date to Timestamp internally
|
|
36
|
-
[FIRESTORE_INTERAL_KEYS.CREATED_AT]: data[FIRESTORE_KEYS.CREATED_AT] as unknown as FirestoreTypes.Timestamp,
|
|
37
|
-
[FIRESTORE_INTERAL_KEYS.UPDATED_AT]: data[FIRESTORE_KEYS.UPDATED_AT] as unknown as FirestoreTypes.Timestamp,
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
fromFirestore(
|
|
41
|
-
snapshot: FirestoreTypes.QueryDocumentSnapshot<DocumentData, DocumentData>,
|
|
42
|
-
options: FirestoreTypes.SnapshotOptions
|
|
43
|
-
) {
|
|
44
|
-
const data = snapshot.data(options) as FirestoreSerializedMetadata
|
|
45
|
-
return {
|
|
46
|
-
id: snapshot.ref.id,
|
|
47
|
-
// TODO: add warning in case data type is not as expected
|
|
48
|
-
createdAt: data[FIRESTORE_INTERAL_KEYS.CREATED_AT]?.toDate(),
|
|
49
|
-
updatedAt: data[FIRESTORE_INTERAL_KEYS.UPDATED_AT]?.toDate(),
|
|
50
|
-
deleted: data[FIRESTORE_INTERAL_KEYS.DELETED],
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export const queryNotDeleted = <T extends DocumentData, SerializedT extends DocumentData>(
|
|
56
|
-
query: FirestoreTypes.Query<T, SerializedT>,
|
|
57
|
-
firestoreQuery: FirestoreQuery<T, SerializedT>,
|
|
58
|
-
firestoreWhere: FirestoreWhere
|
|
59
|
-
) => firestoreQuery(query, firestoreWhere(FIRESTORE_INTERAL_KEYS.DELETED, '==', false))
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { LiveReference, LiveReferenceOptions } from '@data-weave/datamanager'
|
|
2
|
-
import { DocumentData, Firestore, FirestoreReadMode, FirestoreTypes } from './firestoreTypes'
|
|
3
|
-
import { checkIfReferenceExists } from './utils'
|
|
4
|
-
|
|
5
|
-
export interface FirestoreReferenceContext {
|
|
6
|
-
path: string
|
|
7
|
-
id: string
|
|
8
|
-
readMode?: FirestoreReadMode
|
|
9
|
-
snapshotOptions?: FirestoreTypes.SnapshotOptions
|
|
10
|
-
type: 'reference'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface FirestoreReferenceOptions<T> extends LiveReferenceOptions<T> {
|
|
14
|
-
readMode?: FirestoreReadMode
|
|
15
|
-
snapshotOptions?: FirestoreTypes.SnapshotOptions
|
|
16
|
-
errorInterceptor?: (error: unknown, ctx: FirestoreReferenceContext) => void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export class FirestoreReference<T extends DocumentData, S extends DocumentData> extends LiveReference<T> {
|
|
20
|
-
private unsubscribeFromSnapshot: undefined | (() => void)
|
|
21
|
-
|
|
22
|
-
constructor(
|
|
23
|
-
private readonly firestore: Firestore,
|
|
24
|
-
private readonly docRef: FirestoreTypes.DocumentReference<T, S>,
|
|
25
|
-
readonly options: FirestoreReferenceOptions<T>
|
|
26
|
-
) {
|
|
27
|
-
super(options)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
public async resolve(): Promise<T | undefined> {
|
|
31
|
-
if (this.options?.readMode === 'realtime') {
|
|
32
|
-
return new Promise<T | undefined>((res, reject) => {
|
|
33
|
-
this.unsubscribeFromSnapshot = this.firestore.onSnapshot(
|
|
34
|
-
this.docRef,
|
|
35
|
-
documentSnapshot => {
|
|
36
|
-
try {
|
|
37
|
-
this.onUpdate(this.parseDocumentSnapshot(documentSnapshot))
|
|
38
|
-
// TODO: When calling ".resolve()" with realtime listener,
|
|
39
|
-
// the snapshot data might be stale from cache.
|
|
40
|
-
res(this.value)
|
|
41
|
-
} catch (error) {
|
|
42
|
-
this.onError(error)
|
|
43
|
-
reject(error)
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
error => {
|
|
47
|
-
this.onError(error)
|
|
48
|
-
reject(error)
|
|
49
|
-
}
|
|
50
|
-
)
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
try {
|
|
54
|
-
const doc = await this.firestore.getDoc(this.docRef)
|
|
55
|
-
this.onUpdate(this.parseDocumentSnapshot(doc))
|
|
56
|
-
} catch (error) {
|
|
57
|
-
this.onError(error)
|
|
58
|
-
throw error
|
|
59
|
-
}
|
|
60
|
-
return this.value
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public get id(): string {
|
|
64
|
-
return this.docRef.id
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
public get path(): string {
|
|
68
|
-
return this.docRef.path
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
protected onError(error: unknown): void {
|
|
72
|
-
console.error(`FirestoreReference error ${this.docRef.path}`, error)
|
|
73
|
-
this.options?.errorInterceptor?.(error, {
|
|
74
|
-
path: this.docRef.path,
|
|
75
|
-
id: this.docRef.id,
|
|
76
|
-
readMode: this.options?.readMode,
|
|
77
|
-
snapshotOptions: this.options?.snapshotOptions,
|
|
78
|
-
type: 'reference',
|
|
79
|
-
})
|
|
80
|
-
super.onError(error)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
public unSubscribe() {
|
|
84
|
-
this.unsubscribeFromSnapshot?.()
|
|
85
|
-
this.setStale()
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
private parseDocumentSnapshot(docSnapshot: FirestoreTypes.DocumentSnapshot<T, S>): T | undefined {
|
|
89
|
-
if (!checkIfReferenceExists(docSnapshot)) throw new Error(`Document does not exist ${this.docRef.path}`)
|
|
90
|
-
return docSnapshot.data(this.options?.snapshotOptions)
|
|
91
|
-
}
|
|
92
|
-
}
|