-
Notifications
You must be signed in to change notification settings - Fork 73
/
BaseFirestoreRepository.ts
121 lines (97 loc) · 2.99 KB
/
BaseFirestoreRepository.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
import 'reflect-metadata';
import { Query, WhereFilterOp } from '@google-cloud/firestore';
import {
IRepository,
IFireOrmQueryLine,
IOrderByParams,
IEntity,
PartialBy,
ITransactionRepository,
ICustomQuery,
} from './types';
import { getMetadataStorage } from './MetadataUtils';
import { AbstractFirestoreRepository } from './AbstractFirestoreRepository';
import { FirestoreBatch } from './Batch/FirestoreBatch';
export class BaseFirestoreRepository<T extends IEntity>
extends AbstractFirestoreRepository<T>
implements IRepository<T>
{
async findById(id: string) {
return this.firestoreColRef
.doc(id)
.get()
.then(d => (d.exists ? this.extractTFromDocSnap(d) : null));
}
async create(item: PartialBy<T, 'id'>): Promise<T> {
if (this.config.validateModels) {
const errors = await this.validate(item as T);
if (errors.length) {
throw errors;
}
}
if (item.id) {
const found = await this.findById(item.id);
if (found) {
throw new Error(`A document with id ${item.id} already exists.`);
}
}
const doc = item.id ? this.firestoreColRef.doc(item.id) : this.firestoreColRef.doc();
if (!item.id) {
item.id = doc.id;
}
await doc.set(this.toSerializableObject(item as T));
this.initializeSubCollections(item as T);
return item as T;
}
async update(item: T) {
if (this.config.validateModels) {
const errors = await this.validate(item);
if (errors.length) {
throw errors;
}
}
// TODO: handle errors
await this.firestoreColRef.doc(item.id).update(this.toSerializableObject(item));
return item;
}
async delete(id: string): Promise<void> {
// TODO: handle errors
await this.firestoreColRef.doc(id).delete();
}
async runTransaction<R>(executor: (tran: ITransactionRepository<T>) => Promise<R>) {
// Importing here to prevent circular dependency
const { runTransaction } = await import('./helpers');
return runTransaction<R>(tran => {
const repository = tran.getRepository<T>(this.path);
return executor(repository);
});
}
createBatch() {
const { firestoreRef } = getMetadataStorage();
return new FirestoreBatch(firestoreRef).getSingleRepository(this.path);
}
async execute(
queries: Array<IFireOrmQueryLine>,
limitVal?: number,
orderByObj?: IOrderByParams,
single?: boolean,
customQuery?: ICustomQuery<T>
): Promise<T[]> {
let query = queries.reduce<Query>((acc, cur) => {
const op = cur.operator as WhereFilterOp;
return acc.where(cur.prop, op, cur.val);
}, this.firestoreColRef);
if (orderByObj) {
query = query.orderBy(orderByObj.fieldPath, orderByObj.directionStr);
}
if (single) {
query = query.limit(1);
} else if (limitVal) {
query = query.limit(limitVal);
}
if (customQuery) {
query = await customQuery(query, this.firestoreColRef);
}
return query.get().then(this.extractTFromColSnap);
}
}