@nu-art/firebase-backend 0.400.5
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/ModuleBE_Firebase.d.ts +20 -0
- package/ModuleBE_Firebase.js +69 -0
- package/auth/FirebaseBaseWrapper.d.ts +19 -0
- package/auth/FirebaseBaseWrapper.js +39 -0
- package/auth/FirebaseSession_Admin.d.ts +19 -0
- package/auth/FirebaseSession_Admin.js +45 -0
- package/auth/firebase-session.d.ts +61 -0
- package/auth/firebase-session.js +93 -0
- package/database/DatabaseWrapperBE.d.ts +71 -0
- package/database/DatabaseWrapperBE.js +182 -0
- package/database/types.d.ts +4 -0
- package/database/types.js +18 -0
- package/firestore/FirestoreCollection.d.ts +71 -0
- package/firestore/FirestoreCollection.js +184 -0
- package/firestore/FirestoreInterface.d.ts +11 -0
- package/firestore/FirestoreInterface.js +111 -0
- package/firestore/FirestoreTransaction.d.ts +30 -0
- package/firestore/FirestoreTransaction.js +153 -0
- package/firestore/FirestoreWrapperBE.d.ts +16 -0
- package/firestore/FirestoreWrapperBE.js +53 -0
- package/firestore/types.d.ts +6 -0
- package/firestore/types.js +25 -0
- package/firestore-v3/DocWrapperV3.d.ts +32 -0
- package/firestore-v3/DocWrapperV3.js +148 -0
- package/firestore-v3/FirestoreCollectionV3.d.ts +154 -0
- package/firestore-v3/FirestoreCollectionV3.js +470 -0
- package/firestore-v3/FirestoreInterfaceV3.d.ts +10 -0
- package/firestore-v3/FirestoreInterfaceV3.js +107 -0
- package/firestore-v3/FirestoreWrapperBEV3.d.ts +16 -0
- package/firestore-v3/FirestoreWrapperBEV3.js +154 -0
- package/firestore-v3/consts.d.ts +13 -0
- package/firestore-v3/consts.js +18 -0
- package/firestore-v3/types.d.ts +6 -0
- package/firestore-v3/types.js +1 -0
- package/functions/firebase-function.d.ts +38 -0
- package/functions/firebase-function.js +53 -0
- package/functions-v2/ModuleBE_BaseFunction.d.ts +11 -0
- package/functions-v2/ModuleBE_BaseFunction.js +32 -0
- package/functions-v2/ModuleBE_ExpressFunction_V2.d.ts +11 -0
- package/functions-v2/ModuleBE_ExpressFunction_V2.js +29 -0
- package/functions-v2/ModuleBE_FirebaseDBListener.d.ts +10 -0
- package/functions-v2/ModuleBE_FirebaseDBListener.js +24 -0
- package/functions-v2/ModuleBE_FirebaseScheduler.d.ts +32 -0
- package/functions-v2/ModuleBE_FirebaseScheduler.js +57 -0
- package/functions-v2/ModuleBE_FirestoreListener.d.ts +13 -0
- package/functions-v2/ModuleBE_FirestoreListener.js +21 -0
- package/functions-v2/ModuleBE_PubSubFunction.d.ts +13 -0
- package/functions-v2/ModuleBE_PubSubFunction.js +47 -0
- package/functions-v2/ModuleBE_StorageListener.d.ts +13 -0
- package/functions-v2/ModuleBE_StorageListener.js +34 -0
- package/index.d.ts +21 -0
- package/index.js +38 -0
- package/package.json +75 -0
- package/push/PushMessagesWrapperBE.d.ts +14 -0
- package/push/PushMessagesWrapperBE.js +44 -0
- package/push/types.d.ts +3 -0
- package/push/types.js +18 -0
- package/storage/StorageWrapperBE.d.ts +63 -0
- package/storage/StorageWrapperBE.js +246 -0
- package/storage/emulator.d.ts +4 -0
- package/storage/emulator.js +46 -0
- package/storage/types.d.ts +4 -0
- package/storage/types.js +18 -0
- package/tools/lock-operation.d.ts +10 -0
- package/tools/lock-operation.js +35 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Subset, TS_Object } from '@nu-art/ts-common';
|
|
2
|
+
import { FirestoreType_Collection, FirestoreType_DocumentSnapshot } from './types.js';
|
|
3
|
+
import { Clause_Select, Clause_Where, FilterKeys, FirestoreQuery } from '@nu-art/firebase-shared';
|
|
4
|
+
import { FirestoreWrapperBE } from './FirestoreWrapperBE.js';
|
|
5
|
+
import { FirestoreTransaction } from './FirestoreTransaction.js';
|
|
6
|
+
import { Transaction } from 'firebase-admin/firestore';
|
|
7
|
+
/**
|
|
8
|
+
* FirestoreCollection is a class for handling Firestore collections. It takes in the name, FirestoreWrapperBE instance, and uniqueKeys as parameters.
|
|
9
|
+
*/
|
|
10
|
+
export declare class FirestoreCollection<Type extends TS_Object> {
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly wrapper: FirestoreWrapperBE;
|
|
13
|
+
readonly collection: FirestoreType_Collection;
|
|
14
|
+
/**
|
|
15
|
+
* External unique as in there must never ever be two that answer the same query
|
|
16
|
+
*/
|
|
17
|
+
readonly externalUniqueFilter: ((object: Subset<Type>) => Clause_Where<Type>);
|
|
18
|
+
/**
|
|
19
|
+
* @param name
|
|
20
|
+
* @param wrapper
|
|
21
|
+
* @param uniqueKeys
|
|
22
|
+
*/
|
|
23
|
+
constructor(name: string, wrapper: FirestoreWrapperBE, uniqueKeys?: FilterKeys<Type>);
|
|
24
|
+
/**
|
|
25
|
+
Executes a Firestore query on the collection.
|
|
26
|
+
@param ourQuery - The query to execute.
|
|
27
|
+
@returns A Promise that resolves to an array of FirestoreType_DocumentSnapshot objects.
|
|
28
|
+
@private
|
|
29
|
+
*/
|
|
30
|
+
private _query;
|
|
31
|
+
/**
|
|
32
|
+
|
|
33
|
+
Executes a unique Firestore query on the collection.
|
|
34
|
+
@param ourQuery - The query to execute.
|
|
35
|
+
@returns A Promise that resolves to a single FirestoreType_DocumentSnapshot object, or undefined if no match is found.
|
|
36
|
+
@private
|
|
37
|
+
*/
|
|
38
|
+
private _queryUnique;
|
|
39
|
+
/**
|
|
40
|
+
Executes a unique Firestore query on the collection and returns the matching object.
|
|
41
|
+
@param ourQuery - The query to execute.
|
|
42
|
+
@returns A Promise that resolves to the matching object, or undefined if no match is found.
|
|
43
|
+
*/
|
|
44
|
+
queryUnique(ourQuery: FirestoreQuery<Type>): Promise<Type | undefined>;
|
|
45
|
+
insert(instance: Type, _id?: string): Promise<Type>;
|
|
46
|
+
insertAll(instances: Type[]): Promise<Awaited<Type>[]>;
|
|
47
|
+
query(ourQuery: FirestoreQuery<Type>): Promise<Type[]>;
|
|
48
|
+
upsert(instance: Type): Promise<Type>;
|
|
49
|
+
upsertAll(instances: Type[]): Promise<Type[]>;
|
|
50
|
+
patch(instance: Subset<Type>): Promise<Type>;
|
|
51
|
+
deleteItem(instance: Type): Promise<Type | undefined>;
|
|
52
|
+
deleteUnique(query: FirestoreQuery<Type>): Promise<Type | undefined>;
|
|
53
|
+
delete(query: FirestoreQuery<Type>): Promise<Type[]>;
|
|
54
|
+
private deleteBatch;
|
|
55
|
+
deleteAll(): Promise<Type[]>;
|
|
56
|
+
getAll(select?: Clause_Select<Type>): Promise<Type[]>;
|
|
57
|
+
runInTransaction<ReturnType>(processor: (transaction: FirestoreTransaction) => Promise<ReturnType>): Promise<ReturnType>;
|
|
58
|
+
createDocumentReference(_id?: string): FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>;
|
|
59
|
+
getUniqueFilter: () => (object: Subset<Type>) => Clause_Where<Type>;
|
|
60
|
+
newQueryUnique(ourQuery: FirestoreQuery<Type>): Promise<DocWrapper<Type> | undefined>;
|
|
61
|
+
newQuery(ourQuery: FirestoreQuery<Type>): Promise<DocWrapper<Type>[]>;
|
|
62
|
+
}
|
|
63
|
+
export declare class DocWrapper<T extends TS_Object> {
|
|
64
|
+
wrapper: FirestoreWrapperBE;
|
|
65
|
+
doc: FirestoreType_DocumentSnapshot<T>;
|
|
66
|
+
constructor(wrapper: FirestoreWrapperBE, doc: FirestoreType_DocumentSnapshot<T>);
|
|
67
|
+
runInTransaction<R>(processor: (transaction: Transaction) => Promise<R>): Promise<R>;
|
|
68
|
+
delete: (transaction?: Transaction) => Promise<T>;
|
|
69
|
+
get: () => T;
|
|
70
|
+
set: (instance: T, transaction?: Transaction) => Promise<T>;
|
|
71
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Firebase is a simpler Typescript wrapper to all of firebase services.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
import { BadImplementationException, batchAction, exists, generateHex, StaticLogger } from '@nu-art/ts-common';
|
|
19
|
+
import { FirestoreInterface } from './FirestoreInterface.js';
|
|
20
|
+
import { FirestoreTransaction } from './FirestoreTransaction.js';
|
|
21
|
+
/**
|
|
22
|
+
* FirestoreCollection is a class for handling Firestore collections. It takes in the name, FirestoreWrapperBE instance, and uniqueKeys as parameters.
|
|
23
|
+
*/
|
|
24
|
+
export class FirestoreCollection {
|
|
25
|
+
name;
|
|
26
|
+
wrapper;
|
|
27
|
+
collection;
|
|
28
|
+
/**
|
|
29
|
+
* External unique as in there must never ever be two that answer the same query
|
|
30
|
+
*/
|
|
31
|
+
externalUniqueFilter;
|
|
32
|
+
/**
|
|
33
|
+
* @param name
|
|
34
|
+
* @param wrapper
|
|
35
|
+
* @param uniqueKeys
|
|
36
|
+
*/
|
|
37
|
+
constructor(name, wrapper, uniqueKeys) {
|
|
38
|
+
this.name = name;
|
|
39
|
+
this.wrapper = wrapper;
|
|
40
|
+
if (!/[a-z-]{3,}/.test(name))
|
|
41
|
+
StaticLogger.logWarning('Please follow name pattern for collections /[a-z-]{3,}/');
|
|
42
|
+
this.collection = wrapper.firestore.collection(name);
|
|
43
|
+
this.externalUniqueFilter = (instance) => {
|
|
44
|
+
if (!uniqueKeys)
|
|
45
|
+
throw new BadImplementationException('In order to use a unique query your collection MUST have a unique filter');
|
|
46
|
+
return uniqueKeys.reduce((where, key) => {
|
|
47
|
+
if (!exists(instance[key]))
|
|
48
|
+
throw new BadImplementationException(`No where properties are allowed to be null or undefined.\nWhile querying collection '${this.name}' we found property '${String(key)}' to be '${where[key]}'`);
|
|
49
|
+
where[key] = instance[key];
|
|
50
|
+
return where;
|
|
51
|
+
}, {});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
Executes a Firestore query on the collection.
|
|
56
|
+
@param ourQuery - The query to execute.
|
|
57
|
+
@returns A Promise that resolves to an array of FirestoreType_DocumentSnapshot objects.
|
|
58
|
+
@private
|
|
59
|
+
*/
|
|
60
|
+
async _query(ourQuery) {
|
|
61
|
+
const myQuery = FirestoreInterface.buildQuery(this, ourQuery);
|
|
62
|
+
return (await myQuery.get()).docs;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
|
|
66
|
+
Executes a unique Firestore query on the collection.
|
|
67
|
+
@param ourQuery - The query to execute.
|
|
68
|
+
@returns A Promise that resolves to a single FirestoreType_DocumentSnapshot object, or undefined if no match is found.
|
|
69
|
+
@private
|
|
70
|
+
*/
|
|
71
|
+
async _queryUnique(ourQuery) {
|
|
72
|
+
const results = await this._query(ourQuery);
|
|
73
|
+
return FirestoreInterface.assertUniqueDocument(results, ourQuery, this.name);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
Executes a unique Firestore query on the collection and returns the matching object.
|
|
77
|
+
@param ourQuery - The query to execute.
|
|
78
|
+
@returns A Promise that resolves to the matching object, or undefined if no match is found.
|
|
79
|
+
*/
|
|
80
|
+
async queryUnique(ourQuery) {
|
|
81
|
+
const doc = await this._queryUnique(ourQuery);
|
|
82
|
+
if (!doc)
|
|
83
|
+
return;
|
|
84
|
+
return doc.data();
|
|
85
|
+
}
|
|
86
|
+
async insert(instance, _id) {
|
|
87
|
+
await this.createDocumentReference(_id).set(instance);
|
|
88
|
+
return instance;
|
|
89
|
+
}
|
|
90
|
+
async insertAll(instances) {
|
|
91
|
+
return await Promise.all(instances.map(instance => this.insert(instance)));
|
|
92
|
+
}
|
|
93
|
+
async query(ourQuery) {
|
|
94
|
+
return (await this._query(ourQuery)).map(result => result.data());
|
|
95
|
+
}
|
|
96
|
+
async upsert(instance) {
|
|
97
|
+
return this.runInTransaction((transaction) => transaction.upsert(this, instance));
|
|
98
|
+
}
|
|
99
|
+
async upsertAll(instances) {
|
|
100
|
+
return batchAction(instances, 500, async (chunked) => this.runInTransaction(transaction => transaction.upsertAll(this, chunked)));
|
|
101
|
+
}
|
|
102
|
+
async patch(instance) {
|
|
103
|
+
return this.runInTransaction(transaction => transaction.patch(this, instance));
|
|
104
|
+
}
|
|
105
|
+
async deleteItem(instance) {
|
|
106
|
+
return this.runInTransaction((transaction) => transaction.deleteItem(this, instance));
|
|
107
|
+
}
|
|
108
|
+
async deleteUnique(query) {
|
|
109
|
+
return this.runInTransaction((transaction) => transaction.deleteUnique(this, query));
|
|
110
|
+
}
|
|
111
|
+
async delete(query) {
|
|
112
|
+
const docRefs = await this._query(query);
|
|
113
|
+
return this.deleteBatch(docRefs);
|
|
114
|
+
}
|
|
115
|
+
async deleteBatch(docRefs) {
|
|
116
|
+
return await batchAction(docRefs, 200, async (chunk) => {
|
|
117
|
+
const batch = this.wrapper.firestore.batch();
|
|
118
|
+
const toRet = [];
|
|
119
|
+
await chunk.reduce((_batch, val) => {
|
|
120
|
+
toRet.push(val.data());
|
|
121
|
+
return _batch.delete(val.ref);
|
|
122
|
+
}, batch).commit();
|
|
123
|
+
return toRet;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async deleteAll() {
|
|
127
|
+
const docRefs = await this._query();
|
|
128
|
+
return this.deleteBatch(docRefs);
|
|
129
|
+
}
|
|
130
|
+
async getAll(select) {
|
|
131
|
+
return this.query({ select });
|
|
132
|
+
}
|
|
133
|
+
async runInTransaction(processor) {
|
|
134
|
+
const firestore = this.wrapper.firestore;
|
|
135
|
+
return firestore.runTransaction(async (transaction) => {
|
|
136
|
+
return processor(new FirestoreTransaction(transaction));
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
createDocumentReference(_id) {
|
|
140
|
+
const id = _id || generateHex(16);
|
|
141
|
+
return this.wrapper.firestore.doc(`${this.name}/${id}`);
|
|
142
|
+
}
|
|
143
|
+
getUniqueFilter = () => this.externalUniqueFilter;
|
|
144
|
+
async newQueryUnique(ourQuery) {
|
|
145
|
+
const doc = await this._queryUnique(ourQuery);
|
|
146
|
+
if (!doc || !doc.exists)
|
|
147
|
+
return;
|
|
148
|
+
return new DocWrapper(this.wrapper, doc);
|
|
149
|
+
}
|
|
150
|
+
async newQuery(ourQuery) {
|
|
151
|
+
const docs = await this._query(ourQuery);
|
|
152
|
+
return docs.filter(doc => doc.exists).map(doc => new DocWrapper(this.wrapper, doc));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
export class DocWrapper {
|
|
156
|
+
wrapper;
|
|
157
|
+
doc;
|
|
158
|
+
constructor(wrapper, doc) {
|
|
159
|
+
this.wrapper = wrapper;
|
|
160
|
+
this.doc = doc;
|
|
161
|
+
}
|
|
162
|
+
async runInTransaction(processor) {
|
|
163
|
+
const firestore = this.wrapper.firestore;
|
|
164
|
+
return firestore.runTransaction(processor);
|
|
165
|
+
}
|
|
166
|
+
delete = async (transaction) => {
|
|
167
|
+
if (!transaction) {
|
|
168
|
+
// const item = this.doc.data(); TODO: TBD do we need a create and run in transaction for each delete??
|
|
169
|
+
// this.doc.ref.delete();
|
|
170
|
+
// return item;
|
|
171
|
+
return this.runInTransaction(this.delete);
|
|
172
|
+
}
|
|
173
|
+
const item = this.get();
|
|
174
|
+
transaction.delete(this.doc.ref);
|
|
175
|
+
return item;
|
|
176
|
+
};
|
|
177
|
+
get = () => this.doc.data();
|
|
178
|
+
set = async (instance, transaction) => {
|
|
179
|
+
if (!transaction)
|
|
180
|
+
return this.runInTransaction((_transaction) => this.set(instance, _transaction));
|
|
181
|
+
transaction.set(this.doc.ref, instance);
|
|
182
|
+
return instance;
|
|
183
|
+
};
|
|
184
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FirestoreQuery } from '@nu-art/firebase-shared';
|
|
2
|
+
import { FirestoreType_DocumentSnapshot } from './types.js';
|
|
3
|
+
import { FirestoreCollection } from './FirestoreCollection.js';
|
|
4
|
+
import { TS_Object } from '@nu-art/ts-common';
|
|
5
|
+
import { Query } from 'firebase-admin/firestore';
|
|
6
|
+
export declare class FirestoreInterface {
|
|
7
|
+
static buildQuery<Type extends TS_Object>(collection: FirestoreCollection<Type>, query?: FirestoreQuery<Type>): Query<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>;
|
|
8
|
+
private static isQueryObject;
|
|
9
|
+
static assertUniqueDocument(results: FirestoreType_DocumentSnapshot[], query: FirestoreQuery<any>, collectionName: string): (FirestoreType_DocumentSnapshot | undefined);
|
|
10
|
+
static buildUniqueQuery<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Type): FirestoreQuery<Type>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Firebase is a simpler Typescript wrapper to all of firebase services.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
import { ComparatorMap } from '@nu-art/firebase-shared';
|
|
19
|
+
import { __stringify, BadImplementationException, ImplementationMissingException, StaticLogger } from '@nu-art/ts-common';
|
|
20
|
+
export class FirestoreInterface {
|
|
21
|
+
static buildQuery(collection, query) {
|
|
22
|
+
try {
|
|
23
|
+
let myQuery = collection.collection;
|
|
24
|
+
if (query && query.select)
|
|
25
|
+
myQuery = myQuery.select ? myQuery.select(...query.select) : myQuery;
|
|
26
|
+
if (query && query.where) {
|
|
27
|
+
const whereClause = query.where;
|
|
28
|
+
myQuery = Object.keys(whereClause).reduce((_query, _whereField) => {
|
|
29
|
+
const whereField = _whereField;
|
|
30
|
+
const whereValue = whereClause[whereField];
|
|
31
|
+
if (whereValue === undefined || whereValue === null)
|
|
32
|
+
return _query;
|
|
33
|
+
const processObject = (___query, whereKey, _whereValue) => {
|
|
34
|
+
const valueType = typeof _whereValue;
|
|
35
|
+
if (Array.isArray(_whereValue)) {
|
|
36
|
+
if (_whereValue.length === 0 || _whereValue.length > 10)
|
|
37
|
+
throw new BadImplementationException('While querying in an array you can only provide one or more values to query by and not more than 10... this ' +
|
|
38
|
+
'is due to Firestore limitation... ');
|
|
39
|
+
if (_whereValue.length === 1)
|
|
40
|
+
return _query.where(whereKey, 'array-contains', _whereValue[0]);
|
|
41
|
+
return _query.where(whereKey, 'array-contains-any', _whereValue);
|
|
42
|
+
}
|
|
43
|
+
if (this.isQueryObject(_whereValue)) {
|
|
44
|
+
const comparatorKey = Object.keys(_whereValue)[0];
|
|
45
|
+
const comparator = ComparatorMap[comparatorKey];
|
|
46
|
+
if (!comparator)
|
|
47
|
+
throw new ImplementationMissingException(`could not find comparator for: ${comparatorKey} in query: ${JSON.stringify(query)}`);
|
|
48
|
+
const value = _whereValue[comparatorKey];
|
|
49
|
+
if (value === undefined)
|
|
50
|
+
throw new ImplementationMissingException(`no value: ${comparatorKey} in query: ${JSON.stringify(query)}`);
|
|
51
|
+
return _query.where(whereKey, comparator, value);
|
|
52
|
+
}
|
|
53
|
+
if (valueType === 'string' || valueType === 'number' || valueType === 'boolean')
|
|
54
|
+
return ___query.where(whereKey, '==', _whereValue);
|
|
55
|
+
if (valueType === 'object') {
|
|
56
|
+
return Object.keys(_whereValue).reduce((__query, key) => {
|
|
57
|
+
return processObject(__query, `${whereKey}.${key}`, _whereValue[key]);
|
|
58
|
+
}, ___query);
|
|
59
|
+
}
|
|
60
|
+
throw new ImplementationMissingException(`Could not compose where clause for '${whereKey}' with value type '${valueType}'in query: ${__stringify(___query)}`);
|
|
61
|
+
};
|
|
62
|
+
return processObject(_query, whereField, whereValue);
|
|
63
|
+
}, myQuery);
|
|
64
|
+
}
|
|
65
|
+
if (query && query.orderBy)
|
|
66
|
+
myQuery = query.orderBy.reduce((_query, field) => {
|
|
67
|
+
return _query.orderBy ? _query.orderBy(field.key, field.order) : _query;
|
|
68
|
+
}, myQuery);
|
|
69
|
+
if (query && query.limit)
|
|
70
|
+
if (typeof query.limit === 'number')
|
|
71
|
+
myQuery = myQuery.limit(query.limit);
|
|
72
|
+
else {
|
|
73
|
+
const page = query.limit.page || 0;
|
|
74
|
+
// console.log(`limit: ${query.limit.itemsCount} * ${page}`);
|
|
75
|
+
if (page > 0)
|
|
76
|
+
myQuery = myQuery.offset(query.limit.itemsCount * page + 1);
|
|
77
|
+
myQuery = myQuery.limit(query.limit.itemsCount);
|
|
78
|
+
}
|
|
79
|
+
return myQuery;
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
StaticLogger.logError(`Query: ${JSON.stringify(query)}`);
|
|
83
|
+
StaticLogger.logError(`Error: ${e}`);
|
|
84
|
+
throw e;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
static isQueryObject(whereValue) {
|
|
88
|
+
const keys = Object.keys(whereValue);
|
|
89
|
+
return typeof whereValue === 'object' && keys.length === 1 && (whereValue['$ac'] !== undefined ||
|
|
90
|
+
whereValue['$aca'] !== undefined ||
|
|
91
|
+
whereValue['$in'] !== undefined ||
|
|
92
|
+
whereValue['$nin'] !== undefined ||
|
|
93
|
+
whereValue['$gt'] !== undefined ||
|
|
94
|
+
whereValue['$gte'] !== undefined ||
|
|
95
|
+
whereValue['$lt'] !== undefined ||
|
|
96
|
+
whereValue['$lte'] !== undefined ||
|
|
97
|
+
whereValue['$neq'] !== undefined ||
|
|
98
|
+
whereValue['$eq'] !== undefined);
|
|
99
|
+
}
|
|
100
|
+
static assertUniqueDocument(results, query, collectionName) {
|
|
101
|
+
if (results.length > 1)
|
|
102
|
+
throw new BadImplementationException(`too many results for query: ${__stringify(query)} in collection: ${collectionName}`);
|
|
103
|
+
if (results.length === 0)
|
|
104
|
+
return;
|
|
105
|
+
return results[0];
|
|
106
|
+
}
|
|
107
|
+
static buildUniqueQuery(collection, instance) {
|
|
108
|
+
const where = collection.externalUniqueFilter(instance);
|
|
109
|
+
return { where };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DocWrapper, FirestoreCollection } from './FirestoreCollection.js';
|
|
2
|
+
import { Subset, TS_Object } from '@nu-art/ts-common';
|
|
3
|
+
import { FirestoreQuery } from '@nu-art/firebase-shared';
|
|
4
|
+
import { Transaction } from 'firebase-admin/firestore';
|
|
5
|
+
export declare class FirestoreTransaction {
|
|
6
|
+
transaction: Transaction;
|
|
7
|
+
constructor(transaction: Transaction);
|
|
8
|
+
private _query;
|
|
9
|
+
private _queryUnique;
|
|
10
|
+
private _queryItem;
|
|
11
|
+
query<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<Type[]>;
|
|
12
|
+
queryItem<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Type): Promise<Type | undefined>;
|
|
13
|
+
queryUnique<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<Type | undefined>;
|
|
14
|
+
insert<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Type, _id?: string): Promise<Type>;
|
|
15
|
+
insertAll<Type extends TS_Object>(collection: FirestoreCollection<Type>, instances: Type[], _ids?: string[]): Promise<Awaited<Type>[]>;
|
|
16
|
+
upsert<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Type, _id?: string): Promise<Type>;
|
|
17
|
+
upsert_Read<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Type, _id?: string): Promise<() => Promise<Type>>;
|
|
18
|
+
getOrCreateDocument<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Type, _id?: string): Promise<FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData, FirebaseFirestore.DocumentData>>;
|
|
19
|
+
upsertAll<Type extends TS_Object>(collection: FirestoreCollection<Type>, instances: Type[], _ids?: string[]): Promise<Type[]>;
|
|
20
|
+
upsertAll_Read<Type extends TS_Object>(collection: FirestoreCollection<Type>, instances: Type[], _ids?: string[]): Promise<() => Promise<Type[]>>;
|
|
21
|
+
patch<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Subset<Type>): Promise<any>;
|
|
22
|
+
patch_Read<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Subset<Type>): Promise<() => Promise<any>>;
|
|
23
|
+
delete<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<Type[]>;
|
|
24
|
+
delete_Read<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<() => Promise<Type[]>>;
|
|
25
|
+
deleteItem<Type extends TS_Object>(collection: FirestoreCollection<Type>, instance: Type): Promise<Type | undefined>;
|
|
26
|
+
deleteUnique<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<Type | undefined>;
|
|
27
|
+
deleteUnique_Read<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<undefined | (() => Promise<Type>)>;
|
|
28
|
+
newQueryUnique<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<DocWrapper<Type> | undefined>;
|
|
29
|
+
newQuery<Type extends TS_Object>(collection: FirestoreCollection<Type>, ourQuery: FirestoreQuery<Type>): Promise<DocWrapper<Type>[]>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Firebase is a simpler Typescript wrapper to all of firebase services.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
import { DocWrapper, } from './FirestoreCollection.js';
|
|
19
|
+
import { BadImplementationException, merge } from '@nu-art/ts-common';
|
|
20
|
+
import { FirestoreInterface } from './FirestoreInterface.js';
|
|
21
|
+
export class FirestoreTransaction {
|
|
22
|
+
transaction;
|
|
23
|
+
constructor(transaction) {
|
|
24
|
+
this.transaction = transaction;
|
|
25
|
+
}
|
|
26
|
+
async _query(collection, ourQuery) {
|
|
27
|
+
const query = FirestoreInterface.buildQuery(collection, ourQuery);
|
|
28
|
+
return (await this.transaction.get(query)).docs;
|
|
29
|
+
}
|
|
30
|
+
async _queryUnique(collection, ourQuery) {
|
|
31
|
+
const results = await this._query(collection, ourQuery);
|
|
32
|
+
return FirestoreInterface.assertUniqueDocument(results, ourQuery, collection.name);
|
|
33
|
+
}
|
|
34
|
+
async _queryItem(collection, instance) {
|
|
35
|
+
const ourQuery = FirestoreInterface.buildUniqueQuery(collection, instance);
|
|
36
|
+
const results = await this._query(collection, ourQuery);
|
|
37
|
+
return FirestoreInterface.assertUniqueDocument(results, ourQuery, collection.name);
|
|
38
|
+
}
|
|
39
|
+
async query(collection, ourQuery) {
|
|
40
|
+
return (await this._query(collection, ourQuery)).map(result => result.data());
|
|
41
|
+
}
|
|
42
|
+
async queryItem(collection, instance) {
|
|
43
|
+
const ourQuery = FirestoreInterface.buildUniqueQuery(collection, instance);
|
|
44
|
+
return this.queryUnique(collection, ourQuery);
|
|
45
|
+
}
|
|
46
|
+
async queryUnique(collection, ourQuery) {
|
|
47
|
+
const doc = await this._queryUnique(collection, ourQuery);
|
|
48
|
+
if (!doc)
|
|
49
|
+
return;
|
|
50
|
+
return doc.data();
|
|
51
|
+
}
|
|
52
|
+
async insert(collection, instance, _id) {
|
|
53
|
+
const doc = collection.createDocumentReference(_id);
|
|
54
|
+
try {
|
|
55
|
+
await this.transaction.create(doc, instance);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
console.error('Failed creating object: ', instance, e);
|
|
59
|
+
throw e;
|
|
60
|
+
}
|
|
61
|
+
return instance;
|
|
62
|
+
}
|
|
63
|
+
async insertAll(collection, instances, _ids) {
|
|
64
|
+
return await Promise.all(instances.map((instance, i) => this.insert(collection, instance, _ids?.[i])));
|
|
65
|
+
}
|
|
66
|
+
//------------------------
|
|
67
|
+
async upsert(collection, instance, _id) {
|
|
68
|
+
return (await this.upsert_Read(collection, instance, _id))();
|
|
69
|
+
}
|
|
70
|
+
async upsert_Read(collection, instance, _id) {
|
|
71
|
+
const ref = await this.getOrCreateDocument(collection, instance, _id);
|
|
72
|
+
return async () => {
|
|
73
|
+
try {
|
|
74
|
+
await this.transaction.set(ref, instance);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
console.error('Failed creating object: ', instance, e);
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
return instance;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
async getOrCreateDocument(collection, instance, _id) {
|
|
84
|
+
let ref = (await this._queryItem(collection, instance))?.ref;
|
|
85
|
+
if (!ref)
|
|
86
|
+
ref = collection.createDocumentReference(_id);
|
|
87
|
+
return ref;
|
|
88
|
+
}
|
|
89
|
+
async upsertAll(collection, instances, _ids) {
|
|
90
|
+
if (instances.length > 500)
|
|
91
|
+
throw new BadImplementationException('Firestore transaction supports maximum 500 at a time');
|
|
92
|
+
return (await this.upsertAll_Read(collection, instances, _ids))();
|
|
93
|
+
}
|
|
94
|
+
async upsertAll_Read(collection, instances, _ids) {
|
|
95
|
+
const writes = await Promise.all(instances.map(async (instance, i) => this.upsert_Read(collection, instance, _ids?.[i])));
|
|
96
|
+
return async () => Promise.all(writes.map(async (_write) => _write()));
|
|
97
|
+
}
|
|
98
|
+
async patch(collection, instance) {
|
|
99
|
+
return (await this.patch_Read(collection, instance))();
|
|
100
|
+
}
|
|
101
|
+
async patch_Read(collection, instance) {
|
|
102
|
+
const doc = await this._queryItem(collection, instance);
|
|
103
|
+
if (!doc)
|
|
104
|
+
throw new BadImplementationException(`Patching a non existent doc for query ${FirestoreInterface.buildUniqueQuery(collection, instance)}`);
|
|
105
|
+
return async () => {
|
|
106
|
+
const patchedInstance = merge(await doc.data(), instance);
|
|
107
|
+
this.transaction.set(doc.ref, patchedInstance);
|
|
108
|
+
return patchedInstance;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
async delete(collection, ourQuery) {
|
|
112
|
+
return await (await this.delete_Read(collection, ourQuery))();
|
|
113
|
+
}
|
|
114
|
+
async delete_Read(collection, ourQuery) {
|
|
115
|
+
const docs = await this._query(collection, ourQuery);
|
|
116
|
+
if (docs.length > 500)
|
|
117
|
+
throw new BadImplementationException(`Trying to delete ${docs.length} documents. Not allowed more then 5oo in a single transaction`);
|
|
118
|
+
return async () => {
|
|
119
|
+
const toReturn = docs.map(doc => doc.data());
|
|
120
|
+
await Promise.all(docs.map(async (doc) => this.transaction.delete(doc.ref)));
|
|
121
|
+
return toReturn;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async deleteItem(collection, instance) {
|
|
125
|
+
return this.deleteUnique(collection, FirestoreInterface.buildUniqueQuery(collection, instance));
|
|
126
|
+
}
|
|
127
|
+
async deleteUnique(collection, ourQuery) {
|
|
128
|
+
const write = await this.deleteUnique_Read(collection, ourQuery);
|
|
129
|
+
if (!write)
|
|
130
|
+
return;
|
|
131
|
+
return write();
|
|
132
|
+
}
|
|
133
|
+
async deleteUnique_Read(collection, ourQuery) {
|
|
134
|
+
const doc = (await this._queryUnique(collection, ourQuery));
|
|
135
|
+
if (!doc)
|
|
136
|
+
return;
|
|
137
|
+
return async () => {
|
|
138
|
+
const result = doc.data();
|
|
139
|
+
await this.transaction.delete(doc.ref);
|
|
140
|
+
return result;
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async newQueryUnique(collection, ourQuery) {
|
|
144
|
+
const doc = await this._queryUnique(collection, ourQuery);
|
|
145
|
+
if (!doc || !doc.exists)
|
|
146
|
+
return;
|
|
147
|
+
return new DocWrapper(collection.wrapper, doc);
|
|
148
|
+
}
|
|
149
|
+
async newQuery(collection, ourQuery) {
|
|
150
|
+
const docs = await this._query(collection, ourQuery);
|
|
151
|
+
return docs.filter(doc => doc.exists).map(doc => new DocWrapper(collection.wrapper, doc));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FirestoreCollection } from './FirestoreCollection.js';
|
|
2
|
+
import { FirestoreType, FirestoreType_Collection } from './types.js';
|
|
3
|
+
import { FilterKeys } from '@nu-art/firebase-shared';
|
|
4
|
+
import { FirebaseSession } from '../auth/firebase-session.js';
|
|
5
|
+
import { FirebaseBaseWrapper } from '../auth/FirebaseBaseWrapper.js';
|
|
6
|
+
import { DB_Object, TS_Object } from '@nu-art/ts-common';
|
|
7
|
+
export declare class FirestoreWrapperBE extends FirebaseBaseWrapper {
|
|
8
|
+
readonly firestore: FirestoreType;
|
|
9
|
+
private readonly collections;
|
|
10
|
+
constructor(firebaseSession: FirebaseSession<any>, dbName?: string);
|
|
11
|
+
getCollection<Type extends TS_Object>(name: string, uniqueKeys?: FilterKeys<Type>): FirestoreCollection<Type>;
|
|
12
|
+
listen<Type extends DB_Object>(collection: FirestoreCollection<Type>, doc: string): void;
|
|
13
|
+
deleteCollection(name: string): Promise<TS_Object[]>;
|
|
14
|
+
listCollections(): Promise<FirestoreType_Collection[]>;
|
|
15
|
+
isEmulator(): boolean;
|
|
16
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Firebase is a simpler Typescript wrapper to all of firebase services.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
import { FirestoreCollection, } from './FirestoreCollection.js';
|
|
19
|
+
import { FirebaseBaseWrapper } from '../auth/FirebaseBaseWrapper.js';
|
|
20
|
+
import { getFirestore } from 'firebase-admin/firestore';
|
|
21
|
+
export class FirestoreWrapperBE extends FirebaseBaseWrapper {
|
|
22
|
+
firestore;
|
|
23
|
+
collections = {};
|
|
24
|
+
constructor(firebaseSession, dbName) {
|
|
25
|
+
super(firebaseSession);
|
|
26
|
+
if (dbName)
|
|
27
|
+
this.firestore = getFirestore(firebaseSession.app, dbName);
|
|
28
|
+
else
|
|
29
|
+
this.firestore = getFirestore(firebaseSession.app);
|
|
30
|
+
}
|
|
31
|
+
getCollection(name, uniqueKeys) {
|
|
32
|
+
const collection = this.collections[name];
|
|
33
|
+
if (collection)
|
|
34
|
+
return collection;
|
|
35
|
+
return this.collections[name] = new FirestoreCollection(name, this, uniqueKeys);
|
|
36
|
+
}
|
|
37
|
+
listen(collection, doc) {
|
|
38
|
+
collection.wrapper.firestore.doc(`${collection.name}/${doc}`).onSnapshot(() => {
|
|
39
|
+
this.logInfo('recieved snapshot!');
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async deleteCollection(name) {
|
|
43
|
+
return this.getCollection(name).deleteAll();
|
|
44
|
+
}
|
|
45
|
+
async listCollections() {
|
|
46
|
+
if (!this.firestore.listCollections)
|
|
47
|
+
return [];
|
|
48
|
+
return this.firestore.listCollections();
|
|
49
|
+
}
|
|
50
|
+
isEmulator() {
|
|
51
|
+
return !!process.env.FIRESTORE_EMULATOR_HOST || false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { CollectionReference, QueryDocumentSnapshot, Query, DocumentReference, Firestore, DocumentData } from 'firebase-admin/firestore';
|
|
2
|
+
export type FirestoreType_Collection = CollectionReference;
|
|
3
|
+
export type FirestoreType_DocumentSnapshot<T = DocumentData> = QueryDocumentSnapshot<T>;
|
|
4
|
+
export type FirestoreType_Query = Query;
|
|
5
|
+
export type FirestoreType_DocumentReference<T> = DocumentReference<T>;
|
|
6
|
+
export type FirestoreType = Firestore;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Firebase is a simpler Typescript wrapper to all of firebase services.
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2020 Adam van der Kruk aka TacB0sS
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
// export type FirestoreType_Collection = admin.CollectionReference | firebase.firestore.CollectionReference;
|
|
20
|
+
// export type FirestoreType_DocumentSnapshot = admin.firestore.QueryDocumentSnapshot | firebase.firestore.QueryDocumentSnapshot;
|
|
21
|
+
// export type FirestoreType_Query = (admin.firestore.Query | firebase.firestore.Query) & { select?: (...field: string[]) => FirestoreType_Query };
|
|
22
|
+
// export type FirestoreType_DocumentReference = admin.firestore.DocumentReference | firebase.firestore.DocumentReference;
|
|
23
|
+
// export type FirestoreType = (admin.firestore.Firestore | firebase.firestore.Firestore) & ({ listCollections?: () => Promise<FirestoreType_Collection[]> });
|
|
24
|
+
// export type FirestoreType_Transaction = admin.firestore.Transaction | firebase.firestore.Transaction;
|
|
25
|
+
//
|