Skip to content

Commit 1d7627c

Browse files
committed
Fix some typings, add tests
1 parent cb24d72 commit 1d7627c

File tree

8 files changed

+300
-15
lines changed

8 files changed

+300
-15
lines changed

packages-exp/app-exp/src/constants.ts

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { name as analyticsName } from '../../../packages/analytics/package.json'
2121
import { name as authName } from '../../../packages/auth/package.json';
2222
import { name as databaseName } from '../../../packages/database/package.json';
2323
import { name as functionsName } from '../../../packages-exp/functions-exp/package.json';
24+
import { name as functionsCompatName } from '../../../packages-exp/functions-compat/package.json';
2425
import { name as installationsName } from '../../../packages/installations/package.json';
2526
import { name as messagingName } from '../../../packages/messaging/package.json';
2627
import { name as performanceName } from '../../../packages/performance/package.json';
@@ -43,6 +44,7 @@ export const PLATFORM_LOG_STRING = {
4344
[authName]: 'fire-auth',
4445
[databaseName]: 'fire-rtdb',
4546
[functionsName]: 'fire-fn',
47+
[functionsCompatName]: 'fire-fn-compat',
4648
[installationsName]: 'fire-iid',
4749
[messagingName]: 'fire-fcm',
4850
[performanceName]: 'fire-perf',

packages-exp/functions-compat/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
"@firebase/functions-types-exp": "0.0.800",
5555
"@firebase/messaging-types": "0.5.0",
5656
"@firebase/util": "0.3.1",
57-
"isomorphic-fetch": "2.2.1",
5857
"tslib": "^1.11.1"
5958
},
6059
"nyc": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/**
2+
* @license
3+
* Copyright 2017 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+
import { expect } from 'chai';
18+
import { FirebaseApp } from '@firebase/app-types';
19+
import { FunctionsErrorCode } from '@firebase/functions-types-exp';
20+
import { createTestService } from '../test/utils';
21+
import { firebase } from '@firebase/app-compat';
22+
23+
// eslint-disable-next-line @typescript-eslint/no-require-imports
24+
export const TEST_PROJECT = require('../../../config/project.json');
25+
26+
// Chai doesn't handle Error comparisons in a useful way.
27+
// https://github.com/chaijs/chai/issues/608
28+
async function expectError(
29+
promise: Promise<any>,
30+
code: FunctionsErrorCode,
31+
message: string,
32+
details?: any
33+
): Promise<void> {
34+
let failed = false;
35+
try {
36+
await promise;
37+
} catch (e) {
38+
failed = true;
39+
// Errors coming from callable functions usually have the functions-exp
40+
// code in the message since it's thrown inside functions-exp.
41+
expect(e.code).to.match(new RegExp(`functions.*/${code}`));
42+
expect(e.message).to.equal(message);
43+
expect(e.details).to.deep.equal(details);
44+
}
45+
if (!failed) {
46+
expect(false, 'Promise should have failed.').to.be.true;
47+
}
48+
}
49+
50+
describe('Firebase Functions > Call', () => {
51+
let app: FirebaseApp;
52+
const region = 'us-central1';
53+
54+
before(() => {
55+
const useEmulator = !!process.env.FIREBASE_FUNCTIONS_EMULATOR_ORIGIN;
56+
const projectId = useEmulator
57+
? 'functions-integration-test'
58+
: TEST_PROJECT.projectId;
59+
const messagingSenderId = 'messaging-sender-id';
60+
61+
app = firebase.initializeApp({ projectId, messagingSenderId });
62+
});
63+
64+
after(async () => {
65+
await app.delete();
66+
});
67+
68+
it('simple data', async () => {
69+
const functions = createTestService(app, region);
70+
// TODO(klimt): Should we add an API to create a "long" in JS?
71+
const data = {
72+
bool: true,
73+
int: 2,
74+
str: 'four',
75+
array: [5, 6],
76+
null: null
77+
};
78+
79+
const func = functions.httpsCallable('dataTest');
80+
const result = await func(data);
81+
82+
expect(result.data).to.deep.equal({
83+
message: 'stub response',
84+
code: 42,
85+
long: 420
86+
});
87+
});
88+
89+
it('scalars', async () => {
90+
const functions = createTestService(app, region);
91+
const func = functions.httpsCallable('scalarTest');
92+
const result = await func(17);
93+
expect(result.data).to.equal(76);
94+
});
95+
96+
it('null', async () => {
97+
const functions = createTestService(app, region);
98+
const func = functions.httpsCallable('nullTest');
99+
let result = await func(null);
100+
expect(result.data).to.be.null;
101+
102+
// Test with void arguments version.
103+
result = await func();
104+
expect(result.data).to.be.null;
105+
});
106+
107+
it('missing result', async () => {
108+
const functions = createTestService(app, region);
109+
const func = functions.httpsCallable('missingResultTest');
110+
await expectError(func(), 'internal', 'Response is missing data field.');
111+
});
112+
113+
it('unhandled error', async () => {
114+
const functions = createTestService(app, region);
115+
const func = functions.httpsCallable('unhandledErrorTest');
116+
await expectError(func(), 'internal', 'internal');
117+
});
118+
119+
it('unknown error', async () => {
120+
const functions = createTestService(app, region);
121+
const func = functions.httpsCallable('unknownErrorTest');
122+
await expectError(func(), 'internal', 'internal');
123+
});
124+
125+
it('explicit error', async () => {
126+
const functions = createTestService(app, region);
127+
const func = functions.httpsCallable('explicitErrorTest');
128+
await expectError(func(), 'out-of-range', 'explicit nope', {
129+
start: 10,
130+
end: 20,
131+
long: 30
132+
});
133+
});
134+
135+
it('http error', async () => {
136+
const functions = createTestService(app, region);
137+
const func = functions.httpsCallable('httpErrorTest');
138+
await expectError(func(), 'invalid-argument', 'invalid-argument');
139+
});
140+
141+
it('timeout', async () => {
142+
const functions = createTestService(app, region);
143+
const func = functions.httpsCallable('timeoutTest', { timeout: 10 });
144+
await expectError(func(), 'deadline-exceeded', 'deadline-exceeded');
145+
});
146+
});

packages-exp/functions-compat/src/register.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
import firebase from '@firebase/app-compat';
1919
import { _FirebaseNamespace } from '@firebase/app-types/private';
20-
import { registerVersion } from '@firebase/app-exp';
21-
import { name, version } from '../package.json';
2220
import { FunctionsService } from './service';
2321
import {
2422
Component,
@@ -28,16 +26,20 @@ import {
2826
} from '@firebase/component';
2927
import { FirebaseApp } from '@firebase/app-types';
3028

31-
registerVersion(name, version);
29+
declare module '@firebase/component' {
30+
interface NameServiceMapping {
31+
'functions-compat': FunctionsService;
32+
}
33+
}
3234

33-
const factory: InstanceFactory<'functions'> = (
35+
const factory: InstanceFactory<'functions-compat'> = (
3436
container: ComponentContainer,
35-
region?: string
37+
regionOrCustomDomain?: string
3638
) => {
3739
// Dependencies
3840
const app = container.getProvider('app-exp').getImmediate();
3941

40-
return new FunctionsService(app as FirebaseApp, region);
42+
return new FunctionsService(app as FirebaseApp, regionOrCustomDomain);
4143
};
4244

4345
export function registerFunctions(): void {
@@ -46,7 +48,7 @@ export function registerFunctions(): void {
4648
Functions: FunctionsService
4749
};
4850
(firebase as _FirebaseNamespace).INTERNAL.registerComponent(
49-
new Component('functions', factory, ComponentType.PUBLIC)
51+
new Component('functions-compat', factory, ComponentType.PUBLIC)
5052
.setServiceProps(namespaceExports)
5153
.setMultipleInstances(true)
5254
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**
2+
* @license
3+
* Copyright 2017 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+
import { assert } from 'chai';
18+
import { createTestService } from '../test/utils';
19+
import { FunctionsService } from './service';
20+
import { firebase } from '@firebase/app-compat';
21+
import { FirebaseApp } from '@firebase/app-types';
22+
23+
describe('Firebase Functions > Service', () => {
24+
let app: FirebaseApp;
25+
let service: FunctionsService;
26+
27+
beforeEach(() => {
28+
app = firebase.initializeApp({
29+
projectId: 'my-project',
30+
messagingSenderId: 'messaging-sender-id'
31+
});
32+
});
33+
34+
afterEach(async () => {
35+
await app.delete();
36+
});
37+
38+
it('can use emulator', () => {
39+
service = createTestService(app);
40+
service.useFunctionsEmulator('http://localhost:5005');
41+
// Can't evaluate internals, just check it doesn't throw.
42+
// functions-exp tests will evaluate details.
43+
});
44+
45+
it('correctly sets region', () => {
46+
service = createTestService(app, 'my-region');
47+
assert.equal(service._region, 'my-region');
48+
});
49+
50+
it('correctly sets custom domain', () => {
51+
service = createTestService(app, 'https://mydomain.com');
52+
assert.equal(service._customDomain, 'https://mydomain.com');
53+
});
54+
});

packages-exp/functions-compat/src/service.ts

+19-6
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,27 @@ import { HttpsCallableOptions, Functions } from '@firebase/functions-types-exp';
2525
import { FirebaseApp } from '@firebase/app-types';
2626

2727
export class FunctionsService implements FirebaseFunctions {
28-
private functionsInstance: Functions;
29-
constructor(public app: FirebaseApp, public region?: string) {
30-
this.functionsInstance = getFunctions(app, region);
28+
private _functionsInstance: Functions;
29+
/**
30+
* For testing.
31+
* @internal
32+
*/
33+
_region: string;
34+
/**
35+
* For testing.
36+
* @internal
37+
*/
38+
_customDomain: string | null;
39+
40+
constructor(public app: FirebaseApp, regionOrCustomDomain?: string) {
41+
this._functionsInstance = getFunctions(app, regionOrCustomDomain);
42+
this._region = this._functionsInstance.region;
43+
this._customDomain = this._functionsInstance.customDomain;
3144
}
32-
httpsCallable(name: string, options: HttpsCallableOptions): HttpsCallable {
33-
return httpsCallableExp(this.functionsInstance, name, options);
45+
httpsCallable(name: string, options?: HttpsCallableOptions): HttpsCallable {
46+
return httpsCallableExp(this._functionsInstance, name, options);
3447
}
3548
useFunctionsEmulator(origin: string): void {
36-
return useFunctionsEmulatorExp(this.functionsInstance, origin);
49+
return useFunctionsEmulatorExp(this._functionsInstance, origin);
3750
}
3851
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @license
3+
* Copyright 2019 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 { FirebaseApp } from '@firebase/app-types';
19+
import { FunctionsService } from '../src/service';
20+
21+
export function createTestService(
22+
app: FirebaseApp,
23+
region?: string
24+
): FunctionsService {
25+
const functions = new FunctionsService(app, region);
26+
const useEmulator = !!process.env.FIREBASE_FUNCTIONS_EMULATOR_ORIGIN;
27+
if (useEmulator) {
28+
functions.useFunctionsEmulator(
29+
process.env.FIREBASE_FUNCTIONS_EMULATOR_ORIGIN!
30+
);
31+
}
32+
return functions;
33+
}

0 commit comments

Comments
 (0)