Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support streaming streaming responses for callable functions. #8609

Merged
merged 37 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
48eca40
Support streaming streaming responses for callable functions.
taeold Oct 25, 2024
21db516
Merge branch 'main' into dl-fn-streaming
taeold Oct 25, 2024
d28863c
Support AbortSignal
taeold Oct 25, 2024
a02b25f
Update API reports
taeold Oct 25, 2024
ab99970
Re-introduce fetchImpl.
taeold Oct 27, 2024
8ec2787
Merge branch 'dl-fn-streaming' of https://github.com/firebase/firebas…
taeold Oct 27, 2024
4aee03e
Add more tests.
taeold Oct 27, 2024
fdc70d8
Update API reports
taeold Oct 27, 2024
55d9266
Add comment.
taeold Oct 27, 2024
9a430cc
Merge branch 'dl-fn-streaming' of https://github.com/firebase/firebas…
taeold Oct 28, 2024
785e906
Allow reading multiple data chunks from single read() chunk.
taeold Nov 6, 2024
861edce
Fix lint errors.
taeold Nov 6, 2024
4b188d0
Don't swallow errors.
taeold Nov 6, 2024
39c44d5
Fix linter issues.
taeold Nov 6, 2024
1c1f533
Update API reports
taeold Nov 7, 2024
b481220
Merge remote-tracking branch 'origin/main' into dl-fn-streaming
taeold Nov 9, 2024
9f4c973
Merge branch 'dl-fn-streaming' of https://github.com/firebase/firebas…
taeold Nov 9, 2024
ebd74cb
Add changeset.
taeold Nov 9, 2024
cf56623
Update code to use pump() patter.
taeold Nov 22, 2024
cfbcd46
Refactor implementation for better readability.
taeold Nov 22, 2024
1801c53
Nits.
taeold Nov 22, 2024
bf4f5e9
More nits.
taeold Nov 22, 2024
c6ef6a0
Merge branch 'main' into dl-fn-streaming
taeold Nov 22, 2024
31998c2
Revert changes to the functions-type package.
taeold Nov 22, 2024
72e87b4
Remove duplicate entry in the package script for functions.
taeold Nov 22, 2024
d8ebbb8
Run formatter.
taeold Nov 22, 2024
fd2cb15
Generate updated docs.
taeold Nov 22, 2024
9a85bb2
Refactor code to make auth headers.
taeold Nov 23, 2024
f581b61
Fix misc.
taeold Nov 23, 2024
b9a42e5
Update API reports
taeold Nov 23, 2024
d0cc907
Revert uninteded changes to untouched test case.
taeold Dec 3, 2024
f773110
Add docstring comments to utility functions.
taeold Dec 3, 2024
aa993f0
Merge branch 'dl-fn-streaming' of https://github.com/firebase/firebas…
taeold Dec 3, 2024
9ae31c0
Correct documentation on AbortSignal.
taeold Dec 3, 2024
327c1cb
Run formatter
taeold Dec 3, 2024
5fb40f1
Update docs.
taeold Dec 3, 2024
9ea463d
Update docs per comments.
taeold Dec 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/bright-scissors-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@firebase/functions': minor
taeold marked this conversation as resolved.
Show resolved Hide resolved
'firebase': minor
---

Add `.stream()` api for callable functions for consuming streaming responses.
25 changes: 22 additions & 3 deletions common/api-review/functions.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,18 @@ export type FunctionsErrorCodeCore = 'ok' | 'cancelled' | 'unknown' | 'invalid-a
export function getFunctions(app?: FirebaseApp, regionOrCustomDomain?: string): Functions;

// @public
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>;
export interface HttpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown> {
// (undocumented)
(data?: RequestData | null): Promise<HttpsCallableResult<ResponseData>>;
// (undocumented)
stream: (data?: RequestData | null, options?: HttpsCallableStreamOptions) => Promise<HttpsCallableStreamResult<ResponseData, StreamData>>;
}

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

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

