Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: firebase/firebase-functions
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.9.1
Choose a base ref
...
head repository: firebase/firebase-functions
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.10.0
Choose a head ref
  • 3 commits
  • 6 files changed
  • 2 contributors

Commits on Aug 12, 2020

  1. Copy the full SHA
    b751429 View commit details

Commits on Aug 17, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5e28728 View commit details

Commits on Aug 20, 2020

  1. 3.10.0

    google-oss-bot committed Aug 20, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7bd795e View commit details
Showing with 108 additions and 13 deletions.
  1. +1 −2 CHANGELOG.md
  2. +1 −1 package.json
  3. +31 −0 spec/function-builder.spec.ts
  4. +23 −2 src/cloud-functions.ts
  5. +39 −8 src/function-builder.ts
  6. +13 −0 src/function-configuration.ts
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
- Updates `firebase-admin` dependency to support `^9.0.0` in addition to `^8.0.0`. Note that `firebase-admin` no longer supports Node.js 8.x as of `9.0.0`.
- Fixes logging of unexpected errors in `https.onCall()` functions.
- Adds support for functions failure policies (#482)
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "firebase-functions",
"version": "3.9.1",
"version": "3.10.0",
"description": "Firebase SDK for Cloud Functions",
"keywords": [
"firebase",
31 changes: 31 additions & 0 deletions spec/function-builder.spec.ts
Original file line number Diff line number Diff line change
@@ -82,13 +82,28 @@ describe('FunctionBuilder', () => {
const fn = functions
.runWith({
timeoutSeconds: 90,
failurePolicy: { retry: {} },
memory: '256MB',
})
.auth.user()
.onCreate((user) => user);

expect(fn.__trigger.availableMemoryMb).to.deep.equal(256);
expect(fn.__trigger.timeout).to.deep.equal('90s');
expect(fn.__trigger.failurePolicy).to.deep.equal({ retry: {} });
});

it("should apply a default failure policy if it's aliased with `true`", () => {
const fn = functions
.runWith({
failurePolicy: true,
memory: '256MB',
timeoutSeconds: 90,
})
.auth.user()
.onCreate((user) => user);

expect(fn.__trigger.failurePolicy).to.deep.equal({ retry: {} });
});

it('should allow both supported region and valid runtime options to be set', () => {
@@ -129,6 +144,22 @@ describe('FunctionBuilder', () => {
}).to.throw(Error, 'TimeoutSeconds');
});

it('should throw an error if user chooses a failurePolicy which is neither an object nor a boolean', () => {
expect(() =>
functions.runWith({
failurePolicy: (1234 as unknown) as functions.RuntimeOptions['failurePolicy'],
})
).to.throw(Error, 'failurePolicy must be a boolean or an object');
});

it('should throw an error if user chooses a failurePolicy.retry which is not an object', () => {
expect(() =>
functions.runWith({
failurePolicy: { retry: (1234 as unknown) as object },
})
).to.throw(Error, 'failurePolicy.retry');
});

it('should throw an error if user chooses an invalid memory allocation', () => {
expect(() => {
return functions.runWith({
25 changes: 23 additions & 2 deletions src/cloud-functions.ts
Original file line number Diff line number Diff line change
@@ -22,8 +22,13 @@

import { Request, Response } from 'express';
import * as _ from 'lodash';
import { DeploymentOptions, Schedule } from './function-configuration';
import { warn } from './logger';
import {
DEFAULT_FAILURE_POLICY,
DeploymentOptions,
FailurePolicy,
Schedule,
} from './function-configuration';
export { Request, Response };

/** @hidden */
@@ -205,6 +210,7 @@ export namespace Change {
if (json.fieldMask) {
before = applyFieldMask(before, json.after, json.fieldMask);
}

return Change.fromObjects(
customizer(before || {}),
customizer(json.after || {})
@@ -219,14 +225,16 @@ export namespace Change {
) {
const before = _.assign({}, after);
const masks = fieldMask.split(',');
_.forEach(masks, (mask) => {

masks.forEach((mask) => {
const val = _.get(sparseBefore, mask);
if (typeof val === 'undefined') {
_.unset(before, mask);
} else {
_.set(before, mask, val);
}
});

return before;
}
}
@@ -256,6 +264,7 @@ export interface TriggerAnnotated {
resource: string;
service: string;
};
failurePolicy?: FailurePolicy;
httpsTrigger?: {};
labels?: { [key: string]: string };
regions?: string[];
@@ -473,6 +482,18 @@ export function optionsToTrigger(options: DeploymentOptions) {
if (options.regions) {
trigger.regions = options.regions;
}
if (options.failurePolicy !== undefined) {
switch (options.failurePolicy) {
case false:
trigger.failurePolicy = undefined;
break;
case true:
trigger.failurePolicy = DEFAULT_FAILURE_POLICY;
break;
default:
trigger.failurePolicy = options.failurePolicy;
}
}
if (options.timeoutSeconds) {
trigger.timeout = options.timeoutSeconds.toString() + 's';
}
47 changes: 39 additions & 8 deletions src/function-builder.ts
Original file line number Diff line number Diff line change
@@ -66,6 +66,23 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean {
`TimeoutSeconds must be between 0 and ${MAX_TIMEOUT_SECONDS}`
);
}
if (runtimeOptions.failurePolicy !== undefined) {
if (
_.isBoolean(runtimeOptions.failurePolicy) === false &&
_.isObjectLike(runtimeOptions.failurePolicy) === false
) {
throw new Error(`failurePolicy must be a boolean or an object.`);
}

if (typeof runtimeOptions.failurePolicy === 'object') {
if (
_.isObjectLike(runtimeOptions.failurePolicy.retry) === false ||
_.isEmpty(runtimeOptions.failurePolicy.retry) === false
) {
throw new Error('failurePolicy.retry must be an empty object.');
}
}
}
return true;
}

@@ -100,10 +117,14 @@ export function region(
/**
* Configure runtime options for the function.
* @param runtimeOptions Object with three optional fields:
* 1. memory: amount of memory to allocate to the function, possible values
* are: '128MB', '256MB', '512MB', '1GB', and '2GB'.
* 2. timeoutSeconds: timeout for the function in seconds, possible values are
* 0 to 540.
* 1. failurePolicy: failure policy of the function, with boolean `true` being
* equivalent to providing an empty retry object.
* 2. memory: amount of memory to allocate to the function, with possible
* values being '128MB', '256MB', '512MB', '1GB', and '2GB'.
* 3. timeoutSeconds: timeout for the function in seconds, with possible
* values being 0 to 540.
*
* Value must not be null.
*/
export function runWith(runtimeOptions: RuntimeOptions): FunctionBuilder {
if (assertRuntimeOptionsValid(runtimeOptions)) {
@@ -134,10 +155,14 @@ export class FunctionBuilder {
/**
* Configure runtime options for the function.
* @param runtimeOptions Object with three optional fields:
* 1. memory: amount of memory to allocate to the function, possible values
* are: '128MB', '256MB', '512MB', '1GB', and '2GB'.
* 2. timeoutSeconds: timeout for the function in seconds, possible values are
* 0 to 540.
* 1. failurePolicy: failure policy of the function, with boolean `true` being
* equivalent to providing an empty retry object.
* 2. memory: amount of memory to allocate to the function, with possible
* values being '128MB', '256MB', '512MB', '1GB', and '2GB'.
* 3. timeoutSeconds: timeout for the function in seconds, with possible
* values being 0 to 540.
*
* Value must not be null.
*/
runWith(runtimeOptions: RuntimeOptions): FunctionBuilder {
if (assertRuntimeOptionsValid(runtimeOptions)) {
@@ -147,6 +172,12 @@ export class FunctionBuilder {
}

get https() {
if (this.options.failurePolicy !== undefined) {
console.warn(
'RuntimeOptions.failurePolicy is not supported in https functions.'
);
}

return {
/**
* Handle HTTP requests.
13 changes: 13 additions & 0 deletions src/function-configuration.ts
Original file line number Diff line number Diff line change
@@ -64,7 +64,20 @@ export interface Schedule {
retryConfig?: ScheduleRetryConfig;
}

export interface FailurePolicy {
retry: {};
}

export const DEFAULT_FAILURE_POLICY: FailurePolicy = {
retry: {},
};

export interface RuntimeOptions {
/**
* Failure policy of the function, with boolean `true` being equivalent to
* providing an empty retry object.
*/
failurePolicy?: FailurePolicy | boolean;
/**
* Amount of memory to allocate to the function.
*/