From 6a8e381b789dab29a3d1f5c515aa36cd3b239b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 21 Jun 2019 14:36:46 +0200 Subject: [PATCH 01/36] Define an interface for FailurePolicy --- integration_test/functions/src/index.ts | 3 - spec/function-builder.spec.ts | 38 +++- src/cloud-functions.ts | 272 ++++++++++++++---------- src/function-builder.ts | 239 +++++++++++++-------- src/index.ts | 6 +- src/providers/https.ts | 10 +- 6 files changed, 360 insertions(+), 208 deletions(-) diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 6541dee23..55dadd739 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -4,9 +4,6 @@ import * as admin from 'firebase-admin'; import { Request, Response } from 'express'; import * as fs from 'fs'; -import * as PubSub from '@google-cloud/pubsub'; -const pubsub = PubSub(); - export * from './pubsub-tests'; export * from './database-tests'; export * from './auth-tests'; diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 2ddbb40df..cad751ae7 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -130,7 +130,39 @@ describe('FunctionBuilder', () => { functions .region('asia-northeast1') .runWith({ timeoutSeconds: 600, memory: '256MB' }); - }).to.throw(Error, 'TimeoutSeconds'); + }).to.throw(Error, 'RuntimeOptions.timeoutSeconds'); + }); + + it('should throw an error if user chooses a failurePolicy of type number', () => { + expect(() => + functions.runWith({ + failurePolicy: (1234 as unknown) as boolean, + }) + ).to.throw(Error, 'RuntimeOptions.failurePolicy'); + }); + + it('should throw an error if user chooses a failurePolicy of type string', () => { + expect(() => + functions.runWith({ + failurePolicy: ('string-value' as unknown) as boolean, + }) + ).to.throw(Error, 'RuntimeOptions.failurePolicy'); + }); + + it('should throw an error if user chooses a failurePolicy.retry of type number', () => { + expect(() => + functions.runWith({ + failurePolicy: { retry: (1234 as unknown) as object }, + }) + ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); + }); + + it('should throw an error if user chooses a failurePolicy.retry of type string', () => { + expect(() => + functions.runWith({ + failurePolicy: { retry: ('string-value' as unknown) as object }, + }) + ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); }); it('should throw an error if user chooses an invalid memory allocation', () => { @@ -152,13 +184,13 @@ describe('FunctionBuilder', () => { return functions.runWith({ timeoutSeconds: 1000000, } as any); - }).to.throw(Error, 'TimeoutSeconds'); + }).to.throw(Error, 'RuntimeOptions.timeoutSeconds'); expect(() => { return functions.region('asia-east2').runWith({ timeoutSeconds: 1000000, } as any); - }).to.throw(Error, 'TimeoutSeconds'); + }).to.throw(Error, 'RuntimeOptions.timeoutSeconds'); }); it('should throw an error if user chooses an invalid region', () => { diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 1a5c6e214..b6989e3e1 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -22,13 +22,13 @@ import { Request, Response } from 'express'; import * as _ from 'lodash'; -import { apps } from './apps'; -import { DeploymentOptions } from './function-builder'; +import { DeploymentOptions, FailurePolicy } from './function-builder'; export { Request, Response }; const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); -/** Wire format for an event +/** + * Wire format for an event * @internal */ export interface Event { @@ -41,56 +41,79 @@ export interface Event { data: any; } -/** The context in which an event occurred. +/** + * The context in which an event occurred. + * * An EventContext describes: * - The time an event occurred. * - A unique identifier of the event. * - The resource on which the event occurred, if applicable. - * - Authorization of the request that triggered the event, if applicable and available. + * - Authorization of the request that triggered the event, if applicable and + * available. */ export interface EventContext { - /** ID of the event */ + /** + * Firebase auth variable for the user whose action triggered the function. + * Field will be null for unauthenticated users, and will not exist for admin + * users. Only available for database functions. + */ + auth?: { + token: object; + uid: string; + }; + /** + * Type of authentication for the triggering action. Only available for + * database functions. + */ + authType?: 'ADMIN' | 'USER' | 'UNAUTHENTICATED'; + /** + * ID of the event + */ eventId: string; - /** Timestamp for when the event occured (ISO string) */ - timestamp: string; - /** Type of event */ + /** + * Type of event + */ eventType: string; - /** Resource that triggered the event */ - resource: Resource; - /** Key-value pairs that represent the values of wildcards in a database reference - * Cannot be accessed while inside the handler namespace. + /** + * Key-value pairs that represent the values of wildcards in a database + * reference. Cannot be accessed while inside the handler namespace. */ params: { [option: string]: any }; - /** Type of authentication for the triggering action, valid value are: 'ADMIN', 'USER', - * 'UNAUTHENTICATED'. Only available for database functions. + /** + * Resource that triggered the event */ - authType?: 'ADMIN' | 'USER' | 'UNAUTHENTICATED'; - /** Firebase auth variable for the user whose action triggered the function. Field will be - * null for unauthenticated users, and will not exist for admin users. Only available - * for database functions. + resource: Resource; + /** + * Timestamp for when the event ocurred (ISO 8601 string) */ - auth?: { - uid: string; - token: object; - }; + timestamp: string; } -/** Change describes a change of state - "before" represents the state prior - * to the event, "after" represents the state after the event. +/** + * Change describes a change of state - "before" represents the state prior to + * the event, "after" represents the state after the event. */ export class Change { constructor(public before: T, public after: T) {} } -/** ChangeJson is the JSON format used to construct a Change object. */ +/** + * ChangeJson is the JSON format used to construct a Change object. + * @internal + */ export interface ChangeJson { - /** Key-value pairs representing state of data before the change. - * If `fieldMask` is set, then only fields that changed are present in `before`. + /** + * Key-value pairs representing state of data after the change. */ - before?: any; - /** Key-value pairs representing state of data after the change. */ after?: any; - /** Comma-separated string that represents names of field that changed. */ + /** + * Key-value pairs representing state of data before the change. If + * `fieldMask` is set, then only fields that changed are present in `before`. + */ + before?: any; + /** + * Comma-separated string that represents names of field that changed. + */ fieldMask?: string; } @@ -99,13 +122,17 @@ export namespace Change { return x as T; } - /** Factory method for creating a Change from a `before` object and an `after` object. */ + /** + * Factory method for creating a Change from a `before` object and an `after` + * object. + */ export function fromObjects(before: T, after: T) { return new Change(before, after); } - /** Factory method for creating a Change from a JSON and an optional customizer function to be - * applied to both the `before` and the `after` fields. + /** + * Factory method for creating a Change from a JSON and an optional customizer + * function to be applied to both the `before` and the `after` fields. */ export function fromJSON( json: ChangeJson, @@ -115,21 +142,25 @@ export namespace Change { if (json.fieldMask) { before = applyFieldMask(before, json.after, json.fieldMask); } + return Change.fromObjects( customizer(before || {}), customizer(json.after || {}) ); } - /** @internal */ + /** + * @internal + */ export function applyFieldMask( sparseBefore: any, after: any, fieldMask: string ) { - let before = _.assign({}, after); - let masks = fieldMask.split(','); - _.forEach(masks, (mask) => { + const before = _.assign({}, after); + const masks = fieldMask.split(','); + + masks.forEach((mask) => { const val = _.get(sparseBefore, mask); if (typeof val === 'undefined') { _.unset(before, mask); @@ -137,12 +168,15 @@ export namespace Change { _.set(before, mask, val); } }); + return before; } } -/** Resource is a standard format for defining a resource (google.rpc.context.AttributeContext.Resource). - * In Cloud Functions, it is the resource that triggered the function - such as a storage bucket. +/** + * Resource is a standard format for defining a resource + * (google.rpc.context.AttributeContext.Resource). In Cloud Functions, it is the + * resource that triggered the function - such as a storage bucket. */ export interface Resource { service: string; @@ -151,23 +185,6 @@ export interface Resource { labels?: { [tag: string]: string }; } -/** TriggerAnnotated is used internally by the firebase CLI to understand what type of Cloud Function to deploy. */ -export interface TriggerAnnotated { - __trigger: { - httpsTrigger?: {}; - eventTrigger?: { - eventType: string; - resource: string; - service: string; - }; - labels?: { [key: string]: string }; - regions?: string[]; - timeout?: string; - availableMemoryMb?: number; - schedule?: Schedule; - }; -} - export interface ScheduleRetryConfig { retryCount?: number; maxRetryDuration?: string; @@ -182,66 +199,121 @@ export interface Schedule { retryConfig?: ScheduleRetryConfig; } -/** A Runnable has a `run` method which directly invokes the user-defined function - useful for unit testing. */ +/** + * TriggerAnnotated is used internally by the firebase CLI to understand what + * type of Cloud Function to deploy. + */ +export interface TriggerAnnotated { + __trigger: { + availableMemoryMb?: number; + eventTrigger?: { + eventType: string; + resource: string; + service: string; + }; + failurePolicy?: FailurePolicy; + httpsTrigger?: {}; + labels?: { [key: string]: string }; + regions?: string[]; + schedule?: Schedule; + timeout?: string; + }; +} + +/** + * A Runnable has a `run` method which directly invokes the user-defined + * function - useful for unit testing. + */ export interface Runnable { run: (data: T, context: any) => PromiseLike | any; } /** - * An HttpsFunction is both an object that exports its trigger definitions at __trigger and - * can be called as a function that takes an express.js Request and Response object. + * An HttpsFunction is both an object that exports its trigger definitions at + * __trigger and can be called as a function that takes an express.js Request + * and Response object. */ export type HttpsFunction = TriggerAnnotated & ((req: Request, resp: Response) => void); /** - * A CloudFunction is both an object that exports its trigger definitions at __trigger and - * can be called as a function using the raw JS API for Google Cloud Functions. + * A CloudFunction is both an object that exports its trigger definitions at + * __trigger and can be called as a function using the raw JS API for Google + * Cloud Functions. */ export type CloudFunction = Runnable & TriggerAnnotated & ((input: any, context?: any) => PromiseLike | any); -/** @internal */ +/** + * @internal + */ export interface MakeCloudFunctionArgs { - // TODO should remove `provider` and require a fully qualified `eventType` - // once all providers have migrated to new format. - provider: string; - eventType: string; - triggerResource: () => string; - service: string; + after?: (raw: Event) => void; + before?: (raw: Event) => void; + contextOnlyHandler?: (context: EventContext) => PromiseLike | any; dataConstructor?: (raw: Event) => EventData; + eventType: string; handler?: (data: EventData, context: EventContext) => PromiseLike | any; - contextOnlyHandler?: (context: EventContext) => PromiseLike | any; - before?: (raw: Event) => void; - after?: (raw: Event) => void; + labels?: { [key: string]: any }; legacyEventType?: string; opts?: { [key: string]: any }; - labels?: { [key: string]: any }; + /* + * TODO: should remove `provider` and require a fully qualified `eventType` + * once all providers have migrated to new format. + */ + provider: string; + service: string; + triggerResource: () => string; +} + +export function optionsToTrigger({ + regions, + timeoutSeconds, + memory, + schedule, +}: DeploymentOptions): TriggerAnnotated['__trigger'] { + const memoryLookup = { + '128MB': 128, + '256MB': 256, + '512MB': 512, + '1GB': 1024, + '2GB': 2048, + }; + + return { + ...(memory === undefined + ? {} + : { availableMemoryMb: memoryLookup[memory] }), + ...(regions === undefined ? {} : { regions }), + ...(schedule === undefined ? {} : { schedule }), + ...(timeoutSeconds === undefined ? {} : { timeout: `${timeoutSeconds}s` }), + }; } -/** @internal */ +/** + * @internal + */ export function makeCloudFunction({ - provider, - eventType, - triggerResource, - service, + after = () => {}, + before = () => {}, + contextOnlyHandler, dataConstructor = (raw: Event) => raw.data, + eventType, handler, - contextOnlyHandler, - before = () => { - return; - }, - after = () => { - return; - }, + labels = {}, legacyEventType, opts = {}, - labels = {}, + provider, + service, + triggerResource, }: MakeCloudFunctionArgs): CloudFunction { let cloudFunction: any = (data: any, context: any) => { if (legacyEventType && context.eventType === legacyEventType) { - // v1beta1 event flow has different format for context, transform them to new format. + /* + * v1beta1 event flow has different format for context, transform them to + * new format. + */ context.eventType = provider + '.' + eventType; context.resource = { service: service, @@ -305,7 +377,7 @@ export function makeCloudFunction({ return {}; } - let trigger: any = _.assign(optsToTrigger(opts), { + let trigger: any = _.assign(optionsToTrigger(opts), { eventTrigger: { resource: triggerResource(), eventType: legacyEventType || provider + '.' + eventType, @@ -369,27 +441,3 @@ function _detectAuthType(event: Event) { } return 'UNAUTHENTICATED'; } - -export function optsToTrigger(opts: DeploymentOptions) { - let trigger: any = {}; - if (opts.regions) { - trigger.regions = opts.regions; - } - if (opts.timeoutSeconds) { - trigger.timeout = opts.timeoutSeconds.toString() + 's'; - } - if (opts.memory) { - const memoryLookup = { - '128MB': 128, - '256MB': 256, - '512MB': 512, - '1GB': 1024, - '2GB': 2048, - }; - trigger.availableMemoryMb = _.get(memoryLookup, opts.memory); - } - if (opts.schedule) { - trigger.schedule = opts.schedule; - } - return trigger; -} diff --git a/src/function-builder.ts b/src/function-builder.ts index 42de739eb..ee95c17d1 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -20,7 +20,14 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as _ from 'lodash'; +import { + assign, + difference, + includes, + isBoolean, + isEmpty, + isObjectLike, +} from 'lodash'; import * as express from 'express'; import * as analytics from './providers/analytics'; @@ -49,12 +56,12 @@ const SUPPORTED_REGIONS = [ /** * List of available memory options supported by Cloud Functions. */ -const VALID_MEMORY_OPTS = ['128MB', '256MB', '512MB', '1GB', '2GB']; +const VALID_MEMORY_OPTS = ['128MB', '256MB', '512MB', '1GB', '2GB'] as const; -// Adding this memory type here to error on compile for TS users. -// Unfortunately I have not found a way to merge this with VALID_MEMORY_OPS -// without it being super ugly. But here they are right next to each other at least. -type Memory = '128MB' | '256MB' | '512MB' | '1GB' | '2GB'; +/** + * Cloud Functions min timeout value. + */ +const MIN_TIMEOUT_SECONDS = 0; /** * Cloud Functions max timeout value. @@ -64,28 +71,60 @@ const MAX_TIMEOUT_SECONDS = 540; /** * Assert that the runtime options passed in are valid. * @param runtimeOptions object containing memory and timeout information. - * @throws { Error } Memory and TimeoutSeconds values must be valid. + * @throws { Error } FailurePolicy, Memory and TimeoutSeconds values must be + * valid. */ -function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean { - if ( - runtimeOptions.memory && - !_.includes(VALID_MEMORY_OPTS, runtimeOptions.memory) - ) { - throw new Error( - `The only valid memory allocation values are: ${VALID_MEMORY_OPTS.join( - ', ' - )}` - ); +function assertRuntimeOptionsValidity(runtimeOptions: RuntimeOptions): void { + if (isObjectLike(runtimeOptions) === false) { + throw new Error('RuntimeOptions must be an object.'); } - if ( - runtimeOptions.timeoutSeconds > MAX_TIMEOUT_SECONDS || - runtimeOptions.timeoutSeconds < 0 - ) { - throw new Error( - `TimeoutSeconds must be between 0 and ${MAX_TIMEOUT_SECONDS}` - ); + + const { failurePolicy, memory, timeoutSeconds } = runtimeOptions; + + if (failurePolicy !== undefined) { + if ( + isBoolean(failurePolicy) === false && + isObjectLike(failurePolicy) === false + ) { + throw new Error( + `RuntimeOptions.failurePolicy must be a boolean or an object.` + ); + } + + if (typeof failurePolicy === 'object') { + if ( + isObjectLike(failurePolicy.retry) === false || + isEmpty(failurePolicy.retry) === false + ) { + throw new Error( + 'RuntimeOptions.failurePolicy.retry must be an empty object.' + ); + } + } + } + + if (memory !== undefined) { + if (includes(VALID_MEMORY_OPTS, memory) === false) { + throw new Error( + `RuntimeOptions.memory must be one of: ${VALID_MEMORY_OPTS.join(', ')}.` + ); + } + } + + if (timeoutSeconds !== undefined) { + if (typeof timeoutSeconds !== 'number') { + throw new Error('RuntimeOptions.timeoutSeconds must be a number.'); + } + + if ( + timeoutSeconds < MIN_TIMEOUT_SECONDS || + timeoutSeconds > MAX_TIMEOUT_SECONDS + ) { + throw new Error( + `RuntimeOptions.timeoutSeconds must be between ${MIN_TIMEOUT_SECONDS} and ${MAX_TIMEOUT_SECONDS}.` + ); + } } - return true; } /** @@ -93,51 +132,70 @@ function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean { * @param regions list of regions. * @throws { Error } Regions must be in list of supported regions. */ -function assertRegionsAreValid(regions: string[]): boolean { - if (!regions.length) { - throw new Error('You must specify at least one region'); +function assertRegionsValidity(regions: string[]): void { + if (regions.length === 0) { + throw new Error('You must specify at least one region.'); } - if (_.difference(regions, SUPPORTED_REGIONS).length) { + + if (difference(regions, SUPPORTED_REGIONS).length !== 0) { throw new Error( - `The only valid regions are: ${SUPPORTED_REGIONS.join(', ')}` + `The only valid regions are: ${SUPPORTED_REGIONS.join(', ')},` ); } - return true; } /** * Configure the regions that the function is deployed to. * @param regions One of more region strings. - * For example: `functions.region('us-east1')` or `functions.region('us-east1', 'us-central1')` + * @example + * functions.region('us-east1') + * @example + * functions.region('us-east1', 'us-central1') */ export function region(...regions: string[]): FunctionBuilder { - if (assertRegionsAreValid(regions)) { - return new FunctionBuilder({ regions }); - } + assertRegionsValidity(regions); + + return new FunctionBuilder({ regions }); } /** * Configure runtime options for the function. - * @param runtimeOptions Object with 2 optional fields: - * 1. `timeoutSeconds`: timeout for the function in seconds, possible values are 0 to 540 - * 2. `memory`: amount of memory to allocate to the function, - * possible values are: '128MB', '256MB', '512MB', '1GB', and '2GB'. + * @param runtimeOptions Object with three optional fields: + * 1. failurePolicy: failure policy policy of the function, boolean `true` is + * equivalent to providing an empty policy. + * 2. memory: amount of memory to allocate to the function, possible values + * are: '128MB', '256MB', '512MB', '1GB', and '2GB'. + * 3. timeoutSeconds: timeout for the function in seconds, possible values are + * 0 to 540. */ export function runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { - if (assertRuntimeOptionsValid(runtimeOptions)) { - return new FunctionBuilder(runtimeOptions); - } + assertRuntimeOptionsValidity(runtimeOptions); + + return new FunctionBuilder(runtimeOptions); +} + +export interface FailurePolicy { + retry: {}; } export interface RuntimeOptions { + /** + * Failure policy of the function, boolean `true` is equivalent to providing + * an empty policy. + */ + failurePolicy?: FailurePolicy | boolean; + /** + * Amount of memory to allocate to the function. + */ + memory?: typeof VALID_MEMORY_OPTS[number]; + /** + * Timeout for the function in seconds, possible values are 0 to 540. + */ timeoutSeconds?: number; - memory?: Memory; } -export interface DeploymentOptions { +export interface DeploymentOptions extends RuntimeOptions { regions?: string[]; - timeoutSeconds?: number; - memory?: Memory; schedule?: Schedule; } @@ -147,27 +205,35 @@ export class FunctionBuilder { /** * Configure the regions that the function is deployed to. * @param regions One or more region strings. - * For example: `functions.region('us-east1')` or `functions.region('us-east1', 'us-central1')` + * @example + * functions.region('us-east1') + * @example + * functions.region('us-east1', 'us-central1') */ region(...regions: string[]): FunctionBuilder { - if (assertRegionsAreValid(regions)) { - this.options.regions = regions; - return this; - } + assertRegionsValidity(regions); + + this.options.regions = regions; + + return this; } /** * Configure runtime options for the function. - * @param runtimeOptions Object with 2 optional fields: - * 1. timeoutSeconds: timeout for the function in seconds, possible values are 0 to 540 - * 2. memory: amount of memory to allocate to the function, possible values are: - * '128MB', '256MB', '512MB', '1GB', and '2GB'. + * @param runtimeOptions Object with three optional fields: + * 1. failurePolicy: failure policy policy of the function, boolean `true` is + * equivalent to providing an empty policy. + * 2. memory: amount of memory to allocate to the function, possible values + * are: '128MB', '256MB', '512MB', '1GB', and '2GB'. + * 3. timeoutSeconds: timeout for the function in seconds, possible values are + * 0 to 540. */ runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { - if (assertRuntimeOptionsValid(runtimeOptions)) { - this.options = _.assign(this.options, runtimeOptions); - return this; - } + assertRuntimeOptionsValidity(runtimeOptions); + + this.options = assign(this.options, runtimeOptions); + + return this; } get https() { @@ -180,6 +246,7 @@ export class FunctionBuilder { onRequest: ( handler: (req: https.Request, resp: express.Response) => void ) => https._onRequestWithOpts(handler, this.options), + /** * Declares a callable method for clients to call using a Firebase SDK. * @param handler A method that takes a data and context and returns a value. @@ -196,33 +263,36 @@ export class FunctionBuilder { get database() { return { /** - * Selects a database instance that will trigger the function. - * If omitted, will pick the default database for your project. + * Selects a database instance that will trigger the function. If omitted, + * will pick the default database for your project. * @param instance The Realtime Database instance to use. */ instance: (instance: string) => database._instanceWithOpts(instance, this.options), + /** * Select Firebase Realtime Database Reference to listen to. * - * This method behaves very similarly to the method of the same name in the - * client and Admin Firebase SDKs. Any change to the Database that affects the - * data at or below the provided `path` will fire an event in Cloud Functions. + * This method behaves very similarly to the method of the same name in + * the client and Admin Firebase SDKs. Any change to the Database that + * affects the data at or below the provided `path` will fire an event in + * Cloud Functions. * * There are three important differences between listening to a Realtime - * Database event in Cloud Functions and using the Realtime Database in the - * client and Admin SDKs: - * 1. Cloud Functions allows wildcards in the `path` name. Any `path` component - * in curly brackets (`{}`) is a wildcard that matches all strings. The value - * that matched a certain invocation of a Cloud Function is returned as part - * of the `context.params` object. For example, `ref("messages/{messageId}")` - * matches changes at `/messages/message1` or `/messages/message2`, resulting - * in `context.params.messageId` being set to `"message1"` or `"message2"`, - * respectively. - * 2. Cloud Functions do not fire an event for data that already existed before - * the Cloud Function was deployed. - * 3. Cloud Function events have access to more information, including information - * about the user who triggered the Cloud Function. + * Database event in Cloud Functions and using the Realtime Database in + * the client and Admin SDKs: + * 1. Cloud Functions allows wildcards in the `path` name. Any `path` + * component in curly brackets (`{}`) is a wildcard that matches all + * strings. The value that matched a certain invocation of a Cloud + * Function is returned as part of the `context.params` object. For + * example, `ref("messages/{messageId}")` matches changes at + * `/messages/message1` or `/messages/message2`, resulting in + * `context.params.messageId` being set to `"message1"` or + * `"message2"`, respectively. + * 2. Cloud Functions do not fire an event for data that already existed + * before the Cloud Function was deployed. + * 3. Cloud Function events have access to more information, including + * information about the user who triggered the Cloud Function. * @param ref Path of the database to listen to. */ ref: (path: string) => database._refWithOpts(path, this.options), @@ -240,9 +310,11 @@ export class FunctionBuilder { */ document: (path: string) => firestore._documentWithOpts(path, this.options), + /** @internal */ namespace: (namespace: string) => firestore._namespaceWithOpts(namespace, this.options), + /** @internal */ database: (database: string) => firestore._databaseWithOpts(database, this.options), @@ -252,8 +324,8 @@ export class FunctionBuilder { get crashlytics() { return { /** - * Handle events related to Crashlytics issues. An issue in Crashlytics is an - * aggregation of crashes which have a shared root cause. + * Handle events related to Crashlytics issues. An issue in Crashlytics is + * an aggregation of crashes which have a shared root cause. */ issue: () => crashlytics._issueWithOpts(this.options), }; @@ -293,9 +365,9 @@ export class FunctionBuilder { get storage() { return { /** - * The optional bucket function allows you to choose which buckets' events to handle. - * This step can be bypassed by calling object() directly, which will use the default - * Cloud Storage for Firebase bucket. + * The optional bucket function allows you to choose which buckets' events + * to handle. This step can be bypassed by calling object() directly, + * which will use the default Cloud Storage for Firebase bucket. * @param bucket Name of the Google Cloud Storage bucket to listen to. */ bucket: (bucket?: string) => @@ -311,7 +383,8 @@ export class FunctionBuilder { get pubsub() { return { /** Select Cloud Pub/Sub topic to listen to. - * @param topic Name of Pub/Sub topic, must belong to the same project as the function. + * @param topic Name of Pub/Sub topic, must belong to the same project as + * the function. */ topic: (topic: string) => pubsub._topicWithOpts(topic, this.options), schedule: (schedule: string) => diff --git a/src/index.ts b/src/index.ts index e82b05c1e..2d57759e2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,8 +23,6 @@ // Providers: import * as analytics from './providers/analytics'; import * as auth from './providers/auth'; - -import * as apps from './apps'; import * as crashlytics from './providers/crashlytics'; import * as database from './providers/database'; import * as firestore from './providers/firestore'; @@ -32,10 +30,12 @@ import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as remoteConfig from './providers/remoteConfig'; import * as storage from './providers/storage'; + +import * as apps from './apps'; import { handler } from './handler-builder'; import { setup } from './setup'; -var app = apps.apps(); +const app = apps.apps(); export { analytics, diff --git a/src/providers/https.ts b/src/providers/https.ts index 315ce0768..48ce2f95f 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -25,7 +25,7 @@ import * as firebase from 'firebase-admin'; import * as _ from 'lodash'; import * as cors from 'cors'; import { apps } from '../apps'; -import { HttpsFunction, optsToTrigger, Runnable } from '../cloud-functions'; +import { HttpsFunction, optionsToTrigger, Runnable } from '../cloud-functions'; import { DeploymentOptions } from '../function-builder'; /** @@ -42,7 +42,7 @@ export interface Request extends express.Request { */ export function onRequest( handler: (req: Request, resp: express.Response) => void -) { +): HttpsFunction { return _onRequestWithOpts(handler, {}); } @@ -65,7 +65,9 @@ export function _onRequestWithOpts( let cloudFunction: any = (req: Request, res: express.Response) => { handler(req, res); }; - cloudFunction.__trigger = _.assign(optsToTrigger(opts), { httpsTrigger: {} }); + cloudFunction.__trigger = _.assign(optionsToTrigger(opts), { + httpsTrigger: {}, + }); // TODO parse the opts return cloudFunction; } @@ -483,7 +485,7 @@ export function _onCallWithOpts( return corsHandler(req, res, () => func(req, res)); }; - corsFunc.__trigger = _.assign(optsToTrigger(opts), { + corsFunc.__trigger = _.assign(optionsToTrigger(opts), { httpsTrigger: {}, labels: { 'deployment-callable': 'true' }, }); From 282da1b3469154c6b5e664d7c6e488a141d88448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 21 Jun 2019 16:24:47 +0200 Subject: [PATCH 02/36] Extract functions config to avoid dependency cycles, assign failure policy to triggers --- src/cloud-functions.ts | 35 +++++++++-------- src/function-builder.ts | 68 ++++++++------------------------- src/function-configuration.ts | 71 +++++++++++++++++++++++++++++++++++ src/index.ts | 1 + src/providers/analytics.ts | 2 +- src/providers/auth.ts | 2 +- src/providers/crashlytics.ts | 2 +- src/providers/database.ts | 2 +- src/providers/firestore.ts | 2 +- src/providers/https.ts | 7 +--- src/providers/pubsub.ts | 6 ++- src/providers/remoteConfig.ts | 2 +- src/providers/storage.ts | 2 +- 13 files changed, 117 insertions(+), 85 deletions(-) create mode 100644 src/function-configuration.ts diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index b6989e3e1..f8438bbf9 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -22,7 +22,11 @@ import { Request, Response } from 'express'; import * as _ from 'lodash'; -import { DeploymentOptions, FailurePolicy } from './function-builder'; +import { + DeploymentOptions, + FailurePolicy, + Schedule, +} from './function-configuration'; export { Request, Response }; const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); @@ -99,7 +103,6 @@ export class Change { /** * ChangeJson is the JSON format used to construct a Change object. - * @internal */ export interface ChangeJson { /** @@ -185,20 +188,6 @@ export interface Resource { labels?: { [tag: string]: string }; } -export interface ScheduleRetryConfig { - retryCount?: number; - maxRetryDuration?: string; - minBackoffDuration?: string; - maxBackoffDuration?: string; - maxDoublings?: number; -} - -export interface Schedule { - schedule: string; - timeZone?: string; - retryConfig?: ScheduleRetryConfig; -} - /** * TriggerAnnotated is used internally by the firebase CLI to understand what * type of Cloud Function to deploy. @@ -268,11 +257,16 @@ export interface MakeCloudFunctionArgs { } export function optionsToTrigger({ - regions, - timeoutSeconds, + failurePolicy, memory, + regions, schedule, + timeoutSeconds, }: DeploymentOptions): TriggerAnnotated['__trigger'] { + const defaultFailurePolicy: FailurePolicy = { + retry: {}, + }; + const memoryLookup = { '128MB': 128, '256MB': 256, @@ -282,6 +276,11 @@ export function optionsToTrigger({ }; return { + ...(failurePolicy === undefined || failurePolicy === false + ? {} + : failurePolicy === true + ? { failurePolicy: defaultFailurePolicy } + : { failurePolicy }), ...(memory === undefined ? {} : { availableMemoryMb: memoryLookup[memory] }), diff --git a/src/function-builder.ts b/src/function-builder.ts index ee95c17d1..dfc3e9365 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -39,34 +39,15 @@ import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as remoteConfig from './providers/remoteConfig'; import * as storage from './providers/storage'; -import { CloudFunction, EventContext, Schedule } from './cloud-functions'; - -/** - * List of all regions supported by Cloud Functions. - */ -const SUPPORTED_REGIONS = [ - 'us-central1', - 'us-east1', - 'europe-west1', - 'europe-west2', - 'asia-east2', - 'asia-northeast1', -]; - -/** - * List of available memory options supported by Cloud Functions. - */ -const VALID_MEMORY_OPTS = ['128MB', '256MB', '512MB', '1GB', '2GB'] as const; - -/** - * Cloud Functions min timeout value. - */ -const MIN_TIMEOUT_SECONDS = 0; - -/** - * Cloud Functions max timeout value. - */ -const MAX_TIMEOUT_SECONDS = 540; +import { CloudFunction, EventContext } from './cloud-functions'; +import { + RuntimeOptions, + VALID_MEMORY_OPTS, + MIN_TIMEOUT_SECONDS, + MAX_TIMEOUT_SECONDS, + SUPPORTED_REGIONS, + DeploymentOptions, +} from './function-configuration'; /** * Assert that the runtime options passed in are valid. @@ -174,31 +155,6 @@ export function runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { return new FunctionBuilder(runtimeOptions); } -export interface FailurePolicy { - retry: {}; -} - -export interface RuntimeOptions { - /** - * Failure policy of the function, boolean `true` is equivalent to providing - * an empty policy. - */ - failurePolicy?: FailurePolicy | boolean; - /** - * Amount of memory to allocate to the function. - */ - memory?: typeof VALID_MEMORY_OPTS[number]; - /** - * Timeout for the function in seconds, possible values are 0 to 540. - */ - timeoutSeconds?: number; -} - -export interface DeploymentOptions extends RuntimeOptions { - regions?: string[]; - schedule?: Schedule; -} - export class FunctionBuilder { constructor(private options: DeploymentOptions) {} @@ -237,6 +193,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. diff --git a/src/function-configuration.ts b/src/function-configuration.ts new file mode 100644 index 000000000..ad4312026 --- /dev/null +++ b/src/function-configuration.ts @@ -0,0 +1,71 @@ +/** + * List of all regions supported by Cloud Functions. + */ +export const SUPPORTED_REGIONS = [ + 'us-central1', + 'us-east1', + 'europe-west1', + 'europe-west2', + 'asia-east2', + 'asia-northeast1', +]; + +/** + * Cloud Functions min timeout value. + */ +export const MIN_TIMEOUT_SECONDS = 0; + +/** + * Cloud Functions max timeout value. + */ +export const MAX_TIMEOUT_SECONDS = 540; + +/** + * List of available memory options supported by Cloud Functions. + */ +export const VALID_MEMORY_OPTS = [ + '128MB', + '256MB', + '512MB', + '1GB', + '2GB', +] as const; + +export interface ScheduleRetryConfig { + retryCount?: number; + maxRetryDuration?: string; + minBackoffDuration?: string; + maxBackoffDuration?: string; + maxDoublings?: number; +} + +export interface Schedule { + schedule: string; + timeZone?: string; + retryConfig?: ScheduleRetryConfig; +} + +export interface FailurePolicy { + retry: {}; +} + +export interface RuntimeOptions { + /** + * Failure policy of the function, boolean `true` is equivalent to providing + * an empty policy. + */ + failurePolicy?: FailurePolicy | boolean; + /** + * Amount of memory to allocate to the function. + */ + memory?: typeof VALID_MEMORY_OPTS[number]; + /** + * Timeout for the function in seconds, possible values are 0 to 540. + */ + timeoutSeconds?: number; +} + +export interface DeploymentOptions extends RuntimeOptions { + regions?: string[]; + schedule?: Schedule; +} diff --git a/src/index.ts b/src/index.ts index 2d57759e2..5e3c44d80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -55,5 +55,6 @@ export { export * from './config'; export * from './cloud-functions'; export * from './function-builder'; +export * from './function-configuration'; setup(); diff --git a/src/providers/analytics.ts b/src/providers/analytics.ts index 3287264b3..49b0339f7 100644 --- a/src/providers/analytics.ts +++ b/src/providers/analytics.ts @@ -28,7 +28,7 @@ import { Event, EventContext, } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.analytics'; diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 708aca5ad..026bcc04c 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -28,7 +28,7 @@ import { } from '../cloud-functions'; import * as firebase from 'firebase-admin'; import * as _ from 'lodash'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.auth'; diff --git a/src/providers/crashlytics.ts b/src/providers/crashlytics.ts index 2ad1db6b3..a194afed6 100644 --- a/src/providers/crashlytics.ts +++ b/src/providers/crashlytics.ts @@ -25,7 +25,7 @@ import { CloudFunction, EventContext, } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.crashlytics'; diff --git a/src/providers/database.ts b/src/providers/database.ts index 4b0aaf938..bffb492aa 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -32,7 +32,7 @@ import { import { normalizePath, applyChange, pathParts, joinPath } from '../utils'; import * as firebase from 'firebase-admin'; import { firebaseConfig } from '../config'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.database'; diff --git a/src/providers/firestore.ts b/src/providers/firestore.ts index 20dee281f..40ac53051 100644 --- a/src/providers/firestore.ts +++ b/src/providers/firestore.ts @@ -32,7 +32,7 @@ import { EventContext, } from '../cloud-functions'; import { dateToTimestampProto } from '../encoder'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firestore'; diff --git a/src/providers/https.ts b/src/providers/https.ts index 48ce2f95f..9e943b4f2 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -26,15 +26,12 @@ import * as _ from 'lodash'; import * as cors from 'cors'; import { apps } from '../apps'; import { HttpsFunction, optionsToTrigger, Runnable } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; -/** - * - * - */ export interface Request extends express.Request { rawBody: Buffer; } + /** * Handle HTTP requests. * @param handler A function that takes a request and response object, diff --git a/src/providers/pubsub.ts b/src/providers/pubsub.ts index 194ecda9c..fb968ff1d 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -24,10 +24,12 @@ import { CloudFunction, makeCloudFunction, EventContext, +} from '../cloud-functions'; +import { + DeploymentOptions, Schedule, ScheduleRetryConfig, -} from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +} from '../function-configuration'; /** @internal */ export const provider = 'google.pubsub'; diff --git a/src/providers/remoteConfig.ts b/src/providers/remoteConfig.ts index 00fb8b877..60d5345d6 100644 --- a/src/providers/remoteConfig.ts +++ b/src/providers/remoteConfig.ts @@ -28,7 +28,7 @@ import { EventContext, makeCloudFunction, } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.remoteconfig'; diff --git a/src/providers/storage.ts b/src/providers/storage.ts index eb7821b99..436ffdce6 100644 --- a/src/providers/storage.ts +++ b/src/providers/storage.ts @@ -26,7 +26,7 @@ import { makeCloudFunction, } from '../cloud-functions'; import { firebaseConfig } from '../config'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.storage'; From 33137a641ab9c30918d97e8abda7adcb64c1dd34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 21 Jun 2019 16:29:11 +0200 Subject: [PATCH 03/36] Add a changelog entry --- changelog.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.txt b/changelog.txt index e69de29bb..3aacc1acb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -0,0 +1 @@ +- feature - Allows specifying retry policies for event triggered functions. From facd69c3839ab62148eec4d9b67b7739d3132357 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 21 Jun 2019 16:29:36 +0200 Subject: [PATCH 04/36] Update dependencies, minor version bump --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 2f2b38325..05f8df18d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.0.1", + "version": "3.1.0", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", @@ -31,7 +31,7 @@ }, "dependencies": { "@types/cors": "^2.8.5", - "@types/express": "^4.11.1", + "@types/express": "^4.17.0", "@types/jsonwebtoken": "^8.3.2", "@types/lodash": "^4.14.133", "cors": "^2.8.4", @@ -46,21 +46,21 @@ "@types/mock-require": "^2.0.0", "@types/nock": "^10.0.2", "@types/node": "^8.10.49", - "@types/sinon": "^7.0.12", + "@types/sinon": "^7.0.13", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", - "firebase-admin": "^8.0.0", + "firebase-admin": "^8.2.0", "istanbul": "^0.4.2", "mocha": "^6.1.4", "mock-require": "^3.0.3", "nock": "^10.0.6", - "prettier": "^1.17.1", + "prettier": "^1.18.2", "sinon": "^7.3.2", - "ts-node": "^8.2.0", + "ts-node": "^8.3.0", "tslint": "^5.17.0", "tslint-no-unused-expression-chai": "^0.1.4", "tslint-plugin-prettier": "^2.0.0", - "typescript": "^3.5.1" + "typescript": "^3.5.2" }, "peerDependencies": { "firebase-admin": "^8.0.0" From 819a564e5334738fe2b5fa5a8b22391feea7e8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 21 Jun 2019 16:29:46 +0200 Subject: [PATCH 05/36] Reformat --- integration_test/functions/src/database-tests.ts | 4 +--- integration_test/functions/src/firestore-tests.ts | 4 +--- integration_test/functions/src/index.ts | 8 ++------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/integration_test/functions/src/database-tests.ts b/integration_test/functions/src/database-tests.ts index 403c85bc7..215e6bd3b 100644 --- a/integration_test/functions/src/database-tests.ts +++ b/integration_test/functions/src/database-tests.ts @@ -50,9 +50,7 @@ export const databaseTests: any = functions.database .it('should have refs resources', (change, context) => expectEq( context.resource.name, - `projects/_/instances/${process.env.GCLOUD_PROJECT}/refs/dbTests/${ - context.params.testId - }/start` + `projects/_/instances/${process.env.GCLOUD_PROJECT}/refs/dbTests/${context.params.testId}/start` ) ) diff --git a/integration_test/functions/src/firestore-tests.ts b/integration_test/functions/src/firestore-tests.ts index de6581392..e5f8acdbf 100644 --- a/integration_test/functions/src/firestore-tests.ts +++ b/integration_test/functions/src/firestore-tests.ts @@ -22,9 +22,7 @@ export const firestoreTests: any = functions .it('should have well-formatted resource', (snap, context) => expectEq( context.resource.name, - `projects/${ - process.env.GCLOUD_PROJECT - }/databases/(default)/documents/tests/${context.params.documentId}` + `projects/${process.env.GCLOUD_PROJECT}/databases/(default)/documents/tests/${context.params.documentId}` ) ) diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index 55dadd739..9360bd615 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -49,9 +49,7 @@ function callScheduleTrigger(functionName: string, region: string) { { method: 'POST', host: 'cloudscheduler.googleapis.com', - path: `projects/${ - firebaseConfig.projectId - }/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`, + path: `projects/${firebaseConfig.projectId}/locations/us-central1/jobs/firebase-schedule-${functionName}-${region}:run`, headers: { 'Content-Type': 'application/json', }, @@ -196,9 +194,7 @@ export const integrationTests: any = functions resp .status(500) .send( - `FAIL - details at https://${ - process.env.GCLOUD_PROJECT - }.firebaseio.com/testRuns/${testId}` + `FAIL - details at https://${process.env.GCLOUD_PROJECT}.firebaseio.com/testRuns/${testId}` ); }); }); From f42584c790813203ecb188d584d028869e5ecfe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 21 Jun 2019 16:30:48 +0200 Subject: [PATCH 06/36] Fix a typo: allows -> allow --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 3aacc1acb..9ff89871e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1 +1 @@ -- feature - Allows specifying retry policies for event triggered functions. +- feature - Allow specifying retry policies for event triggered functions. From 0c554dbb29d9cd64d6ac6c1c5e50b8d30be4c047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 21 Jun 2019 16:33:27 +0200 Subject: [PATCH 07/36] Avoid abbreviations to improve readability --- src/cloud-functions.ts | 6 +++--- src/function-builder.ts | 8 +++++--- src/function-configuration.ts | 4 ++-- src/providers/analytics.ts | 8 ++++---- src/providers/auth.ts | 8 ++++---- src/providers/crashlytics.ts | 8 ++++---- src/providers/database.ts | 16 ++++++++-------- src/providers/firestore.ts | 29 ++++++++++++++++------------- src/providers/https.ts | 10 +++++----- src/providers/pubsub.ts | 24 ++++++++++++------------ src/providers/remoteConfig.ts | 8 ++++---- src/providers/storage.ts | 16 ++++++++-------- 12 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index f8438bbf9..886af4c4b 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -246,7 +246,7 @@ export interface MakeCloudFunctionArgs { handler?: (data: EventData, context: EventContext) => PromiseLike | any; labels?: { [key: string]: any }; legacyEventType?: string; - opts?: { [key: string]: any }; + options?: { [key: string]: any }; /* * TODO: should remove `provider` and require a fully qualified `eventType` * once all providers have migrated to new format. @@ -302,7 +302,7 @@ export function makeCloudFunction({ handler, labels = {}, legacyEventType, - opts = {}, + options = {}, provider, service, triggerResource, @@ -376,7 +376,7 @@ export function makeCloudFunction({ return {}; } - let trigger: any = _.assign(optionsToTrigger(opts), { + let trigger: any = _.assign(optionsToTrigger(options), { eventTrigger: { resource: triggerResource(), eventType: legacyEventType || provider + '.' + eventType, diff --git a/src/function-builder.ts b/src/function-builder.ts index dfc3e9365..fc7f521d0 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -42,7 +42,7 @@ import * as storage from './providers/storage'; import { CloudFunction, EventContext } from './cloud-functions'; import { RuntimeOptions, - VALID_MEMORY_OPTS, + VALID_MEMORY_OPTIONS, MIN_TIMEOUT_SECONDS, MAX_TIMEOUT_SECONDS, SUPPORTED_REGIONS, @@ -85,9 +85,11 @@ function assertRuntimeOptionsValidity(runtimeOptions: RuntimeOptions): void { } if (memory !== undefined) { - if (includes(VALID_MEMORY_OPTS, memory) === false) { + if (includes(VALID_MEMORY_OPTIONS, memory) === false) { throw new Error( - `RuntimeOptions.memory must be one of: ${VALID_MEMORY_OPTS.join(', ')}.` + `RuntimeOptions.memory must be one of: ${VALID_MEMORY_OPTIONS.join( + ', ' + )}.` ); } } diff --git a/src/function-configuration.ts b/src/function-configuration.ts index ad4312026..96ea5ea5e 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -23,7 +23,7 @@ export const MAX_TIMEOUT_SECONDS = 540; /** * List of available memory options supported by Cloud Functions. */ -export const VALID_MEMORY_OPTS = [ +export const VALID_MEMORY_OPTIONS = [ '128MB', '256MB', '512MB', @@ -58,7 +58,7 @@ export interface RuntimeOptions { /** * Amount of memory to allocate to the function. */ - memory?: typeof VALID_MEMORY_OPTS[number]; + memory?: typeof VALID_MEMORY_OPTIONS[number]; /** * Timeout for the function in seconds, possible values are 0 to 540. */ diff --git a/src/providers/analytics.ts b/src/providers/analytics.ts index 49b0339f7..59332955a 100644 --- a/src/providers/analytics.ts +++ b/src/providers/analytics.ts @@ -46,7 +46,7 @@ export function event(analyticsEventType: string) { /** @internal */ export function _eventWithOpts( analyticsEventType: string, - opts: DeploymentOptions + options: DeploymentOptions ) { return new AnalyticsEventBuilder(() => { if (!process.env.GCLOUD_PROJECT) { @@ -55,7 +55,7 @@ export function _eventWithOpts( return ( 'projects/' + process.env.GCLOUD_PROJECT + '/events/' + analyticsEventType ); - }, opts); + }, options); } /** @@ -67,7 +67,7 @@ export class AnalyticsEventBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) {} /** @@ -97,7 +97,7 @@ export class AnalyticsEventBuilder { legacyEventType: `providers/google.firebase.analytics/eventTypes/event.log`, triggerResource: this.triggerResource, dataConstructor, - opts: this.opts, + options: this.options, }); } } diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 026bcc04c..ad6de1571 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -43,13 +43,13 @@ export function user() { } /** @internal */ -export function _userWithOpts(opts: DeploymentOptions) { +export function _userWithOpts(options: DeploymentOptions) { return new UserBuilder(() => { if (!process.env.GCLOUD_PROJECT) { throw new Error('process.env.GCLOUD_PROJECT is not set.'); } return 'projects/' + process.env.GCLOUD_PROJECT; - }, opts); + }, options); } export class UserRecordMetadata implements firebase.auth.UserMetadata { @@ -73,7 +73,7 @@ export class UserBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts?: DeploymentOptions + private options?: DeploymentOptions ) {} /** Respond to the creation of a Firebase Auth user. */ @@ -105,7 +105,7 @@ export class UserBuilder { triggerResource: this.triggerResource, dataConstructor: UserBuilder.dataConstructor, legacyEventType: `providers/firebase.auth/eventTypes/${eventType}`, - opts: this.opts, + options: this.options, }); } } diff --git a/src/providers/crashlytics.ts b/src/providers/crashlytics.ts index a194afed6..060b3d547 100644 --- a/src/providers/crashlytics.ts +++ b/src/providers/crashlytics.ts @@ -41,13 +41,13 @@ export function issue() { } /** @internal */ -export function _issueWithOpts(opts: DeploymentOptions) { +export function _issueWithOpts(options: DeploymentOptions) { return new IssueBuilder(() => { if (!process.env.GCLOUD_PROJECT) { throw new Error('process.env.GCLOUD_PROJECT is not set.'); } return 'projects/' + process.env.GCLOUD_PROJECT; - }, opts); + }, options); } /** Builder used to create Cloud Functions for Crashlytics issue events. */ @@ -55,7 +55,7 @@ export class IssueBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) {} /** @internal */ @@ -95,7 +95,7 @@ export class IssueBuilder { service, legacyEventType: `providers/firebase.crashlytics/eventTypes/${eventType}`, triggerResource: this.triggerResource, - opts: this.opts, + options: this.options, }); } } diff --git a/src/providers/database.ts b/src/providers/database.ts index bffb492aa..ac4c9128f 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -81,21 +81,21 @@ export function ref(path: string) { /** @internal */ export function _instanceWithOpts( instance: string, - opts: DeploymentOptions + options: DeploymentOptions ): InstanceBuilder { - return new InstanceBuilder(instance, opts); + return new InstanceBuilder(instance, options); } export class InstanceBuilder { /* @internal */ - constructor(private instance: string, private opts: DeploymentOptions) {} + constructor(private instance: string, private options: DeploymentOptions) {} ref(path: string): RefBuilder { const normalized = normalizePath(path); return new RefBuilder( apps(), () => `projects/_/instances/${this.instance}/refs/${normalized}`, - this.opts + this.options ); } } @@ -103,7 +103,7 @@ export class InstanceBuilder { /** @internal */ export function _refWithOpts( path: string, - opts: DeploymentOptions + options: DeploymentOptions ): RefBuilder { const resourceGetter = () => { const normalized = normalizePath(path); @@ -126,7 +126,7 @@ export function _refWithOpts( return `projects/_/instances/${subdomain}/refs/${normalized}`; }; - return new RefBuilder(apps(), resourceGetter, opts); + return new RefBuilder(apps(), resourceGetter, options); } /** Builder used to create Cloud Functions for Firebase Realtime Database References. */ @@ -135,7 +135,7 @@ export class RefBuilder { constructor( private apps: apps.Apps, private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) {} /** Respond to any write that affects a ref. */ @@ -210,7 +210,7 @@ export class RefBuilder { dataConstructor: dataConstructor, before: (event) => this.apps.retain(), after: (event) => this.apps.release(), - opts: this.opts, + options: this.options, }); } diff --git a/src/providers/firestore.ts b/src/providers/firestore.ts index 40ac53051..d29ad0e0c 100644 --- a/src/providers/firestore.ts +++ b/src/providers/firestore.ts @@ -67,31 +67,34 @@ export function database(database: string) { /** @internal */ export function _databaseWithOpts( database: string = defaultDatabase, - opts: DeploymentOptions + options: DeploymentOptions ) { - return new DatabaseBuilder(database, opts); + return new DatabaseBuilder(database, options); } /** @internal */ -export function _namespaceWithOpts(namespace: string, opts: DeploymentOptions) { - return _databaseWithOpts(defaultDatabase, opts).namespace(namespace); +export function _namespaceWithOpts( + namespace: string, + options: DeploymentOptions +) { + return _databaseWithOpts(defaultDatabase, options).namespace(namespace); } /** @internal */ -export function _documentWithOpts(path: string, opts: DeploymentOptions) { - return _databaseWithOpts(defaultDatabase, opts).document(path); +export function _documentWithOpts(path: string, options: DeploymentOptions) { + return _databaseWithOpts(defaultDatabase, options).document(path); } export class DatabaseBuilder { /** @internal */ - constructor(private database: string, private opts: DeploymentOptions) {} + constructor(private database: string, private options: DeploymentOptions) {} namespace(namespace: string) { - return new NamespaceBuilder(this.database, this.opts, namespace); + return new NamespaceBuilder(this.database, this.options, namespace); } document(path: string) { - return new NamespaceBuilder(this.database, this.opts).document(path); + return new NamespaceBuilder(this.database, this.options).document(path); } } @@ -99,7 +102,7 @@ export class NamespaceBuilder { /** @internal */ constructor( private database: string, - private opts: DeploymentOptions, + private options: DeploymentOptions, private namespace?: string ) {} @@ -119,7 +122,7 @@ export class NamespaceBuilder { this.namespace ? `documents@${this.namespace}` : 'documents', path ); - }, this.opts); + }, this.options); } } @@ -183,7 +186,7 @@ export class DocumentBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) { // TODO what validation do we want to do here? } @@ -245,7 +248,7 @@ export class DocumentBuilder { triggerResource: this.triggerResource, legacyEventType: `providers/cloud.firestore/eventTypes/${eventType}`, dataConstructor, - opts: this.opts, + options: this.options, }); } } diff --git a/src/providers/https.ts b/src/providers/https.ts index 9e943b4f2..17d90cb17 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -56,16 +56,16 @@ export function onCall( /** @internal */ export function _onRequestWithOpts( handler: (req: Request, resp: express.Response) => void, - opts: DeploymentOptions + options: DeploymentOptions ): HttpsFunction { // lets us add __trigger without altering handler: let cloudFunction: any = (req: Request, res: express.Response) => { handler(req, res); }; - cloudFunction.__trigger = _.assign(optionsToTrigger(opts), { + cloudFunction.__trigger = _.assign(optionsToTrigger(options), { httpsTrigger: {}, }); - // TODO parse the opts + // TODO parse the options return cloudFunction; } @@ -415,7 +415,7 @@ const corsHandler = cors({ origin: true, methods: 'POST' }); /** @internal */ export function _onCallWithOpts( handler: (data: any, context: CallableContext) => any | Promise, - opts: DeploymentOptions + options: DeploymentOptions ): HttpsFunction & Runnable { const func = async (req: Request, res: express.Response) => { try { @@ -482,7 +482,7 @@ export function _onCallWithOpts( return corsHandler(req, res, () => func(req, res)); }; - corsFunc.__trigger = _.assign(optionsToTrigger(opts), { + corsFunc.__trigger = _.assign(optionsToTrigger(options), { httpsTrigger: {}, labels: { 'deployment-callable': 'true' }, }); diff --git a/src/providers/pubsub.ts b/src/providers/pubsub.ts index fb968ff1d..060dfbbb5 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -46,7 +46,7 @@ export function topic(topic: string) { /** @internal */ export function _topicWithOpts( topic: string, - opts: DeploymentOptions + options: DeploymentOptions ): TopicBuilder { if (topic.indexOf('/') !== -1) { throw new Error('Topic name may not have a /'); @@ -57,7 +57,7 @@ export function _topicWithOpts( throw new Error('process.env.GCLOUD_PROJECT is not set.'); } return `projects/${process.env.GCLOUD_PROJECT}/topics/${topic}`; - }, opts); + }, options); } export function schedule(schedule: string): ScheduleBuilder { @@ -65,20 +65,20 @@ export function schedule(schedule: string): ScheduleBuilder { } export class ScheduleBuilder { - private _opts: DeploymentOptions; + private _options: DeploymentOptions; /** @internal */ - constructor(private schedule: Schedule, private opts: DeploymentOptions) { - this._opts = { schedule, ...opts }; + constructor(private schedule: Schedule, private options: DeploymentOptions) { + this._options = { schedule, ...options }; } retryConfig(config: ScheduleRetryConfig): ScheduleBuilder { - this._opts.schedule.retryConfig = config; + this._options.schedule.retryConfig = config; return this; } timeZone(timeZone: string): ScheduleBuilder { - this._opts.schedule.timeZone = timeZone; + this._options.schedule.timeZone = timeZone; return this; } @@ -95,7 +95,7 @@ export class ScheduleBuilder { service, triggerResource: triggerResource, eventType: 'topic.publish', - opts: this._opts, + options: this._options, labels: { 'deployment-scheduled': 'true' }, }); return cloudFunction; @@ -105,9 +105,9 @@ export class ScheduleBuilder { /** @internal */ export function _scheduleWithOpts( schedule: string, - opts: DeploymentOptions + options: DeploymentOptions ): ScheduleBuilder { - return new ScheduleBuilder({ schedule }, opts); + return new ScheduleBuilder({ schedule }, options); } /** Builder used to create Cloud Functions for Google Pub/Sub topics. */ @@ -115,7 +115,7 @@ export class TopicBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) {} /** Handle a Pub/Sub message that was published to a Cloud Pub/Sub topic */ @@ -129,7 +129,7 @@ export class TopicBuilder { triggerResource: this.triggerResource, eventType: 'topic.publish', dataConstructor: (raw) => new Message(raw.data), - opts: this.opts, + options: this.options, }); } } diff --git a/src/providers/remoteConfig.ts b/src/providers/remoteConfig.ts index 60d5345d6..36ca62733 100644 --- a/src/providers/remoteConfig.ts +++ b/src/providers/remoteConfig.ts @@ -55,7 +55,7 @@ export function _onUpdateWithOpts( version: TemplateVersion, context: EventContext ) => PromiseLike | any, - opts: DeploymentOptions + options: DeploymentOptions ): CloudFunction { const triggerResource = () => { if (!process.env.GCLOUD_PROJECT) { @@ -63,7 +63,7 @@ export function _onUpdateWithOpts( } return `projects/${process.env.GCLOUD_PROJECT}`; }; - return new UpdateBuilder(triggerResource, opts).onUpdate(handler); + return new UpdateBuilder(triggerResource, options).onUpdate(handler); } /** Builder used to create Cloud Functions for Remote Config. */ @@ -71,7 +71,7 @@ export class UpdateBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) {} /** @@ -92,7 +92,7 @@ export class UpdateBuilder { service, triggerResource: this.triggerResource, eventType: 'update', - opts: this.opts, + options: this.options, }); } } diff --git a/src/providers/storage.ts b/src/providers/storage.ts index 436ffdce6..4e8f76541 100644 --- a/src/providers/storage.ts +++ b/src/providers/storage.ts @@ -52,7 +52,7 @@ export function object() { /** @internal */ export function _bucketWithOpts( - opts: DeploymentOptions, + options: DeploymentOptions, bucket?: string ): BucketBuilder { const resourceGetter = () => { @@ -68,24 +68,24 @@ export function _bucketWithOpts( } return `projects/_/buckets/${bucket}`; }; - return new BucketBuilder(resourceGetter, opts); + return new BucketBuilder(resourceGetter, options); } /** @internal */ -export function _objectWithOpts(opts: DeploymentOptions): ObjectBuilder { - return _bucketWithOpts(opts).object(); +export function _objectWithOpts(options: DeploymentOptions): ObjectBuilder { + return _bucketWithOpts(options).object(); } export class BucketBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) {} /** Handle events for objects in this bucket. */ object() { - return new ObjectBuilder(this.triggerResource, this.opts); + return new ObjectBuilder(this.triggerResource, this.options); } } @@ -93,7 +93,7 @@ export class ObjectBuilder { /** @internal */ constructor( private triggerResource: () => string, - private opts: DeploymentOptions + private options: DeploymentOptions ) {} /** @internal */ @@ -157,7 +157,7 @@ export class ObjectBuilder { service, eventType, triggerResource: this.triggerResource, - opts: this.opts, + options: this.options, }); } } From c633a782274b3f3e2f77f02911ff361726ec1557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Mon, 24 Jun 2019 12:29:41 +0200 Subject: [PATCH 08/36] Rename remaining Opts to Options --- src/function-builder.ts | 35 ++++++++++++++++++----------------- src/handler-builder.ts | 4 ++-- src/providers/analytics.ts | 4 ++-- src/providers/auth.ts | 4 ++-- src/providers/crashlytics.ts | 4 ++-- src/providers/database.ts | 8 ++++---- src/providers/firestore.ts | 16 ++++++++-------- src/providers/https.ts | 8 ++++---- src/providers/pubsub.ts | 8 ++++---- src/providers/remoteConfig.ts | 4 ++-- src/providers/storage.ts | 10 +++++----- 11 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index fc7f521d0..dfa971990 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -209,7 +209,7 @@ export class FunctionBuilder { */ onRequest: ( handler: (req: https.Request, resp: express.Response) => void - ) => https._onRequestWithOpts(handler, this.options), + ) => https._onRequestWithOptions(handler, this.options), /** * Declares a callable method for clients to call using a Firebase SDK. @@ -220,7 +220,7 @@ export class FunctionBuilder { data: any, context: https.CallableContext ) => any | Promise - ) => https._onCallWithOpts(handler, this.options), + ) => https._onCallWithOptions(handler, this.options), }; } @@ -232,7 +232,7 @@ export class FunctionBuilder { * @param instance The Realtime Database instance to use. */ instance: (instance: string) => - database._instanceWithOpts(instance, this.options), + database._instanceWithOptions(instance, this.options), /** * Select Firebase Realtime Database Reference to listen to. @@ -259,7 +259,7 @@ export class FunctionBuilder { * information about the user who triggered the Cloud Function. * @param ref Path of the database to listen to. */ - ref: (path: string) => database._refWithOpts(path, this.options), + ref: (path: string) => database._refWithOptions(path, this.options), }; } @@ -273,15 +273,15 @@ export class FunctionBuilder { * path is "/users/Ada". */ document: (path: string) => - firestore._documentWithOpts(path, this.options), + firestore._documentWithOptions(path, this.options), /** @internal */ namespace: (namespace: string) => - firestore._namespaceWithOpts(namespace, this.options), + firestore._namespaceWithOptions(namespace, this.options), /** @internal */ database: (database: string) => - firestore._databaseWithOpts(database, this.options), + firestore._databaseWithOptions(database, this.options), }; } @@ -291,7 +291,7 @@ export class FunctionBuilder { * Handle events related to Crashlytics issues. An issue in Crashlytics is * an aggregation of crashes which have a shared root cause. */ - issue: () => crashlytics._issueWithOpts(this.options), + issue: () => crashlytics._issueWithOptions(this.options), }; } @@ -302,7 +302,7 @@ export class FunctionBuilder { * @param analyticsEventType Name of the analytics event type. */ event: (analyticsEventType: string) => - analytics._eventWithOpts(analyticsEventType, this.options), + analytics._eventWithOptions(analyticsEventType, this.options), }; } @@ -320,9 +320,10 @@ export class FunctionBuilder { context: EventContext ) => PromiseLike | any ) => - remoteConfig._onUpdateWithOpts(handler, this.options) as CloudFunction< - remoteConfig.TemplateVersion - >, + remoteConfig._onUpdateWithOptions( + handler, + this.options + ) as CloudFunction, }; } @@ -335,12 +336,12 @@ export class FunctionBuilder { * @param bucket Name of the Google Cloud Storage bucket to listen to. */ bucket: (bucket?: string) => - storage._bucketWithOpts(this.options, bucket), + storage._bucketWithOptions(this.options, bucket), /** * Handle events related to Cloud Storage objects. */ - object: () => storage._objectWithOpts(this.options), + object: () => storage._objectWithOptions(this.options), }; } @@ -350,9 +351,9 @@ export class FunctionBuilder { * @param topic Name of Pub/Sub topic, must belong to the same project as * the function. */ - topic: (topic: string) => pubsub._topicWithOpts(topic, this.options), + topic: (topic: string) => pubsub._topicWithOptions(topic, this.options), schedule: (schedule: string) => - pubsub._scheduleWithOpts(schedule, this.options), + pubsub._scheduleWithOptions(schedule, this.options), }; } @@ -361,7 +362,7 @@ export class FunctionBuilder { /** * Handle events related to Firebase authentication users. */ - user: () => auth._userWithOpts(this.options), + user: () => auth._userWithOptions(this.options), }; } } diff --git a/src/handler-builder.ts b/src/handler-builder.ts index 5c466fabc..e507ae42e 100644 --- a/src/handler-builder.ts +++ b/src/handler-builder.ts @@ -47,7 +47,7 @@ export class HandlerBuilder { onRequest: ( handler: (req: express.Request, resp: express.Response) => void ): HttpsFunction => { - const func = https._onRequestWithOpts(handler, {}); + const func = https._onRequestWithOptions(handler, {}); func.__trigger = {}; return func; }, @@ -61,7 +61,7 @@ export class HandlerBuilder { context: https.CallableContext ) => any | Promise ): HttpsFunction => { - const func = https._onCallWithOpts(handler, {}); + const func = https._onCallWithOptions(handler, {}); func.__trigger = {}; return func; }, diff --git a/src/providers/analytics.ts b/src/providers/analytics.ts index 59332955a..676a354f3 100644 --- a/src/providers/analytics.ts +++ b/src/providers/analytics.ts @@ -40,11 +40,11 @@ export const service = 'app-measurement.com'; * @param analyticsEventType Name of the analytics event type. */ export function event(analyticsEventType: string) { - return _eventWithOpts(analyticsEventType, {}); + return _eventWithOptions(analyticsEventType, {}); } /** @internal */ -export function _eventWithOpts( +export function _eventWithOptions( analyticsEventType: string, options: DeploymentOptions ) { diff --git a/src/providers/auth.ts b/src/providers/auth.ts index ad6de1571..5cdfe82f6 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -39,11 +39,11 @@ export const service = 'firebaseauth.googleapis.com'; * Handle events related to Firebase authentication users. */ export function user() { - return _userWithOpts({}); + return _userWithOptions({}); } /** @internal */ -export function _userWithOpts(options: DeploymentOptions) { +export function _userWithOptions(options: DeploymentOptions) { return new UserBuilder(() => { if (!process.env.GCLOUD_PROJECT) { throw new Error('process.env.GCLOUD_PROJECT is not set.'); diff --git a/src/providers/crashlytics.ts b/src/providers/crashlytics.ts index 060b3d547..3621139bb 100644 --- a/src/providers/crashlytics.ts +++ b/src/providers/crashlytics.ts @@ -37,11 +37,11 @@ export const service = 'fabric.io'; * aggregation of crashes which have a shared root cause. */ export function issue() { - return _issueWithOpts({}); + return _issueWithOptions({}); } /** @internal */ -export function _issueWithOpts(options: DeploymentOptions) { +export function _issueWithOptions(options: DeploymentOptions) { return new IssueBuilder(() => { if (!process.env.GCLOUD_PROJECT) { throw new Error('process.env.GCLOUD_PROJECT is not set.'); diff --git a/src/providers/database.ts b/src/providers/database.ts index ac4c9128f..8def2b2df 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -48,7 +48,7 @@ const databaseURLRegex = new RegExp('https://([^.]+).firebaseio.com'); * @param instance The Realtime Database instance to use. */ export function instance(instance: string) { - return _instanceWithOpts(instance, {}); + return _instanceWithOptions(instance, {}); } /** @@ -75,11 +75,11 @@ export function instance(instance: string) { * @param ref Path of the database to listen to. */ export function ref(path: string) { - return _refWithOpts(path, {}); + return _refWithOptions(path, {}); } /** @internal */ -export function _instanceWithOpts( +export function _instanceWithOptions( instance: string, options: DeploymentOptions ): InstanceBuilder { @@ -101,7 +101,7 @@ export class InstanceBuilder { } /** @internal */ -export function _refWithOpts( +export function _refWithOptions( path: string, options: DeploymentOptions ): RefBuilder { diff --git a/src/providers/firestore.ts b/src/providers/firestore.ts index d29ad0e0c..4a4f516cf 100644 --- a/src/providers/firestore.ts +++ b/src/providers/firestore.ts @@ -51,21 +51,21 @@ export type DocumentSnapshot = firebase.firestore.DocumentSnapshot; * path is "/users/Ada". */ export function document(path: string) { - return _documentWithOpts(path, {}); + return _documentWithOptions(path, {}); } /** @internal */ // Multiple namespaces are not yet supported by Firestore. export function namespace(namespace: string) { - return _namespaceWithOpts(namespace, {}); + return _namespaceWithOptions(namespace, {}); } /** @internal */ // Multiple databases are not yet supported by Firestore. export function database(database: string) { - return _databaseWithOpts(database, {}); + return _databaseWithOptions(database, {}); } /** @internal */ -export function _databaseWithOpts( +export function _databaseWithOptions( database: string = defaultDatabase, options: DeploymentOptions ) { @@ -73,16 +73,16 @@ export function _databaseWithOpts( } /** @internal */ -export function _namespaceWithOpts( +export function _namespaceWithOptions( namespace: string, options: DeploymentOptions ) { - return _databaseWithOpts(defaultDatabase, options).namespace(namespace); + return _databaseWithOptions(defaultDatabase, options).namespace(namespace); } /** @internal */ -export function _documentWithOpts(path: string, options: DeploymentOptions) { - return _databaseWithOpts(defaultDatabase, options).document(path); +export function _documentWithOptions(path: string, options: DeploymentOptions) { + return _databaseWithOptions(defaultDatabase, options).document(path); } export class DatabaseBuilder { diff --git a/src/providers/https.ts b/src/providers/https.ts index 17d90cb17..b12114b74 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -40,7 +40,7 @@ export interface Request extends express.Request { export function onRequest( handler: (req: Request, resp: express.Response) => void ): HttpsFunction { - return _onRequestWithOpts(handler, {}); + return _onRequestWithOptions(handler, {}); } /** @@ -50,11 +50,11 @@ export function onRequest( export function onCall( handler: (data: any, context: CallableContext) => any | Promise ): HttpsFunction & Runnable { - return _onCallWithOpts(handler, {}); + return _onCallWithOptions(handler, {}); } /** @internal */ -export function _onRequestWithOpts( +export function _onRequestWithOptions( handler: (req: Request, resp: express.Response) => void, options: DeploymentOptions ): HttpsFunction { @@ -413,7 +413,7 @@ export function decode(data: any): any { const corsHandler = cors({ origin: true, methods: 'POST' }); /** @internal */ -export function _onCallWithOpts( +export function _onCallWithOptions( handler: (data: any, context: CallableContext) => any | Promise, options: DeploymentOptions ): HttpsFunction & Runnable { diff --git a/src/providers/pubsub.ts b/src/providers/pubsub.ts index 060dfbbb5..8006ff6ca 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -40,11 +40,11 @@ export const service = 'pubsub.googleapis.com'; * @param topic Name of Pub/Sub topic, must belong to the same project as the function. */ export function topic(topic: string) { - return _topicWithOpts(topic, {}); + return _topicWithOptions(topic, {}); } /** @internal */ -export function _topicWithOpts( +export function _topicWithOptions( topic: string, options: DeploymentOptions ): TopicBuilder { @@ -61,7 +61,7 @@ export function _topicWithOpts( } export function schedule(schedule: string): ScheduleBuilder { - return _scheduleWithOpts(schedule, {}); + return _scheduleWithOptions(schedule, {}); } export class ScheduleBuilder { @@ -103,7 +103,7 @@ export class ScheduleBuilder { } /** @internal */ -export function _scheduleWithOpts( +export function _scheduleWithOptions( schedule: string, options: DeploymentOptions ): ScheduleBuilder { diff --git a/src/providers/remoteConfig.ts b/src/providers/remoteConfig.ts index 36ca62733..91903a58f 100644 --- a/src/providers/remoteConfig.ts +++ b/src/providers/remoteConfig.ts @@ -46,11 +46,11 @@ export function onUpdate( context: EventContext ) => PromiseLike | any ): CloudFunction { - return _onUpdateWithOpts(handler, {}); + return _onUpdateWithOptions(handler, {}); } /** @internal */ -export function _onUpdateWithOpts( +export function _onUpdateWithOptions( handler: ( version: TemplateVersion, context: EventContext diff --git a/src/providers/storage.ts b/src/providers/storage.ts index 4e8f76541..2fc705776 100644 --- a/src/providers/storage.ts +++ b/src/providers/storage.ts @@ -40,18 +40,18 @@ export const service = 'storage.googleapis.com'; * @param bucket Name of the Google Cloud Storage bucket to listen to. */ export function bucket(bucket?: string) { - return _bucketWithOpts({}, bucket); + return _bucketWithOptions({}, bucket); } /** * Handle events related to Cloud Storage objects. */ export function object() { - return _objectWithOpts({}); + return _objectWithOptions({}); } /** @internal */ -export function _bucketWithOpts( +export function _bucketWithOptions( options: DeploymentOptions, bucket?: string ): BucketBuilder { @@ -72,8 +72,8 @@ export function _bucketWithOpts( } /** @internal */ -export function _objectWithOpts(options: DeploymentOptions): ObjectBuilder { - return _bucketWithOpts(options).object(); +export function _objectWithOptions(options: DeploymentOptions): ObjectBuilder { + return _bucketWithOptions(options).object(); } export class BucketBuilder { From 568113ce3bf23010608e5f2a589f38bb119e5a90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Mon, 24 Jun 2019 13:07:20 +0200 Subject: [PATCH 09/36] Add tests for specifying failure policy --- spec/function-builder.spec.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index cad751ae7..1ebfedb16 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -59,7 +59,7 @@ describe('FunctionBuilder', () => { 'europe-west1', 'europe-west2', 'asia-east2', - 'asia-northeast1' + 'asia-northeast1', ) .auth.user() .onCreate((user) => user); @@ -77,14 +77,29 @@ describe('FunctionBuilder', () => { it('should allow valid runtime options to be set', () => { const fn = functions .runWith({ - timeoutSeconds: 90, + failurePolicy: { retry: {} }, memory: '256MB', + timeoutSeconds: 90, }) .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', () => { @@ -137,7 +152,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: (1234 as unknown) as boolean, - }) + }), ).to.throw(Error, 'RuntimeOptions.failurePolicy'); }); @@ -145,7 +160,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: ('string-value' as unknown) as boolean, - }) + }), ).to.throw(Error, 'RuntimeOptions.failurePolicy'); }); @@ -153,7 +168,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: { retry: (1234 as unknown) as object }, - }) + }), ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); }); @@ -161,7 +176,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: { retry: ('string-value' as unknown) as object }, - }) + }), ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); }); From 993907f9d1ca0305182af812fd5c8a317ea6fd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Mon, 24 Jun 2019 13:19:10 +0200 Subject: [PATCH 10/36] Reformat --- spec/function-builder.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 1ebfedb16..1198b599f 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -59,7 +59,7 @@ describe('FunctionBuilder', () => { 'europe-west1', 'europe-west2', 'asia-east2', - 'asia-northeast1', + 'asia-northeast1' ) .auth.user() .onCreate((user) => user); @@ -152,7 +152,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: (1234 as unknown) as boolean, - }), + }) ).to.throw(Error, 'RuntimeOptions.failurePolicy'); }); @@ -160,7 +160,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: ('string-value' as unknown) as boolean, - }), + }) ).to.throw(Error, 'RuntimeOptions.failurePolicy'); }); @@ -168,7 +168,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: { retry: (1234 as unknown) as object }, - }), + }) ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); }); @@ -176,7 +176,7 @@ describe('FunctionBuilder', () => { expect(() => functions.runWith({ failurePolicy: { retry: ('string-value' as unknown) as object }, - }), + }) ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); }); From 3e46d655ee328bd185adf581e0dbcbf1da920917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 16:06:56 +0200 Subject: [PATCH 11/36] Change format of an entry in the changelog --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index a68ea49a3..404ffb503 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1 +1 @@ -* Allows specifying retry policies for event triggered functions. +feature - Allow specifying retry policies for event triggered functions. From b0ad579d882f5a268774999fcc2ae988fe1785da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 16:09:31 +0200 Subject: [PATCH 12/36] Revert version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 05f8df18d..f12daf524 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firebase-functions", - "version": "3.1.0", + "version": "3.0.2", "description": "Firebase SDK for Cloud Functions", "keywords": [ "firebase", From 6893b355b378165b4806ca2fb7c7e0bf8a28adae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 20:31:00 +0200 Subject: [PATCH 13/36] Extract configuration to break dependency cycle --- src/cloud-functions.ts | 16 +-------- src/function-builder.ts | 55 ++++++------------------------ src/function-configuration.ts | 63 +++++++++++++++++++++++++++++++++++ src/index.ts | 7 ++-- src/providers/analytics.ts | 4 +-- src/providers/auth.ts | 10 +++--- src/providers/crashlytics.ts | 4 +-- src/providers/database.ts | 2 +- src/providers/firestore.ts | 10 +++--- src/providers/https.ts | 4 +-- src/providers/pubsub.ts | 10 +++--- src/providers/remoteConfig.ts | 2 +- src/providers/storage.ts | 2 +- 13 files changed, 104 insertions(+), 85 deletions(-) create mode 100644 src/function-configuration.ts diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 1a5c6e214..796553ead 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -23,7 +23,7 @@ import { Request, Response } from 'express'; import * as _ from 'lodash'; import { apps } from './apps'; -import { DeploymentOptions } from './function-builder'; +import { DeploymentOptions, Schedule } from './function-configuration'; export { Request, Response }; const WILDCARD_REGEX = new RegExp('{[^/{}]*}', 'g'); @@ -168,20 +168,6 @@ export interface TriggerAnnotated { }; } -export interface ScheduleRetryConfig { - retryCount?: number; - maxRetryDuration?: string; - minBackoffDuration?: string; - maxBackoffDuration?: string; - maxDoublings?: number; -} - -export interface Schedule { - schedule: string; - timeZone?: string; - retryConfig?: ScheduleRetryConfig; -} - /** A Runnable has a `run` method which directly invokes the user-defined function - useful for unit testing. */ export interface Runnable { run: (data: T, context: any) => PromiseLike | any; diff --git a/src/function-builder.ts b/src/function-builder.ts index 83e9a00b0..082d5e2e9 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -20,9 +20,17 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import * as _ from 'lodash'; import * as express from 'express'; +import * as _ from 'lodash'; +import { CloudFunction, EventContext } from './cloud-functions'; +import { + DeploymentOptions, + MAX_TIMEOUT_SECONDS, + RuntimeOptions, + SUPPORTED_REGIONS, + VALID_MEMORY_OPTIONS, +} from './function-configuration'; import * as analytics from './providers/analytics'; import * as auth from './providers/auth'; import * as crashlytics from './providers/crashlytics'; @@ -32,35 +40,6 @@ import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as remoteConfig from './providers/remoteConfig'; import * as storage from './providers/storage'; -import { CloudFunction, EventContext, Schedule } from './cloud-functions'; - -/** - * List of all regions supported by Cloud Functions. - */ -const SUPPORTED_REGIONS = [ - 'us-central1', - 'us-east1', - 'us-east4', - 'europe-west1', - 'europe-west2', - 'asia-east2', - 'asia-northeast1', -]; - -/** - * List of available memory options supported by Cloud Functions. - */ -const VALID_MEMORY_OPTS = ['128MB', '256MB', '512MB', '1GB', '2GB']; - -// Adding this memory type here to error on compile for TS users. -// Unfortunately I have not found a way to merge this with VALID_MEMORY_OPS -// without it being super ugly. But here they are right next to each other at least. -type Memory = '128MB' | '256MB' | '512MB' | '1GB' | '2GB'; - -/** - * Cloud Functions max timeout value. - */ -const MAX_TIMEOUT_SECONDS = 540; /** * Assert that the runtime options passed in are valid. @@ -70,10 +49,10 @@ const MAX_TIMEOUT_SECONDS = 540; function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean { if ( runtimeOptions.memory && - !_.includes(VALID_MEMORY_OPTS, runtimeOptions.memory) + !_.includes(VALID_MEMORY_OPTIONS, runtimeOptions.memory) ) { throw new Error( - `The only valid memory allocation values are: ${VALID_MEMORY_OPTS.join( + `The only valid memory allocation values are: ${VALID_MEMORY_OPTIONS.join( ', ' )}` ); @@ -130,18 +109,6 @@ export function runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { } } -export interface RuntimeOptions { - timeoutSeconds?: number; - memory?: Memory; -} - -export interface DeploymentOptions { - regions?: string[]; - timeoutSeconds?: number; - memory?: Memory; - schedule?: Schedule; -} - export class FunctionBuilder { constructor(private options: DeploymentOptions) {} diff --git a/src/function-configuration.ts b/src/function-configuration.ts new file mode 100644 index 000000000..85e74f603 --- /dev/null +++ b/src/function-configuration.ts @@ -0,0 +1,63 @@ +/** + * List of all regions supported by Cloud Functions. + */ +export const SUPPORTED_REGIONS = [ + 'us-central1', + 'us-east1', + 'us-east4', + 'europe-west1', + 'europe-west2', + 'asia-east2', + 'asia-northeast1', +]; + +/** + * Cloud Functions min timeout value. + */ +export const MIN_TIMEOUT_SECONDS = 0; + +/** + * Cloud Functions max timeout value. + */ +export const MAX_TIMEOUT_SECONDS = 540; + +/** + * List of available memory options supported by Cloud Functions. + */ +export const VALID_MEMORY_OPTIONS = [ + '128MB', + '256MB', + '512MB', + '1GB', + '2GB', +] as const; + +export interface ScheduleRetryConfig { + retryCount?: number; + maxRetryDuration?: string; + minBackoffDuration?: string; + maxBackoffDuration?: string; + maxDoublings?: number; +} + +export interface Schedule { + schedule: string; + timeZone?: string; + retryConfig?: ScheduleRetryConfig; +} + +export interface RuntimeOptions { + /** + * Amount of memory to allocate to the function. + */ + memory?: typeof VALID_MEMORY_OPTIONS[number]; + /** + * Timeout for the function in seconds, possible values are 0 to 540. + */ + timeoutSeconds?: number; +} + +export interface DeploymentOptions extends RuntimeOptions { + regions?: string[]; + schedule?: Schedule; +} diff --git a/src/index.ts b/src/index.ts index e82b05c1e..a3636fa3d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,8 +23,6 @@ // Providers: import * as analytics from './providers/analytics'; import * as auth from './providers/auth'; - -import * as apps from './apps'; import * as crashlytics from './providers/crashlytics'; import * as database from './providers/database'; import * as firestore from './providers/firestore'; @@ -32,6 +30,8 @@ import * as https from './providers/https'; import * as pubsub from './providers/pubsub'; import * as remoteConfig from './providers/remoteConfig'; import * as storage from './providers/storage'; + +import * as apps from './apps'; import { handler } from './handler-builder'; import { setup } from './setup'; @@ -52,8 +52,9 @@ export { }; // Exported root types: -export * from './config'; export * from './cloud-functions'; +export * from './config'; export * from './function-builder'; +export * from './function-configuration'; setup(); diff --git a/src/providers/analytics.ts b/src/providers/analytics.ts index 3287264b3..012e5aebf 100644 --- a/src/providers/analytics.ts +++ b/src/providers/analytics.ts @@ -23,12 +23,12 @@ import * as _ from 'lodash'; import { - makeCloudFunction, CloudFunction, Event, EventContext, + makeCloudFunction, } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.analytics'; diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 95747ede5..6dc081eae 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -20,15 +20,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import * as firebase from 'firebase-admin'; +import * as _ from 'lodash'; import { - makeCloudFunction, CloudFunction, - EventContext, Event, + EventContext, + makeCloudFunction, } from '../cloud-functions'; -import * as firebase from 'firebase-admin'; -import * as _ from 'lodash'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.auth'; diff --git a/src/providers/crashlytics.ts b/src/providers/crashlytics.ts index 2ad1db6b3..eca79da68 100644 --- a/src/providers/crashlytics.ts +++ b/src/providers/crashlytics.ts @@ -21,11 +21,11 @@ // SOFTWARE. import { - makeCloudFunction, CloudFunction, EventContext, + makeCloudFunction, } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.crashlytics'; diff --git a/src/providers/database.ts b/src/providers/database.ts index 4b0aaf938..bffb492aa 100644 --- a/src/providers/database.ts +++ b/src/providers/database.ts @@ -32,7 +32,7 @@ import { import { normalizePath, applyChange, pathParts, joinPath } from '../utils'; import * as firebase from 'firebase-admin'; import { firebaseConfig } from '../config'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.database'; diff --git a/src/providers/firestore.ts b/src/providers/firestore.ts index 20dee281f..c322e85a1 100644 --- a/src/providers/firestore.ts +++ b/src/providers/firestore.ts @@ -20,19 +20,19 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import { posix } from 'path'; -import * as _ from 'lodash'; import * as firebase from 'firebase-admin'; +import * as _ from 'lodash'; +import { posix } from 'path'; import { apps } from '../apps'; import { - makeCloudFunction, - CloudFunction, Change, + CloudFunction, Event, EventContext, + makeCloudFunction, } from '../cloud-functions'; import { dateToTimestampProto } from '../encoder'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firestore'; diff --git a/src/providers/https.ts b/src/providers/https.ts index 315ce0768..ff58e5532 100644 --- a/src/providers/https.ts +++ b/src/providers/https.ts @@ -20,13 +20,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +import * as cors from 'cors'; import * as express from 'express'; import * as firebase from 'firebase-admin'; import * as _ from 'lodash'; -import * as cors from 'cors'; import { apps } from '../apps'; import { HttpsFunction, optsToTrigger, Runnable } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** * diff --git a/src/providers/pubsub.ts b/src/providers/pubsub.ts index 194ecda9c..6de173397 100644 --- a/src/providers/pubsub.ts +++ b/src/providers/pubsub.ts @@ -22,12 +22,14 @@ import { CloudFunction, - makeCloudFunction, EventContext, + makeCloudFunction, +} from '../cloud-functions'; +import { + DeploymentOptions, Schedule, ScheduleRetryConfig, -} from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +} from '../function-configuration'; /** @internal */ export const provider = 'google.pubsub'; @@ -91,7 +93,7 @@ export class ScheduleBuilder { contextOnlyHandler: handler, provider, service, - triggerResource: triggerResource, + triggerResource, eventType: 'topic.publish', opts: this._opts, labels: { 'deployment-scheduled': 'true' }, diff --git a/src/providers/remoteConfig.ts b/src/providers/remoteConfig.ts index 00fb8b877..60d5345d6 100644 --- a/src/providers/remoteConfig.ts +++ b/src/providers/remoteConfig.ts @@ -28,7 +28,7 @@ import { EventContext, makeCloudFunction, } from '../cloud-functions'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.firebase.remoteconfig'; diff --git a/src/providers/storage.ts b/src/providers/storage.ts index eb7821b99..436ffdce6 100644 --- a/src/providers/storage.ts +++ b/src/providers/storage.ts @@ -26,7 +26,7 @@ import { makeCloudFunction, } from '../cloud-functions'; import { firebaseConfig } from '../config'; -import { DeploymentOptions } from '../function-builder'; +import { DeploymentOptions } from '../function-configuration'; /** @internal */ export const provider = 'google.storage'; From f28e5bfc5eb67b9ee9a3c2b788121ae2b7d4ef1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 21:38:01 +0200 Subject: [PATCH 14/36] Add comments to Schedule and ScheduleRetryConfig --- src/function-configuration.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 85e74f603..70cff0828 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -32,6 +32,9 @@ export const VALID_MEMORY_OPTIONS = [ '2GB', ] as const; +/** + * Scheduler retry options. Applies only to scheduled functions. + */ export interface ScheduleRetryConfig { retryCount?: number; maxRetryDuration?: string; @@ -40,6 +43,9 @@ export interface ScheduleRetryConfig { maxDoublings?: number; } +/** + * Configuration options for scheduled functions. + */ export interface Schedule { schedule: string; timeZone?: string; From 22b6d0b674ddcfac969f8eb337a2afcad1975897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 21:40:43 +0200 Subject: [PATCH 15/36] Stricten regions definition --- src/function-builder.ts | 4 +++- src/function-configuration.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 082d5e2e9..49e8f330a 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -90,7 +90,9 @@ function assertRegionsAreValid(regions: string[]): boolean { * @param regions One of more region strings. * For example: `functions.region('us-east1')` or `functions.region('us-east1', 'us-central1')` */ -export function region(...regions: string[]): FunctionBuilder { +export function region( + ...regions: Array +): FunctionBuilder { if (assertRegionsAreValid(regions)) { return new FunctionBuilder({ regions }); } diff --git a/src/function-configuration.ts b/src/function-configuration.ts index 70cff0828..92ef2d3ff 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -9,7 +9,7 @@ export const SUPPORTED_REGIONS = [ 'europe-west2', 'asia-east2', 'asia-northeast1', -]; +] as const; /** * Cloud Functions min timeout value. From 85d13296149be2301394cac652e3747c4893e2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 21:45:09 +0200 Subject: [PATCH 16/36] Fix tests broken due to strict typings --- spec/function-builder.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 04575e28b..b4f71e718 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -165,21 +165,21 @@ describe('FunctionBuilder', () => { it('should throw an error if user chooses an invalid region', () => { expect(() => { - return functions.region('unsupported'); + return functions.region('unsupported' as any); }).to.throw(Error, 'region'); expect(() => { - return functions.region('unsupported').runWith({ + return functions.region('unsupported' as any).runWith({ timeoutSeconds: 500, } as any); }).to.throw(Error, 'region'); expect(() => { - return functions.region('unsupported', 'us-east1'); + return functions.region('unsupported' as any, 'us-east1'); }).to.throw(Error, 'region'); expect(() => { - return functions.region('unsupported', 'us-east1').runWith({ + return functions.region('unsupported' as any, 'us-east1').runWith({ timeoutSeconds: 500, } as any); }).to.throw(Error, 'region'); From 3ee8a0a41c7464a8da21bc12f41be6cb0cb3c38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 22:10:49 +0200 Subject: [PATCH 17/36] More strict types for regions - reverse order --- spec/function-builder.spec.ts | 4 ++-- src/function-builder.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index b4f71e718..7f3c032d3 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -60,7 +60,7 @@ describe('FunctionBuilder', () => { 'europe-west1', 'europe-west2', 'asia-east2', - 'asia-northeast1' + 'asia-northeast1', ) .auth.user() .onCreate((user) => user); @@ -123,7 +123,7 @@ describe('FunctionBuilder', () => { expect(() => { functions .runWith({ timeoutSeconds: 90, memory: '256MB' }) - .region('unsupported'); + .region('unsupported' as any); }).to.throw(Error, 'region'); }); diff --git a/src/function-builder.ts b/src/function-builder.ts index 49e8f330a..28d9240c1 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -119,7 +119,7 @@ export class FunctionBuilder { * @param regions One or more region strings. * For example: `functions.region('us-east1')` or `functions.region('us-east1', 'us-central1')` */ - region(...regions: string[]): FunctionBuilder { + region(...regions: Array): FunctionBuilder { if (assertRegionsAreValid(regions)) { this.options.regions = regions; return this; From 0cbe15aa91b052ba7572fba1823970d59a54b29c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 3 Jul 2019 22:16:28 +0200 Subject: [PATCH 18/36] Reformat --- spec/function-builder.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 7f3c032d3..b0c4d4c28 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -60,7 +60,7 @@ describe('FunctionBuilder', () => { 'europe-west1', 'europe-west2', 'asia-east2', - 'asia-northeast1', + 'asia-northeast1' ) .auth.user() .onCreate((user) => user); From a95e7ded5f64ff3a0d3ccaa6a10812cbe713b1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Thu, 4 Jul 2019 01:23:46 +0200 Subject: [PATCH 19/36] Remove unused import --- src/function-builder.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 3b13f0dbf..fbe356c3a 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -29,7 +29,6 @@ import { isEmpty, isObjectLike, } from 'lodash'; -import * as _ from 'lodash'; import { CloudFunction, EventContext } from './cloud-functions'; import { From b1c673770e62fd49db52ed7c3fc5b5003e496a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 10 Jul 2019 20:18:42 +0200 Subject: [PATCH 20/36] Conform with npm formatting --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 916c4159f..4c66182ef 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ }, "license": "MIT", "author": "Firebase Team", - "files": ["lib"], + "files": [ + "lib" + ], "main": "lib/index.js", "types": "lib/index.d.ts", "scripts": { From b2c9b7fb3260a4ce327adaba0d536e482ca38215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 10 Jul 2019 20:20:57 +0200 Subject: [PATCH 21/36] Reintroduce unused variable to minimize diff size --- integration_test/functions/src/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/integration_test/functions/src/index.ts b/integration_test/functions/src/index.ts index c97703140..967b3d376 100644 --- a/integration_test/functions/src/index.ts +++ b/integration_test/functions/src/index.ts @@ -4,6 +4,9 @@ import * as admin from 'firebase-admin'; import { Request, Response } from 'express'; import * as fs from 'fs'; +import * as PubSub from '@google-cloud/pubsub'; +const pubsub = PubSub(); + export * from './pubsub-tests'; export * from './database-tests'; export * from './auth-tests'; From 3a0dd5a4ba4780379241374ba5c9b00376f259a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 10 Jul 2019 20:26:56 +0200 Subject: [PATCH 22/36] Import lodash using _ exclusively --- src/function-builder.ts | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 797817ac8..833ac6325 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -21,14 +21,7 @@ // SOFTWARE. import * as express from 'express'; -import { - assign, - difference, - includes, - isBoolean, - isEmpty, - isObjectLike, -} from 'lodash'; +import * as _ from 'lodash'; import { CloudFunction, EventContext } from './cloud-functions'; import { @@ -56,7 +49,7 @@ import * as storage from './providers/storage'; * valid. */ function assertRuntimeOptionsValidity(runtimeOptions: RuntimeOptions): void { - if (isObjectLike(runtimeOptions) === false) { + if (_.isObjectLike(runtimeOptions) === false) { throw new Error('RuntimeOptions must be an object.'); } @@ -64,8 +57,8 @@ function assertRuntimeOptionsValidity(runtimeOptions: RuntimeOptions): void { if (failurePolicy !== undefined) { if ( - isBoolean(failurePolicy) === false && - isObjectLike(failurePolicy) === false + _.isBoolean(failurePolicy) === false && + _.isObjectLike(failurePolicy) === false ) { throw new Error( `RuntimeOptions.failurePolicy must be a boolean or an object.` @@ -74,8 +67,8 @@ function assertRuntimeOptionsValidity(runtimeOptions: RuntimeOptions): void { if (typeof failurePolicy === 'object') { if ( - isObjectLike(failurePolicy.retry) === false || - isEmpty(failurePolicy.retry) === false + _.isObjectLike(failurePolicy.retry) === false || + _.isEmpty(failurePolicy.retry) === false ) { throw new Error( 'RuntimeOptions.failurePolicy.retry must be an empty object.' @@ -85,7 +78,7 @@ function assertRuntimeOptionsValidity(runtimeOptions: RuntimeOptions): void { } if (memory !== undefined) { - if (includes(VALID_MEMORY_OPTIONS, memory) === false) { + if (_.includes(VALID_MEMORY_OPTIONS, memory) === false) { throw new Error( `RuntimeOptions.memory must be one of: ${VALID_MEMORY_OPTIONS.join( ', ' @@ -120,7 +113,7 @@ function assertRegionsValidity(regions: string[]): void { throw new Error('You must specify at least one region.'); } - if (difference(regions, SUPPORTED_REGIONS).length !== 0) { + if (_.difference(regions, SUPPORTED_REGIONS).length !== 0) { throw new Error( `The only valid regions are: ${SUPPORTED_REGIONS.join(', ')},` ); @@ -191,7 +184,7 @@ export class FunctionBuilder { runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { assertRuntimeOptionsValidity(runtimeOptions); - this.options = assign(this.options, runtimeOptions); + this.options = _.assign(this.options, runtimeOptions); return this; } From 85eb8a0af6291d0dc3b0109497a41ba9a458fca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 10 Jul 2019 20:30:35 +0200 Subject: [PATCH 23/36] Kepp @hidden tags in one line if no other JSDoc is present --- src/cloud-functions.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 42898e3a5..203a1a07e 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -152,9 +152,7 @@ export namespace Change { ); } - /** - * @hidden - */ + /** @hidden */ export function applyFieldMask( sparseBefore: any, after: any, @@ -234,9 +232,7 @@ export type CloudFunction = Runnable & TriggerAnnotated & ((input: any, context?: any) => PromiseLike | any); -/** - * @hidden - */ +/** @hidden */ export interface MakeCloudFunctionArgs { after?: (raw: Event) => void; before?: (raw: Event) => void; @@ -290,9 +286,7 @@ export function optionsToTrigger({ }; } -/** - * @hidden - */ +/** @hidden */ export function makeCloudFunction({ after = () => {}, before = () => {}, From 1c42e0ec766d13351072d560d82b799f08311ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 10 Jul 2019 20:32:19 +0200 Subject: [PATCH 24/36] Fix a typo - sentence ending with a comma --- src/function-builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 833ac6325..636da844d 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -115,7 +115,7 @@ function assertRegionsValidity(regions: string[]): void { if (_.difference(regions, SUPPORTED_REGIONS).length !== 0) { throw new Error( - `The only valid regions are: ${SUPPORTED_REGIONS.join(', ')},` + `The only valid regions are: ${SUPPORTED_REGIONS.join(', ')}.` ); } } From 4cb2f284a14989d1d5be15b4fc75580eb6afa90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Tue, 16 Jul 2019 21:47:23 +0200 Subject: [PATCH 25/36] Wrap JSDoc as specified in Google JavaScript Style Guide --- src/function-builder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 636da844d..74d5400d8 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -46,7 +46,7 @@ import * as storage from './providers/storage'; * Assert that the runtime options passed in are valid. * @param runtimeOptions object containing memory and timeout information. * @throws { Error } FailurePolicy, Memory and TimeoutSeconds values must be - * valid. + * valid. */ function assertRuntimeOptionsValidity(runtimeOptions: RuntimeOptions): void { if (_.isObjectLike(runtimeOptions) === false) { @@ -208,7 +208,7 @@ export class FunctionBuilder { /** * Declares a callable method for clients to call using a Firebase SDK. * @param handler A method that takes a data and context and returns - * a value. + * a value. */ onCall: ( handler: ( From cd6b147b9b50fa7d74dcf72cb3e4735b9192b6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 17 Jul 2019 11:31:04 +0200 Subject: [PATCH 26/36] Move memory lookup table to functions-configuration and stricten type definition --- src/cloud-functions.ts | 11 ++--------- src/function-configuration.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 840e1a4f4..68cfd63ea 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -25,6 +25,7 @@ import * as _ from 'lodash'; import { DeploymentOptions, FailurePolicy, + MEMORY_LOOKUP, Schedule, } from './function-configuration'; export { Request, Response }; @@ -332,14 +333,6 @@ export function optionsToTrigger({ retry: {}, }; - const memoryLookup = { - '128MB': 128, - '256MB': 256, - '512MB': 512, - '1GB': 1024, - '2GB': 2048, - }; - return { ...(failurePolicy === undefined || failurePolicy === false ? {} @@ -348,7 +341,7 @@ export function optionsToTrigger({ : { failurePolicy }), ...(memory === undefined ? {} - : { availableMemoryMb: memoryLookup[memory] }), + : { availableMemoryMb: MEMORY_LOOKUP[memory] }), ...(regions === undefined ? {} : { regions }), ...(schedule === undefined ? {} : { schedule }), ...(timeoutSeconds === undefined ? {} : { timeout: `${timeoutSeconds}s` }), diff --git a/src/function-configuration.ts b/src/function-configuration.ts index d581ef4a1..a5a2d0d7f 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -32,6 +32,19 @@ export const VALID_MEMORY_OPTIONS = [ '2GB', ] as const; +/** + * A mapping of memory options to its representation in the Cloud Functions API. + */ +export const MEMORY_LOOKUP: { + [Name in typeof VALID_MEMORY_OPTIONS[number]]: number; +} = { + '128MB': 128, + '256MB': 256, + '512MB': 512, + '1GB': 1024, + '2GB': 2048, +}; + /** * Scheduler retry options. Applies only to scheduled functions. */ From 06002e180185b63ba6d1a6133cfec27d5825d501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 17 Jul 2019 11:32:24 +0200 Subject: [PATCH 27/36] Move default failure policy to functions-configuration --- src/cloud-functions.ts | 7 ++----- src/function-configuration.ts | 4 ++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index 68cfd63ea..da3f368f9 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -23,6 +23,7 @@ import { Request, Response } from 'express'; import * as _ from 'lodash'; import { + DEFAULT_FAILURE_POLICY, DeploymentOptions, FailurePolicy, MEMORY_LOOKUP, @@ -329,15 +330,11 @@ export function optionsToTrigger({ schedule, timeoutSeconds, }: DeploymentOptions): TriggerAnnotated['__trigger'] { - const defaultFailurePolicy: FailurePolicy = { - retry: {}, - }; - return { ...(failurePolicy === undefined || failurePolicy === false ? {} : failurePolicy === true - ? { failurePolicy: defaultFailurePolicy } + ? { failurePolicy: DEFAULT_FAILURE_POLICY } : { failurePolicy }), ...(memory === undefined ? {} diff --git a/src/function-configuration.ts b/src/function-configuration.ts index a5a2d0d7f..d337cb415 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -69,6 +69,10 @@ export interface FailurePolicy { retry: {}; } +export const DEFAULT_FAILURE_POLICY: FailurePolicy = { + retry: {}, +}; + export interface RuntimeOptions { /** * Failure policy of the function, boolean `true` is equivalent to providing From 7b56f21194a9415b3158713e97c74335ecae3bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 17 Jul 2019 11:47:46 +0200 Subject: [PATCH 28/36] Separate standarization of options from construction of the options object --- src/cloud-functions.ts | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/cloud-functions.ts b/src/cloud-functions.ts index da3f368f9..b5be08a0e 100644 --- a/src/cloud-functions.ts +++ b/src/cloud-functions.ts @@ -324,24 +324,35 @@ export interface MakeCloudFunctionArgs { /** @hidden */ export function optionsToTrigger({ - failurePolicy, + failurePolicy: failurePolicyOrAlias, memory, regions, schedule, timeoutSeconds, }: DeploymentOptions): TriggerAnnotated['__trigger'] { + /* + * FailurePolicy can be aliased with a boolean value in the public API. + * Convert aliases `true` and `false` to a standardized interface. + */ + const failurePolicy: FailurePolicy | undefined = + failurePolicyOrAlias === false + ? undefined + : failurePolicyOrAlias === true + ? DEFAULT_FAILURE_POLICY + : failurePolicyOrAlias; + + const availableMemoryMb: number | undefined = + memory === undefined ? undefined : MEMORY_LOOKUP[memory]; + + const timeout: string | undefined = + timeoutSeconds === undefined ? undefined : `${timeoutSeconds}s`; + return { - ...(failurePolicy === undefined || failurePolicy === false - ? {} - : failurePolicy === true - ? { failurePolicy: DEFAULT_FAILURE_POLICY } - : { failurePolicy }), - ...(memory === undefined - ? {} - : { availableMemoryMb: MEMORY_LOOKUP[memory] }), + ...(failurePolicy === undefined ? {} : { failurePolicy }), + ...(availableMemoryMb === undefined ? {} : { availableMemoryMb }), ...(regions === undefined ? {} : { regions }), ...(schedule === undefined ? {} : { schedule }), - ...(timeoutSeconds === undefined ? {} : { timeout: `${timeoutSeconds}s` }), + ...(timeout === undefined ? {} : { timeout }), }; } From 3a7c8512a60996b0848472b467d5d2a80f8e3ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 17 Jul 2019 11:59:52 +0200 Subject: [PATCH 29/36] Rephrase failure policy description --- src/function-builder.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 74d5400d8..551f38050 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -139,8 +139,8 @@ export function region( /** * Configure runtime options for the function. * @param runtimeOptions Object with three optional fields: - * 1. failurePolicy: failure policy policy of the function, boolean `true` is - * equivalent to providing an empty policy. + * 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, possible values * are: '128MB', '256MB', '512MB', '1GB', and '2GB'. * 3. timeoutSeconds: timeout for the function in seconds, possible values are @@ -174,8 +174,8 @@ export class FunctionBuilder { /** * Configure runtime options for the function. * @param runtimeOptions Object with three optional fields: - * 1. failurePolicy: failure policy policy of the function, boolean `true` is - * equivalent to providing an empty policy. + * 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, possible values * are: '128MB', '256MB', '512MB', '1GB', and '2GB'. * 3. timeoutSeconds: timeout for the function in seconds, possible values are From 2e35c26784bb91ca1ee513816cdec440598349be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 17 Jul 2019 12:02:50 +0200 Subject: [PATCH 30/36] Rephrase description of memory and timeoutSeconds options --- src/function-builder.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index 551f38050..e6540d0c5 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -141,10 +141,10 @@ export function region( * @param runtimeOptions Object with three optional fields: * 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, possible values - * are: '128MB', '256MB', '512MB', '1GB', and '2GB'. - * 3. timeoutSeconds: timeout for the function in seconds, possible values are - * 0 to 540. + * 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. */ export function runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { assertRuntimeOptionsValidity(runtimeOptions); @@ -176,10 +176,10 @@ export class FunctionBuilder { * @param runtimeOptions Object with three optional fields: * 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, possible values - * are: '128MB', '256MB', '512MB', '1GB', and '2GB'. - * 3. timeoutSeconds: timeout for the function in seconds, possible values are - * 0 to 540. + * 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. */ runWith(runtimeOptions: RuntimeOptions): FunctionBuilder { assertRuntimeOptionsValidity(runtimeOptions); From 4059b00a73e8136c7dfbb51e234b0495d784c31d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 17 Jul 2019 12:05:45 +0200 Subject: [PATCH 31/36] Simplify tests for invalid failure policies --- spec/function-builder.spec.ts | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 2294c74ca..600a36ac6 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -150,7 +150,7 @@ describe('FunctionBuilder', () => { }).to.throw(Error, 'RuntimeOptions.timeoutSeconds'); }); - it('should throw an error if user chooses a failurePolicy of type number', () => { + 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 boolean, @@ -158,15 +158,7 @@ describe('FunctionBuilder', () => { ).to.throw(Error, 'RuntimeOptions.failurePolicy'); }); - it('should throw an error if user chooses a failurePolicy of type string', () => { - expect(() => - functions.runWith({ - failurePolicy: ('string-value' as unknown) as boolean, - }) - ).to.throw(Error, 'RuntimeOptions.failurePolicy'); - }); - - it('should throw an error if user chooses a failurePolicy.retry of type number', () => { + 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 }, @@ -174,14 +166,6 @@ describe('FunctionBuilder', () => { ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); }); - it('should throw an error if user chooses a failurePolicy.retry of type string', () => { - expect(() => - functions.runWith({ - failurePolicy: { retry: ('string-value' as unknown) as object }, - }) - ).to.throw(Error, 'RuntimeOptions.failurePolicy.retry'); - }); - it('should throw an error if user chooses an invalid memory allocation', () => { expect(() => { return functions.runWith({ From 2b95df94ceec3eb433a24eac10da36b7af45f039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Wed, 17 Jul 2019 12:12:45 +0200 Subject: [PATCH 32/36] Align public description of failure policies with a documentation of its interface --- src/function-configuration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/function-configuration.ts b/src/function-configuration.ts index d337cb415..cd3cdde33 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -75,8 +75,8 @@ export const DEFAULT_FAILURE_POLICY: FailurePolicy = { export interface RuntimeOptions { /** - * Failure policy of the function, boolean `true` is equivalent to providing - * an empty policy. + * Failure policy of the function, with boolean true being equivalent to + * providing an empty retry object. */ failurePolicy?: FailurePolicy | boolean; /** From 31952e2b4ef28b7535d0230e4568d59c8822bb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Thu, 18 Jul 2019 21:10:04 +0200 Subject: [PATCH 33/36] Add a missing dot in the Changelog --- changelog.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 17fd4acfe..2fa096563 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,2 +1,2 @@ -fixed - Upgrade lodash dependency to resolve security vulnerability CVE-2019-10744 -feature - Allow specifying retry policies for event triggered functions. \ No newline at end of file +fixed - Upgrade lodash dependency to resolve security vulnerability CVE-2019-10744. +feature - Allow specifying retry policies for event triggered functions. From 668c02fe4b9fbff28136c79e36959c1df4d23e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Thu, 18 Jul 2019 21:12:43 +0200 Subject: [PATCH 34/36] Minor stylistic changes to the documentation --- src/function-builder.ts | 8 ++++++-- src/function-configuration.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/function-builder.ts b/src/function-builder.ts index e6540d0c5..a3146c9a8 100644 --- a/src/function-builder.ts +++ b/src/function-builder.ts @@ -139,12 +139,14 @@ export function region( /** * Configure runtime options for the function. * @param runtimeOptions Object with three optional fields: - * 1. failurePolicy: failure policy of the function, with boolean true being + * 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 { assertRuntimeOptionsValidity(runtimeOptions); @@ -174,12 +176,14 @@ export class FunctionBuilder { /** * Configure runtime options for the function. * @param runtimeOptions Object with three optional fields: - * 1. failurePolicy: failure policy of the function, with boolean true being + * 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 { assertRuntimeOptionsValidity(runtimeOptions); diff --git a/src/function-configuration.ts b/src/function-configuration.ts index cd3cdde33..f3883155b 100644 --- a/src/function-configuration.ts +++ b/src/function-configuration.ts @@ -75,7 +75,7 @@ export const DEFAULT_FAILURE_POLICY: FailurePolicy = { export interface RuntimeOptions { /** - * Failure policy of the function, with boolean true being equivalent to + * Failure policy of the function, with boolean `true` being equivalent to * providing an empty retry object. */ failurePolicy?: FailurePolicy | boolean; From ac09c189a28b5514f3b78d7eff7b32368e46a469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Fri, 19 Jul 2019 09:45:00 +0200 Subject: [PATCH 35/36] Make a test case more understandable --- spec/function-builder.spec.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/function-builder.spec.ts b/spec/function-builder.spec.ts index 600a36ac6..1ba12c7fa 100644 --- a/spec/function-builder.spec.ts +++ b/spec/function-builder.spec.ts @@ -153,9 +153,12 @@ describe('FunctionBuilder', () => { 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 boolean, + failurePolicy: (1234 as unknown) as functions.RuntimeOptions['failurePolicy'], }) - ).to.throw(Error, 'RuntimeOptions.failurePolicy'); + ).to.throw( + Error, + 'RuntimeOptions.failurePolicy must be a boolean or an object' + ); }); it('should throw an error if user chooses a failurePolicy.retry which is not an object', () => { From 1b6281fb8f3d9abe9ff7d7b6d2532441d8c2b602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natan=20S=C4=85gol?= Date: Mon, 25 Nov 2019 23:28:37 +0100 Subject: [PATCH 36/36] Reformat --- spec/providers/auth.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/providers/auth.spec.ts b/spec/providers/auth.spec.ts index 769bc03bf..bb2ab7761 100644 --- a/spec/providers/auth.spec.ts +++ b/spec/providers/auth.spec.ts @@ -197,9 +197,7 @@ describe('Auth Functions', () => { }); describe('#onDelete', () => { - const cloudFunctionDelete: CloudFunction< - firebase.auth.UserRecord - > = functions.handler.auth.user.onDelete( + const cloudFunctionDelete: CloudFunction = functions.handler.auth.user.onDelete( (data: firebase.auth.UserRecord) => data );