UNPKG

simplyfire

Version:

A lightweight firestore api for firebase cloud functions & Angular.

294 lines (285 loc) 10.8 kB
import { __awaiter, __rest } from 'tslib'; class AbstractFirestoreApi { constructor() { // Maximum number of writes that can be passed to a Commit operation // or performed in a transaction // https://cloud.google.com/firestore/quotas#writes_and_transactions this.BATCH_MAX_WRITES = 500; } getValueFromSnapshot(snapshot) { return (snapshot.exists ? snapshot.data() : null); } } class QueryBuilder { constructor() { this._where = []; this._orderBy = []; this._leftJoins = []; } get joins() { return this._leftJoins; } where(...where) { this._where.push(where); return this; } orderBy(...orderBy) { this._orderBy.push(orderBy); return this; } leftJoin(...leftJoin) { this._leftJoins.push(leftJoin); } limit(limit) { this._limit = limit; return this; } limitToLast(limitToLast) { this._limitToLast = limitToLast; return this; } startAt(...startAt) { this._startAt = startAt; return this; } startAfter(...startAfter) { this._startAfter = startAfter; return this; } endAt(...endAt) { this._endAt = endAt; return this; } endBefore(...endBefore) { this._endBefore = endBefore; return this; } // Still have to use <any> type due to most interfaces of @google-cloud/firestore // are not compatible with @firebase/firestore's interfaces. exec(ref, queryOps) { var _a, _b, _c, _d; if (typeof window === 'undefined') { return this.execQueryForCloud(ref); } if (!queryOps) { throw Error('invalid arguments'); } const { query, where, orderBy, limit, limitToLast, startAt, startAfter, endAt, endBefore } = queryOps; const queryConstraints = [ ...this._where.map((w) => where(...w)), ...this._orderBy.map((o) => orderBy(...o)), ...(this._limit ? [limit(this._limit)] : []), ...(this._limitToLast ? [limitToLast(this._limitToLast)] : []), ...(((_a = this._startAt) === null || _a === void 0 ? void 0 : _a.every((i) => !!i)) ? [startAt(...this._startAt)] : []), ...(((_b = this._startAfter) === null || _b === void 0 ? void 0 : _b.every((i) => !!i)) ? [startAfter(...this._startAfter)] : []), ...(((_c = this._endAt) === null || _c === void 0 ? void 0 : _c.every((i) => !!i)) ? [endAt(...this._endAt)] : []), ...(((_d = this._endBefore) === null || _d === void 0 ? void 0 : _d.every((i) => !!i)) ? [endBefore(...this._endBefore)] : []) ]; return query(ref, ...queryConstraints); } execQueryForCloud(ref) { let query = this._where.reduce((q, wh) => q.where(...wh), ref); query = this._orderBy.reduce((q, ob) => q.orderBy(...ob), query); if (this._limit) { query = query.limit(this._limit); } if (this._limitToLast) { query = query.limitToLast(this._limitToLast); } if (this._startAt) { query = query.startAt(this._startAt); } if (this._startAfter) { query = query.startAfter(this._startAfter); } if (this._endAt) { query = query.endAt(this._endAt); } if (this._endBefore) { query = query.endBefore(this._endBefore); } return query; } } // chunk array to a certain size const arrayToChunks = (list, size) => { list = [...list]; return [...Array(Math.ceil(list.length / size))].map((_) => list.splice(0, size)); }; class FirestoreCloudService extends AbstractFirestoreApi { static getInstance(admin, settings = {}) { var _a; (_a = this.instance) !== null && _a !== void 0 ? _a : (this.instance = new this()); this.instance.initialize(admin, settings); return this.instance; } initialize(admin, settings) { admin.initializeApp(); this.db = admin.firestore(); this.db.settings(settings); this.admin = admin; } // ----------------------------------------------------------------------------------------------------- // @ Abstract members // ----------------------------------------------------------------------------------------------------- collection(collection, qb) { return __awaiter(this, void 0, void 0, function* () { return (yield this.collectionSnapshot(collection, qb)).docs.map((doc) => (Object.assign({ id: doc.id }, doc.data()))); }); } collectionGroup(collectionId, qb) { return __awaiter(this, void 0, void 0, function* () { return (yield this.collectionGroupSnapshot(collectionId, qb)).docs.map((doc) => (Object.assign({ id: doc.id }, doc.data()))); }); } doc(path) { return __awaiter(this, void 0, void 0, function* () { const snapshot = yield this.docRef(path).get(); return (snapshot.exists && Object.assign({ id: snapshot.id }, snapshot.data())) || null; }); } upsert(collection, data, opts = { merge: true }) { var _a; return __awaiter(this, void 0, void 0, function* () { const timestamp = this.serverTimestamp; // eslint-disable-next-line prefer-const let { id } = data, updata = __rest(data, ["id"]); (_a = updata.createdTs) !== null && _a !== void 0 ? _a : (updata.createdTs = timestamp); if (!id) { id = this.db.collection(collection).doc().id; } updata.updatedTs = timestamp; yield this.docRef(`${collection}/${id}`).set(Object.assign({}, updata), opts); return id; }); } update(path, data) { return __awaiter(this, void 0, void 0, function* () { yield this.docRef(path).update(data); }); } delete(path) { return __awaiter(this, void 0, void 0, function* () { yield this.docRef(path).delete(); }); } /** * Bulk update data */ bulkUpsert(path, data, opts = { merge: true }) { return __awaiter(this, void 0, void 0, function* () { const bulkIds = []; const promises = []; const timestamp = this.serverTimestamp; if (Array.isArray(data)) { // Due to a batch limitation, need to split docs array into chunks for (const chunks of arrayToChunks(data, this.BATCH_MAX_WRITES)) { const batch = this.batch; chunks.forEach((d) => { var _a; let { id } = d, updata = __rest(d, ["id"]); id !== null && id !== void 0 ? id : (id = this.db.collection(path).doc().id); (_a = updata.createdTs) !== null && _a !== void 0 ? _a : (updata.createdTs = timestamp); updata.updatedTs = timestamp; batch.set(this.docRef(`${path}/${id}`), updata, opts); bulkIds.push(id); }); const p = batch.commit(); promises.push(p); } } else { const snapshot = yield this.collectionSnapshot(path, data.qb); // Due to a batch limitation, need to split docs array into chunks for (const chunks of arrayToChunks(snapshot.docs, this.BATCH_MAX_WRITES)) { const batch = this.batch; chunks.forEach((d) => batch.set(d.ref, Object.assign({ updatedTs: timestamp }, data.data), opts) && bulkIds.push(d.id)); const p = batch.commit(); promises.push(p); } } yield Promise.all(promises); return bulkIds; }); } /** * Bulk delete data */ bulkDelete(collection, qb, maxSize = 1000) { return __awaiter(this, void 0, void 0, function* () { if (!qb) { qb = new QueryBuilder(); qb.limit(maxSize); } const bulkIds = []; const promises = []; const snapshot = yield this.collectionSnapshot(collection, qb); // Due to a batch limitation, need to split docs array into chunks for (const chunks of arrayToChunks(snapshot.docs, this.BATCH_MAX_WRITES)) { const batch = this.batch; chunks.forEach((doc) => batch.delete(doc.ref) && bulkIds.push(doc.id)); const p = batch.commit(); promises.push(p); } yield Promise.all(promises); return bulkIds; }); } get batch() { return this.db.batch(); } get serverTimestamp() { return this.admin.firestore.FieldValue.serverTimestamp(); } increment(n = 1) { return this.admin.firestore.FieldValue.increment(n); } /** * Returns a generated Firestore Document Id. */ createId(colPath) { return this.db.collection(colPath !== null && colPath !== void 0 ? colPath : '_').doc().id; } runTransaction(updateFunction) { return this.db.runTransaction(updateFunction); } // Recursively delete a reference and log the references of failures. // https://github.com/googleapis/nodejs-firestore/pull/1494 recursiveDelete(ref, bulkWriter) { return this.db.recursiveDelete(ref, bulkWriter); } // ----------------------------------------------------------------------------------------------------- // @ Custom methods // ----------------------------------------------------------------------------------------------------- /** * Create a Firestore Timestamp * * @param date */ createTimestamp(date = new Date()) { return this.admin.firestore.Timestamp.fromDate(date); } collectionSnapshot(path, qb) { const collectionRef = this.db.collection(path); return (qb ? qb.exec(collectionRef) : collectionRef).get(); } collectionGroupSnapshot(collectionId, qb) { const groupRef = this.db.collectionGroup(collectionId); return (qb ? qb.exec(groupRef) : groupRef).get(); } docRef(path) { return this.db.doc(path); } } FirestoreCloudService.instance = null; /* * Public API Surface of common */ /* * Public API Surface of common */ /** * Generated bundle index. Do not edit. */ export { AbstractFirestoreApi, FirestoreCloudService, QueryBuilder, arrayToChunks }; //# sourceMappingURL=simplyfire.mjs.map