@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
package/dist/index.cjs.js CHANGED
@@ -8,7 +8,7 @@ var util = require('@firebase/util');
8
8
  var logger$1 = require('@firebase/logger');
9
9
 
10
10
  const name = "@firebase/data-connect";
11
- const version = "0.0.2-dataconnect-preview.388b61c7e";
11
+ const version = "0.0.3-dataconnect-preview.d986d4bf2";
12
12
 
13
13
  /**
14
14
  * @license
@@ -58,7 +58,8 @@ const Code = {
58
58
  NOT_INITIALIZED: 'not-initialized',
59
59
  NOT_SUPPORTED: 'not-supported',
60
60
  INVALID_ARGUMENT: 'invalid-argument',
61
- PARTIAL_ERROR: 'partial-error'
61
+ PARTIAL_ERROR: 'partial-error',
62
+ UNAUTHORIZED: 'unauthorized'
62
63
  };
63
64
  /** An error returned by a DataConnect operation. */
64
65
  class DataConnectError extends util.FirebaseError {
@@ -167,7 +168,8 @@ class FirebaseAuthProvider {
167
168
  removeTokenChangeListener(listener) {
168
169
  this._authProvider
169
170
  .get()
170
- .then(auth => auth.removeAuthTokenListener(listener));
171
+ .then(auth => auth.removeAuthTokenListener(listener))
172
+ .catch(err => logError(err));
171
173
  }
172
174
  }
173
175
 
@@ -435,12 +437,20 @@ function addToken(url, apiKey) {
435
437
  * limitations under the License.
436
438
  */
437
439
  let connectFetch = globalThis.fetch;
438
- function dcFetch(url, body, { signal }, accessToken) {
440
+ function getGoogApiClientValue(_isUsingGen) {
441
+ let str = 'gl-js/ fire/' + SDK_VERSION;
442
+ if (_isUsingGen) {
443
+ str += ' web/gen';
444
+ }
445
+ return str;
446
+ }
447
+ function dcFetch(url, body, { signal }, accessToken, _isUsingGen) {
439
448
  if (!connectFetch) {
440
449
  throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
441
450
  }
442
451
  const headers = {
443
- 'Content-Type': 'application/json'
452
+ 'Content-Type': 'application/json',
453
+ 'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen)
444
454
  };
445
455
  if (accessToken) {
446
456
  headers['X-Firebase-Auth-Token'] = accessToken;
@@ -452,8 +462,9 @@ function dcFetch(url, body, { signal }, accessToken) {
452
462
  method: 'POST',
453
463
  headers,
454
464
  signal
455
- }).catch(err => {
456
- throw new DataConnectError(Code.OTHER, "Failed to fetch: " + JSON.stringify(err));
465
+ })
466
+ .catch(err => {
467
+ throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
457
468
  })
458
469
  .then(async (response) => {
459
470
  let jsonResponse = null;
@@ -463,9 +474,13 @@ function dcFetch(url, body, { signal }, accessToken) {
463
474
  catch (e) {
464
475
  throw new DataConnectError(Code.OTHER, JSON.stringify(e));
465
476
  }
477
+ const message = getMessage(jsonResponse);
466
478
  if (response.status >= 400) {
467
479
  logError('Error while performing request: ' + JSON.stringify(jsonResponse));
468
- throw new DataConnectError(Code.OTHER, JSON.stringify(jsonResponse));
480
+ if (response.status === 401) {
481
+ throw new DataConnectError(Code.UNAUTHORIZED, message);
482
+ }
483
+ throw new DataConnectError(Code.OTHER, message);
469
484
  }
470
485
  return jsonResponse;
471
486
  })
@@ -477,6 +492,12 @@ function dcFetch(url, body, { signal }, accessToken) {
477
492
  }
478
493
  return res;
479
494
  });
495
+ }
496
+ function getMessage(obj) {
497
+ if ('message' in obj) {
498
+ return obj.message;
499
+ }
500
+ return JSON.stringify(obj);
480
501
  }
481
502
 
482
503
  /**
@@ -496,10 +517,11 @@ function dcFetch(url, body, { signal }, accessToken) {
496
517
  * limitations under the License.
497
518
  */
498
519
  class RESTTransport {
499
- constructor(options, apiKey, authProvider, transportOptions) {
520
+ constructor(options, apiKey, authProvider, transportOptions, _isUsingGen = false) {
500
521
  var _a;
501
522
  this.apiKey = apiKey;
502
523
  this.authProvider = authProvider;
524
+ this._isUsingGen = _isUsingGen;
503
525
  this._host = '';
504
526
  this._location = 'l';
505
527
  this._connectorName = '';
@@ -507,30 +529,30 @@ class RESTTransport {
507
529
  this._project = 'p';
508
530
  this._accessToken = null;
509
531
  this._authInitialized = false;
532
+ this._lastToken = null;
510
533
  // TODO(mtewani): Update U to include shape of body defined in line 13.
511
534
  this.invokeQuery = (queryName, body) => {
512
535
  const abortController = new AbortController();
513
536
  // TODO(mtewani): Update to proper value
514
- const withAuth = this.getWithAuth().then(() => {
515
- return dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
516
- name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
517
- operationName: queryName,
518
- variables: body
519
- }, // TODO(mtewani): This is a patch, fix this.
520
- abortController, this._accessToken);
521
- });
537
+ const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
538
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
539
+ operationName: queryName,
540
+ variables: body
541
+ }, // TODO(mtewani): This is a patch, fix this.
542
+ abortController, this._accessToken, this._isUsingGen));
522
543
  return {
523
- then: withAuth.then.bind(withAuth)
544
+ then: withAuth.then.bind(withAuth),
545
+ catch: withAuth.catch.bind(withAuth)
524
546
  };
525
547
  };
526
548
  this.invokeMutation = (mutationName, body) => {
527
549
  const abortController = new AbortController();
528
- const taskResult = this.getWithAuth().then(() => {
550
+ const taskResult = this.withRetry(() => {
529
551
  return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
530
552
  name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
531
553
  operationName: mutationName,
532
554
  variables: body
533
- }, abortController, this._accessToken);
555
+ }, abortController, this._accessToken, this._isUsingGen);
534
556
  });
