@@ -29,6 +29,8 @@ import * as gcpMetadata from 'gcp-metadata';
2929import * as util from 'util' ;
3030const debuglog = util . debuglog ( 'cdbg.firebase' ) ;
3131
32+ const FIREBASE_APP_NAME = 'cdbg' ;
33+
3234export class FirebaseController implements Controller {
3335 db : firebase . database . Database ;
3436 debuggeeId ?: string ;
@@ -74,26 +76,49 @@ export class FirebaseController implements Controller {
7476 if ( options . databaseUrl ) {
7577 databaseUrl = options . databaseUrl ;
7678 } else {
77- // TODO: Test whether this exists. If not, fall back to -default.
79+ // TODO: Add fallback to -default
7880 databaseUrl = `https://${ projectId } -cdbg.firebaseio.com` ;
7981 }
8082
83+ let app : firebase . app . App ;
8184 if ( credential ) {
82- firebase . initializeApp ( {
83- credential : credential ,
84- databaseURL : databaseUrl ,
85- } ) ;
85+ app = firebase . initializeApp (
86+ {
87+ credential : credential ,
88+ databaseURL : databaseUrl ,
89+ } ,
90+ FIREBASE_APP_NAME
91+ ) ;
8692 } else {
8793 // Use the default credentials.
88- firebase . initializeApp ( {
89- databaseURL : databaseUrl ,
90- } ) ;
94+ app = firebase . initializeApp (
95+ {
96+ databaseURL : databaseUrl ,
97+ } ,
98+ 'cdbg'
99+ ) ;
91100 }
92101
93102 const db = firebase . database ( ) ;
94103
95- // TODO: Test this setup and emit a reasonable error.
96- debuglog ( 'Firebase app initialized. Connected to' , databaseUrl ) ;
104+ // Test the connection by reading the schema version.
105+ try {
106+ const version_snapshot = await db . ref ( 'cdbg/schema_version' ) . get ( ) ;
107+ if ( version_snapshot ) {
108+ const version = version_snapshot . val ( ) ;
109+ debuglog (
110+ `Firebase app initialized. Connected to ${ databaseUrl } ` +
111+ ` with schema version ${ version } `
112+ ) ;
113+ } else {
114+ app . delete ( ) ;
115+ throw new Error ( 'failed to fetch schema version from database' ) ;
116+ }
117+ } catch ( e ) {
118+ app . delete ( ) ;
119+ throw e ;
120+ }
121+
97122 return db ;
98123 }
99124
@@ -135,6 +160,7 @@ export class FirebaseController implements Controller {
135160 // This MUST be consistent across all debuggee instances.
136161 // TODO: JSON.stringify may provide different strings if labels are added
137162 // in different orders.
163+ debuggee . id = '' ; // Don't use the debuggee id when computing the id.
138164 const debuggeeHash = crypto
139165 . createHash ( 'sha1' )
140166 . update ( JSON . stringify ( debuggee ) )
@@ -143,11 +169,14 @@ export class FirebaseController implements Controller {
143169 debuggee . id = this . debuggeeId ;
144170
145171 const debuggeeRef = this . db . ref ( `cdbg/debuggees/${ this . debuggeeId } ` ) ;
146- debuggeeRef . set ( debuggee ) ;
147-
148- // TODO: Handle errors. I can .set(data, (error) => if (error) {})
149- const agentId = 'unsupported' ;
150- callback ( null , { debuggee, agentId} ) ;
172+ debuggeeRef . set ( debuggee , err => {
173+ if ( err ) {
174+ callback ( err ) ;
175+ } else {
176+ const agentId = 'unsupported' ;
177+ callback ( null , { debuggee, agentId} ) ;
178+ }
179+ } ) ;
151180 }
152181
153182 /**
@@ -156,11 +185,11 @@ export class FirebaseController implements Controller {
156185 * @param {!Breakpoint } breakpoint
157186 * @param {!Function } callback accepting (err, body)
158187 */
159- updateBreakpoint (
188+ async updateBreakpoint (
160189 debuggee : Debuggee ,
161190 breakpoint : stackdriver . Breakpoint ,
162191 callback : ( err ?: Error , body ?: { } ) => void
163- ) : void {
192+ ) : Promise < void > {
164193 debuglog ( 'updating a breakpoint' ) ;
165194 assert ( debuggee . id , 'should have a registered debuggee' ) ;
166195
@@ -184,32 +213,43 @@ export class FirebaseController implements Controller {
184213 // https://firebase.google.com/docs/reference/rest/database#section-server-values
185214 breakpoint_map [ 'finalTimeUnixMsec' ] = { '.sv' : 'timestamp' } ;
186215
187- this . db
188- . ref ( `cdbg/breakpoints/${ this . debuggeeId } /active/${ breakpoint . id } ` )
189- . remove ( ) ;
216+ try {
217+ await this . db
218+ . ref ( `cdbg/breakpoints/${ this . debuggeeId } /active/${ breakpoint . id } ` )
219+ . remove ( ) ;
220+ } catch ( err ) {
221+ debuglog ( `failed to delete breakpoint ${ breakpoint . id } : ` + err ) ;
222+ callback ( err as Error ) ;
223+ throw err ;
224+ }
225+
226+ try {
227+ if ( is_snapshot ) {
228+ // We could also restrict this to only write to this node if it wasn't
229+ // an error and there is actual snapshot data. For now though we'll
230+ // write it regardless, makes sense if you want to get everything for
231+ // a snapshot it's at this location, regardless of what it contains.
232+ await this . db
233+ . ref ( `cdbg/breakpoints/${ this . debuggeeId } /snapshot/${ breakpoint . id } ` )
234+ . set ( breakpoint_map ) ;
235+ // Now strip the snapshot data for the write to 'final' path.
236+ const fields_to_strip = [
237+ 'evaluatedExpressions' ,
238+ 'stackFrames' ,
239+ 'variableTable' ,
240+ ] ;
241+ fields_to_strip . forEach ( field => delete breakpoint_map [ field ] ) ;
242+ }
190243
191- // TODO: error handling from here on
192- if ( is_snapshot ) {
193- // We could also restrict this to only write to this node if it wasn't
194- // an error and there is actual snapshot data. For now though we'll
195- // write it regardless, makes sense if you want to get everything for
196- // a snapshot it's at this location, regardless of what it contains.
197- this . db
198- . ref ( `cdbg/breakpoints/${ this . debuggeeId } /snapshot/${ breakpoint . id } ` )
244+ await this . db
245+ . ref ( `cdbg/breakpoints/${ this . debuggeeId } /final/${ breakpoint . id } ` )
199246 . set ( breakpoint_map ) ;
200- // Now strip the snapshot data for the write to 'final' path.
201- const fields_to_strip = [
202- 'evaluatedExpressions' ,
203- 'stackFrames' ,
204- 'variableTable' ,
205- ] ;
206- fields_to_strip . forEach ( field => delete breakpoint_map [ field ] ) ;
247+ } catch ( err ) {
248+ debuglog ( `failed to finalize breakpoint ${ breakpoint . id } : ` + err ) ;
249+ callback ( err as Error ) ;
250+ throw err ;
207251 }
208252
209- this . db
210- . ref ( `cdbg/breakpoints/${ this . debuggeeId } /final/${ breakpoint . id } ` )
211- . set ( breakpoint_map ) ;
212-
213253 // Indicate success to the caller.
214254 callback ( ) ;
215255 }
@@ -224,26 +264,53 @@ export class FirebaseController implements Controller {
224264 this . bpRef = this . db . ref ( `cdbg/breakpoints/${ this . debuggeeId } /active` ) ;
225265
226266 let breakpoints = [ ] as stackdriver . Breakpoint [ ] ;
227- this . bpRef . on ( 'child_added' , ( snapshot : firebase . database . DataSnapshot ) => {
228- debuglog ( `new breakpoint: ${ snapshot . key } ` ) ;
229- const breakpoint = snapshot . val ( ) ;
230- breakpoint . id = snapshot . key ;
231- breakpoints . push ( breakpoint ) ;
232- callback ( null , breakpoints ) ;
233- } ) ;
234- this . bpRef . on ( 'child_removed' , snapshot => {
235- // remove the breakpoint.
236- const bpId = snapshot . key ;
237- breakpoints = breakpoints . filter ( bp => bp . id !== bpId ) ;
238- debuglog ( `breakpoint removed: ${ bpId } ` ) ;
239- callback ( null , breakpoints ) ;
240- } ) ;
267+ this . bpRef . on (
268+ 'child_added' ,
269+ ( snapshot : firebase . database . DataSnapshot ) => {
270+ debuglog ( `new breakpoint: ${ snapshot . key } ` ) ;
271+ const breakpoint = snapshot . val ( ) ;
272+ breakpoint . id = snapshot . key ;
273+ breakpoints . push ( breakpoint ) ;
274+ callback ( null , breakpoints ) ;
275+ } ,
276+ ( e : Error ) => {
277+ debuglog (
278+ 'unable to listen to child_added events on ' +
279+ `cdbg/breakpoints/${ this . debuggeeId } /active. ` +
280+ 'Please check your database settings.'
281+ ) ;
282+ callback ( e , [ ] ) ;
283+ }
284+ ) ;
285+ this . bpRef . on (
286+ 'child_removed' ,
287+ snapshot => {
288+ // remove the breakpoint.
289+ const bpId = snapshot . key ;
290+ breakpoints = breakpoints . filter ( bp => bp . id !== bpId ) ;
291+ debuglog ( `breakpoint removed: ${ bpId } ` ) ;
292+ callback ( null , breakpoints ) ;
293+ } ,
294+ ( e : Error ) => {
295+ debuglog (
296+ 'unable to listen to child_removed events on ' +
297+ `cdbg/breakpoints/${ this . debuggeeId } /active. ` +
298+ 'Please check your database settings.'
299+ ) ;
300+ callback ( e , [ ] ) ;
301+ }
302+ ) ;
241303 }
242304
243305 stop ( ) : void {
244306 if ( this . bpRef ) {
245307 this . bpRef . off ( ) ;
246308 this . bpRef = undefined ;
247309 }
310+ try {
311+ firebase . app ( FIREBASE_APP_NAME ) . delete ( ) ;
312+ } catch ( err ) {
313+ debuglog ( `failed to tear down firebase app: ${ err } )` ) ;
314+ }
248315 }
249316}
0 commit comments