// @public
export interface HttpsCallableOptions {
Expand All @@ -54,5 +59,19 @@ export interface HttpsCallableResult<ResponseData = unknown> {
readonly data: ResponseData;
}

// @public
export interface HttpsCallableStreamOptions {
limitedUseAppCheckTokens?: boolean;
signal?: AbortSignal;
}

// @public
export interface HttpsCallableStreamResult<ResponseData = unknown, StreamData = unknown> {
// (undocumented)
readonly data: Promise<ResponseData>;
// (undocumented)
readonly stream: AsyncIterable<StreamData>;
}


```
1 change: 1 addition & 0 deletions config/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Uncomment this if you'd like others to create their own Firebase project.
6 changes: 6 additions & 0 deletions docs-devsite/_toc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,16 @@ toc:
path: /docs/reference/js/functions.functions.md
- title: FunctionsError
path: /docs/reference/js/functions.functionserror.md
- title: HttpsCallable
path: /docs/reference/js/functions.httpscallable.md
- title: HttpsCallableOptions
path: /docs/reference/js/functions.httpscallableoptions.md
- title: HttpsCallableResult
path: /docs/reference/js/functions.httpscallableresult.md
- title: HttpsCallableStreamOptions
path: /docs/reference/js/functions.httpscallablestreamoptions.md
- title: HttpsCallableStreamResult
path: /docs/reference/js/functions.httpscallablestreamresult.md
- title: installations
path: /docs/reference/js/installations.md
section:
Expand Down
33 changes: 33 additions & 0 deletions docs-devsite/functions.httpscallable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

{% comment %}
DO NOT EDIT THIS FILE!
This is generated by the JS SDK team, and any local changes will be
overwritten. Changes should be made in the source code at
https://github.com/firebase/firebase-js-sdk
{% endcomment %}

# HttpsCallable interface
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm puzzled about why this is new/green/add when it seems to have existed already in line 43 of public_types.ts, iiuc.

In any case, it would be great to have clarity on whether we really mean "Google Cloud Functions" or whether we should stick with just "Cloud Functions" or even "Cloud Functions for Firebase."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a while since we made significant changes to this code, and it's possible the these type definition preceeds the devsite doc generation process I'm running here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll stick with Cloud Functions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG, thanks!

A reference to a "callable" HTTP trigger in Cloud Functions.

<b>Signature:</b>

```typescript
export interface HttpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown>
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [stream](./functions.httpscallable.md#httpscallablestream) | (data?: RequestData \| null, options?: [HttpsCallableStreamOptions](./functions.httpscallablestreamoptions.md#httpscallablestreamoptions_interface)<!-- -->) =&gt; Promise&lt;[HttpsCallableStreamResult](./functions.httpscallablestreamresult.md#httpscallablestreamresult_interface)<!-- -->&lt;ResponseData, StreamData&gt;&gt; | |

## HttpsCallable.stream

<b>Signature:</b>

```typescript
stream: (data?: RequestData | null, options?: HttpsCallableStreamOptions) => Promise<HttpsCallableStreamResult<ResponseData, StreamData>>;
```
4 changes: 2 additions & 2 deletions docs-devsite/functions.httpscallableoptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ export interface HttpsCallableOptions

| Property | Type | Description |
| --- | --- | --- |
| [limitedUseAppCheckTokens](./functions.httpscallableoptions.md#httpscallableoptionslimiteduseappchecktokens) | boolean | If set to true, uses limited-use App Check token for callable function requests from this instance of [Functions](./functions.functions.md#functions_interface)<!-- -->. You must use limited-use tokens to call functions with replay protection enabled. By default, this is false. |
| [limitedUseAppCheckTokens](./functions.httpscallableoptions.md#httpscallableoptionslimiteduseappchecktokens) | boolean | If set to true, uses a limited-use App Check token for callable function requests from this instance of [Functions](./functions.functions.md#functions_interface)<!-- -->. You must use limited-use tokens to call functions with replay protection enabled. By default, this is false. |
| [timeout](./functions.httpscallableoptions.md#httpscallableoptionstimeout) | number | Time in milliseconds after which to cancel if there is no response. Default is 70000. |

## HttpsCallableOptions.limitedUseAppCheckTokens

If set to true, uses limited-use App Check token for callable function requests from this instance of [Functions](./functions.functions.md#functions_interface)<!-- -->. You must use limited-use tokens to call functions with replay protection enabled. By default, this is false.
If set to true, uses a limited-use App Check token for callable function requests from this instance of [Functions](./functions.functions.md#functions_interface)<!-- -->. You must use limited-use tokens to call functions with replay protection enabled. By default, this is false.

<b>Signature:</b>

Expand Down
46 changes: 46 additions & 0 deletions docs-devsite/functions.httpscallablestreamoptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

{% comment %}
DO NOT EDIT THIS FILE!
This is generated by the JS SDK team, and any local changes will be
overwritten. Changes should be made in the source code at
https://github.com/firebase/firebase-js-sdk
{% endcomment %}

# HttpsCallableStreamOptions interface
An interface for metadata about how a stream call should be executed.

<b>Signature:</b>

```typescript
export interface HttpsCallableStreamOptions
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [limitedUseAppCheckTokens](./functions.httpscallablestreamoptions.md#httpscallablestreamoptionslimiteduseappchecktokens) | boolean | If set to true, uses a limited-use App Check token for callable function requests from this instance of [Functions](./functions.functions.md#functions_interface)<!-- -->. You must use limited-use tokens to call functions with replay protection enabled. By default, this is false. |
| [signal](./functions.httpscallablestreamoptions.md#httpscallablestreamoptionssignal) | AbortSignal | An <code>AbortSignal</code> that can be used to cancel the streaming response. When the signal is aborted, the underlying HTTP connection will be terminated. |

## HttpsCallableStreamOptions.limitedUseAppCheckTokens

If set to true, uses a limited-use App Check token for callable function requests from this instance of [Functions](./functions.functions.md#functions_interface)<!-- -->. You must use limited-use tokens to call functions with replay protection enabled. By default, this is false.

<b>Signature:</b>

```typescript
limitedUseAppCheckTokens?: boolean;
```

## HttpsCallableStreamOptions.signal

An `AbortSignal` that can be used to cancel the streaming response. When the signal is aborted, the underlying HTTP connection will be terminated.

<b>Signature:</b>

```typescript
signal?: AbortSignal;
```
42 changes: 42 additions & 0 deletions docs-devsite/functions.httpscallablestreamresult.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Project: /docs/reference/js/_project.yaml
Book: /docs/reference/_book.yaml
page_type: reference

{% comment %}
DO NOT EDIT THIS FILE!
This is generated by the JS SDK team, and any local changes will be
overwritten. Changes should be made in the source code at
https://github.com/firebase/firebase-js-sdk
{% endcomment %}

# HttpsCallableStreamResult interface
An `HttpsCallableStreamResult` wraps a single streaming result from a function call.

<b>Signature:</b>

```typescript
export interface HttpsCallableStreamResult<ResponseData = unknown, StreamData = unknown>
```

## Properties

| Property | Type | Description |
| --- | --- | --- |
| [data](./functions.httpscallablestreamresult.md#httpscallablestreamresultdata) | Promise&lt;ResponseData&gt; | |
| [stream](./functions.httpscallablestreamresult.md#httpscallablestreamresultstream) | AsyncIterable&lt;StreamData&gt; | |

## HttpsCallableStreamResult.data

<b>Signature:</b>

```typescript
readonly data: Promise<ResponseData>;
```

## HttpsCallableStreamResult.stream

<b>Signature:</b>

```typescript
readonly stream: AsyncIterable<StreamData>;
```
22 changes: 7 additions & 15 deletions docs-devsite/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,18 @@ Cloud Functions for Firebase
| Interface | Description |
| --- | --- |
| [Functions](./functions.functions.md#functions_interface) | A <code>Functions</code> instance. |
| [HttpsCallable](./functions.httpscallable.md#httpscallable_interface) | A reference to a "callable" HTTP trigger in Cloud Functions. |
| [HttpsCallableOptions](./functions.httpscallableoptions.md#httpscallableoptions_interface) | An interface for metadata about how calls should be executed. |
| [HttpsCallableResult](./functions.httpscallableresult.md#httpscallableresult_interface) | An <code>HttpsCallableResult</code> wraps a single result from a function call. |
| [HttpsCallableStreamOptions](./functions.httpscallablestreamoptions.md#httpscallablestreamoptions_interface) | An interface for metadata about how a stream call should be executed. |
| [HttpsCallableStreamResult](./functions.httpscallablestreamresult.md#httpscallablestreamresult_interface) | An <code>HttpsCallableStreamResult</code> wraps a single streaming result from a function call. |

## Type Aliases

| Type Alias | Description |
| --- | --- |
| [FunctionsErrorCode](./functions.md#functionserrorcode) | The set of Firebase Functions status codes. The codes are the same at the ones exposed by gRPC here: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md<!-- -->Possible values: - 'cancelled': The operation was cancelled (typically by the caller). - 'unknown': Unknown error or an error from a different error domain. - 'invalid-argument': Client specified an invalid argument. Note that this differs from 'failed-precondition'. 'invalid-argument' indicates arguments that are problematic regardless of the state of the system (e.g. an invalid field name). - 'deadline-exceeded': Deadline expired before operation could complete. For operations that change the state of the system, this error may be returned even if the operation has completed successfully. For example, a successful response from a server could have been delayed long enough for the deadline to expire. - 'not-found': Some requested document was not found. - 'already-exists': Some document that we attempted to create already exists. - 'permission-denied': The caller does not have permission to execute the specified operation. - 'resource-exhausted': Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space. - 'failed-precondition': Operation was rejected because the system is not in a state required for the operation's execution. - 'aborted': The operation was aborted, typically due to a concurrency issue like transaction aborts, etc. - 'out-of-range': Operation was attempted past the valid range. - 'unimplemented': Operation is not implemented or not supported/enabled. - 'internal': Internal errors. Means some invariants expected by underlying system has been broken. If you see one of these errors, something is very broken. - 'unavailable': The service is currently unavailable. This is most likely a transient condition and may be corrected by retrying with a backoff. - 'data-loss': Unrecoverable data loss or corruption. - 'unauthenticated': The request does not have valid authentication credentials for the operation. |
| [FunctionsErrorCodeCore](./functions.md#functionserrorcodecore) | Functions error code string appended after "functions/" product prefix. See [FunctionsErrorCode](./functions.md#functionserrorcode) for full documentation of codes. |
| [HttpsCallable](./functions.md#httpscallable) | A reference to a "callable" HTTP trigger in Google Cloud Functions. |

## function(app, ...)

Expand Down Expand Up @@ -101,7 +103,7 @@ Returns a reference to the callable HTTPS trigger with the given name.
<b>Signature:</b>

```typescript
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>;
```

#### Parameters
Expand All @@ -114,7 +116,7 @@ export declare function httpsCallable<RequestData = unknown, ResponseData = unkn

<b>Returns:</b>

[HttpsCallable](./functions.md#httpscallable)<!-- -->&lt;RequestData, ResponseData&gt;
[HttpsCallable](./functions.httpscallable.md#httpscallable_interface)<!-- -->&lt;RequestData, ResponseData, StreamData&gt;

### httpsCallableFromURL(functionsInstance, url, options) {:#httpscallablefromurl_7af6987}

Expand All @@ -123,7 +125,7 @@ Returns a reference to the callable HTTPS trigger with the specified url.
<b>Signature:</b>

```typescript
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>;
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>;
```

#### Parameters
Expand All @@ -136,7 +138,7 @@ export declare function httpsCallableFromURL<RequestData = unknown, ResponseData

<b>Returns:</b>

[HttpsCallable](./functions.md#httpscallable)<!-- -->&lt;RequestData, ResponseData&gt;
[HttpsCallable](./functions.httpscallable.md#httpscallable_interface)<!-- -->&lt;RequestData, ResponseData, StreamData&gt;

## FunctionsErrorCode

Expand All @@ -159,13 +161,3 @@ Functions error code string appended after "functions/" product prefix. See [Fun
```typescript
export type FunctionsErrorCodeCore = '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';
```

## HttpsCallable

A reference to a "callable" HTTP trigger in Google Cloud Functions.

<b>Signature:</b>

```typescript
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>;
```
2 changes: 1 addition & 1 deletion packages/functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"test:browser": "karma start",
"test:browser:debug": "karma start --browsers=Chrome --auto-watch",
"test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'src/{,!(browser)/**/}*.test.ts' --file src/index.ts --config ../../config/mocharc.node.js",
"test:emulator": "env FIREBASE_FUNCTIONS_EMULATOR_ORIGIN=http://localhost:5005 run-p --npm-path npm test:node",
"test:emulator": "env FIREBASE_FUNCTIONS_EMULATOR_ORIGIN=http://127.0.0.1:5005 run-p --npm-path npm test:node",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is my github diff wonky or are these two lines with the same script name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops i think this is an artifact of a merge gone wrong. Reverted.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this change to use 127.0.0.1 instead of localhost still be here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On my dev machine, only 127.0.0.1 works (due to changes in recent Mac version that resolves localhost to ipv6), but I can revert the change if preferred.

"trusted-type-check": "tsec -p tsconfig.json --noEmit",
"api-report": "api-extractor run --local --verbose",
"doc": "api-documenter markdown --input temp --output docs",
Expand Down
17 changes: 11 additions & 6 deletions packages/functions/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,16 @@ export function connectFunctionsEmulator(
* @param name - The name of the trigger.
* @public
*/
export function httpsCallable<RequestData = unknown, ResponseData = unknown>(
export function httpsCallable<
RequestData = unknown,
ResponseData = unknown,
StreamData = unknown
>(
functionsInstance: Functions,
name: string,
options?: HttpsCallableOptions
): HttpsCallable<RequestData, ResponseData> {
return _httpsCallable<RequestData, ResponseData>(
): HttpsCallable<RequestData, ResponseData, StreamData> {
return _httpsCallable<RequestData, ResponseData, StreamData>(
getModularInstance<FunctionsService>(functionsInstance as FunctionsService),
name,
options
Expand All @@ -107,13 +111,14 @@ export function httpsCallable<RequestData = unknown, ResponseData = unknown>(
*/
export function httpsCallableFromURL<
RequestData = unknown,
ResponseData = unknown
ResponseData = unknown,
StreamData = unknown
>(
functionsInstance: Functions,
url: string,
options?: HttpsCallableOptions
): HttpsCallable<RequestData, ResponseData> {
return _httpsCallableFromURL<RequestData, ResponseData>(
): HttpsCallable<RequestData, ResponseData, StreamData> {
return _httpsCallableFromURL<RequestData, ResponseData, StreamData>(
getModularInstance<FunctionsService>(functionsInstance as FunctionsService),
url,
options
Expand Down
Loading
Loading