@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
@@ -25,7 +25,8 @@ const Code = {
25
25
  NOT_INITIALIZED: 'not-initialized',
26
26
  NOT_SUPPORTED: 'not-supported',
27
27
  INVALID_ARGUMENT: 'invalid-argument',
28
- PARTIAL_ERROR: 'partial-error'
28
+ PARTIAL_ERROR: 'partial-error',
29
+ UNAUTHORIZED: 'unauthorized'
29
30
  };
30
31
  /** An error returned by a DataConnect operation. */
31
32
  class DataConnectError extends FirebaseError {
@@ -122,12 +123,20 @@ let connectFetch = globalThis.fetch;
122
123
  function initializeFetch(fetchImpl) {
123
124
  connectFetch = fetchImpl;
124
125
  }
125
- function dcFetch(url, body, { signal }, accessToken) {
126
+ function getGoogApiClientValue(_isUsingGen) {
127
+ let str = 'gl-js/ fire/' + SDK_VERSION;
128
+ if (_isUsingGen) {
129
+ str += ' web/gen';
130
+ }
131
+ return str;
132
+ }
133
+ function dcFetch(url, body, { signal }, accessToken, _isUsingGen) {
126
134
  if (!connectFetch) {
127
135
  throw new DataConnectError(Code.OTHER, 'No Fetch Implementation detected!');
128
136
  }
129
137
  const headers = {
130
- 'Content-Type': 'application/json'
138
+ 'Content-Type': 'application/json',
139
+ 'X-Goog-Api-Client': getGoogApiClientValue(_isUsingGen)
131
140
  };
132
141
  if (accessToken) {
133
142
  headers['X-Firebase-Auth-Token'] = accessToken;
@@ -139,8 +148,9 @@ function dcFetch(url, body, { signal }, accessToken) {
139
148
  method: 'POST',
140
149
  headers,
141
150
  signal
142
- }).catch(err => {
143
- throw new DataConnectError(Code.OTHER, "Failed to fetch: " + JSON.stringify(err));
151
+ })
152
+ .catch(err => {
153
+ throw new DataConnectError(Code.OTHER, 'Failed to fetch: ' + JSON.stringify(err));
144
154
  })
145
155
  .then(async (response) => {
146
156
  let jsonResponse = null;
@@ -150,9 +160,13 @@ function dcFetch(url, body, { signal }, accessToken) {
150
160
  catch (e) {
151
161
  throw new DataConnectError(Code.OTHER, JSON.stringify(e));
152
162
  }
163
+ const message = getMessage(jsonResponse);
153
164
  if (response.status >= 400) {
154
165
  logError('Error while performing request: ' + JSON.stringify(jsonResponse));
155
- throw new DataConnectError(Code.OTHER, JSON.stringify(jsonResponse));
166
+ if (response.status === 401) {
167
+ throw new DataConnectError(Code.UNAUTHORIZED, message);
168
+ }
169
+ throw new DataConnectError(Code.OTHER, message);
156
170
  }
157
171
  return jsonResponse;
158
172
  })
@@ -164,10 +178,16 @@ function dcFetch(url, body, { signal }, accessToken) {
164
178
  }
165
179
  return res;
166
180
  });
181
+ }
182
+ function getMessage(obj) {
183
+ if ('message' in obj) {
184
+ return obj.message;
185
+ }
186
+ return JSON.stringify(obj);
167
187
  }
168
188
 
169
189
  const name = "@firebase/data-connect";
170
- const version = "0.0.2-dataconnect-preview.388b61c7e";
190
+ const version = "0.0.3-dataconnect-preview.d986d4bf2";
171
191
 
172
192
  /**
173
193
  * @license
@@ -227,7 +247,8 @@ class FirebaseAuthProvider {
227
247
  removeTokenChangeListener(listener) {
228
248
  this._authProvider
229
249
  .get()
230
- .then(auth => auth.removeAuthTokenListener(listener));
250
+ .then(auth => auth.removeAuthTokenListener(listener))
251
+ .catch(err => logError(err));
231
252
  }
232
253
  }
233
254
 
@@ -495,10 +516,11 @@ function addToken(url, apiKey) {
495
516
  * limitations under the License.
496
517
  */
497
518
  class RESTTransport {
498
- constructor(options, apiKey, authProvider, transportOptions) {
519
+ constructor(options, apiKey, authProvider, transportOptions, _isUsingGen = false) {
499
520
  var _a;
500
521
  this.apiKey = apiKey;
501
522
  this.authProvider = authProvider;
523
+ this._isUsingGen = _isUsingGen;
502
524
  this._host = '';
503
525
  this._location = 'l';
504
526
  this._connectorName = '';
@@ -506,30 +528,30 @@ class RESTTransport {
506
528
  this._project = 'p';
507
529
  this._accessToken = null;
508
530
  this._authInitialized = false;
531
+ this._lastToken = null;
509
532
  // TODO(mtewani): Update U to include shape of body defined in line 13.
510
533
  this.invokeQuery = (queryName, body) => {
511
534
  const abortController = new AbortController();
512
535
  // TODO(mtewani): Update to proper value
513
- const withAuth = this.getWithAuth().then(() => {
514
- return dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
515
- name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
516
- operationName: queryName,
517
- variables: body
518
- }, // TODO(mtewani): This is a patch, fix this.
519
- abortController, this._accessToken);
520
- });
536
+ const withAuth = this.withRetry(() => dcFetch(addToken(`${this.endpointUrl}:executeQuery`, this.apiKey), {
537
+ name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
538
+ operationName: queryName,
539
+ variables: body
540
+ }, // TODO(mtewani): This is a patch, fix this.
541
+ abortController, this._accessToken, this._isUsingGen));
521
542
  return {
522
- then: withAuth.then.bind(withAuth)
543
+ then: withAuth.then.bind(withAuth),
544
+ catch: withAuth.catch.bind(withAuth)
523
545
  };
524
546
  };
525
547
  this.invokeMutation = (mutationName, body) => {
526
548
  const abortController = new AbortController();
527
- const taskResult = this.getWithAuth().then(() => {
549
+ const taskResult = this.withRetry(() => {
528
550
  return dcFetch(addToken(`${this.endpointUrl}:executeMutation`, this.apiKey), {
529
551
  name: `projects/${this._project}/locations/${this._location}/services/${this._serviceName}/connectors/${this._connectorName}`,
530
552
  operationName: mutationName,
531
553
  variables: body
532
- }, abortController, this._accessToken);
554
+ }, abortController, this._accessToken, this._isUsingGen);
533
555
  });
534
556
  return {
535
557
  then: taskResult.then.bind(taskResult),
@@ -584,12 +606,12 @@ class RESTTransport {
584
606
  onTokenChanged(newToken) {
585
607
  this._accessToken = newToken;
586
608
  }
587
- getWithAuth() {
609
+ getWithAuth(forceToken = false) {
588
610
  let starterPromise = new Promise(resolve => resolve(this._accessToken));
589
611
  if (!this._authInitialized) {
590
612
  if (this.authProvider) {
591
613
  starterPromise = this.authProvider
592
- .getToken(/*forceToken=*/ false)
614
+ .getToken(/*forceToken=*/ forceToken)
593
615
  .then(data => {
594
616
  if (!data) {
595
617
  return null;
@@ -604,6 +626,30 @@ class RESTTransport {
604
626
  }
605
627
  return starterPromise;
606
628
  }
629
+ _setLastToken(lastToken) {
630
+ this._lastToken = lastToken;
631
+ }
632
+ withRetry(promiseFactory, retry = false) {
633
+ let isNewToken = false;
634
+ return this.getWithAuth(retry)
635
+ .then(res => {
636
+ isNewToken = this._lastToken !== res;
637
+ this._lastToken = res;
638
+ return res;
639
+ })
640
+ .then(promiseFactory)
641
+ .catch(err => {
642
+ // Only retry if the result is unauthorized and the last token isn't the same as the new one.
643
+ if ('code' in err &&
644
+ err.code === Code.UNAUTHORIZED &&
645
+ !retry &&
646
+ isNewToken) {
647
+ logDebug('Retrying due to unauthorized');
648
+ return this.withRetry(promiseFactory, true);
649
+ }
650
+ throw err;
651
+ });
652
+ }
607
653
  }
608
654
 
609
655
  /**
@@ -710,6 +756,7 @@ class DataConnect {
710
756
  this._authProvider = _authProvider;
711
757
  this.isEmulator = false;
712
758
  this.initialized = false;
759
+ this._isUsingGeneratedSdk = false;
713
760
  if (typeof process !== 'undefined' && process.env) {
714
761
  const host = process.env[FIREBASE_DATA_CONNECT_EMULATOR_HOST_VAR];
715
762
  if (host) {
@@ -719,6 +766,14 @@ class DataConnect {
719
766
  }
720
767
  }
721
768
  }
769
+ /*
770
+ @internal
771
+ */
772
+ _useGeneratedSdk() {
773
+ if (!this._isUsingGeneratedSdk) {
774
+ this._isUsingGeneratedSdk = true;
775
+ }
776
+ }
722
777
  _delete() {
723
778
  _removeServiceInstance(this.app, 'data-connect', JSON.stringify(this.getSettings()));
724
779
  return Promise.resolve();
@@ -740,7 +795,7 @@ class DataConnect {
740
795
  this._authTokenProvider = new FirebaseAuthProvider(this.app.name, this.app.options, this._authProvider);
741
796
  }
742
797
  this.initialized = true;
743
- this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider);
798
+ this._transport = new this._transportClass(this.dataConnectOptions, this.app.options.apiKey, this._authTokenProvider, undefined, this._isUsingGeneratedSdk);
744
799
  if (this._transportOptions) {
745
800
  this._transport.useEmulator(this._transportOptions.host, this._transportOptions.port, this._transportOptions.sslEnabled);
746
801
  }
@@ -777,7 +832,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
777
832
  dcOptions = optionalOptions;
778
833
  app = appOrOptions;
779
834
  }
780
- if (!app) {
835
+ if (!app || Object.keys(app).length === 0) {
781
836
  app = getApp();
782
837
  }
783
838
  const provider = _getProvider(app, 'data-connect');
@@ -791,9 +846,7 @@ function getDataConnect(appOrOptions, optionalOptions) {
791
846
  return dcInstance;
792
847
  }
793
848
  }
794
- if (!dcOptions) {
795
- throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
796
- }
849
+ validateDCOptions(dcOptions);
797
850
  logDebug('Creating new DataConnect instance');
798
851
  // Initialize with options.
799
852
  return provider.initialize({
@@ -801,6 +854,24 @@ function getDataConnect(appOrOptions, optionalOptions) {
801
854
  options: dcOptions
802
855
  });
803
856
  }
857
+ /**
858
+ *
859
+ * @param dcOptions
860
+ * @returns {void}
861
+ * @internal
862
+ */
863
+ function validateDCOptions(dcOptions) {
864
+ const fields = ['connector', 'location', 'service'];
865
+ if (!dcOptions) {
866
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
867
+ }
868
+ fields.forEach(field => {
869
+ if (dcOptions[field] === null || dcOptions[field] === undefined) {
870
+ throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
871
+ }
872
+ });
873
+ return true;
874
+ }
804
875
  /**
805
876
  * Delete DataConnect instance
806
877
  * @param dataConnect DataConnect instance
@@ -836,6 +907,9 @@ function registerDataConnect(variant) {
836
907
  if (settings) {
837
908
  newOpts = JSON.parse(settings);
838
909
  }
910
+ if (!app.options.projectId) {
911
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Project ID must be provided. Did you pass in a proper projectId to initializeApp?');
912
+ }
839
913
  return new DataConnect(app, Object.assign(Object.assign({}, newOpts), { projectId: app.options.projectId }), authProvider);
840
914
  }, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
841
915
  registerVersion(name, version, variant);
@@ -895,6 +969,49 @@ function toQueryRef(serializedRef) {
895
969
  return queryRef(getDataConnect(connectorConfig), name, variables);
896
970
  }
897
971
 
972
+ /**
973
+ * @license
974
+ * Copyright 2024 Google LLC
975
+ *
976
+ * Licensed under the Apache License, Version 2.0 (the "License");
977
+ * you may not use this file except in compliance with the License.
978
+ * You may obtain a copy of the License at
979
+ *
980
+ * http://www.apache.org/licenses/LICENSE-2.0
981
+ *
982
+ * Unless required by applicable law or agreed to in writing, software
983
+ * distributed under the License is distributed on an "AS IS" BASIS,
984
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
985
+ * See the License for the specific language governing permissions and
986
+ * limitations under the License.
987
+ */
988
+ /**
989
+ * The generated SDK will allow the user to pass in either the variable or the data connect instance with the variable,
990
+ * and this function validates the variables and returns back the DataConnect instance and variables based on the arguments passed in.
991
+ * @param connectorConfig
992
+ * @param dcOrVars
993
+ * @param vars
994
+ * @param validateVars
995
+ * @returns {DataConnect} and {Variables} instance
996
+ * @internal
997
+ */
998
+ function validateArgs(connectorConfig, dcOrVars, vars, validateVars) {
999
+ let dcInstance;
1000
+ let realVars;
1001
+ if (dcOrVars && 'enableEmulator' in dcOrVars) {
1002
+ dcInstance = dcOrVars;
1003
+ realVars = vars;
1004
+ }
1005
+ else {
1006
+ dcInstance = getDataConnect(connectorConfig);
1007
+ realVars = dcOrVars;
1008
+ }
1009
+ if (!dcInstance || (!realVars && validateVars)) {
1010
+ throw new DataConnectError(Code.INVALID_ARGUMENT, 'Variables required.');
1011
+ }
1012
+ return { dc: dcInstance, vars: realVars };
1013
+ }
1014
+
898
1015
  /**
899
1016
  * @license
900
1017
  * Copyright 2024 Google LLC
@@ -969,5 +1086,5 @@ function subscribe(queryRefOrSerializedResult, observerOrOnNext, onError, onComp
969
1086
  initializeFetch(fetch);
970
1087
  registerDataConnect('node');
971
1088
 
972
- 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 };
1089
+ 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 };
973
1090
  //# sourceMappingURL=index.node.esm.js.map