Skip to content

Commit f7d06bc

Browse files
committed
Add httpsCallableFromURL
1 parent 38da5d9 commit f7d06bc

File tree

7 files changed

+149
-58
lines changed

7 files changed

+149
-58
lines changed

common/api-review/functions.api.md

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,52 @@
1-
## API Report File for "@firebase/functions"
2-
3-
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4-
5-
```ts
6-
7-
import { FirebaseApp } from '@firebase/app';
8-
import { FirebaseError } from '@firebase/util';
9-
10-
// @public
11-
export function connectFunctionsEmulator(functionsInstance: Functions, host: string, port: number): void;
12-
13-
// @public
14-
export interface Functions {
15-
app: FirebaseApp;
16-
customDomain: string | null;
17-
region: string;
18-
}
19-
20-
// @public
21-
export interface FunctionsError extends FirebaseError {
22-
readonly code: FunctionsErrorCode;
23-
readonly details?: unknown;
24-
}
25-
26-
// @public
27-
export type FunctionsErrorCode = 'ok' | 'cancelled' | 'unknown' | 'invalid-argument' | 'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' | 'data-loss' | 'unauthenticated';
28-
29-
// @public
30-
export function getFunctions(app?: FirebaseApp, regionOrCustomDomain?: string): Functions;
31-
32-
// @public
33-
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>;
34-
35-
// @public
36-
export function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;
37-
38-
// @public
39-
export interface HttpsCallableOptions {
40-
timeout?: number;
41-
}
42-
43-
// @public
44-
export interface HttpsCallableResult<ResponseData = unknown> {
45-
readonly data: ResponseData;
46-
}
47-
48-
49-
```
1+
## API Report File for "@firebase/functions"
2+
3+
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4+
5+
```ts
6+
7+
import { FirebaseApp } from '@firebase/app';
8+
import { FirebaseError } from '@firebase/util';
9+
10+
// @public
11+
export function connectFunctionsEmulator(functionsInstance: Functions, host: string, port: number): void;
12+
13+
// @public
14+
export interface Functions {
15+
app: FirebaseApp;
16+
customDomain: string | null;
17+
region: string;
18+
}
19+
20+
// @public
21+
export interface FunctionsError extends FirebaseError {
22+
readonly code: FunctionsErrorCode;
23+
readonly details?: unknown;
24+
}
25+
26+
// @public
27+
export type FunctionsErrorCode = 'ok' | 'cancelled' | 'unknown' | 'invalid-argument' | 'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' | 'data-loss' | 'unauthenticated';
28+
29+
// @public
30+
export function getFunctions(app?: FirebaseApp, regionOrCustomDomain?: string): Functions;
31+
32+
// @public
33+
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>;
34+
35+
// @public
36+
export function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;
37+
38+
// @public (undocumented)
39+
export function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;
40+
41+
// @public
42+
export interface HttpsCallableOptions {
43+
timeout?: number;
44+
}
45+
46+
// @public
47+
export interface HttpsCallableResult<ResponseData = unknown> {
48+
readonly data: ResponseData;
49+
}
50+
51+
52+
```

