Skip to content

Commit c69c689

Browse files
authored
Add httpsCallableFromURL (#6162)
* Add httpsCallableFromURL * Create new-bugs-think.md * Update API reports * Run formatter * Remove support for the emulator since we don't have a name * Formatter * PR feedback * Fixes again * Force revert functions.json
1 parent aa41fd6 commit c69c689

File tree

7 files changed

+150
-54
lines changed

7 files changed

+150
-54
lines changed

.changeset/new-bugs-think.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@firebase/functions-compat": patch
3+
"@firebase/functions": patch
4+
---
5+
6+
Add httpsCallableFromURL

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
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: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,27 @@ async function authLogout(app) {
119119
async function callFunctions(app) {
120120
console.log('[FUNCTIONS] start');
121121
const functions = getFunctions(app);
122-
const callTest = httpsCallable(functions, 'callTest');
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);
126+
} catch (e) {
127+
if (e.message.includes('Unauthenticated')) {
128+
console.warn(
129+
'Functions blocked by App Check. ' +
130+
'Activate app check with a live sitekey to allow Functions calls'
131+
);
132+
} else {
133+
throw e;
134+
}
135+
}
136+
callTest = httpsCallableByUrl(
137+
functions,
138+
`https://us-central-${app.options.projectId}.cloudfunctions.net/callTest`
139+
);
140+
try {
141+
const result = await callTest({ data: 'blah' });
142+
console.log('[FUNCTIONS] result (by URL):', result.data);
126143
} catch (e) {
127144
if (e.message.includes('Unauthenticated')) {
128145
console.warn(

e2e/tests/modular.test.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ import {
6565
Firestore,
6666
initializeFirestore
6767
} from 'firebase/firestore';
68-
import { Functions, getFunctions, httpsCallable } from 'firebase/functions';
68+
import {
69+
Functions,
70+
getFunctions,
71+
httpsCallable,
72+
httpsCallableFromURL
73+
} from 'firebase/functions';
6974
import { getMessaging } from 'firebase/messaging';
7075
import {
7176
FirebasePerformance,
@@ -144,6 +149,15 @@ describe('MODULAR', () => {
144149
expect(result.data.word).to.equal('hellooo');
145150
// This takes a while. Extend timeout past default (2000)
146151
}).timeout(5000);
152+
it('httpsCallableFromURL()', async () => {
153+
const callTest = httpsCallableFromURL<{ data: string }, { word: string }>(
154+
functions,
155+
`https://us-central1-${app.options.projectId}.cloudfunctions.net/callTest`
156+
);
157+
const result = await callTest({ data: 'blah' });
158+
expect(result.data.word).to.equal('hellooo');
159+
// This takes a while. Extend timeout past default (2000)
160+
}).timeout(5000);
147161
});
148162

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

packages/functions-compat/src/service.ts

Lines changed: 7 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,12 @@ export class FunctionsService implements FirebaseFunctions, _FirebaseService {
4748
httpsCallable(name: string, options?: HttpsCallableOptions): HttpsCallable {
4849
return httpsCallableExp(this._delegate, name, options);
4950
}
51+
httpsCallableFromURL(
52+
url: string,
53+
options?: HttpsCallableOptions
54+
): HttpsCallable {
55+
return httpsCallableFromURLExp(this._delegate, url, options);
56+
}
5057
/**
5158
* Deprecated in pre-modularized repo, does not exist in modularized
5259
* functions package, need to convert to "host" and "port" args that

packages/functions/src/api.ts

Lines changed: 22 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,23 @@ export function httpsCallable<RequestData = unknown, ResponseData = unknown>(
9091
options
9192
);
9293
}
94+
95+
/**
96+
* Returns a reference to the callable HTTPS trigger with the specified url.
97+
* @param url - The url of the trigger.
98+
* @public
99+
*/
100+
export function httpsCallableFromURL<
101+
RequestData = unknown,
102+
ResponseData = unknown
103+
>(
104+
functionsInstance: Functions,
105+
url: string,
106+
options?: HttpsCallableOptions
107+
): HttpsCallable<RequestData, ResponseData> {
108+
return _httpsCallableFromURL<RequestData, ResponseData>(
109+
getModularInstance<FunctionsService>(functionsInstance as FunctionsService),
110+
url,
111+
options
112+
);
113+
}

packages/functions/src/service.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,21 @@ export function httpsCallable<RequestData, ResponseData>(
186186
}) as HttpsCallable<RequestData, ResponseData>;
187187
}
188188

189+
/**
190+
* Returns a reference to the callable https trigger with the given url.
191+
* @param url - The url of the trigger.
192+
* @public
193+
*/
194+
export function httpsCallableFromURL<RequestData, ResponseData>(
195+
functionsInstance: FunctionsService,
196+
url: string,
197+
options?: HttpsCallableOptions
198+
): HttpsCallable<RequestData, ResponseData> {
199+
return (data => {
200+
return callAtURL(functionsInstance, url, data, options || {});
201+
}) as HttpsCallable<RequestData, ResponseData>;
202+
}
203+
189204
/**
190205
* Does an HTTP POST and returns the completed response.
191206
* @param url The url to post to.
@@ -235,14 +250,27 @@ async function postJSON(
235250
* @param name The name of the callable trigger.
236251
* @param data The data to pass as params to the function.s
237252
*/
238-
async function call(
253+
function call(
239254
functionsInstance: FunctionsService,
240255
name: string,
241256
data: unknown,
242257
options: HttpsCallableOptions
243258
): Promise<HttpsCallableResult> {
244259
const url = functionsInstance._url(name);
260+
return callAtURL(functionsInstance, url, data, options);
261+
}
245262

263+
/**
264+
* Calls a callable function asynchronously and returns the result.
265+
* @param url The url of the callable trigger.
266+
* @param data The data to pass as params to the function.s
267+
*/
268+
async function callAtURL(
269+
functionsInstance: FunctionsService,
270+
url: string,
271+
data: unknown,
272+
options: HttpsCallableOptions
273+
): Promise<HttpsCallableResult> {
246274
// Encode any special types, such as dates, in the input data.
247275
data = encode(data);
248276
const body = { data };

0 commit comments

Comments
 (0)