service-utilities
Version:
Utility Package for FIORI UI5
255 lines (236 loc) • 8.51 kB
JavaScript
/**
* @module Firebase
* @description Utility class for Firebase integration
* @author jpanti
* @version 1.0.0
* @created 2025-08-01
* @lastModified 2025-08-01
* @license ISC
*/
sap.ui.define(
[
"./AbstractController",
"./ServiceIndexedDB",
"./EventRadio",
],
(AbstractController, ServiceIndexedDB, EventRadio) => {
"use strict";
const eventRadio = new EventRadio("firebase");
var firebaseConfig = {};
class Firebase extends AbstractController {
// Set Firebase Config ==============================
setConfigs(configs) {
Object.entries(configs).map(
([key, value]) => (firebaseConfig[key] = value)
);
return this;
}
setConfig(key, value) {
if (firebaseConfig.hasOwnProperty(key)) firebaseConfig[key] = value;
return this;
}
setAPIKey = (value) => this.setConfig("apiKey", value);
setAuthDomain = (value) => this.setConfig("authDomain", value);
setProjectID = (value) => this.setConfig("projectId", value);
setStorageBucket = (value) => this.setConfig("storageBucket", value);
setMessagingSenderID = (value) =>
this.setConfig("messagingSenderId", value);
setAppID = (value) => this.setConfig("appId", value);
setMeasurementID = (value) => this.setConfig("measurementId", value);
setDatabaseURL = (value) => this.setConfig("databaseURL", value);
// ==================================================
// Initialization ===================================
#firebase;
#firestore;
#persistentDatabase;
#state;
constructor(
oController,
persistentDBName,
{ storageName = "firebase" } = {}
) {
// Parent Init
super(oController);
this.state = "started";
this.stateTransmitter();
// Firebase Init
try {
this.#firebase = firebase.initializeApp(firebaseConfig);
} catch {
this.#firebase = firebase;
}
this.#firestore = firebase.firestore();
this.#persistentDatabase = new ServiceIndexedDB(
storageName,
persistentDBName
);
// Dependency Init
this.init().then(() => {
this.initListeners();
this.state = "done";
this.stateTransmitter();
});
}
async init() {
this.#collectionArchive = await this.initArchive("collections");
this.#documentArchive = await this.initArchive("documents");
return this;
}
getArchive(archiveType) {
if (archiveType === "collections") return this.getCollections();
if (archiveType === "documents") return this.getDocuments();
}
initArchive = (archiveType, defaultValue) =>
this.#persistentDatabase.load(archiveType).then((loadedData) => {
if (loadedData) return loadedData;
loadedData = defaultValue;
return this.#persistentDatabase
.save(archiveType, this.getArchive(archiveType))
.then(() => this.getArchive(archiveType));
});
// ==================================================
// Event Transmitters and Listeners =================
getEventRadio = () => eventRadio;
stateTransmitter = (state = "transmit") =>
eventRadio.transmit(`init-state-${state}`, { state: this.state });
collectionsTransmitter = () =>
eventRadio.transmit("collections-response", {
collections: this.getCollections(),
});
documentsTransmitter = () =>
eventRadio.transmit("documents-response", {
documents: this.getDocuments(),
});
collectionTransmitter = (...path) =>
eventRadio.transmit("collection-response", {
collection: this.getCollection(...path),
});
documentTransmitter = (...path) =>
eventRadio.transmit("document-response", {
document: this.getDocument(...path),
});
initListeners() {
eventRadio
.listen("init-state-request", () => this.stateTransmitter("response"))
.listen("collections-request", () => this.collectionsTransmitter())
.listen("documents-request", () => this.documentsTransmitter())
.listen("collection-request", (oData) =>
this.collectionTransmitter(...oData.path)
)
.listen("document-request", (oData) =>
this.documentTransmitter(...oData.path)
);
}
// ==================================================
// Utilities ========================================
pathSeparator(...path) {
return {
strFullPath: path.join("/"),
strDirPath: path.slice(0, path.length - 1).join("/"),
fullPath: path,
dirPath: path.slice(0, path.length - 1),
token: path[path.length - 1],
};
}
getBase() {
return { firebase: this.#firebase, firestore: this.#firestore };
}
// ==================================================
// Collections ======================================
#collectionArchive = {};
createOCollection({
documents = [],
path = undefined,
token = undefined,
modelname = undefined,
} = {}) {
return { documents, path, token, modelname };
}
async syncCollection(...path) {
var oCollection = this.createOCollection();
if (path.length === 0) return oCollection;
const { token, strFullPath, strDirPath } = this.pathSeparator(...path);
const refCollection = this.#firestore.collection(strFullPath);
const collectionWrapper = await refCollection.get();
oCollection.path = strDirPath;
oCollection.token = token;
oCollection.documents = collectionWrapper.docs.map((doc) => doc.data());
this.#collectionArchive[strFullPath] = oCollection;
await this.#persistentDatabase.save(
"collections",
this.#collectionArchive
);
return oCollection;
}
async getCollection(...path) {
var oCollection = this.createOCollection();
if (path.length === 0) return oCollection;
const { strFullPath } = this.pathSeparator(...path);
const hasOfflineCopy =
this.#collectionArchive.hasOwnProperty(strFullPath);
if (hasOfflineCopy) oCollection = this.#collectionArchive[strFullPath];
else oCollection = await this.syncCollection(...path);
return oCollection;
}
getCollections() {
return this.#collectionArchive;
}
// ==================================================
// Documents ========================================
#documentArchive = {};
createODocument({
path = undefined,
token = undefined,
modelname = undefined,
} = {}) {
return { path, token, modelname };
}
async getDocument(...path) {
if (path.length === 0 || path.length % 2 === 1)
return this.createODocument();
const { token, dirPath, strFullPath } = this.pathSeparator(...path);
var oDocument;
var hasOfflineCopy = this.#documentArchive.hasOwnProperty(strFullPath);
if (!hasOfflineCopy) {
const oCollection = await this.getCollection(...dirPath);
oDocument = oCollection.documents.find(
(document) => document.token === token
);
} else oDocument = this.#documentArchive[strFullPath];
this.#documentArchive[strFullPath] = oDocument;
if (!hasOfflineCopy) {
await this.#persistentDatabase.save(
"documents",
this.#documentArchive
);
}
return oDocument;
}
getDocuments() {
return this.#documentArchive;
}
// ==================================================
}
var instance;
return {
getInstance(
oController,
persistentDBName,
{ modelname = "firebase" } = {}
) {
if (instance) return instance;
return this.createInstance(oController, persistentDBName, {
modelname,
});
},
createInstance(
oController,
persistentDBName,
{ modelname = "firebase" } = {}
) {
instance = new Firebase(oController, persistentDBName, { modelname });
return instance;
},
};
}
);