Skip to content

Commit 7f9b3d9

Browse files
Stop using WebChannelConnection in Lite SDK (#3482)
1 parent 2fa0353 commit 7f9b3d9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+827
-262
lines changed

config/webpack.test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,10 @@ module.exports = {
8686
new webpack.NormalModuleReplacementPlugin(
8787
FIRESTORE_PLATFORM_RE,
8888
resource => {
89+
const targetPlatform = process.env.TEST_PLATFORM || 'browser';
8990
resource.request = resource.request.replace(
9091
FIRESTORE_PLATFORM_RE,
91-
'$1/platform/browser/$2.ts'
92+
`$1/platform/${targetPlatform}/$2.ts`
9293
);
9394
}
9495
),

packages/firestore/.idea/runConfigurations/firestore_lite_Tests__Emulator_.xml

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/firestore/karma.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ function getTestFiles(argv) {
5555
} else if (argv.integration) {
5656
return [legcayIntegrationTests];
5757
} else if (argv.lite) {
58+
process.env.TEST_PLATFORM = 'browser_lite';
5859
return [liteIntegrationTests];
5960
} else if (argv.exp) {
6061
return [expIntegrationTests];

packages/firestore/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"gendeps:exp": "../../scripts/exp/extract-deps.sh --types ./exp-types/index.d.ts --bundle ./dist/exp/tmp.js --output ./exp/dependencies.json",
2525
"pregendeps:lite": "node scripts/build-bundle.js --input ./lite/index.ts --output ./dist/lite/tmp.js",
2626
"gendeps:lite": "../../scripts/exp/extract-deps.sh --types ./lite-types/index.d.ts --bundle ./dist/lite/tmp.js --output ./lite/dependencies.json",
27-
"test:lite": "node ./scripts/run-tests.js --emulator --main=lite/index.ts 'lite/test/**/*.test.ts'",
27+
"test:lite": "node ./scripts/run-tests.js --emulator --platform node_lite --main=lite/index.ts 'lite/test/**/*.test.ts'",
2828
"test:lite:browser": "karma start --single-run --lite",
2929
"test:lite:browser:debug": "karma start --single-run --lite --auto-watch",
3030
"test:exp": "node ./scripts/run-tests.js --emulator --main=exp/index.ts test/integration/api/*.test.ts",
@@ -63,6 +63,7 @@
6363
"@firebase/webchannel-wrapper": "0.2.41",
6464
"@grpc/grpc-js": "^1.0.0",
6565
"@grpc/proto-loader": "^0.5.0",
66+
"node-fetch": "2.6.0",
6667
"tslib": "^1.11.1"
6768
},
6869
"peerDependencies": {

packages/firestore/rollup.config.lite.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const allBuilds = [
7171
format: 'es',
7272
sourcemap: true
7373
},
74-
plugins: [alias(util.generateAliasConfig('node')), ...nodePlugins],
74+
plugins: [alias(util.generateAliasConfig('node_lite')), ...nodePlugins],
7575
external: util.resolveNodeExterns,
7676
treeshake: {
7777
moduleSideEffects: false
@@ -111,7 +111,10 @@ const allBuilds = [
111111
format: 'es',
112112
sourcemap: true
113113
},
114-
plugins: [alias(util.generateAliasConfig('browser')), ...browserPlugins],
114+
plugins: [
115+
alias(util.generateAliasConfig('browser_lite')),
116+
...browserPlugins
117+
],
115118
external: util.resolveBrowserExterns,
116119
treeshake: {
117120
moduleSideEffects: false
@@ -125,7 +128,7 @@ const allBuilds = [
125128
format: 'es',
126129
sourcemap: true
127130
},
128-
plugins: [alias(util.generateAliasConfig('rn')), ...browserPlugins],
131+
plugins: [alias(util.generateAliasConfig('rn_lite')), ...browserPlugins],
129132
external: util.resolveBrowserExterns,
130133
treeshake: {
131134
moduleSideEffects: false

packages/firestore/scripts/run-tests.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1515
* See the License for the specific language governing permissions and
1616
* limitations under the License.
17-
*/exports.__esModule=true;var yargs=require("yargs");var path_1=require("path");var child_process_promise_1=require("child-process-promise");var argv=yargs.options({main:{type:"string",demandOption:true},emulator:{type:"boolean"},persistence:{type:"boolean"}}).argv;var nyc=path_1.resolve(__dirname,"../../../node_modules/.bin/nyc");var mocha=path_1.resolve(__dirname,"../../../node_modules/.bin/mocha");process.env.TS_NODE_CACHE="NO";process.env.TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}';var args=[mocha,"--require","ts-node/register","--require",argv.main,"--config","../../config/mocharc.node.js"];if(argv.emulator){process.env.FIRESTORE_EMULATOR_PORT="8080";process.env.FIRESTORE_EMULATOR_PROJECT_ID="test-emulator"}if(argv.persistence){process.env.USE_MOCK_PERSISTENCE="YES";args.push("--require","test/util/node_persistence.ts")}args=args.concat(argv._);var childProcess=child_process_promise_1.spawn(nyc,args,{stdio:"inherit",cwd:process.cwd()}).childProcess;process.once("exit",(function(){return childProcess.kill()}));process.once("SIGINT",(function(){return childProcess.kill("SIGINT")}));process.once("SIGTERM",(function(){return childProcess.kill("SIGTERM")}));
17+
*/exports.__esModule=true;var yargs=require("yargs");var path_1=require("path");var child_process_promise_1=require("child-process-promise");var argv=yargs.options({main:{type:"string",demandOption:true},platform:{type:"string",default:"node"},emulator:{type:"boolean"},persistence:{type:"boolean"}}).argv;var nyc=path_1.resolve(__dirname,"../../../node_modules/.bin/nyc");var mocha=path_1.resolve(__dirname,"../../../node_modules/.bin/mocha");process.env.TS_NODE_CACHE="NO";process.env.TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}';process.env.TEST_PLATFORM=argv.platform;var args=[mocha,"--require","ts-node/register","--require",argv.main,"--config","../../config/mocharc.node.js"];if(argv.emulator){process.env.FIRESTORE_EMULATOR_PORT="8080";process.env.FIRESTORE_EMULATOR_PROJECT_ID="test-emulator"}if(argv.persistence){process.env.USE_MOCK_PERSISTENCE="YES";args.push("--require","test/util/node_persistence.ts")}args=args.concat(argv._);var childProcess=child_process_promise_1.spawn(nyc,args,{stdio:"inherit",cwd:process.cwd()}).childProcess;process.once("exit",(function(){return childProcess.kill()}));process.once("SIGINT",(function(){return childProcess.kill("SIGINT")}));process.once("SIGTERM",(function(){return childProcess.kill("SIGTERM")}));

packages/firestore/scripts/run-tests.ts

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ const argv = yargs.options({
2424
type: 'string',
2525
demandOption: true
2626
},
27+
platform: {
28+
type: 'string',
29+
default: 'node'
30+
},
2731
emulator: {
2832
type: 'boolean'
2933
},
@@ -37,6 +41,7 @@ const mocha = resolve(__dirname, '../../../node_modules/.bin/mocha');
3741

3842
process.env.TS_NODE_CACHE = 'NO';
3943
process.env.TS_NODE_COMPILER_OPTIONS = '{"module":"commonjs"}';
44+
process.env.TEST_PLATFORM = argv.platform;
4045

4146
let args = [
4247
mocha,

packages/firestore/src/platform/base64.ts

+6-26
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,21 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { isNode, isReactNative } from '@firebase/util';
19-
20-
import * as node from './node/base64';
21-
import * as rn from './rn/base64';
22-
import * as browser from './browser/base64';
18+
// This file is only used under ts-node.
19+
// eslint-disable-next-line @typescript-eslint/no-require-imports
20+
const platform = require(`./${process.env.TEST_PLATFORM ?? 'node'}/base64`);
2321

2422
/** Converts a Base64 encoded string to a binary string. */
2523
export function decodeBase64(encoded: string): string {
26-
if (isNode()) {
27-
return node.decodeBase64(encoded);
28-
} else if (isReactNative()) {
29-
return rn.decodeBase64(encoded);
30-
} else {
31-
return browser.decodeBase64(encoded);
32-
}
24+
return platform.decodeBase64(encoded);
3325
}
3426

3527
/** Converts a binary string to a Base64 encoded string. */
3628
export function encodeBase64(raw: string): string {
37-
if (isNode()) {
38-
return node.encodeBase64(raw);
39-
} else if (isReactNative()) {
40-
return rn.encodeBase64(raw);
41-
} else {
42-
return browser.encodeBase64(raw);
43-
}
29+
return platform.encodeBase64(raw);
4430
}
4531

4632
/** True if and only if the Base64 conversion functions are available. */
4733
export function isBase64Available(): boolean {
48-
if (isNode()) {
49-
return node.isBase64Available();
50-
} else if (isReactNative()) {
51-
return rn.isBase64Available();
52-
} else {
53-
return browser.isBase64Available();
54-
}
34+
return platform.isBase64Available();
5535
}

packages/firestore/src/platform/browser/webchannel_connection.ts

+12-98
Original file line numberDiff line numberDiff line change
@@ -35,79 +35,40 @@ import {
3535
} from '@firebase/util';
3636

3737
import { Token } from '../../api/credentials';
38-
import { DatabaseId, DatabaseInfo } from '../../core/database_info';
39-
import { SDK_VERSION } from '../../core/version';
40-
import { Connection, Stream } from '../../remote/connection';
38+
import { DatabaseInfo } from '../../core/database_info';
39+
import { Stream } from '../../remote/connection';
4140
import {
4241
mapCodeFromRpcStatus,
4342
mapCodeFromHttpResponseErrorStatus
4443
} from '../../remote/rpc_error';
4544
import { StreamBridge } from '../../remote/stream_bridge';
46-
import { debugAssert, fail, hardAssert } from '../../util/assert';
45+
import { fail, hardAssert } from '../../util/assert';
4746
import { Code, FirestoreError } from '../../util/error';
4847
import { logDebug, logWarn } from '../../util/log';
49-
import { Indexable } from '../../util/misc';
5048
import { Rejecter, Resolver } from '../../util/promise';
5149
import { StringMap } from '../../util/types';
50+
import { RestConnection } from '../../remote/rest_connection';
5251

5352
const LOG_TAG = 'Connection';
5453

5554
const RPC_STREAM_SERVICE = 'google.firestore.v1.Firestore';
56-
const RPC_URL_VERSION = 'v1';
57-
58-
/**
59-
* Maps RPC names to the corresponding REST endpoint name.
60-
* Uses Object Literal notation to avoid renaming.
61-
*/
62-
const RPC_NAME_REST_MAPPING: { [key: string]: string } = {};
63-
RPC_NAME_REST_MAPPING['BatchGetDocuments'] = 'batchGet';
64-
RPC_NAME_REST_MAPPING['Commit'] = 'commit';
65-
RPC_NAME_REST_MAPPING['RunQuery'] = 'runQuery';
66-
67-
// TODO(b/38203344): The SDK_VERSION is set independently from Firebase because
68-
// we are doing out-of-band releases. Once we release as part of Firebase, we
69-
// should use the Firebase version instead.
70-
const X_GOOG_API_CLIENT_VALUE = 'gl-js/ fire/' + SDK_VERSION;
7155

7256
const XHR_TIMEOUT_SECS = 15;
7357

74-
export class WebChannelConnection implements Connection {
75-
private readonly databaseId: DatabaseId;
76-
private readonly baseUrl: string;
58+
export class WebChannelConnection extends RestConnection {
7759
private readonly forceLongPolling: boolean;
7860

7961
constructor(info: DatabaseInfo) {
80-
this.databaseId = info.databaseId;
81-
const proto = info.ssl ? 'https' : 'http';
82-
this.baseUrl = proto + '://' + info.host;
62+
super(info);
8363
this.forceLongPolling = info.forceLongPolling;
8464
}
8565

86-
/**
87-
* Modifies the headers for a request, adding any authorization token if
88-
* present and any additional headers for the request.
89-
*/
90-
private modifyHeadersForRequest(
91-
headers: StringMap,
92-
token: Token | null
93-
): void {
94-
if (token) {
95-
for (const header in token.authHeaders) {
96-
if (token.authHeaders.hasOwnProperty(header)) {
97-
headers[header] = token.authHeaders[header];
98-
}
99-
}
100-
}
101-
headers['X-Goog-Api-Client'] = X_GOOG_API_CLIENT_VALUE;
102-
}
103-
104-
invokeRPC<Req, Resp>(
66+
protected performRPCRequest<Req, Resp>(
10567
rpcName: string,
106-
request: Req,
107-
token: Token | null
68+
url: string,
69+
headers: StringMap,
70+
body: Req
10871
): Promise<Resp> {
109-
const url = this.makeUrl(rpcName);
110-
11172
return new Promise((resolve: Resolver<Resp>, reject: Rejecter) => {
11273
const xhr = new XhrIo();
11374
xhr.listenOnce(EventType.COMPLETE, () => {
@@ -161,7 +122,6 @@ export class WebChannelConnection implements Connection {
161122
} else {
162123
// If we received an HTTP_ERROR but there's no status code,
163124
// it's most probably a connection issue
164-
logDebug(LOG_TAG, 'RPC "' + rpcName + '" failed');
165125
reject(
166126
new FirestoreError(Code.UNAVAILABLE, 'Connection failed.')
167127
);
@@ -184,37 +144,11 @@ export class WebChannelConnection implements Connection {
184144
}
185145
});
186146

187-
// The database field is already encoded in URL. Specifying it again in
188-
// the body is not necessary in production, and will cause duplicate field
189-
// errors in the Firestore Emulator. Let's remove it.
190-
const jsonObj = ({ ...request } as unknown) as Indexable;
191-
delete jsonObj.database;
192-
193-
const requestString = JSON.stringify(jsonObj);
194-
logDebug(LOG_TAG, 'XHR sending: ', url + ' ' + requestString);
195-
// Content-Type: text/plain will avoid preflight requests which might
196-
// mess with CORS and redirects by proxies. If we add custom headers
197-
// we will need to change this code to potentially use the
198-
// $httpOverwrite parameter supported by ESF to avoid
199-
// triggering preflight requests.
200-
const headers: StringMap = { 'Content-Type': 'text/plain' };
201-
202-
this.modifyHeadersForRequest(headers, token);
203-
147+
const requestString = JSON.stringify(body);
204148
xhr.send(url, 'POST', requestString, headers, XHR_TIMEOUT_SECS);
205149
});
206150
}
207151

208-
invokeStreamingRPC<Req, Resp>(
209-
rpcName: string,
210-
request: Req,
211-
token: Token | null
212-
): Promise<Resp[]> {
213-
// The REST API automatically aggregates all of the streamed results, so we
214-
// can just use the normal invoke() method.
215-
return this.invokeRPC<Req, Resp[]>(rpcName, request, token);
216-
}
217-
218152
openStream<Req, Resp>(
219153
rpcName: string,
220154
token: Token | null
@@ -283,7 +217,7 @@ export class WebChannelConnection implements Connection {
283217
}
284218

285219
const url = urlParts.join('');
286-
logDebug(LOG_TAG, 'Creating WebChannel: ' + url + ' ' + request);
220+
logDebug(LOG_TAG, 'Creating WebChannel: ' + url, request);
287221
const channel = webchannelTransport.createWebChannel(url, request);
288222

289223
// WebChannel supports sending the first message with the handshake - saving
@@ -420,24 +354,4 @@ export class WebChannelConnection implements Connection {
420354
}, 0);
421355
return streamBridge;
422356
}
423-
424-
// visible for testing
425-
makeUrl(rpcName: string): string {
426-
const urlRpcName = RPC_NAME_REST_MAPPING[rpcName];
427-
debugAssert(
428-
urlRpcName !== undefined,
429-
'Unknown REST mapping for: ' + rpcName
430-
);
431-
return (
432-
this.baseUrl +
433-
'/' +
434-
RPC_URL_VERSION +
435-
'/projects/' +
436-
this.databaseId.projectId +
437-
'/databases/' +
438-
this.databaseId.database +
439-
'/documents:' +
440-
urlRpcName
441-
);
442-
}
443357
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export * from '../browser/base64';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { DatabaseInfo } from '../../core/database_info';
19+
import { Connection } from '../../remote/connection';
20+
import { FetchConnection } from './fetch_connection';
21+
22+
export { newConnectivityMonitor } from '../browser/connection';
23+
24+
/** Initializes the HTTP connection for the REST API. */
25+
export function newConnection(databaseInfo: DatabaseInfo): Promise<Connection> {
26+
return Promise.resolve(new FetchConnection(databaseInfo, fetch.bind(null)));
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export * from '../browser/dom';

0 commit comments

Comments
 (0)