@firebase/data-connect 0.0.2-dataconnect-preview.388b61c7e → 0.0.3-dataconnect-preview.d986d4bf2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/index.cjs.js +146 -27
  2. package/dist/index.cjs.js.map +1 -1
  3. package/dist/index.esm2017.js +145 -28
  4. package/dist/index.esm2017.js.map +1 -1
  5. package/dist/index.esm5.js +146 -23
  6. package/dist/index.esm5.js.map +1 -1
  7. package/dist/index.node.cjs.js +147 -22
  8. package/dist/index.node.cjs.js.map +1 -1
  9. package/dist/internal.d.ts +32 -5
  10. package/dist/node-esm/index.node.esm.js +145 -28
  11. package/dist/node-esm/index.node.esm.js.map +1 -1
  12. package/dist/node-esm/src/api/DataConnect.d.ts +9 -0
  13. package/dist/node-esm/src/api/index.d.ts +1 -0
  14. package/dist/node-esm/src/api/query.d.ts +1 -1
  15. package/dist/node-esm/src/core/FirebaseAuthProvider.d.ts +1 -1
  16. package/dist/node-esm/src/core/QueryManager.d.ts +1 -1
  17. package/dist/node-esm/src/core/error.d.ts +2 -1
  18. package/dist/node-esm/src/network/fetch.d.ts +1 -1
  19. package/dist/node-esm/src/network/transport/index.d.ts +1 -1
  20. package/dist/node-esm/src/network/transport/rest.d.ts +20 -9
  21. package/dist/node-esm/src/util/validateArgs.d.ts +33 -0
  22. package/dist/private.d.ts +16 -5
  23. package/dist/public.d.ts +5 -3
  24. package/dist/src/api/DataConnect.d.ts +9 -0
  25. package/dist/src/api/index.d.ts +1 -0
  26. package/dist/src/api/query.d.ts +1 -1
  27. package/dist/src/core/FirebaseAuthProvider.d.ts +1 -1
  28. package/dist/src/core/QueryManager.d.ts +1 -1
  29. package/dist/src/core/error.d.ts +2 -1
  30. package/dist/src/network/fetch.d.ts +1 -1
  31. package/dist/src/network/transport/index.d.ts +1 -1
  32. package/dist/src/network/transport/rest.d.ts +20 -9
  33. package/dist/src/util/validateArgs.d.ts +33 -0
  34. package/package.json +10 -6
@@ -4,7 +4,7 @@ import { FirebaseError } from '@firebase/util';
4
4
  import { Logger } from '@firebase/logger';
5
5
 
6
6
  const name = "@firebase/data-connect";
7
- const version = "0.0.2-dataconnect-preview.388b61c7e";
7
+ const version = "0.0.3-dataconnect-preview.d986d4bf2";
8
8
 
9
9
  /**
10
10
  * @license
@@ -54,7 +54,8 @@ const Code = {
54
54
  NOT_INITIALIZED: 'not-initialized',
55
55
  NOT_SUPPORTED: 'not-supported',
56
56
  INVALID_ARGUMENT: 'invalid-argument',
57
- PARTIAL_ERROR: 'partial-error'
57
+ PARTIAL_ERROR: 'partial-error',
58
+ UNAUTHORIZED: 'unauthorized'
58
59
  };
59
60
  /** An error returned by a DataConnect operation. */
