@@ -81,6 +81,10 @@ describe('DataConnectApiClient', () => {
81
81
connectorConfig ,
82
82
mocks . mockCredentialApp ( ) ) ;
83
83
84
+ const clientWithoutProjectId_with_connector = new DataConnectApiClient (
85
+ connectorConfig_with_connector ,
86
+ mocks . mockCredentialApp ( ) ) ;
87
+
84
88
const mockOptions = {
85
89
credential : new mocks . MockCredential ( ) ,
86
90
projectId : 'test-project' ,
@@ -89,12 +93,14 @@ describe('DataConnectApiClient', () => {
89
93
let app : FirebaseApp ;
90
94
91
95
let apiClient : DataConnectApiClient ;
96
+ let apiClient_with_connector : DataConnectApiClient ;
92
97
let sandbox : sinon . SinonSandbox ;
93
98
94
99
beforeEach ( ( ) => {
95
100
sandbox = sinon . createSandbox ( ) ;
96
101
app = mocks . appWithOptions ( mockOptions ) ;
97
102
apiClient = new DataConnectApiClient ( connectorConfig , app ) ;
103
+ apiClient_with_connector = new DataConnectApiClient ( connectorConfig_with_connector , app ) ;
98
104
} ) ;
99
105
100
106
afterEach ( ( ) => {
@@ -116,6 +122,17 @@ describe('DataConnectApiClient', () => {
116
122
it ( 'should initialize httpClient with the provided app' , ( ) => {
117
123
expect ( ( apiClient as any ) . httpClient ) . to . be . an . instanceOf ( AuthorizedHttpClient ) ;
118
124
} ) ;
125
+ // Test for an app instance with a connector within the connector config
126
+ it ( 'should throw an error if app is not a valid Firebase app instance' , ( ) => {
127
+ expect ( ( ) => new DataConnectApiClient ( connectorConfig_with_connector , null as unknown as FirebaseApp ) ) . to . throw (
128
+ FirebaseDataConnectError ,
129
+ 'First argument passed to getDataConnect() must be a valid Firebase app instance.'
130
+ ) ;
131
+ } ) ;
132
+
133
+ it ( 'should initialize httpClient with the provided app' , ( ) => {
134
+ expect ( ( apiClient_with_connector as any ) . httpClient ) . to . be . an . instanceOf ( AuthorizedHttpClient ) ;
135
+ } ) ;
119
136
} ) ;
120
137
121
138
describe ( 'executeGraphql' , ( ) => {
@@ -215,7 +232,7 @@ describe('DataConnectApiClient', () => {
215
232
method : 'POST' ,
216
233
url : `https://firebasedataconnect.googleapis.com/v1alpha/projects/test-project/locations/${ connectorConfig . location } /services/${ connectorConfig . serviceId } :executeGraphql` ,
217
234
headers : EXPECTED_HEADERS ,
218
- data : { query : 'query' }
235
+ data : { query : 'query' }
219
236
} ) ;
220
237
} ) ;
221
238
} ) ;
@@ -235,6 +252,164 @@ describe('DataConnectApiClient', () => {
235
252
} ) ;
236
253
} ) ;
237
254
} ) ;
255
+
256
+ it ( 'should resolve with the GraphQL response on success when a connector is passed in' , ( ) => {
257
+ interface UsersResponse {
258
+ users : [
259
+ user : {
260
+ id : string ;
261
+ name : string ;
262
+ address : string ;
263
+ }
264
+ ] ;
265
+ }
266
+ const stub = sandbox
267
+ . stub ( HttpClient . prototype , 'send' )
268
+ . resolves ( utils . responseFrom ( TEST_RESPONSE , 200 ) ) ;
269
+ return apiClient_with_connector . executeGraphql < UsersResponse , unknown > ( 'query' , { } )
270
+ . then ( ( resp ) => {
271
+ expect ( resp . data . users ) . to . be . not . empty ;
272
+ expect ( resp . data . users [ 0 ] . name ) . to . be . not . undefined ;
273
+ expect ( resp . data . users [ 0 ] . address ) . to . be . not . undefined ;
274
+ expect ( resp . data . users ) . to . deep . equal ( TEST_RESPONSE . data . users ) ;
275
+ expect ( stub ) . to . have . been . calledOnce . and . calledWith ( {
276
+ method : 'POST' ,
277
+ url : `https://firebasedataconnect.googleapis.com/v1alpha/projects/test-project/locations/${ connectorConfig . location } /services/${ connectorConfig . serviceId } :executeGraphql` ,
278
+ headers : EXPECTED_HEADERS ,
279
+ data : { query : 'query' }
280
+ } ) ;
281
+ } ) ;
282
+ } ) ;
283
+
284
+ it ( 'should use DATA_CONNECT_EMULATOR_HOST if set' , ( ) => {
285
+ process . env . DATA_CONNECT_EMULATOR_HOST = 'localhost:9399' ;
286
+ const stub = sandbox
287
+ . stub ( HttpClient . prototype , 'send' )
288
+ . resolves ( utils . responseFrom ( TEST_RESPONSE , 200 ) ) ;
289
+ return apiClient_with_connector . executeGraphql ( 'query' , { } )
290
+ . then ( ( ) => {
291
+ expect ( stub ) . to . have . been . calledOnce . and . calledWith ( {
292
+ method : 'POST' ,
293
+ url : `http://localhost:9399/v1alpha/projects/test-project/locations/${ connectorConfig . location } /services/${ connectorConfig . serviceId } :executeGraphql` ,
294
+ headers : EMULATOR_EXPECTED_HEADERS ,
295
+ data : { query : 'query' }
296
+ } ) ;
297
+ } ) ;
298
+ } ) ;
299
+
300
+ } ) ;
301
+
302
+ describe ( 'executeMutation' , ( ) => {
303
+ //what if there's no project id and also there's no connector, what error would that be? -> should error the connector first because you won't even reach the endpoint to find out if there's a project id or not
304
+ it ( 'should reject when project id is not available' , ( ) => {
305
+ return clientWithoutProjectId_with_connector . executeMutation ( { operationName : 'getById' } )
306
+ . should . eventually . be . rejectedWith ( noProjectId ) ;
307
+ } ) ;
308
+
309
+ it ( 'should throw an error if no arguments are passed in' , async ( ) => {
310
+ await expect ( apiClient_with_connector . executeMutation ( undefined as any ) ) . to . be . rejectedWith (
311
+ FirebaseDataConnectError ,
312
+ 'GraphqlOptions should be a non-null object'
313
+ ) ;
314
+ } ) ;
315
+
316
+ const invalidOptions = [ null , NaN , 0 , 1 , true , false , [ ] , _ . noop ] ;
317
+ invalidOptions . forEach ( ( invalidOption ) => {
318
+ it ( 'should throw given an invalid options object: ' + JSON . stringify ( invalidOption ) , async ( ) => {
319
+ await expect ( apiClient_with_connector . executeMutation ( invalidOption as any ) ) . to . be . rejectedWith (
320
+ FirebaseDataConnectError ,
321
+ 'GraphqlOptions must be a non-null object'
322
+ ) ;
323
+ } ) ;
324
+ } ) ;
325
+ //could this pass as a null object, also what if the wrong operaton was passed in, would it be handled in another test- say integration?
326
+ it ( 'should throw an error if there is no operationName' , async ( ) => {
327
+ await expect ( apiClient_with_connector . executeMutation ( { } ) ) . to . be . rejectedWith (
328
+ FirebaseDataConnectError ,
329
+ 'GraphqlOptions must contain `operationName`.'
330
+ ) ;
331
+ } ) ;
332
+
333
+ it ( 'should reject when a full platform error response is received' , ( ) => {
334
+ sandbox
335
+ . stub ( HttpClient . prototype , 'send' )
336
+ . rejects ( utils . errorFrom ( ERROR_RESPONSE , 404 ) ) ;
337
+ const expected = new FirebaseDataConnectError ( 'not-found' , 'Requested entity not found' ) ;
338
+ return apiClient_with_connector . executeMutation ( { operationName : 'getById' } )
339
+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
340
+ } ) ;
341
+
342
+ it ( 'should reject with unknown-error when error code is not present' , ( ) => {
343
+ sandbox
344
+ . stub ( HttpClient . prototype , 'send' )
345
+ . rejects ( utils . errorFrom ( { } , 404 ) ) ;
346
+ const expected = new FirebaseDataConnectError ( 'unknown-error' , 'Unknown server error: {}' ) ;
347
+ return apiClient_with_connector . executeMutation ( { operationName : 'getById' } )
348
+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
349
+ } ) ;
350
+
351
+ it ( 'should reject with unknown-error for non-json response' , ( ) => {
352
+ sandbox
353
+ . stub ( HttpClient . prototype , 'send' )
354
+ . rejects ( utils . errorFrom ( 'not json' , 404 ) ) ;
355
+ const expected = new FirebaseDataConnectError (
356
+ 'unknown-error' , 'Unexpected response with status: 404 and body: not json' ) ;
357
+ return apiClient_with_connector . executeMutation ( { operationName : 'getById' } )
358
+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
359
+ } ) ;
360
+
361
+ it ( 'should reject when rejected with a FirebaseDataConnectError' , ( ) => {
362
+ const expected = new FirebaseDataConnectError ( 'internal-error' , 'socket hang up' ) ;
363
+ sandbox
364
+ . stub ( HttpClient . prototype , 'send' )
365
+ . rejects ( expected ) ;
366
+ return apiClient_with_connector . executeMutation ( { operationName : 'getById' } )
367
+ . should . eventually . be . rejected . and . deep . include ( expected ) ;
368
+ } ) ;
369
+
370
+ it ( 'should resolve with the Mutation response on success' , ( ) => {
371
+ interface UsersResponse {
372
+ users : [
373
+ user : {
374
+ id : string ;
375
+ name : string ;
376
+ address : string ;
377
+ }
378
+ ] ;
379
+ }
380
+ const stub = sandbox
381
+ . stub ( HttpClient . prototype , 'send' )
382
+ . resolves ( utils . responseFrom ( TEST_RESPONSE , 200 ) ) ;
383
+ return apiClient_with_connector . executeMutation < UsersResponse , unknown > ( { operationName : 'getById' } )
384
+ . then ( ( resp ) => {
385
+ expect ( resp . data . users ) . to . be . not . empty ;
386
+ expect ( resp . data . users [ 0 ] . name ) . to . be . not . undefined ;
387
+ expect ( resp . data . users [ 0 ] . address ) . to . be . not . undefined ;
388
+ expect ( resp . data . users ) . to . deep . equal ( TEST_RESPONSE . data . users ) ;
389
+ expect ( stub ) . to . have . been . calledOnce . and . calledWith ( {
390
+ method : 'POST' ,
391
+ url : `https://firebasedataconnect.googleapis.com/v1alpha/projects/test-project/locations/${ connectorConfig_with_connector . location } /services/${ connectorConfig_with_connector . serviceId } /connectors/${ connectorConfig_with_connector . connector } :executeMutation` ,
392
+ headers : EXPECTED_HEADERS ,
393
+ data : { query : undefined , name : 'getById' , operationName : 'getById' }
394
+ } ) ;
395
+ } ) ;
396
+ } ) ;
397
+
398
+ it ( 'should use DATA_CONNECT_EMULATOR_HOST if set' , ( ) => {
399
+ process . env . DATA_CONNECT_EMULATOR_HOST = 'localhost:9399' ;
400
+ const stub = sandbox
401
+ . stub ( HttpClient . prototype , 'send' )
402
+ . resolves ( utils . responseFrom ( TEST_RESPONSE , 200 ) ) ;
403
+ return apiClient_with_connector . executeMutation ( { operationName : 'getById' } )
404
+ . then ( ( ) => {
405
+ expect ( stub ) . to . have . been . calledOnce . and . calledWith ( {
406
+ method : 'POST' ,
407
+ url : `http://localhost:9399/v1alpha/projects/test-project/locations/${ connectorConfig_with_connector . location } /services/${ connectorConfig_with_connector . serviceId } /connectors/${ connectorConfig_with_connector . connector } :executeMutation` ,
408
+ headers : EMULATOR_EXPECTED_HEADERS ,
409
+ data : { query : undefined , name : 'getById' , operationName : 'getById' }
410
+ } ) ;
411
+ } ) ;
412
+ } ) ;
238
413
} ) ;
239
414
} ) ;
240
415
0 commit comments