Skip to content

Commit f15e702

Browse files
authored
Merge 60b022d into 6e0e303
2 parents 6e0e303 + 60b022d commit f15e702

File tree

13 files changed

+127
-14
lines changed

13 files changed

+127
-14
lines changed

.changeset/spotty-shirts-design.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/data-connect": patch
3+
---
4+
5+
Fixed issue where onComplete wasn't triggering when the user calls `unsubscribe` on a subscription.

common/api-review/data-connect.api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export interface DataConnectSubscription<Data, Variables> {
114114
// (undocumented)
115115
errCallback?: (e?: DataConnectError) => void;
116116
// (undocumented)
117+
onCompleteCallback?: () => void;
118+
// (undocumented)
117119
unsubscribe: () => void;
118120
// (undocumented)
119121
userCallback: OnResultSubscription<Data, Variables>;

packages/data-connect/src/api.browser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export function subscribe<Data, Variables>(
102102
return ref.dataConnect._queryManager.addSubscription(
103103
ref,
104104
onResult,
105+
onComplete,
105106
onError,
106107
initialCache
107108
);

packages/data-connect/src/api/DataConnect.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ export class DataConnect {
198198
// @internal
199199
enableEmulator(transportOptions: TransportOptions): void {
200200
if (
201+
this._transportOptions &&
201202
this._initialized &&
202203
!areTransportOptionsEqual(this._transportOptions, transportOptions)
203204
) {
@@ -314,7 +315,10 @@ export function validateDCOptions(dcOptions: ConnectorConfig): boolean {
314315
throw new DataConnectError(Code.INVALID_ARGUMENT, 'DC Option Required');
315316
}
316317
fields.forEach(field => {
317-
if (dcOptions[field] === null || dcOptions[field] === undefined) {
318+
if (
319+
dcOptions[field as keyof ConnectorConfig] === null ||
320+
dcOptions[field as keyof ConnectorConfig] === undefined
321+
) {
318322
throw new DataConnectError(Code.INVALID_ARGUMENT, `${field} Required`);
319323
}
320324
});

packages/data-connect/src/api/query.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export type QueryUnsubscribe = () => void;
4545
export interface DataConnectSubscription<Data, Variables> {
4646
userCallback: OnResultSubscription<Data, Variables>;
4747
errCallback?: (e?: DataConnectError) => void;
48+
onCompleteCallback?: () => void;
4849
unsubscribe: () => void;
4950
}
5051

@@ -124,7 +125,7 @@ export function queryRef<Data, Variables>(
124125
dataConnect: dcInstance,
125126
refType: QUERY_STR,
126127
name: queryName,
127-
variables
128+
variables: variables as Variables
128129
};
129130
}
130131
/**

packages/data-connect/src/core/AppCheckTokenProvider.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { Provider } from '@firebase/component';
2929
* Abstraction around AppCheck's token fetching capabilities.
3030
*/
3131
export class AppCheckTokenProvider {
32-
private appCheck?: FirebaseAppCheckInternal;
32+
private appCheck?: FirebaseAppCheckInternal | null;
3333
private serverAppAppCheckToken?: string;
3434
constructor(
3535
app: FirebaseApp,
@@ -47,13 +47,13 @@ export class AppCheckTokenProvider {
4747
}
4848
}
4949

50-
getToken(): Promise<AppCheckTokenResult> {
50+
getToken(): Promise<AppCheckTokenResult | null> {
5151
if (this.serverAppAppCheckToken) {
5252
return Promise.resolve({ token: this.serverAppAppCheckToken });
5353
}
5454

5555
if (!this.appCheck) {
56-
return new Promise<AppCheckTokenResult>((resolve, reject) => {
56+
return new Promise<AppCheckTokenResult | null>((resolve, reject) => {
5757
// Support delayed initialization of FirebaseAppCheck. This allows our
5858
// customers to initialize the RTDB SDK before initializing Firebase
5959
// AppCheck and ensures that all requests are authenticated if a token

packages/data-connect/src/core/QueryManager.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import {
1919
DataConnectSubscription,
20+
OnCompleteSubscription,
2021
OnErrorSubscription,
2122
OnResultSubscription,
2223
QueryPromise,
@@ -97,6 +98,7 @@ export class QueryManager {
9798
addSubscription<Data, Variables>(
9899
queryRef: OperationRef<Data, Variables>,
99100
onResultCallback: OnResultSubscription<Data, Variables>,
101+
onCompleteCallback?: OnCompleteSubscription,
100102
onErrorCallback?: OnErrorSubscription,
101103
initialCache?: OpResult<Data>
102104
): () => void {
@@ -111,13 +113,15 @@ export class QueryManager {
111113
>;
112114
const subscription = {
113115
userCallback: onResultCallback,
116+
onCompleteCallback,
114117
errCallback: onErrorCallback
115118
};
116119
const unsubscribe = (): void => {
117120
const trackedQuery = this._queries.get(key)!;
118121
trackedQuery.subscriptions = trackedQuery.subscriptions.filter(
119122
sub => sub !== subscription
120123
);
124+
onCompleteCallback?.();
121125
};
122126
if (initialCache && trackedQuery.currentCache !== initialCache) {
123127
logDebug('Initial cache found. Comparing dates.');

packages/data-connect/src/network/fetch.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ export function dcFetch<T, U>(
5656
url: string,
5757
body: DataConnectFetchBody<U>,
5858
{ signal }: AbortController,
59-
appId: string | null,
59+
appId: string | null | undefined,
6060
accessToken: string | null,
61-
appCheckToken: string | null,
61+
appCheckToken: string | null | undefined,
6262
_isUsingGen: boolean,
6363
_callerSdkType: CallerSdkType,
6464
_isUsingEmulator: boolean
@@ -135,7 +135,7 @@ interface MessageObject {
135135
message?: string;
136136
}
137137
function getMessage(obj: MessageObject): string {
138-
if ('message' in obj) {
138+
if ('message' in obj && obj.message) {
139139
return obj.message;
140140
}
141141
return JSON.stringify(obj);

packages/data-connect/src/network/transport/rest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class RESTTransport implements DataConnectTransport {
3434
private _project = 'p';
3535
private _serviceName: string;
3636
private _accessToken: string | null = null;
37-
private _appCheckToken: string | null = null;
37+
private _appCheckToken: string | null | undefined = null;
3838
private _lastToken: string | null = null;
3939
private _isUsingEmulator = false;
4040
constructor(
@@ -106,7 +106,7 @@ export class RESTTransport implements DataConnectTransport {
106106
this._accessToken = newToken;
107107
}
108108

109-
async getWithAuth(forceToken = false): Promise<string> {
109+
async getWithAuth(forceToken = false): Promise<string | null> {
110110
let starterPromise: Promise<string | null> = new Promise(resolve =>
111111
resolve(this._accessToken)
112112
);

packages/data-connect/src/util/validateArgs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export function validateArgs<Variables extends object>(
4646
let realVars: Variables;
4747
if (dcOrVars && 'enableEmulator' in dcOrVars) {
4848
dcInstance = dcOrVars as DataConnect;
49-
realVars = vars;
49+
realVars = vars as Variables;
5050
} else {
5151
dcInstance = getDataConnect(connectorConfig);
5252
realVars = dcOrVars as Variables;

0 commit comments

Comments
 (0)