60
61
  class DataConnectError extends FirebaseError {
@@ -163,7 +164,8 @@ class FirebaseAuthProvider {
163
164
  removeTokenChangeListener(listener) {
164
165
  this._authProvider
165
166
  .get()
166
- .then(auth => auth.removeAuthTokenListener(listener));
167
+ .then(auth => auth.removeAuthTokenListener(listener))
168
+ .catch(err => logError(err));
167
169
  }
168
170
  }
169
171
 
@@ -431,12 +433,20 @@ function addToken(url, apiKey) {
431
433
  * limitations under the License.
432
434
  */
433
435
  let connectFetch = globalThis.fetch;
434
- function dcFetch(url, body, { signal }, accessToken) {
436
+ function getGoogApiClientValue(_isUsingGen) {
437
+ let str = 'gl-js/ fire/' + SDK_VERSION;
438
+ if (_isUsingGen) {
439
+ str += ' web/gen';
440
+ }
441
+ return str;
442
+ }
443
+ function dcFetch(url, body, { signal }, accessToken, _isUsingGen) {
435
444
  if (!connectFetch) {
436
445
  throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
437
446
  }
438
447
  const headers = {
439
- 'Content-Type': 'application/json'
448
+ 'Content-Type': 'application/json',
449
+ 'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen)
440
450
  };
441
451
  if (accessToken) {
442
452
  headers['X-Firebase-Auth-Token'] = accessToken;
@@ -448,8 +458,9 @@ function dcFetch(url, body, { signal }, accessToken) {
448
458
  method: 'POST',
449
459
  headers,
450
460
  signal
451
- }).catch(err => {
452
- throw new DataConnectError(Code.OTHER, "Failed to fetch: " + JSON.stringify(err));
461
+ })
462
+ .catch(err => {
463
+ throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
453
464
  })
454
465
  .then(async (response) => {
455
466
  let jsonResponse = null;
@@ -459,9 +470,13 @@ function dcFetch(url, body, { signal }, accessToken) {
459
470
  catch (e) {
460
471
  throw new DataConnectError(Code.OTHER, JSON.stringify(e));
461
472
  }
473
+ const message = getMessage(jsonResponse);
462
474
  if (response.status >= 400) {
463
475
  logError('Error while performing request: ' + JSON.stringify(jsonResponse));
464
- throw new DataConnectError(Code.OTHER, JSON.stringify(jsonResponse));
476
+ if (response.status === 401) {
477
+ throw new DataConnectError(Code.UNAUTHORIZED, message);
478
+ }
479
+ throw new DataConnectError(Code.OTHER, message);
465
480
  }
466
481
  return jsonResponse;
467
482
  })
@@ -473,6 +488,12 @@ function dcFetch(url, body, { signal }, accessToken) {
473
488
  }
474
489
  return res;
475
490
  });
491
+ }
492
+ function getMessage(obj) {
493
+ if ('message' in obj) {
494
+ return obj.message;
495
+ }
496
+ return JSON.stringify(obj);
476
497
  }
477
498
 
478
499
  /**
@@ -492,10 +513,11 @@ function dcFetch(url, body, { signal }, accessToken) {
492
513
  * limitations under the License.
493
514
  */
494
515
  class RESTTransport {
495
- constructor(options, apiKey, authProvider, transportOptions) {
516
+ constructor(options, apiKey, authProvider, transportOptions, _isUsingGen = false) {
496
517
  var _a;
497
518
  this.apiKey = apiKey;
498
519
  this.authProvider = authProvider;
520
+ this._isUsingGen = _isUsingGen;
499
521
  this._host = '';
500
522
  this._location = 'l';
501
523
  this._connectorName = '';
@@ -503,30 +525,30 @@ class RESTTransport {
503
525
  this._project = 'p';
504
526
  this._accessToken = null;
505
527
  this._authInitialized = false;
528
+ this._lastToken = null;
506
529
  // TODO(mtewani): Update U to include shape of body defined in line 13.
507
530
  this.invokeQuery = (queryName, body) => {
508
531
  const abortController = new AbortController();
509
532
  // TODO(mtewani): Update to proper value
510
- const withAuth = this.getWithAuth().then(() => {
511
- return dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
512
- name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
513
- operationName: queryName,
514
- variables: body
515
- }, // TODO(mtewani): This is a patch, fix this.
516
- abortController, this._accessToken);
517
- });
533
+ const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
534
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
535
+ operationName: queryName,
536
+ variables: body
537
+ }, // TODO(mtewani): This is a patch, fix this.
538
+ abortController, this._accessToken, this._isUsingGen));
518
539
  return {
519
- then: withAuth.then.bind(withAuth)
540
+ then: withAuth.then.bind(withAuth),
541
+ catch: withAuth.catch.bind(withAuth)
520
542
  };
521
543
  };
522
544
  this.invokeMutation = (mutationName, body) => {
523
545
  const abortController = new AbortController();
524
- const taskResult = this.getWithAuth().then(() => {
546
+ const taskResult = this.withRetry(() => {
525
547
  return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
526
548
  name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
527
549
  operationName: mutationName,
528
550
  variables: body
529
- }, abortController, this._accessToken);
551
+ }, abortController, this._accessToken, this._isUsingGen);
530
552
  });
