From 9cc7fd9656ad8e77f78c96330af25493f0046156 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Wed, 19 Jan 2022 15:00:15 -0800 Subject: [PATCH 1/6] adding in app distro changes --- CHANGELOG.md | 1 + .../providers/alerts/appDistribution.spec.ts | 127 ++++++++++++++++++ src/v2/providers/alerts/appDistribution.ts | 103 ++++++++++++++ v2/alerts/appDistribution.js | 26 ++++ 4 files changed, 257 insertions(+) create mode 100644 spec/v2/providers/alerts/appDistribution.spec.ts create mode 100644 src/v2/providers/alerts/appDistribution.ts create mode 100644 v2/alerts/appDistribution.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d6230214..7a80dea4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,2 +1,3 @@ - Parallelizes network calls that occur when validating authorization for onCall handlers. - Adds new regions to V2 API +- Adds new provider for alerts diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts new file mode 100644 index 000000000..947de97b6 --- /dev/null +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -0,0 +1,127 @@ +import { expect } from 'chai'; +import * as alerts from '../../../../src/v2/providers/alerts'; +import * as appDistribution from '../../../../src/v2/providers/alerts/appDistribution'; +import { BASIC_ENDPOINT, BASIC_OPTIONS } from '../helpers'; + +const APPID = '123456789'; +const myHandler = () => 42; + +describe('appDistribution', () => { + describe('onNewTesterIosDevicePublished', () => { + it('should create a function with alertType & appId', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + APPID, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function with base opts', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + { ...BASIC_OPTIONS }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...BASIC_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with appid in opts', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + { ...BASIC_OPTIONS, appId: APPID }, + myHandler + ); + + expect(func.__endpoint).to.deep.equal({ + ...BASIC_ENDPOINT, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + appId: APPID, + }, + retry: false, + }, + }); + }); + + it('should create a function without opts or appId', () => { + const func = appDistribution.onNewTesterIosDevicePublished(myHandler); + + expect(func.__endpoint).to.deep.equal({ + platform: 'gcfv2', + labels: {}, + eventTrigger: { + eventType: alerts.eventType, + eventFilters: { + alertType: appDistribution.newTesterIosDeviceAlert, + }, + retry: false, + }, + }); + }); + + it('should create a function with a run method', () => { + const func = appDistribution.onNewTesterIosDevicePublished( + APPID, + (event) => event + ); + + const res = func.run('input' as any); + + expect(res).to.equal('input'); + }); + }); + + describe('getOptsAndApp', () => { + it('should parse a string', () => { + const [opts, appId] = appDistribution.getOptsAndApp(APPID); + + expect(opts).to.deep.equal({}); + expect(appId).to.equal(APPID); + }); + + it('should parse an options object without appId', () => { + const myOpts: appDistribution.AppDistributionOptions = { + region: 'us-west1', + }; + + const [opts, appId] = appDistribution.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.be.undefined; + }); + + it('should parse an options object with appId', () => { + const myOpts: appDistribution.AppDistributionOptions = { + appId: APPID, + region: 'us-west1', + }; + + const [opts, appId] = appDistribution.getOptsAndApp(myOpts); + + expect(opts).to.deep.equal({ region: 'us-west1' }); + expect(appId).to.equal(APPID); + }); + }); +}); diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts new file mode 100644 index 000000000..6e077a1ae --- /dev/null +++ b/src/v2/providers/alerts/appDistribution.ts @@ -0,0 +1,103 @@ +import { defineEndpoint, FirebaseAlertData } from '.'; +import { ManifestEndpoint } from '../../../common/manifest'; +import { CloudEvent, CloudFunction } from '../../core'; +import * as options from '../../options'; + +/** Data */ +export interface NewTesterDevicePayload { + ['@type']: 'com.google.firebase.firebasealerts.NewTesterDevicePayload'; + testerName: string; + testerEmail: string; + testerDeviceModelName: string; + testerDeviceIdentifier: string; +} + +/** Events */ +/** @internal */ +export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; + +/** Options */ +export interface AppDistributionOptions extends options.EventHandlerOptions { + appId?: string; // optional +} + +/** Cloud Event Type */ +interface WithAlertTypeAndApp { + alertType: string; // required in the payload + appId: string; // required in the payload +} +export type AppDistributionEvent = CloudEvent< + FirebaseAlertData, + WithAlertTypeAndApp +>; + +/** Handlers */ +export function onNewTesterIosDevicePublished( + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; +export function onNewTesterIosDevicePublished( + appId: string, + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; +export function onNewTesterIosDevicePublished( + opts: AppDistributionOptions, + handler: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction>; +export function onNewTesterIosDevicePublished( + appIdOrOptsOrHandler: + | string + | AppDistributionOptions + | (( + event: AppDistributionEvent + ) => any | Promise), + handler?: ( + event: AppDistributionEvent + ) => any | Promise +): CloudFunction> { + if (typeof appIdOrOptsOrHandler === 'function') { + handler = appIdOrOptsOrHandler as ( + event: AppDistributionEvent + ) => any | Promise; + appIdOrOptsOrHandler = {}; + } + + const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler); + + const func = (raw: CloudEvent) => { + return handler(raw as AppDistributionEvent); + }; + + func.run = handler; + + // TypeScript doesn't recognize defineProperty as adding a property and complains + // that __endpoint doesn't exist. We can either cast to any and lose all type safety + // or we can just assign a meaningless value before calling defineProperty. + func.__trigger = 'silence the transpiler'; + func.__endpoint = {} as ManifestEndpoint; + defineEndpoint(func, opts, newTesterIosDeviceAlert, appId); + + return func; +} + +/** @internal */ +export function getOptsAndApp( + appIdOrOpts: string | AppDistributionOptions +): [options.EventHandlerOptions, string | undefined] { + let opts: options.EventHandlerOptions; + let appId: string | undefined; + if (typeof appIdOrOpts === 'string') { + opts = {}; + appId = appIdOrOpts; + } else { + appId = appIdOrOpts.appId; + opts = { ...appIdOrOpts }; + delete (opts as any).appId; + } + return [opts, appId]; +} diff --git a/v2/alerts/appDistribution.js b/v2/alerts/appDistribution.js new file mode 100644 index 000000000..7d725acc3 --- /dev/null +++ b/v2/alerts/appDistribution.js @@ -0,0 +1,26 @@ +// The MIT License (MIT) +// +// Copyright (c) 2021 Firebase +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This file is not part of the firebase-functions SDK. It is used to silence the +// imports eslint plugin until it can understand import paths defined by node +// package exports. +// For more information, see github.com/import-js/eslint-plugin-import/issues/1810 From b86466789e755f9a32fff42c58775338c5fb7f61 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 20 Jan 2022 07:38:15 -0800 Subject: [PATCH 2/6] removing comments & addings package refs --- package.json | 6 +++++- src/v2/providers/alerts/appDistribution.ts | 7 +++---- src/v2/providers/alerts/index.ts | 4 ++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d19a8c5cf..2add3d3d2 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,8 @@ "./v2/params": "./lib/v2/params/index.js", "./v2/pubsub": "./lib/v2/providers/pubsub.js", "./v2/storage": "./lib/v2/providers/storage.js", - "./v2/alerts": "./lib/v2/providers/alerts/index.js" + "./v2/alerts": "./lib/v2/providers/alerts/index.js", + "./v2/alerts/appDistribution": "./lib/v2/providers/alerts/appDistribution.js" }, "typesVersions": { "*": { @@ -118,6 +119,9 @@ ], "v2/alerts": [ "lib/v2/providers/alerts" + ], + "v2/alerts/appDistribution": [ + "lib/v2/providers/alerts/appDistribution" ] } }, diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 6e077a1ae..61eef2f7f 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -12,19 +12,18 @@ export interface NewTesterDevicePayload { testerDeviceIdentifier: string; } -/** Events */ /** @internal */ export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; /** Options */ export interface AppDistributionOptions extends options.EventHandlerOptions { - appId?: string; // optional + appId?: string; } /** Cloud Event Type */ interface WithAlertTypeAndApp { - alertType: string; // required in the payload - appId: string; // required in the payload + alertType: string; + appId: string; } export type AppDistributionEvent = CloudEvent< FirebaseAlertData, diff --git a/src/v2/providers/alerts/index.ts b/src/v2/providers/alerts/index.ts index c40670cd1..e693be67e 100644 --- a/src/v2/providers/alerts/index.ts +++ b/src/v2/providers/alerts/index.ts @@ -1 +1,5 @@ +import * as appDistribution from './appDistribution'; + +export { appDistribution }; + export * from './alerts'; From ead25860adb32603f22c82bdbf9d18ff1aa706ab Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Thu, 20 Jan 2022 07:58:16 -0800 Subject: [PATCH 3/6] jsdoc comments --- src/v2/providers/alerts/appDistribution.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 61eef2f7f..c4d1fb2e7 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -4,6 +4,7 @@ import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; /** Data */ +/** Payload for a new tester device */ export interface NewTesterDevicePayload { ['@type']: 'com.google.firebase.firebasealerts.NewTesterDevicePayload'; testerName: string; @@ -31,6 +32,7 @@ export type AppDistributionEvent = CloudEvent< >; /** Handlers */ +/** Handle a new tester IOS device published */ export function onNewTesterIosDevicePublished( handler: ( event: AppDistributionEvent From 2ee682d415dbcbe1c4f6570a83910aaf98d6cae5 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Mon, 24 Jan 2022 14:21:42 -0800 Subject: [PATCH 4/6] fix comments --- .../providers/alerts/appDistribution.spec.ts | 12 ++--- src/v2/providers/alerts/appDistribution.ts | 48 ++++++++++--------- src/v2/providers/alerts/index.ts | 4 -- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/spec/v2/providers/alerts/appDistribution.spec.ts b/spec/v2/providers/alerts/appDistribution.spec.ts index 947de97b6..52fdfd3b9 100644 --- a/spec/v2/providers/alerts/appDistribution.spec.ts +++ b/spec/v2/providers/alerts/appDistribution.spec.ts @@ -1,7 +1,7 @@ import { expect } from 'chai'; import * as alerts from '../../../../src/v2/providers/alerts'; import * as appDistribution from '../../../../src/v2/providers/alerts/appDistribution'; -import { BASIC_ENDPOINT, BASIC_OPTIONS } from '../helpers'; +import { FULL_ENDPOINT, FULL_OPTIONS } from '../helpers'; const APPID = '123456789'; const myHandler = () => 42; @@ -28,14 +28,14 @@ describe('appDistribution', () => { }); }); - it('should create a function with base opts', () => { + it('should create a function with opts', () => { const func = appDistribution.onNewTesterIosDevicePublished( - { ...BASIC_OPTIONS }, + { ...FULL_OPTIONS }, myHandler ); expect(func.__endpoint).to.deep.equal({ - ...BASIC_ENDPOINT, + ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, eventFilters: { @@ -48,12 +48,12 @@ describe('appDistribution', () => { it('should create a function with appid in opts', () => { const func = appDistribution.onNewTesterIosDevicePublished( - { ...BASIC_OPTIONS, appId: APPID }, + { ...FULL_OPTIONS, appId: APPID }, myHandler ); expect(func.__endpoint).to.deep.equal({ - ...BASIC_ENDPOINT, + ...FULL_ENDPOINT, eventTrigger: { eventType: alerts.eventType, eventFilters: { diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index c4d1fb2e7..564b2ea4c 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -1,10 +1,10 @@ -import { defineEndpoint, FirebaseAlertData } from '.'; -import { ManifestEndpoint } from '../../../common/manifest'; +import { getEndpointAnnotation, FirebaseAlertData } from './alerts'; import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; -/** Data */ -/** Payload for a new tester device */ +/** + * The internal payload object that is wrapped inside a FirebaseAlertData object + */ export interface NewTesterDevicePayload { ['@type']: 'com.google.firebase.firebasealerts.NewTesterDevicePayload'; testerName: string; @@ -13,26 +13,31 @@ export interface NewTesterDevicePayload { testerDeviceIdentifier: string; } -/** @internal */ -export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; - -/** Options */ -export interface AppDistributionOptions extends options.EventHandlerOptions { - appId?: string; -} - -/** Cloud Event Type */ interface WithAlertTypeAndApp { alertType: string; appId: string; } +/** + * A custom CloudEvent for Firebase Alerts with custom extension attributes defined + */ export type AppDistributionEvent = CloudEvent< FirebaseAlertData, WithAlertTypeAndApp >; -/** Handlers */ -/** Handle a new tester IOS device published */ +/** @internal */ +export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; + +/** + * Configuration for app distribution functions + */ +export interface AppDistributionOptions extends options.EventHandlerOptions { + appId?: string; +} + +/** + * Declares a function that can handle adding a new tester iOS device + */ export function onNewTesterIosDevicePublished( handler: ( event: AppDistributionEvent @@ -75,18 +80,15 @@ export function onNewTesterIosDevicePublished( }; func.run = handler; - - // TypeScript doesn't recognize defineProperty as adding a property and complains - // that __endpoint doesn't exist. We can either cast to any and lose all type safety - // or we can just assign a meaningless value before calling defineProperty. - func.__trigger = 'silence the transpiler'; - func.__endpoint = {} as ManifestEndpoint; - defineEndpoint(func, opts, newTesterIosDeviceAlert, appId); + func.__endpoint = getEndpointAnnotation(opts, newTesterIosDeviceAlert, appId); return func; } -/** @internal */ +/** + * @internal + * Helper function to parse the function opts and appId + */ export function getOptsAndApp( appIdOrOpts: string | AppDistributionOptions ): [options.EventHandlerOptions, string | undefined] { diff --git a/src/v2/providers/alerts/index.ts b/src/v2/providers/alerts/index.ts index e693be67e..c40670cd1 100644 --- a/src/v2/providers/alerts/index.ts +++ b/src/v2/providers/alerts/index.ts @@ -1,5 +1 @@ -import * as appDistribution from './appDistribution'; - -export { appDistribution }; - export * from './alerts'; From 23399a75534a25db6402fb58ca44cd4bf0bb83bd Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Mon, 24 Jan 2022 18:40:01 -0800 Subject: [PATCH 5/6] adding periods to doc strings --- src/v2/providers/alerts/appDistribution.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 564b2ea4c..6341eb275 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -3,7 +3,7 @@ import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; /** - * The internal payload object that is wrapped inside a FirebaseAlertData object + * The internal payload object that is wrapped inside a FirebaseAlertData object. */ export interface NewTesterDevicePayload { ['@type']: 'com.google.firebase.firebasealerts.NewTesterDevicePayload'; @@ -18,7 +18,7 @@ interface WithAlertTypeAndApp { appId: string; } /** - * A custom CloudEvent for Firebase Alerts with custom extension attributes defined + * A custom CloudEvent for Firebase Alerts with custom extension attributes defined. */ export type AppDistributionEvent = CloudEvent< FirebaseAlertData, @@ -29,14 +29,14 @@ export type AppDistributionEvent = CloudEvent< export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice'; /** - * Configuration for app distribution functions + * Configuration for app distribution functions. */ export interface AppDistributionOptions extends options.EventHandlerOptions { appId?: string; } /** - * Declares a function that can handle adding a new tester iOS device + * Declares a function that can handle adding a new tester iOS device. */ export function onNewTesterIosDevicePublished( handler: ( @@ -87,7 +87,7 @@ export function onNewTesterIosDevicePublished( /** * @internal - * Helper function to parse the function opts and appId + * Helper function to parse the function opts and appId. */ export function getOptsAndApp( appIdOrOpts: string | AppDistributionOptions From 0bbe967884075ac4484f64e16f05b08c9af0e5e1 Mon Sep 17 00:00:00 2001 From: Cole Rogers Date: Mon, 24 Jan 2022 18:50:20 -0800 Subject: [PATCH 6/6] fix wording --- src/v2/providers/alerts/appDistribution.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/v2/providers/alerts/appDistribution.ts b/src/v2/providers/alerts/appDistribution.ts index 6341eb275..58b54e9b6 100644 --- a/src/v2/providers/alerts/appDistribution.ts +++ b/src/v2/providers/alerts/appDistribution.ts @@ -3,7 +3,8 @@ import { CloudEvent, CloudFunction } from '../../core'; import * as options from '../../options'; /** - * The internal payload object that is wrapped inside a FirebaseAlertData object. + * The internal payload object for adding a new tester device to app distribution. + * Payload is wrapped inside a FirebaseAlertData object. */ export interface NewTesterDevicePayload { ['@type']: 'com.google.firebase.firebasealerts.NewTesterDevicePayload'; @@ -18,7 +19,7 @@ interface WithAlertTypeAndApp { appId: string; } /** - * A custom CloudEvent for Firebase Alerts with custom extension attributes defined. + * A custom CloudEvent for Firebase Alerts (with custom extension attributes). */ export type AppDistributionEvent = CloudEvent< FirebaseAlertData,