@@ -81,6 +81,10 @@ describe('DataConnectApiClient', () => {
8181 connectorConfig ,
8282 mocks . mockCredentialApp ( ) ) ;
8383
84+ const clientWithoutProjectId_with_connector = new DataConnectApiClient (
85+ connectorConfig_with_connector ,
86+ mocks . mockCredentialApp ( ) ) ;
87+
8488 const mockOptions = {
8589 credential : new mocks . MockCredential ( ) ,
8690 projectId : 'test-project' ,
@@ -89,12 +93,14 @@ describe('DataConnectApiClient', () => {
8993 let app : FirebaseApp ;
9094
9195 let apiClient : DataConnectApiClient ;
96+ let apiClient_with_connector : DataConnectApiClient ;
9297 let sandbox : sinon . SinonSandbox ;
9398
9499 beforeEach ( ( ) => {
95100 sandbox = sinon . createSandbox ( ) ;
96101 app = mocks . appWithOptions ( mockOptions ) ;
97102 apiClient = new DataConnectApiClient ( connectorConfig , app ) ;
103+ apiClient_with_connector = new DataConnectApiClient ( connectorConfig_with_connector , app ) ;
98104 } ) ;
99105
100106 afterEach ( ( ) => {
@@ -116,6 +122,17 @@ describe('DataConnectApiClient', () => {
116122 it ( 'should initialize httpClient with the provided app' , ( ) => {
117123 expect ( ( apiClient as any ) . httpClient ) . to . be . an . instanceOf ( AuthorizedHttpClient ) ;
118124 } ) ;
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+ } ) ;
119136 } ) ;
120137
121138 describe ( 'executeGraphql' , ( ) => {
@@ -215,7 +232,7 @@ describe('DataConnectApiClient', () => {
215232 method : 'POST' ,
216233 url : `https://firebasedataconnect.googleapis.com/v1alpha/projects/test-project/locations/${ connectorConfig . location } /services/${ connectorConfig . serviceId } :executeGraphql` ,
217234 headers : EXPECTED_HEADERS ,
218- data : { query : 'query' }
235+ data : { query : 'query' }
219236 } ) ;
220237 } ) ;
221238 } ) ;
@@ -235,6 +252,164 @@ describe('DataConnectApiClient', () => {
235252 } ) ;
236253 } ) ;
237254 } ) ;
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+ } ) ;
238413 } ) ;
239414} ) ;
240415
0 commit comments