535
557
  return {
536
558
  then: taskResult.then.bind(taskResult),
@@ -585,12 +607,12 @@ class RESTTransport {
585
607
  onTokenChanged(newToken) {
586
608
  this._accessToken = newToken;
587
609
  }
588
- getWithAuth() {
610
+ getWithAuth(forceToken = false) {
589
611
  let starterPromise = new Promise(resolve => resolve(this._accessToken));
590
612
  if (!this._authInitialized) {
591
613
  if (this.authProvider) {
592
614
  starterPromise = this.authProvider
593
- .getToken(/*forceToken=*/ false)
615
+ .getToken(/*forceToken=*/ forceToken)
594
616
  .then(data => {
595
617
  if (!data) {
596
618
  return null;
@@ -605,6 +627,30 @@ class RESTTransport {
605
627
  }
606
628
  return starterPromise;
607
629
  }
630
+ _setLastToken(lastToken) {
631
+ this._lastToken = lastToken;
632
+ }
633
+ withRetry(promiseFactory, retry = false) {
634
+ let isNewToken = false;
635
+ return this.getWithAuth(retry)
636
+ .then(res => {
637
+ isNewToken = this._lastToken !== res;
638
+ this._lastToken = res;
639
+ return res;
640
+ })
641
+ .then(promiseFactory)
642
+ .catch(err => {
643
+ // Only retry if the result is unauthorized and the last token isn't the same as the new one.
644
+ if ('code' in err &&
645
+ err.code === Code.UNAUTHORIZED &&
646
+ !retry &&
647
+ isNewToken) {
648
+ logDebug('Retrying due to unauthorized');
649
+ return this.withRetry(promiseFactory, true);
650
+ }
651
+ throw err;
652
+ });
653
+ }
608
654
  }
609
655
 
610
656
  /**
@@ -711,6 +757,7 @@ class DataConnect {
711
757
  this._authProvider = _authProvider;
712
758
  this.isEmulator = false;
713
759
  this.initialized = false;
760
+ this._isUsingGeneratedSdk = false;
714
761
  if (typeof process !== 'undefined' && process.env) {
715
762
  const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
716
763
  if (host) {
@@ -720,6 +767,14 @@ class DataConnect {
720
767
  }
721
768
  }
722
769
  }
770
+ /*
771
+ @internal
772
+ */
773
+ _useGeneratedSdk() {
774
+ if (!this._isUsingGeneratedSdk) {
775
+ this._isUsingGeneratedSdk = true;
776
+ }
777
+ }
723
778
  _delete() {
724
779
  app._removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
725
780
  return Promise.resolve();
@@ -741,7 +796,7 @@ class DataConnect {
741
796
  this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
742
797
  }
743
798
  this.initialized = true;
744
- this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider);
799
+ this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider, undefined, this._isUsingGeneratedSdk);
745
800
  if (this._transportOptions) {
746
801
  this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
747
802
  }
@@ -778,7 +833,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
778
833
  dcOptions = optionalOptions;
779
834
  app$1 = appOrOptions;
780
835
  }
781
- if (!app$1) {
836
+ if (!app$1 || Object.keys(app$1).length === 0) {
782
837
  app$1 = app.getApp();
783
838
  }
784
839
  const provider = app._getProvider(app$1, 'data-connect');
@@ -792,9 +847,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
792
847
  return dcInstance;
793
848
  }
794
849
  }
795
- if (!dcOptions) {
796
- throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
797
- }
850
+ validateDCOptions(dcOptions);
798
851
  logDebug('Creating new DataConnect instance');
799
852
  // Initialize with options.
800
853
  return provider.initialize({
@@ -802,6 +855,24 @@ function getDataConnect(appOrOptions, optionalOptions) {
802
855
  options: dcOptions
803
856
  });
804
857
  }
858
+ /**
859
+ *
860
+ * @param dcOptions
861
+ * @returns {void}
862
+ * @internal
863
+ */
864
+ function validateDCOptions(dcOptions) {
865
+ const fields = ['connector', 'location', 'service'];
866
+ if (!dcOptions) {
867
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
868
+ }
869
+ fields.forEach(field => {
870
+ if (dcOptions[field] === null || dcOptions[field] === undefined) {
871
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
872
+ }
873
+ });
874
+ return true;
875
+ }
805
876
  /**
806
877
  * Delete DataConnect instance
807
878
  * @param dataConnect DataConnect instance
@@ -837,6 +908,9 @@ function registerDataConnect(variant) {
837
908
  if (settings) {
838
909
  newOpts = JSON.parse(settings);
839
910
  }
911
+ if (!app.options.projectId) {
912
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
913
+ }
840
914
  return new DataConnect(app, Object.assign(Object.assign({}, newOpts), { projectId: app.options.projectId }), authProvider);
841
915
  }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
842
916
  app.registerVersion(name, version, variant);
@@ -896,6 +970,49 @@ function toQueryRef(serializedRef) {
896
970
  return queryRef(getDataConnect(connectorConfig), name, variables);
897
971
  }
898
972
 
973
+ /**
974
+ * @license
975
+ * Copyright 2024 Google LLC
976
+ *
977
+ * Licensed under the Apache License, Version 2.0 (the "License");
978
+ * you may not use this file except in compliance with the License.
979
+ * You may obtain a copy of the License at
980
+ *
981
+ * http://www.apache.org/licenses/LICENSE-2.0
982
+ *
983
+ * Unless required by applicable law or agreed to in writing, software
984
+ * distributed under the License is distributed on an "AS IS" BASIS,
985
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
986
+ * See the License for the specific language governing permissions and
987
+ * limitations under the License.
988
+ */
989
+ /**
990
+ * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
991
+ * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
992
+ * @param connectorConfig
993
+ * @param dcOrVars
994
+ * @param vars
995
+ * @param validateVars
996
+ * @returns {DataConnect} and {Variables} instance
997
+ * @internal
998
+ */
999
+ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
1000
+ let dcInstance;
1001
+ let realVars;
1002
+ if (dcOrVars && 'enableEmulator' in dcOrVars) {
1003
+ dcInstance = dcOrVars;
1004
+ realVars = vars;
1005
+ }
1006
+ else {
1007
+ dcInstance = getDataConnect(connectorConfig);
1008
+ realVars = dcOrVars;
1009
+ }
1010
+ if (!dcInstance || (!realVars && validateVars)) {
1011
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
1012
+ }
1013
+ return { dc: dcInstance, vars: realVars };
1014
+ }
1015
+
899
1016
  /**
900
1017
  * @license
901
1018
  * Copyright 2024 Google LLC
@@ -977,4 +1094,6 @@ exports.setLogLevel = setLogLevel;
977
1094
  exports.subscribe = subscribe;
978
1095
  exports.terminate = terminate;
979
1096
  exports.toQueryRef = toQueryRef;
1097
+ exports.validateArgs = validateArgs;
1098
+ exports.validateDCOptions = validateDCOptions;
980
1099
  //# sourceMappingURL=index.cjs.js.map