Skip to content

Commit a6a2e64

Browse files
committed
Switched firebase/database to use the fetch() api
1 parent 799de59 commit a6a2e64

File tree

1 file changed

+91
-121
lines changed

1 file changed

+91
-121
lines changed

packages/database/src/core/ReadonlyRestClient.ts

Lines changed: 91 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717

1818
import {
1919
assert,
20-
jsonEval,
2120
safeGet,
22-
querystring,
23-
Deferred
2421
} from '@firebase/util';
2522

2623
import { AppCheckTokenProvider } from './AppCheckTokenProvider';
@@ -56,7 +53,7 @@ export class ReadonlyRestClient extends ServerActions {
5653
} else {
5754
assert(
5855
query._queryParams.isDefault(),
59-
"should have a tag if it's not a default query."
56+
'should have a tag if it\'s not a default query.'
6057
);
6158
return query._path.toString();
6259
}
@@ -81,7 +78,7 @@ export class ReadonlyRestClient extends ServerActions {
8178
}
8279

8380
/** @inheritDoc */
84-
listen(
81+
async listen(
8582
query: QueryContext,
8683
currentHashFn: () => string,
8784
tag: number | null,
@@ -99,35 +96,34 @@ export class ReadonlyRestClient extends ServerActions {
9996
query._queryParams
10097
);
10198

102-
this.restRequest_(
99+
let [response, data] = await this.restRequest_(
103100
pathString + '.json',
104101
queryStringParameters,
105-
(error, result) => {
106-
let data = result;
107-
108-
if (error === 404) {
109-
data = null;
110-
error = null;
111-
}
112-
113-
if (error === null) {
114-
this.onDataUpdate_(pathString, data, /*isMerge=*/ false, tag);
115-
}
116-
117-
if (safeGet(this.listens_, listenId) === thisListen) {
118-
let status;
119-
if (!error) {
120-
status = 'ok';
121-
} else if (error === 401) {
122-
status = 'permission_denied';
123-
} else {
124-
status = 'rest_error:' + error;
125-
}
126-
127-
onComplete(status, null);
128-
}
129-
}
130102
);
103+
104+
let error = response.status;
105+
106+
if (error === 404) {
107+
data = null;
108+
error = null;
109+
}
110+
111+
if (error === null) {
112+
this.onDataUpdate_(pathString, data, /*isMerge=*/ false, tag);
113+
}
114+
115+
if (safeGet(this.listens_, listenId) === thisListen) {
116+
let status;
117+
if (!error) {
118+
status = 'ok';
119+
} else if (error === 401) {
120+
status = 'permission_denied';
121+
} else {
122+
status = 'rest_error:' + error;
123+
}
124+
125+
onComplete(status, null);
126+
}
131127
}
132128

133129
/** @inheritDoc */
@@ -136,40 +132,31 @@ export class ReadonlyRestClient extends ServerActions {
136132
delete this.listens_[listenId];
137133
}
138134

139-
get(query: QueryContext): Promise<string> {
135+
async get(query: QueryContext): Promise<string> {
140136
const queryStringParameters = queryParamsToRestQueryStringParameters(
141137
query._queryParams
142138
);
143139

144140
const pathString = query._path.toString();
145141

146-
const deferred = new Deferred<string>();
147-
148-
this.restRequest_(
142+
let [response, data] = await this.restRequest_(
149143
pathString + '.json',
150-
queryStringParameters,
151-
(error, result) => {
152-
let data = result;
153-
154-
if (error === 404) {
155-
data = null;
156-
error = null;
157-
}
158-
159-
if (error === null) {
160-
this.onDataUpdate_(
161-
pathString,
162-
data,
163-
/*isMerge=*/ false,
164-
/*tag=*/ null
165-
);
166-
deferred.resolve(data as string);
167-
} else {
168-
deferred.reject(new Error(data as string));
169-
}
170-
}
144+
queryStringParameters
171145
);
172-
return deferred.promise;
146+
147+
if (response.status === 404) {
148+
data = null;
149+
} else if (!response.ok) {
150+
throw new Error(data as string);
151+
}
152+
153+
this.onDataUpdate_(
154+
pathString,
155+
data,
156+
/*isMerge=*/ false,
157+
/*tag=*/ null
158+
);
159+
return data as string;
173160
}
174161

175162
/** @inheritDoc */
@@ -181,74 +168,57 @@ export class ReadonlyRestClient extends ServerActions {
181168
* Performs a REST request to the given path, with the provided query string parameters,
182169
* and any auth credentials we have.
183170
*/
184-
private restRequest_(
171+
private async restRequest_<T = unknown>(
185172
pathString: string,
186-
queryStringParameters: { [k: string]: string | number } = {},
187-
callback: ((a: number | null, b?: unknown) => void) | null
188-
) {
189-
queryStringParameters['format'] = 'export';
173+
queryStringParameters: Record<string, string | number> = {},
174+
): Promise<[Response, T | null]> {
190175

191-
return Promise.all([
176+
// Fetch tokens
177+
const [authToken, appCheckToken] = await Promise.all([
192178
this.authTokenProvider_.getToken(/*forceRefresh=*/ false),
193179
this.appCheckTokenProvider_.getToken(/*forceRefresh=*/ false)
194-
]).then(([authToken, appCheckToken]) => {
195-
if (authToken && authToken.accessToken) {
196-
queryStringParameters['auth'] = authToken.accessToken;
197-
}
198-
if (appCheckToken && appCheckToken.token) {
199-
queryStringParameters['ac'] = appCheckToken.token;
200-
}
180+
]);
181+
182+
// Configure URL parameters
183+
const searchParams = new URLSearchParams(queryStringParameters as Record<string, string>);
184+
if (authToken && authToken.accessToken) {
185+
searchParams.set('auth', authToken.accessToken);
186+
}
187+
if (appCheckToken && appCheckToken.token) {
188+
searchParams.set("ac", appCheckToken.token);
189+
}
190+
searchParams.set('format', 'export');
191+
searchParams.set('ns', this.repoInfo_.namespace);
192+
193+
// Build & send the request
194+
const url =
195+
(this.repoInfo_.secure ? 'https://' : 'http://') +
196+
this.repoInfo_.host +
197+
pathString +
198+
'?' +
199+
searchParams.toString();
200+
201+
this.log_('Sending REST request for ' + url);
202+
const response = await fetch(url);
203+
if (!response.ok) {
204+
// Request was not successful, so throw an error
205+
throw new Error(`REST request at ${url} returned error: ${response.status}`);
206+
}
207+
208+
this.log_(
209+
'REST Response for ' + url + ' received. status:',
210+
response.status,
211+
);
212+
let result: T | null = null;
213+
try {
214+
result = await response.json();
215+
} catch (e) {
216+
warn(
217+
'Failed to parse server response as json.',
218+
e
219+
);
220+
}
201221

202-
const url =
203-
(this.repoInfo_.secure ? 'https://' : 'http://') +
204-
this.repoInfo_.host +
205-
pathString +
206-
'?' +
207-
'ns=' +
208-
this.repoInfo_.namespace +
209-
querystring(queryStringParameters);
210-
211-
this.log_('Sending REST request for ' + url);
212-
const xhr = new XMLHttpRequest();
213-
xhr.onreadystatechange = () => {
214-
if (callback && xhr.readyState === 4) {
215-
this.log_(
216-
'REST Response for ' + url + ' received. status:',
217-
xhr.status,
218-
'response:',
219-
xhr.responseText
220-
);
221-
let res = null;
222-
if (xhr.status >= 200 && xhr.status < 300) {
223-
try {
224-
res = jsonEval(xhr.responseText);
225-
} catch (e) {
226-
warn(
227-
'Failed to parse JSON response for ' +
228-
url +
229-
': ' +
230-
xhr.responseText
231-
);
232-
}
233-
callback(null, res);
234-
} else {
235-
// 401 and 404 are expected.
236-
if (xhr.status !== 401 && xhr.status !== 404) {
237-
warn(
238-
'Got unsuccessful REST response for ' +
239-
url +
240-
' Status: ' +
241-
xhr.status
242-
);
243-
}
244-
callback(xhr.status);
245-
}
246-
callback = null;
247-
}
248-
};
249-
250-
xhr.open('GET', url, /*asynchronous=*/ true);
251-
xhr.send();
252-
});
222+
return [response, result];
253223
}
254224
}

0 commit comments

Comments
 (0)