/********************************************************************************
 * Copyright (c) 2021 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the W3C Software Notice and
 * Document License (2015-05-13) which is available at
 * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document.
 *
 * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
 ********************************************************************************/ import { Content } from "@node-wot/core" ; import firebase from "firebase/app" ; import "firebase/auth" ; import "firebase/firestore" ; import { Buffer } from "buffer" ; import { Readable } from "stream" ; /**
 * initialize firestore.
 */ export const initFirestore = async ( fbConfig , fstore ) : Promise < any > => { if ( fstore != null ) { return fstore ; } if ( ! firebase . apps . length ) { firebase . initializeApp ( fbConfig . firebaseConfig ) ; } const currentUser = await new Promise < any > ( ( res , rej ) => { firebase . auth ( ) . onAuthStateChanged ( ( user ) => { res ( user ) ; } ) ; } ) ; if ( ! currentUser ) { if ( ! fbConfig || ! fbConfig . user || ! fbConfig . user . email || ! fbConfig . user . password ) { throw new Error ( "firebase auth error: cannot find email/password" ) ; } const firestore = await new Promise ( ( resolve , reject ) => { firebase . auth ( ) . signInWithEmailAndPassword ( fbConfig . user . email , fbConfig . user . password ) . then ( ( ) => { const firestore = firebase . firestore ( ) ; resolve ( firestore ) ; } ) . catch ( function ( error ) { reject ( `firebase auth error: ${ error }` ) ; } ) ; } ) ; return firestore ; } else { return firebase . firestore ( ) ; } } ; export const writeDataToFirestore = ( firestore , topic , content , reqId = null ) : Promise < any > => { return new Promise ( ( resolve , reject ) => { console . debug ( "[debug] writeDataToFirestore topic:" , topic , " value:" , content , reqId ) ; const ref = firestore . collection ( "things" ) . doc ( encodeURIComponent ( topic ) ) ; let data = { updatedTime : Date . now ( ) , reqId } ; if ( content ) { data [ "content" ] = JSON . stringify ( content ) ; } ref . set ( data ) . then ( ( value ) => { resolve ( value ) ; } ) . catch ( ( err ) => { console . error ( "[error] failed to write data to firestore: " , err , " topic: " , topic , " data: " , data ) ; reject ( err ) ; } ) ; } ) ; } ; export const readDataFromFirestore = ( firestore , topic ) : Promise < Content > => { return new Promise < Content > ( ( resolve , reject ) => { console . debug ( "[debug] readDataFromFirestore topic:" , topic ) ; const ref = firestore . collection ( "things" ) . doc ( encodeURIComponent ( topic ) ) ; ref . get ( ) . then ( ( doc ) => { if ( doc . exists ) { const data = doc . data ( ) ; let content = null ; if ( data && data . content ) { let obj = JSON . parse ( data . content ) ; if ( ! obj ) { reject ( new Error ( `invalid ${ topic } content:${ content }` ) ) ; } content = { type : obj . type , body : obj && obj . body && obj . body . type === "Buffer" ? Readable . from ( obj . body . data ) : Readable . from ( "" ) , } ; } resolve ( content ) ; } else { reject ( "no contents" ) ; console . debug ( "[debug] read data from firestore but no contents topic:" , topic ) ; } } ) . catch ( ( err ) => { console . error ( "[error] failed read data from firestore: " , err , " topic: " , topic ) ; reject ( err ) ; } ) ; } ) ; } ; export const subscribeToFirestore = async ( firestore , firestoreObservers , topic , callback ) => { console . debug ( "[debug] subscribeToFirestore topic:" , topic ) ; let firstFlg = true ; const ref = firestore . collection ( "things" ) . doc ( encodeURIComponent ( topic ) ) ; let reqId ; const observer = ref . onSnapshot ( ( doc ) => { const data = doc . data ( ) ; let dividedTopic = topic . split ( "/" ) ; if ( data && data . reqId ) { reqId = data . reqId ; if ( dividedTopic && dividedTopic . length > 2 && dividedTopic [ 2 ] === "actionResults" ) { firstFlg = false ; } } if ( firstFlg ) { firstFlg = false ; console . debug ( "[debug] ignore because first calling: " + topic ) ; return ; } let content = null ; if ( data && data . content ) { let obj = JSON . parse ( data . content ) ; if ( ! obj ) { callback ( `invalid ${ topic } content: ${ content }` , null , reqId ) ; return ; } content = { type : null , body : obj && obj . body && obj . body . type === "Buffer" ? Readable . from ( obj . body . data ) : Readable . from ( "" ) , } ; content = obj ; } callback ( null , content , reqId ) ; } , ( err ) => { console . error ( "[error] failed to subscribe data from firestore: " , err , " topic: " , topic ) ; callback ( err , null , reqId ) ; } ) ; firestoreObservers [ topic ] = observer ; } ; export const unsubscribeToFirestore = ( firestoreObservers , topic ) => { console . debug ( "    unsubscribeToFirestore topic:" , topic ) ; const observer = firestoreObservers [ topic ] ; if ( observer ) { observer ( ) ; } } ; export const removeDataFromFirestore = ( firestore , topic ) : Promise < void > => { return new Promise ( ( resolve , reject ) => { console . debug ( "[debug] removeDataFromFirestore topic: " , topic ) ; const ref = firestore . collection ( "things" ) . doc ( encodeURIComponent ( topic ) ) ; ref . delete ( ) . then ( ( ) => { console . log ( "removed topic: " , topic ) ; resolve ( ) ; } ) . catch ( ( err ) => { console . error ( "error removing topic: " , topic , "error: " , err ) ; reject ( err ) ; } ) ; } ) ; } ; export const writeMetaDataToFirestore = ( firestore , hostName , content ) : Promise < any > => { return new Promise ( ( resolve , reject ) => { console . debug ( "[debug] writeMetaDataToFirestore hostName: " , hostName , " value: " , content ) ; const ref = firestore . collection ( "hostsMetaData" ) . doc ( hostName ) ; let data = { updatedTime : Date . now ( ) } ; if ( content ) { data [ "content" ] = JSON . stringify ( content ) ; } ref . set ( data ) . then ( ( value ) => { resolve ( value ) ; } ) . catch ( ( err ) => { console . error ( "[error] failed to write meta data: " , err , " data: " , data , " hostName: " , hostName ) ; reject ( err ) ; } ) ; } ) ; } ; export const readMetaDataFromFirestore = ( firestore , hostName ) : Promise < Object > => { return new Promise < Object > ( ( resolve , reject ) => { console . debug ( "[debug] readMetaDataFromFirestore hostName:" , hostName ) ; const ref = firestore . collection ( "hostsMetaData" ) . doc ( hostName ) ; ref . get ( ) . then ( ( doc ) => { if ( doc . exists ) { const data = doc . data ( ) ; const content = JSON . parse ( data ) ; resolve ( content [ "body" ] ) ; } else { console . debug ( "[debug] read meta data from firestore but no contents" ) ; reject ( "no contents" ) ; } } ) . catch ( ( err ) => { console . error ( "[error] failed to read meta data: " , err , " hostName: " , hostName ) ; reject ( err ) ; } ) ; } ) ; } ; export const removeMetaDataFromFirestore = ( firestore , hostName ) : Promise < void > => { return new Promise ( ( resolve , reject ) => { console . debug ( "[debug] removeMetaDataFromFirestore hostName: " , hostName ) ; const ref = firestore . collection ( "hostsMetaData" ) . doc ( hostName ) ; ref . delete ( ) . then ( ( ) => { console . log ( "removed hostName: " , hostName ) ; resolve ( ) ; } ) . catch ( ( err ) => { console . error ( "error removing hostName: " , hostName , "error: " , err ) ; reject ( err ) ; } ) ; } ) ; } ;