Skip to content

Commit

Permalink
Added eventarc onCustomEventPublished API (#1061)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelgj committed Apr 6, 2022
1 parent 6fac2f0 commit c32db65
Show file tree
Hide file tree
Showing 5 changed files with 309 additions and 2 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
"./v2/alerts": "./lib/v2/providers/alerts/index.js",
"./v2/alerts/appDistribution": "./lib/v2/providers/alerts/appDistribution.js",
"./v2/alerts/billing": "./lib/v2/providers/alerts/billing.js",
"./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js"
"./v2/alerts/crashlytics": "./lib/v2/providers/alerts/crashlytics.js",
"./v2/eventarc": "./lib/v2/providers/eventarc.js"
},
"typesVersions": {
"*": {
Expand Down Expand Up @@ -124,6 +125,9 @@
"v2/base": [
"lib/v2/base"
],
"v2/eventarc": [
"lib/v2/providers/eventarc"
],
"v2/options": [
"lib/v2/options"
],
Expand Down
162 changes: 162 additions & 0 deletions spec/v2/providers/eventarc.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// The MIT License (MIT)
//
// Copyright (c) 2022 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.

import { expect } from 'chai';
import * as options from '../../../src/v2/options';
import * as eventarc from '../../../src/v2/providers/eventarc';
import { FULL_OPTIONS } from './fixtures';

const ENDPOINT_EVENT_TRIGGER = {
eventType: 'event-type',
retry: false,
eventFilters: {},
};

describe('v2/eventarc', () => {
describe('onCustomEventPublished', () => {
beforeEach(() => {
process.env.GCLOUD_PROJECT = 'aProject';
});

afterEach(() => {
options.setGlobalOptions({});
delete process.env.GCLOUD_PROJECT;
});

it('should create a minimal trigger/endpoint with default channel', () => {
const result = eventarc.onCustomEventPublished('event-type', () => 42);

expect(result.__endpoint).to.deep.equal({
platform: 'gcfv2',
labels: {},
eventTrigger: {
...ENDPOINT_EVENT_TRIGGER,
channel: 'locations/us-central1/channels/firebase',
},
});
});

it('should create a minimal trigger/endpoint with opts', () => {
const result = eventarc.onCustomEventPublished(
{ eventType: 'event-type', region: 'us-west1' },
() => 42
);

expect(result.__endpoint).to.deep.equal({
platform: 'gcfv2',
labels: {},
eventTrigger: {
...ENDPOINT_EVENT_TRIGGER,
channel: 'locations/us-central1/channels/firebase',
},
region: ['us-west1'],
});
});

it('should create a minimal trigger with channel with opts', () => {
const result = eventarc.onCustomEventPublished(
{
eventType: 'event-type',
channel: 'locations/us-west1/channels/my-channel',
filters: { foo: 'bar' },
},
() => 42
);

expect(result.__endpoint).to.deep.equal({
platform: 'gcfv2',
labels: {},
eventTrigger: {
...ENDPOINT_EVENT_TRIGGER,
channel: 'locations/us-west1/channels/my-channel',
eventFilters: {
foo: 'bar',
},
},
});
});

it('should create a complex trigger/endpoint with appropriate values', () => {
const result = eventarc.onCustomEventPublished(
{
...FULL_OPTIONS,
eventType: 'event-type',
channel: 'locations/us-west1/channels/my-channel',
},
() => 42
);

expect(result.__endpoint).to.deep.equal({
platform: 'gcfv2',
region: ['us-west1'],
availableMemoryMb: 512,
timeoutSeconds: 60,
minInstances: 1,
maxInstances: 3,
concurrency: 20,
vpc: {
connector: 'aConnector',
egressSettings: 'ALL_TRAFFIC',
},
serviceAccountEmail: 'root@',
ingressSettings: 'ALLOW_ALL',
labels: {
hello: 'world',
},
eventTrigger: {
...ENDPOINT_EVENT_TRIGGER,
channel: 'locations/us-west1/channels/my-channel',
},
});
});

it('should merge options and globalOptions', () => {
options.setGlobalOptions({
concurrency: 20,
region: 'europe-west1',
minInstances: 1,
});

const result = eventarc.onCustomEventPublished(
{
eventType: 'event-type',
channel: 'locations/us-west1/channels/my-channel',
region: 'us-west1',
minInstances: 3,
},
() => 42
);

expect(result.__endpoint).to.deep.equal({
platform: 'gcfv2',
concurrency: 20,
minInstances: 3,
region: ['us-west1'],
labels: {},
eventTrigger: {
...ENDPOINT_EVENT_TRIGGER,
channel: 'locations/us-west1/channels/my-channel',
},
});
});
});
});
3 changes: 2 additions & 1 deletion src/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ import * as https from './providers/https';
import * as pubsub from './providers/pubsub';
import * as storage from './providers/storage';
import * as tasks from './providers/tasks';
import * as eventarc from './providers/eventarc';

export { alerts, https, pubsub, storage, logger, params, tasks };
export { alerts, https, pubsub, storage, logger, params, tasks, eventarc };

export { setGlobalOptions, GlobalOptions } from './options';

Expand Down
114 changes: 114 additions & 0 deletions src/v2/providers/eventarc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// The MIT License (MIT)
//
// Copyright (c) 2022 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.

import * as options from '../options';
import { CloudEvent, CloudFunction } from '../core';
import { copyIfPresent, convertIfPresent } from '../../common/encoding';
import { ManifestEndpoint } from '../../runtime/manifest';

/** Options that can be set on an Eventarc trigger. */
export interface EventarcTriggerOptions extends options.EventHandlerOptions {
/**
* Type of the event.
*/
eventType: string;

/**
* ID of the channel. Can be either:
* * fully qualified channel resource name:
* `projects/{project}/locations/{location}/channels/{channel-id}`
* * partial resource name with location and channel ID, in which case
* the runtime project ID of the function will be used:
* `locations/{location}/channels/{channel-id}`
* * partial channel ID, in which case the runtime project ID of the
* function and `us-central1` as location will be used:
* `{channel-id}`
*
* If not specified, the default Firebase channel will be used:
* `projects/{project}/locations/us-central1/channels/firebase`
*/
channel?: string;

/**
* Eventarc event exact match filter.
*/
filters?: Record<string, string>;
}

export type CloudEventHandler = (event: CloudEvent<any>) => any | Promise<any>;

/** Handle an Eventarc event published on the default channel. */
export function onCustomEventPublished<T = any>(
eventType: string,
handler: CloudEventHandler
): CloudFunction<CloudEvent<T>>;

export function onCustomEventPublished<T = any>(
opts: EventarcTriggerOptions,
handler: CloudEventHandler
): CloudFunction<CloudEvent<T>>;

export function onCustomEventPublished<T = any>(
eventTypeOrOpts: string | EventarcTriggerOptions,
handler: CloudEventHandler
): CloudFunction<CloudEvent<T>> {
let opts: EventarcTriggerOptions;
if (typeof eventTypeOrOpts === 'string') {
opts = {
eventType: eventTypeOrOpts as string,
};
} else if (typeof eventTypeOrOpts === 'object') {
opts = eventTypeOrOpts as EventarcTriggerOptions;
}
const func = (raw: CloudEvent<unknown>) => {
return handler(raw as CloudEvent<T>);
};

func.run = handler;

const channel = opts.channel ?? 'locations/us-central1/channels/firebase';

const baseOpts = options.optionsToEndpoint(options.getGlobalOptions());
const specificOpts = options.optionsToEndpoint(opts);

const endpoint: ManifestEndpoint = {
platform: 'gcfv2',
...baseOpts,
...specificOpts,
labels: {
...baseOpts?.labels,
...specificOpts?.labels,
},
eventTrigger: {
eventType: opts.eventType,
eventFilters: {},
retry: false,
channel,
},
};
convertIfPresent(endpoint.eventTrigger, opts, 'eventFilters', 'filters');
copyIfPresent(endpoint.eventTrigger, opts, 'retry');

func.__endpoint = endpoint;

return func;
}
26 changes: 26 additions & 0 deletions v2/eventarc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// The MIT License (MIT)
//
// Copyright (c) 2022 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

0 comments on commit c32db65

Please sign in to comment.