Skip to content

Commit

Permalink
Add httpsCallableFromURL (#6162)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
inlined committed Apr 19, 2022
1 parent aa41fd6 commit c69c689
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 54 deletions.
6 changes: 6 additions & 0 deletions .changeset/new-bugs-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@firebase/functions-compat": patch
"@firebase/functions": patch
---

Add httpsCallableFromURL
101 changes: 52 additions & 49 deletions common/api-review/functions.api.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,52 @@
## API Report File for "@firebase/functions"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts

import { FirebaseApp } from '@firebase/app';
import { FirebaseError } from '@firebase/util';

// @public
export function connectFunctionsEmulator(functionsInstance: Functions, host: string, port: number): void;

// @public
export interface Functions {
app: FirebaseApp;
customDomain: string | null;
region: string;
}

// @public
export interface FunctionsError extends FirebaseError {
readonly code: FunctionsErrorCode;
readonly details?: unknown;
}

// @public
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';

// @public
export function getFunctions(app?: FirebaseApp, regionOrCustomDomain?: string): Functions;

// @public
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>;

// @public
export function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;

// @public
export interface HttpsCallableOptions {
timeout?: number;
}

// @public
export interface HttpsCallableResult<ResponseData = unknown> {
readonly data: ResponseData;
}


```
## API Report File for "@firebase/functions"

> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts

import { FirebaseApp } from '@firebase/app';
import { FirebaseError } from '@firebase/util';

// @public
export function connectFunctionsEmulator(functionsInstance: Functions, host: string, port: number): void;

// @public
export interface Functions {
app: FirebaseApp;
customDomain: string | null;
region: string;
}

// @public
export interface FunctionsError extends FirebaseError {
readonly code: FunctionsErrorCode;
readonly details?: unknown;
}

// @public
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';

// @public
export function getFunctions(app?: FirebaseApp, regionOrCustomDomain?: string): Functions;

// @public
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>;

// @public
export function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;

// @public
export function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;

// @public
export interface HttpsCallableOptions {
timeout?: number;
}

// @public
export interface HttpsCallableResult<ResponseData = unknown> {
readonly data: ResponseData;
}


```
21 changes: 19 additions & 2 deletions e2e/sample-apps/modular.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,27 @@ async function authLogout(app) {
async function callFunctions(app) {
console.log('[FUNCTIONS] start');
const functions = getFunctions(app);
const callTest = httpsCallable(functions, 'callTest');
let callTest = httpsCallable(functions, 'callTest');
try {
const result = await callTest({ data: 'blah' });
console.log('[FUNCTIONS] result:', result.data);
console.log('[FUNCTIONS] result (by name):', result.data);
} catch (e) {
if (e.message.includes('Unauthenticated')) {
console.warn(
'Functions blocked by App Check. ' +
'Activate app check with a live sitekey to allow Functions calls'
);
} else {
throw e;
}
}
callTest = httpsCallableByUrl(
functions,
`https://us-central-${app.options.projectId}.cloudfunctions.net/callTest`
);
try {
const result = await callTest({ data: 'blah' });
console.log('[FUNCTIONS] result (by URL):', result.data);
} catch (e) {
if (e.message.includes('Unauthenticated')) {
console.warn(
Expand Down
16 changes: 15 additions & 1 deletion e2e/tests/modular.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ import {
Firestore,
initializeFirestore
} from 'firebase/firestore';
import { Functions, getFunctions, httpsCallable } from 'firebase/functions';
import {
Functions,
getFunctions,
httpsCallable,
httpsCallableFromURL
} from 'firebase/functions';
import { getMessaging } from 'firebase/messaging';
import {
FirebasePerformance,
Expand Down Expand Up @@ -144,6 +149,15 @@ describe('MODULAR', () => {
expect(result.data.word).to.equal('hellooo');
// This takes a while. Extend timeout past default (2000)
}).timeout(5000);
it('httpsCallableFromURL()', async () => {
const callTest = httpsCallableFromURL<{ data: string }, { word: string }>(
functions,
`https://us-central1-${app.options.projectId}.cloudfunctions.net/callTest`
);
const result = await callTest({ data: 'blah' });
expect(result.data.word).to.equal('hellooo');
// This takes a while. Extend timeout past default (2000)
}).timeout(5000);
});

describe('STORAGE', async () => {
Expand Down
7 changes: 7 additions & 0 deletions packages/functions-compat/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import { FirebaseFunctions, HttpsCallable } from '@firebase/functions-types';
import {
httpsCallable as httpsCallableExp,
httpsCallableFromURL as httpsCallableFromURLExp,
connectFunctionsEmulator as useFunctionsEmulatorExp,
HttpsCallableOptions,
Functions as FunctionsServiceExp
Expand Down Expand Up @@ -47,6 +48,12 @@ export class FunctionsService implements FirebaseFunctions, _FirebaseService {
httpsCallable(name: string, options?: HttpsCallableOptions): HttpsCallable {
return httpsCallableExp(this._delegate, name, options);
}
httpsCallableFromURL(
url: string,
options?: HttpsCallableOptions
): HttpsCallable {
return httpsCallableFromURLExp(this._delegate, url, options);
}
/**
* Deprecated in pre-modularized repo, does not exist in modularized
* functions package, need to convert to "host" and "port" args that
Expand Down
23 changes: 22 additions & 1 deletion packages/functions/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import {
FunctionsService,
DEFAULT_REGION,
connectFunctionsEmulator as _connectFunctionsEmulator,
httpsCallable as _httpsCallable
httpsCallable as _httpsCallable,
httpsCallableFromURL as _httpsCallableFromURL
} from './service';
import { getModularInstance } from '@firebase/util';

Expand Down Expand Up @@ -90,3 +91,23 @@ export function httpsCallable<RequestData = unknown, ResponseData = unknown>(
options
);
}

/**
* Returns a reference to the callable HTTPS trigger with the specified url.
* @param url - The url of the trigger.
* @public
*/
export function httpsCallableFromURL<
RequestData = unknown,
ResponseData = unknown
>(
functionsInstance: Functions,
url: string,
options?: HttpsCallableOptions
): HttpsCallable<RequestData, ResponseData> {
return _httpsCallableFromURL<RequestData, ResponseData>(
getModularInstance<FunctionsService>(functionsInstance as FunctionsService),
url,
options
);
}
30 changes: 29 additions & 1 deletion packages/functions/src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,21 @@ export function httpsCallable<RequestData, ResponseData>(
}) as HttpsCallable<RequestData, ResponseData>;
}

/**
* Returns a reference to the callable https trigger with the given url.
* @param url - The url of the trigger.
* @public
*/
export function httpsCallableFromURL<RequestData, ResponseData>(
functionsInstance: FunctionsService,
url: string,
options?: HttpsCallableOptions
): HttpsCallable<RequestData, ResponseData> {
return (data => {
return callAtURL(functionsInstance, url, data, options || {});
}) as HttpsCallable<RequestData, ResponseData>;
}

/**
* Does an HTTP POST and returns the completed response.
* @param url The url to post to.
Expand Down Expand Up @@ -235,14 +250,27 @@ async function postJSON(
* @param name The name of the callable trigger.
* @param data The data to pass as params to the function.s
*/
async function call(
function call(
functionsInstance: FunctionsService,
name: string,
data: unknown,
options: HttpsCallableOptions
): Promise<HttpsCallableResult> {
const url = functionsInstance._url(name);
return callAtURL(functionsInstance, url, data, options);
}

/**
* Calls a callable function asynchronously and returns the result.
* @param url The url of the callable trigger.
* @param data The data to pass as params to the function.s
*/
async function callAtURL(
functionsInstance: FunctionsService,
url: string,
data: unknown,
options: HttpsCallableOptions
): Promise<HttpsCallableResult> {
// Encode any special types, such as dates, in the input data.
data = encode(data);
const body = { data };
Expand Down

0 comments on commit c69c689

Please sign in to comment.