531
553
  return {
532
554
  then: taskResult.then.bind(taskResult),
@@ -581,12 +603,12 @@ class RESTTransport {
581
603
  onTokenChanged(newToken) {
582
604
  this._accessToken = newToken;
583
605
  }
584
- getWithAuth() {
606
+ getWithAuth(forceToken = false) {
585
607
  let starterPromise = new Promise(resolve => resolve(this._accessToken));
586
608
  if (!this._authInitialized) {
587
609
  if (this.authProvider) {
588
610
  starterPromise = this.authProvider
589
- .getToken(/*forceToken=*/ false)
611
+ .getToken(/*forceToken=*/ forceToken)
590
612
  .then(data => {
591
613
  if (!data) {
592
614
  return null;
@@ -601,6 +623,30 @@ class RESTTransport {
601
623
  }
602
624
  return starterPromise;
603
625
  }
626
+ _setLastToken(lastToken) {
627
+ this._lastToken = lastToken;
628
+ }
629
+ withRetry(promiseFactory, retry = false) {
630
+ let isNewToken = false;
631
+ return this.getWithAuth(retry)
632
+ .then(res => {
633
+ isNewToken = this._lastToken !== res;
634
+ this._lastToken = res;
635
+ return res;
636
+ })
637
+ .then(promiseFactory)
638
+ .catch(err => {
639
+ // Only retry if the result is unauthorized and the last token isn't the same as the new one.
640
+ if ('code' in err &&
641
+ err.code === Code.UNAUTHORIZED &&
642
+ !retry &&
643
+ isNewToken) {
644
+ logDebug('Retrying due to unauthorized');
645
+ return this.withRetry(promiseFactory, true);
646
+ }
647
+ throw err;
648
+ });
649
+ }
604
650
  }
605
651
 
606
652
  /**
@@ -707,6 +753,7 @@ class DataConnect {
707
753
  this._authProvider = _authProvider;
708
754
  this.isEmulator = false;
709
755
  this.initialized = false;
756
+ this._isUsingGeneratedSdk = false;
710
757
  if (typeof process !== 'undefined' && process.env) {
711
758
  const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
712
759
  if (host) {
@@ -716,6 +763,14 @@ class DataConnect {
716
763
  }
717
764
  }
718
765
  }
766
+ /*
767
+ @internal
768
+ */
769
+ _useGeneratedSdk() {
770
+ if (!this._isUsingGeneratedSdk) {
771
+ this._isUsingGeneratedSdk = true;
772
+ }
773
+ }
719
774
  _delete() {
720
775
  _removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
721
776
  return Promise.resolve();
@@ -737,7 +792,7 @@ class DataConnect {
737
792
  this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
738
793
  }
739
794
  this.initialized = true;
740
- this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider);
795
+ this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider, undefined, this._isUsingGeneratedSdk);
741
796
  if (this._transportOptions) {
742
797
  this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
743
798
  }
@@ -774,7 +829,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
774
829
  dcOptions = optionalOptions;
775
830
  app = appOrOptions;
776
831
  }
777
- if (!app) {
832
+ if (!app || Object.keys(app).length === 0) {
778
833
  app = getApp();
779
834
  }
780
835
  const provider = _getProvider(app, 'data-connect');
@@ -788,9 +843,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
788
843
  return dcInstance;
789
844
  }
790
845
  }
791
- if (!dcOptions) {
792
- throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
793
- }
846
+ validateDCOptions(dcOptions);
794
847
  logDebug('Creating new DataConnect instance');
795
848
  // Initialize with options.
796
849
  return provider.initialize({
@@ -798,6 +851,24 @@ function getDataConnect(appOrOptions, optionalOptions) {
798
851
  options: dcOptions
799
852
  });
800
853
  }
854
+ /**
855
+ *
856
+ * @param dcOptions
857
+ * @returns {void}
858
+ * @internal
859
+ */
860
+ function validateDCOptions(dcOptions) {
861
+ const fields = ['connector', 'location', 'service'];
862
+ if (!dcOptions) {
863
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
864
+ }
865
+ fields.forEach(field => {
866
+ if (dcOptions[field] === null || dcOptions[field] === undefined) {
867
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
868
+ }
869
+ });
870
+ return true;
871
+ }
801
872
  /**
802
873
  * Delete DataConnect instance
803
874
  * @param dataConnect DataConnect instance
@@ -833,6 +904,9 @@ function registerDataConnect(variant) {
833
904
  if (settings) {
834
905
  newOpts = JSON.parse(settings);
835
906
  }
907
+ if (!app.options.projectId) {
908
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
909
+ }
836
910
  return new DataConnect(app, Object.assign(Object.assign({}, newOpts), { projectId: app.options.projectId }), authProvider);
837
911
  }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
838
912
  registerVersion(name, version, variant);
@@ -892,6 +966,49 @@ function toQueryRef(serializedRef) {
892
966
  return queryRef(getDataConnect(connectorConfig), name, variables);
893
967
  }
894
968
 
969
+ /**
970
+ * @license
971
+ * Copyright 2024 Google LLC
972
+ *
973
+ * Licensed under the Apache License, Version 2.0 (the "License");
974
+ * you may not use this file except in compliance with the License.
975
+ * You may obtain a copy of the License at
976
+ *
977
+ * http://www.apache.org/licenses/LICENSE-2.0
978
+ *
979
+ * Unless required by applicable law or agreed to in writing, software
980
+ * distributed under the License is distributed on an "AS IS" BASIS,
981
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
982
+ * See the License for the specific language governing permissions and
983
+ * limitations under the License.
984
+ */
985
+ /**
986
+ * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
987
+ * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
988
+ * @param connectorConfig
989
+ * @param dcOrVars
990
+ * @param vars
991
+ * @param validateVars
992
+ * @returns {DataConnect} and {Variables} instance
993
+ * @internal
994
+ */
995
+ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
996
+ let dcInstance;
997
+ let realVars;
998
+ if (dcOrVars && 'enableEmulator' in dcOrVars) {
999
+ dcInstance = dcOrVars;
1000
+ realVars = vars;
1001
+ }
1002
+ else {
1003
+ dcInstance = getDataConnect(connectorConfig);
1004
+ realVars = dcOrVars;
1005
+ }
1006
+ if (!dcInstance || (!realVars && validateVars)) {
1007
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
1008
+ }
1009
+ return { dc: dcInstance, vars: realVars };
1010
+ }
1011
+
895
1012
  /**
896
1013
  * @license
897
1014
  * Copyright 2024 Google LLC
@@ -954,5 +1071,5 @@ function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComp
954
1071
  */
955
1072
  registerDataConnect();
956
1073
 
957
- export { DataConnect, FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR, FirebaseAuthProvider, MUTATION_STR, MutationManager, QUERY_STR, SOURCE_CACHE, SOURCE_SERVER, connectDataConnectEmulator, executeMutation, executeQuery, getDataConnect, mutationRef, parseOptions, queryRef, setLogLevel, subscribe, terminate, toQueryRef };
1074
+ export { DataConnect, FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR, FirebaseAuthProvider, MUTATION_STR, MutationManager, QUERY_STR, SOURCE_CACHE, SOURCE_SERVER, connectDataConnectEmulator, executeMutation, executeQuery, getDataConnect, mutationRef, parseOptions, queryRef, setLogLevel, subscribe, terminate, toQueryRef, validateArgs, validateDCOptions };
958
1075
  //# sourceMappingURL=index.esm2017.js.map