Skip to content

Commit

Permalink
Stop using WebChannelConnection in Lite SDK (#3482)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmidt-sebastian committed Aug 4, 2020
1 parent 2fa0353 commit 7f9b3d9
Show file tree
Hide file tree
Showing 42 changed files with 827 additions and 262 deletions.
3 changes: 2 additions & 1 deletion config/webpack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ module.exports = {
new webpack.NormalModuleReplacementPlugin(
FIRESTORE_PLATFORM_RE,
resource => {
const targetPlatform = process.env.TEST_PLATFORM || 'browser';
resource.request = resource.request.replace(
FIRESTORE_PLATFORM_RE,
'$1/platform/browser/$2.ts'
`$1/platform/${targetPlatform}/$2.ts`
);
}
),
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/firestore/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ function getTestFiles(argv) {
} else if (argv.integration) {
return [legcayIntegrationTests];
} else if (argv.lite) {
process.env.TEST_PLATFORM = 'browser_lite';
return [liteIntegrationTests];
} else if (argv.exp) {
return [expIntegrationTests];
Expand Down
3 changes: 2 additions & 1 deletion packages/firestore/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"gendeps:exp": "../../scripts/exp/extract-deps.sh --types ./exp-types/index.d.ts --bundle ./dist/exp/tmp.js --output ./exp/dependencies.json",
"pregendeps:lite": "node scripts/build-bundle.js --input ./lite/index.ts --output ./dist/lite/tmp.js",
"gendeps:lite": "../../scripts/exp/extract-deps.sh --types ./lite-types/index.d.ts --bundle ./dist/lite/tmp.js --output ./lite/dependencies.json",
"test:lite": "node ./scripts/run-tests.js --emulator --main=lite/index.ts 'lite/test/**/*.test.ts'",
"test:lite": "node ./scripts/run-tests.js --emulator --platform node_lite --main=lite/index.ts 'lite/test/**/*.test.ts'",
"test:lite:browser": "karma start --single-run --lite",
"test:lite:browser:debug": "karma start --single-run --lite --auto-watch",
"test:exp": "node ./scripts/run-tests.js --emulator --main=exp/index.ts test/integration/api/*.test.ts",
Expand Down Expand Up @@ -63,6 +63,7 @@
"@firebase/webchannel-wrapper": "0.2.41",
"@grpc/grpc-js": "^1.0.0",
"@grpc/proto-loader": "^0.5.0",
"node-fetch": "2.6.0",
"tslib": "^1.11.1"
},
"peerDependencies": {
Expand Down
9 changes: 6 additions & 3 deletions packages/firestore/rollup.config.lite.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const allBuilds = [
format: 'es',
sourcemap: true
},
plugins: [alias(util.generateAliasConfig('node')), ...nodePlugins],
plugins: [alias(util.generateAliasConfig('node_lite')), ...nodePlugins],
external: util.resolveNodeExterns,
treeshake: {
moduleSideEffects: false
Expand Down Expand Up @@ -111,7 +111,10 @@ const allBuilds = [
format: 'es',
sourcemap: true
},
plugins: [alias(util.generateAliasConfig('browser')), ...browserPlugins],
plugins: [
alias(util.generateAliasConfig('browser_lite')),
...browserPlugins
],
external: util.resolveBrowserExterns,
treeshake: {
moduleSideEffects: false
Expand All @@ -125,7 +128,7 @@ const allBuilds = [
format: 'es',
sourcemap: true
},
plugins: [alias(util.generateAliasConfig('rn')), ...browserPlugins],
plugins: [alias(util.generateAliasConfig('rn_lite')), ...browserPlugins],
external: util.resolveBrowserExterns,
treeshake: {
moduleSideEffects: false
Expand Down
2 changes: 1 addition & 1 deletion packages/firestore/scripts/run-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/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")}));
*/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")}));
5 changes: 5 additions & 0 deletions packages/firestore/scripts/run-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ const argv = yargs.options({
type: 'string',
demandOption: true
},
platform: {
type: 'string',
default: 'node'
},
emulator: {
type: 'boolean'
},
Expand All @@ -37,6 +41,7 @@ const mocha = 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;

let args = [
mocha,
Expand Down
32 changes: 6 additions & 26 deletions packages/firestore/src/platform/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,21 @@
* limitations under the License.
*/

import { isNode, isReactNative } from '@firebase/util';

import * as node from './node/base64';
import * as rn from './rn/base64';
import * as browser from './browser/base64';
// This file is only used under ts-node.
// eslint-disable-next-line @typescript-eslint/no-require-imports
const platform = require(`./${process.env.TEST_PLATFORM ?? 'node'}/base64`);

/** Converts a Base64 encoded string to a binary string. */
export function decodeBase64(encoded: string): string {
if (isNode()) {
return node.decodeBase64(encoded);
} else if (isReactNative()) {
return rn.decodeBase64(encoded);
} else {
return browser.decodeBase64(encoded);
}
return platform.decodeBase64(encoded);
}

/** Converts a binary string to a Base64 encoded string. */
export function encodeBase64(raw: string): string {
if (isNode()) {
return node.encodeBase64(raw);
} else if (isReactNative()) {
return rn.encodeBase64(raw);
} else {
return browser.encodeBase64(raw);
}
return platform.encodeBase64(raw);
}

/** True if and only if the Base64 conversion functions are available. */
export function isBase64Available(): boolean {
if (isNode()) {
return node.isBase64Available();
} else if (isReactNative()) {
return rn.isBase64Available();
} else {
return browser.isBase64Available();
}
return platform.isBase64Available();
}
110 changes: 12 additions & 98 deletions packages/firestore/src/platform/browser/webchannel_connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,79 +35,40 @@ import {
} from '@firebase/util';

import { Token } from '../../api/credentials';
import { DatabaseId, DatabaseInfo } from '../../core/database_info';
import { SDK_VERSION } from '../../core/version';
import { Connection, Stream } from '../../remote/connection';
import { DatabaseInfo } from '../../core/database_info';
import { Stream } from '../../remote/connection';
import {
mapCodeFromRpcStatus,
mapCodeFromHttpResponseErrorStatus
} from '../../remote/rpc_error';
import { StreamBridge } from '../../remote/stream_bridge';
import { debugAssert, fail, hardAssert } from '../../util/assert';
import { fail, hardAssert } from '../../util/assert';
import { Code, FirestoreError } from '../../util/error';
import { logDebug, logWarn } from '../../util/log';
import { Indexable } from '../../util/misc';
import { Rejecter, Resolver } from '../../util/promise';
import { StringMap } from '../../util/types';
import { RestConnection } from '../../remote/rest_connection';

const LOG_TAG = 'Connection';

const RPC_STREAM_SERVICE = 'google.firestore.v1.Firestore';
const RPC_URL_VERSION = 'v1';

/**
* Maps RPC names to the corresponding REST endpoint name.
* Uses Object Literal notation to avoid renaming.
*/
const RPC_NAME_REST_MAPPING: { [key: string]: string } = {};
RPC_NAME_REST_MAPPING['BatchGetDocuments'] = 'batchGet';
RPC_NAME_REST_MAPPING['Commit'] = 'commit';
RPC_NAME_REST_MAPPING['RunQuery'] = 'runQuery';

// TODO(b/38203344): The SDK_VERSION is set independently from Firebase because
// we are doing out-of-band releases. Once we release as part of Firebase, we
// should use the Firebase version instead.
const X_GOOG_API_CLIENT_VALUE = 'gl-js/ fire/' + SDK_VERSION;

const XHR_TIMEOUT_SECS = 15;

export class WebChannelConnection implements Connection {
private readonly databaseId: DatabaseId;
private readonly baseUrl: string;
export class WebChannelConnection extends RestConnection {
private readonly forceLongPolling: boolean;

constructor(info: DatabaseInfo) {
this.databaseId = info.databaseId;
const proto = info.ssl ? 'https' : 'http';
this.baseUrl = proto + '://' + info.host;
super(info);
this.forceLongPolling = info.forceLongPolling;
}

/**
* Modifies the headers for a request, adding any authorization token if
* present and any additional headers for the request.
*/
private modifyHeadersForRequest(
headers: StringMap,
token: Token | null
): void {
if (token) {
for (const header in token.authHeaders) {
if (token.authHeaders.hasOwnProperty(header)) {
headers[header] = token.authHeaders[header];
}
}
}
headers['X-Goog-Api-Client'] = X_GOOG_API_CLIENT_VALUE;
}

invokeRPC<Req, Resp>(
protected performRPCRequest<Req, Resp>(
rpcName: string,
request: Req,
token: Token | null
url: string,
headers: StringMap,
body: Req
): Promise<Resp> {
const url = this.makeUrl(rpcName);

return new Promise((resolve: Resolver<Resp>, reject: Rejecter) => {
const xhr = new XhrIo();
xhr.listenOnce(EventType.COMPLETE, () => {
Expand Down Expand Up @@ -161,7 +122,6 @@ export class WebChannelConnection implements Connection {
} else {
// If we received an HTTP_ERROR but there's no status code,
// it's most probably a connection issue
logDebug(LOG_TAG, 'RPC "' + rpcName + '" failed');
reject(
new FirestoreError(Code.UNAVAILABLE, 'Connection failed.')
);
Expand All @@ -184,37 +144,11 @@ export class WebChannelConnection implements Connection {
}
});

// The database field is already encoded in URL. Specifying it again in
// the body is not necessary in production, and will cause duplicate field
// errors in the Firestore Emulator. Let's remove it.
const jsonObj = ({ ...request } as unknown) as Indexable;
delete jsonObj.database;

const requestString = JSON.stringify(jsonObj);
logDebug(LOG_TAG, 'XHR sending: ', url + ' ' + requestString);
// Content-Type: text/plain will avoid preflight requests which might
// mess with CORS and redirects by proxies. If we add custom headers
// we will need to change this code to potentially use the
// $httpOverwrite parameter supported by ESF to avoid
// triggering preflight requests.
const headers: StringMap = { 'Content-Type': 'text/plain' };

this.modifyHeadersForRequest(headers, token);

const requestString = JSON.stringify(body);
xhr.send(url, 'POST', requestString, headers, XHR_TIMEOUT_SECS);
});
}

invokeStreamingRPC<Req, Resp>(
rpcName: string,
request: Req,
token: Token | null
): Promise<Resp[]> {
// The REST API automatically aggregates all of the streamed results, so we
// can just use the normal invoke() method.
return this.invokeRPC<Req, Resp[]>(rpcName, request, token);
}

openStream<Req, Resp>(
rpcName: string,
token: Token | null
Expand Down Expand Up @@ -283,7 +217,7 @@ export class WebChannelConnection implements Connection {
}

const url = urlParts.join('');
logDebug(LOG_TAG, 'Creating WebChannel: ' + url + ' ' + request);
logDebug(LOG_TAG, 'Creating WebChannel: ' + url, request);
const channel = webchannelTransport.createWebChannel(url, request);

// WebChannel supports sending the first message with the handshake - saving
Expand Down Expand Up @@ -420,24 +354,4 @@ export class WebChannelConnection implements Connection {
}, 0);
return streamBridge;
}

// visible for testing
makeUrl(rpcName: string): string {
const urlRpcName = RPC_NAME_REST_MAPPING[rpcName];
debugAssert(
urlRpcName !== undefined,
'Unknown REST mapping for: ' + rpcName
);
return (
this.baseUrl +
'/' +
RPC_URL_VERSION +
'/projects/' +
this.databaseId.projectId +
'/databases/' +
this.databaseId.database +
'/documents:' +
urlRpcName
);
}
}
18 changes: 18 additions & 0 deletions packages/firestore/src/platform/browser_lite/base64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from '../browser/base64';
27 changes: 27 additions & 0 deletions packages/firestore/src/platform/browser_lite/connection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { DatabaseInfo } from '../../core/database_info';
import { Connection } from '../../remote/connection';
import { FetchConnection } from './fetch_connection';

export { newConnectivityMonitor } from '../browser/connection';

/** Initializes the HTTP connection for the REST API. */
export function newConnection(databaseInfo: DatabaseInfo): Promise<Connection> {
return Promise.resolve(new FetchConnection(databaseInfo, fetch.bind(null)));
}
18 changes: 18 additions & 0 deletions packages/firestore/src/platform/browser_lite/dom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from '../browser/dom';

0 comments on commit 7f9b3d9

Please sign in to comment.