e2e/sample-apps/modular.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,30 @@ async function authLogout(app) {
118118
*/
119119
async function callFunctions(app) {
120120
console.log('[FUNCTIONS] start');
121-
const functions = getFunctions(app);
122-
const callTest = httpsCallable(functions, 'callTest');
121+
let functions = getFunctions(app);
122+
let callTest = httpsCallable(functions, 'callTest');
123123
try {
124124
const result = await callTest({ data: 'blah' });
125-
console.log('[FUNCTIONS] result:', result.data);
125+
console.log('[FUNCTIONS] result (by name):', result.data);
126126
} catch (e) {
127127
if (e.message.includes('Unauthenticated')) {
128128
console.warn(
129129
'Functions blocked by App Check. ' +
130-
'Activate app check with a live sitekey to allow Functions calls'
130+
'Activate app check with a live sitekey to allow Functions calls'
131+
);
132+
} else {
133+
throw e;
134+
}
135+
}
136+
callTest = httpsCallableByUrl(functions, `https://us-central-${app.options.projectId}.cloudfunctions.net/callTest`);
137+
try {
138+
const result = await callTest({ data: 'blah' });
139+
console.log('[FUNCTIONS] result (by URL):', result.data);
140+
} catch (e) {
141+
if (e.message.includes('Unauthenticated')) {
142+
console.warn(
143+
'Functions blocked by App Check. ' +
144+
'Activate app check with a live sitekey to allow Functions calls'
131145
);
132146
} else {
133147
throw e;

e2e/tests/modular.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ import {
6565
Firestore,
6666
initializeFirestore
6767
} from 'firebase/firestore';
68-
import { Functions, getFunctions, httpsCallable } from 'firebase/functions';
68+
import { Functions, getFunctions, httpsCallable, httpsCallableFromURL } from 'firebase/functions';
6969
import { getMessaging } from 'firebase/messaging';
7070
import {
7171
FirebasePerformance,
@@ -144,6 +144,15 @@ describe('MODULAR', () => {
144144
expect(result.data.word).to.equal('hellooo');
145145
// This takes a while. Extend timeout past default (2000)
146146
}).timeout(5000);
147+
it('httpsCallableFromURL()', async () => {
148+
const callTest = httpsCallableFromURL<{ data: string }, { word: string }>(
149+
functions,
150+
`https://us-central1-${app.options.projectId}.cloudfunctions.net/callTest`,
151+
);
152+
const result = await callTest({ data: 'blah' });
153+
expect(result.data.word).to.equal('hellooo');
154+
// This takes a while. Extend timeout past default (2000)
155+
}).timeout(5000);
147156
});
148157

149158
describe('STORAGE', async () => {

packages/functions-compat/src/service.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import { FirebaseFunctions, HttpsCallable } from '@firebase/functions-types';
1919
import {
2020
httpsCallable as httpsCallableExp,
21+
httpsCallableFromURL as httpsCallableFromURLExp,
2122
connectFunctionsEmulator as useFunctionsEmulatorExp,
2223
HttpsCallableOptions,
2324
Functions as FunctionsServiceExp
@@ -47,6 +48,9 @@ export class FunctionsService implements FirebaseFunctions, _FirebaseService {
4748
httpsCallable(name: string, options?: HttpsCallableOptions): HttpsCallable {
4849
return httpsCallableExp(this._delegate, name, options);
4950
}
51+
httpsCallableFromURL(url: string, options?: HttpsCallableOptions): HttpsCallable {
52+
return httpsCallableFromURLExp(this._delegate, url, options);
53+
}
5054
/**
5155
* Deprecated in pre-modularized repo, does not exist in modularized
5256
* functions package, need to convert to "host" and "port" args that

packages/functions/src/api.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
FunctionsService,
2525
DEFAULT_REGION,
2626
connectFunctionsEmulator as _connectFunctionsEmulator,
27-
httpsCallable as _httpsCallable
27+
httpsCallable as _httpsCallable,
28+
httpsCallableFromURL as _httpsCallableFromURL
2829
} from './service';
2930
import { getModularInstance } from '@firebase/util';
3031

@@ -90,3 +91,20 @@ export function httpsCallable<RequestData = unknown, ResponseData = unknown>(
9091
options
9192
);
9293
}
94+
95+
/**
96+
* Returns a reference to the callable HTTPS trigger with the given name.
97+
* @param url - The url of the trigger.
98+
* @public
99+
*/
100+
export function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(
101+
functionsInstance: Functions,
102+
url: string,
103+
options?: HttpsCallableOptions
104+
): HttpsCallable<RequestData, ResponseData> {
105+
return _httpsCallableFromURL<RequestData, ResponseData>(
106+
getModularInstance<FunctionsService>(functionsInstance as FunctionsService),
107+
url,
108+
options
109+
);
110+
}

packages/functions/src/service.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,20 @@ export class FunctionsService implements _FirebaseService {
152152

153153
return `https://${this.region}-${projectId}.cloudfunctions.net/${name}`;
154154
}
155+
156+
/**
157+
* Returns the emulator URL if connected, or the provided URL.
158+
* @param url - The fallback URL.
159+
* @internal
160+
*/
161+
_emulatorUrlOr(url: string): string {
162+
const projectId = this.app.options.projectId;
163+
if (this.emulatorOrigin !== null) {
164+
const origin = this.emulatorOrigin;
165+
return `${origin}/${projectId}/${this.region}/${name}`;
166+
}
167+
return url;
168+
}
155169
}
156170

157171
/**
@@ -186,6 +200,21 @@ export function httpsCallable<RequestData, ResponseData>(
186200
}) as HttpsCallable<RequestData, ResponseData>;
187201
}
188202

203+
/**
204+
* Returns a reference to the callable https trigger with the given url.
205+
* @param url - The url of the trigger.
206+
* @public
207+
*/
208+
export function httpsCallableFromURL<RequestData, ResponseData>(
209+
functionsInstance: FunctionsService,
210+
url: string,
211+
options?: HttpsCallableOptions
212+
): HttpsCallable<RequestData, ResponseData> {
213+
return (data => {
214+
return callAtURL(functionsInstance, functionsInstance._emulatorUrlOr(url), data, options || {});
215+
}) as HttpsCallable<RequestData, ResponseData>;
216+
}
217+
189218
/**
190219
* Does an HTTP POST and returns the completed response.
191220
* @param url The url to post to.
@@ -235,14 +264,27 @@ async function postJSON(
235264
* @param name The name of the callable trigger.
236265
* @param data The data to pass as params to the function.s
237266
*/
238-
async function call(
267+
function call(
239268
functionsInstance: FunctionsService,
240269
name: string,
241270
data: unknown,
242271
options: HttpsCallableOptions
243272
): Promise<HttpsCallableResult> {
244273
const url = functionsInstance._url(name);
274+
return callAtURL(functionsInstance, url, data, options);
275+
}
245276

277+
/**
278+
* Calls a callable function asynchronously and returns the result.
279+
* @param url The url of the callable trigger.
280+
* @param data The data to pass as params to the function.s
281+
*/
282+
async function callAtURL(
283+
functionsInstance: FunctionsService,
284+
url: string,
285+
data: unknown,
286+
options: HttpsCallableOptions
287+
): Promise<HttpsCallableResult> {
246288
// Encode any special types, such as dates, in the input data.
247289
data = encode(data);
248290
const body = { data };

repo-scripts/size-analysis/bundle-definitions/functions.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@
2222
"path": "functions",
2323
"imports": [
2424
"getFunctions",
25-
"httpsCallable"
25+
"httpsCallable",
26+
"httpsCallableFromURL"
2627
]
2728
}
2829
]
2930
}
3031
]
3132
}
32-
]
33+
]

0 commit comments

Comments
 (0)