import {Inject, Injectable} from "@extollo/di" import {collect, Collection, Iterable} from "@extollo/util" import {FirebaseUnit, RTDBRef} from "./units/FirebaseUnit" import * as firebase from "firebase-admin" import {Application} from "@extollo/lib" /** * Base interface for an item in a Firebase RTDB collection. */ export interface FirebaseResourceItem { firebaseID?: string; } /** * An asynchronous iterable wrapper that enables us to use AsyncCollection * to interact with the Firebase realtime database. */ @Injectable() export class FirebaseResource extends Iterable { protected refName!: RTDBRef get firebase() { return Application.getApplication().make(FirebaseUnit) } /** Get the Reference for this resource. */ ref(): firebase.database.Reference { return Application.getApplication().make(FirebaseUnit).ref(this.refName) } /** Get the record at the ith index. */ async at(i: number): Promise { return new Promise((res, rej) => { this.ref().orderByKey() .startAt(i).endAt(i) .once('value', snapshot => res(this.resolveObject(snapshot.val())[0]), rej) }) } /** Fetch an array of records in a range. */ async range(start: number, end: number): Promise> { return new Promise>((res, rej) => { this.ref().orderByKey() .startAt(start).endAt(end) .once('value', snapshot => { res(new Collection(this.resolveObject(snapshot.val()))) }, rej) }) } /** Count the items in the collection. */ async count(): Promise { console.log('[COUNT CALLED ON FIREBASE RESOURCE]') return new Promise((res, rej) => { this.ref().orderByKey() .once('value', snapshot => { res(this.resolveObject(snapshot.val()).length) }, rej) }) } /** * Push a new item into the collection. * @param item */ async push(item: T): Promise { await this.ref().push(item) return item /*await this.ref().transaction((collection) => { if ( !collection ) collection = [] if ( typeof collection === 'object' ) collection = Object.values(collection) item.seqID = this.findNextId(collection) // @ts-ignore delete item.firebaseID collection.push(this.filter(item)) console.log('item seqID', collection) return collection }) await new Promise(res => { this.ref() .orderByChild('seqID') .startAt(item.seqID) .limitToFirst(1) .once('value', snapshot => { item.firebaseID = snapshot.key || '' res() }) }) return item*/ } /** * Given the value of a realtime-database snapshot, resolve it to an array of T. * @param snapshot * @protected */ protected resolveObject(snapshot: any | null | undefined) { if ( !snapshot ) snapshot = {} const returns: T[] = [] for ( const key in snapshot ) { if ( !snapshot.hasOwnProperty(key) ) continue snapshot[key].firebaseID = key returns.push(snapshot[key]) } return returns } clone(): Iterable { const inst = new FirebaseResource() inst.refName = this.refName return inst } filter(obj: {[key: string]: any}) { for (let key in obj) { if (obj[key] === undefined) { delete obj[key] continue } if (obj[key] && typeof obj[key] === "object") { this.filter(obj[key]) if (!Object.keys(obj[key]).length) { delete obj[key] } } } return obj } }