Skip to content

Commit eac20aa

Browse files
committed
Refactor MockCloudEvent generation to include partial
The goal of this commit is to incorporate the user's partial into the generated CloudEvent. All the AbstractFactories for Partial Cloud Events now return Cloud Events, and they all incorporate the user provided partial when creating the Mock Cloud Event.. Theres a few key benefits to this: 1. Type safety. The Partial Factories were not providing all the necessary fields required (as is the nature of the Partial) 1. Consistency with other fields. Eg: If a user updates the bucket for storage event, lets try to update the other fields too. 1. Opaque fields - If we want the user provided partial to take precedence, we can easily control that. (see PubSub and EventArc factories) 1. Easier to handle one-off behavior. Not all the events behave the same way, particularly with PubSub. Although this is a "larger" change, the end-user contract remains intact. This does fix several bugs. Output is different for PubSub, EventArc, and Storage.
1 parent 2784209 commit eac20aa

23 files changed

+265
-229
lines changed

spec/v2.spec.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,11 +134,12 @@ describe('v2', () => {
134134
describe('storage', () => {
135135
describe('storage.onObjectArchived()', () => {
136136
it('should update CloudEvent appropriately', () => {
137-
const bucket = 'bucket';
137+
const bucket = 'bucket_override';
138138
const cloudFn = storage.onObjectArchived(bucket, handler);
139139
const cloudFnWrap = wrapV2(cloudFn);
140140
expect(cloudFnWrap().cloudEvent).to.include({
141141
bucket,
142+
source: `//storage.googleapis.com/projects/_/buckets/${bucket}`,
142143
});
143144
});
144145
});
@@ -195,11 +196,8 @@ describe('v2', () => {
195196
const cloudFnWrap = wrapV2(cloudFn);
196197
const cloudEventPartial = { data };
197198

198-
expect(
199-
cloudFnWrap(cloudEventPartial).cloudEvent.data.message
200-
).to.include({
201-
data: 'eyJoZWxsbyI6IndvcmxkIn0=', // Note: This is a mismatch from the json
202-
});
199+
expect(cloudFnWrap(cloudEventPartial).cloudEvent.data.message.data)
200+
.equal(Buffer.from(JSON.stringify(data.message.json)).toString('base64'));
203201
expect(
204202
cloudFnWrap(cloudEventPartial).cloudEvent.data.message.json
205203
).to.include({ firebase: 'test' });
@@ -216,14 +214,11 @@ describe('v2', () => {
216214
const cloudFnWrap = wrapV2(cloudFn);
217215
const cloudEventPartial = { data };
218216

219-
expect(
220-
cloudFnWrap(cloudEventPartial).cloudEvent.data.message
221-
).to.include({
222-
data: 'eyJmaXJlYmFzZSI6Im5vbl9qc29uX3Rlc3QifQ==',
223-
});
217+
expect(cloudFnWrap(cloudEventPartial).cloudEvent.data.message.data)
218+
.equal(Buffer.from(JSON.stringify(data.message.json)).toString('base64'));
224219
expect(
225220
cloudFnWrap(cloudEventPartial).cloudEvent.data.message.json
226-
).to.include({ firebase: 'non_json_test' });
221+
).to.include(data.message.json);
227222
});
228223
});
229224
});

src/cloudevent/generate.ts

Lines changed: 7 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { CloudEvent } from 'firebase-functions/v2';
22
import { CloudFunction } from 'firebase-functions/v2';
3-
import { LIST_OF_MOCK_CLOUD_EVENT_PARTIALS } from './partials/partials';
4-
import { DeepPartial, MockCloudEventPartials } from './types';
3+
import { LIST_OF_MOCK_CLOUD_EVENT_PARTIALS } from './mocks/partials';
4+
import { DeepPartial, MockCloudEventAbstractFactory } from './types';
55
import merge from 'ts-deepmerge';
6-
import { getEventType } from './partials/helpers';
76

87
/**
98
* @return {CloudEvent} Generated Mock CloudEvent
@@ -14,61 +13,21 @@ export function generateCombinedCloudEvent<
1413
cloudFunction: CloudFunction<EventType>,
1514
cloudEventPartial?: DeepPartial<EventType>
1615
): EventType {
17-
const generatedCloudEvent = generateMockCloudEvent(cloudFunction);
16+
const generatedCloudEvent = generateMockCloudEvent(cloudFunction, cloudEventPartial);
1817
return cloudEventPartial
1918
? (merge(generatedCloudEvent, cloudEventPartial) as EventType)
2019
: generatedCloudEvent;
2120
}
2221

2322
export function generateMockCloudEvent<EventType extends CloudEvent<unknown>>(
24-
cloudFunction: CloudFunction<EventType>
25-
): EventType {
26-
return {
27-
...generateBaseCloudEvent(cloudFunction),
28-
...generateMockCloudEventPartial(cloudFunction),
29-
};
30-
}
31-
32-
/** @internal */
33-
function generateBaseCloudEvent<EventType extends CloudEvent<unknown>>(
34-
cloudFunction: CloudFunction<EventType>
35-
): EventType {
36-
// TODO: Consider refactoring so that we don't use this utility function. This
37-
// is not type safe because EventType may require additional fields, which this
38-
// function does not know how to satisfy.
39-
// This could possibly be augmented to take a CloudEvent<unknown> and AdditionalFields<EventType>
40-
// where AdditionalFields uses the keyof operator to make only new fields required.
41-
return {
42-
specversion: '1.0',
43-
id: makeEventId(),
44-
data: {},
45-
source: '', // Required field that will get overridden by Provider-specific MockCloudEventPartials
46-
type: getEventType(cloudFunction),
47-
time: new Date().toISOString(),
48-
} as any;
49-
}
23+
cloudFunction: CloudFunction<EventType>,
24+
cloudEventPartial?: DeepPartial<EventType>): EventType {
5025

51-
function generateMockCloudEventPartial<EventType extends CloudEvent<unknown>>(
52-
cloudFunction: CloudFunction<EventType>
53-
): DeepPartial<EventType> {
5426
for (const mockCloudEventPartial of LIST_OF_MOCK_CLOUD_EVENT_PARTIALS) {
5527
if (mockCloudEventPartial.match(cloudFunction)) {
56-
return (mockCloudEventPartial as MockCloudEventPartials<
57-
EventType
58-
>).generatePartial(cloudFunction);
28+
return mockCloudEventPartial.generateMock(cloudFunction, cloudEventPartial);
5929
}
6030
}
6131
// No matches were found
62-
return {};
63-
}
64-
65-
function makeEventId(): string {
66-
return (
67-
Math.random()
68-
.toString(36)
69-
.substring(2, 15) +
70-
Math.random()
71-
.toString(36)
72-
.substring(2, 15)
73-
);
32+
return null;
7433
}

src/cloudevent/partials/alerts/alerts-on-alert-published.ts renamed to src/cloudevent/mocks/alerts/alerts-on-alert-published.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
3-
import { APP_ID, getEventType, PROJECT_ID } from '../helpers';
3+
import {APP_ID, getBaseCloudEvent, getEventType, PROJECT_ID} from '../helpers';
44
import { FirebaseAlertData, AlertEvent } from 'firebase-functions/v2/alerts';
55

6-
export const alertsOnAlertPublished: MockCloudEventPartials<AlertEvent<
6+
export const alertsOnAlertPublished: MockCloudEventAbstractFactory<AlertEvent<
77
FirebaseAlertData
88
>> = {
9-
generatePartial(
10-
_: CloudFunction<AlertEvent<FirebaseAlertData>>
11-
): DeepPartial<AlertEvent<FirebaseAlertData>> {
9+
generateMock(
10+
cloudFunction: CloudFunction<AlertEvent<FirebaseAlertData>>
11+
): AlertEvent<FirebaseAlertData> {
1212
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1313

1414
const alertType = 'appDistribution.newTesterIosDevice';
1515
const appId = APP_ID;
1616

1717
return {
18+
// Spread common fields
19+
...getBaseCloudEvent(cloudFunction),
20+
// Spread fields specific to this CloudEvent
21+
1822
alertType,
1923
appId,
2024
data: getOnAlertPublishedData(),

src/cloudevent/partials/alerts/app-distribution-on-new-tester-ios-device-published.ts renamed to src/cloudevent/mocks/alerts/app-distribution-on-new-tester-ios-device-published.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
3-
import { getEventFilters, getEventType, PROJECT_ID } from '../helpers';
3+
import {getBaseCloudEvent, getEventFilters, getEventType, PROJECT_ID} from '../helpers';
44
import {
55
AppDistributionEvent,
66
NewTesterDevicePayload,
77
} from 'firebase-functions/v2/alerts/appDistribution';
88

9-
export const alertsAppDistributionOnNewTesterIosDevicePublished: MockCloudEventPartials<AppDistributionEvent<
9+
export const alertsAppDistributionOnNewTesterIosDevicePublished: MockCloudEventAbstractFactory<AppDistributionEvent<
1010
NewTesterDevicePayload
1111
>> = {
12-
generatePartial(
13-
_: CloudFunction<AppDistributionEvent<NewTesterDevicePayload>>
14-
): DeepPartial<AppDistributionEvent<NewTesterDevicePayload>> {
12+
generateMock(
13+
cloudFunction: CloudFunction<AppDistributionEvent<NewTesterDevicePayload>>
14+
): AppDistributionEvent<NewTesterDevicePayload> {
1515
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1616
const now = new Date().toISOString();
1717

1818
return {
19+
// Spread common fields
20+
...getBaseCloudEvent(cloudFunction),
21+
// Spread fields specific to this CloudEvent
1922
source,
2023
data: {
2124
createTime: now,

src/cloudevent/partials/alerts/billing-on-plan-automated-update-published.ts renamed to src/cloudevent/mocks/alerts/billing-on-plan-automated-update-published.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
33
import { FirebaseAlertData } from 'firebase-functions/v2/alerts';
44
import {
55
BillingEvent,
66
PlanAutomatedUpdatePayload,
77
} from 'firebase-functions/v2/alerts/billing';
8-
import { getEventFilters, getEventType, PROJECT_ID } from '../helpers';
8+
import {getBaseCloudEvent, getEventFilters, getEventType, PROJECT_ID} from '../helpers';
99

10-
export const alertsBillingOnPlanAutomatedUpdatePublished: MockCloudEventPartials<BillingEvent<
10+
export const alertsBillingOnPlanAutomatedUpdatePublished: MockCloudEventAbstractFactory<BillingEvent<
1111
PlanAutomatedUpdatePayload
1212
>> = {
13-
generatePartial(
14-
_: CloudFunction<BillingEvent<PlanAutomatedUpdatePayload>>
15-
): DeepPartial<BillingEvent<PlanAutomatedUpdatePayload>> {
13+
generateMock(
14+
cloudFunction: CloudFunction<BillingEvent<PlanAutomatedUpdatePayload>>
15+
): BillingEvent<PlanAutomatedUpdatePayload> {
1616
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1717

1818
return {
19+
// Spread common fields
20+
...getBaseCloudEvent(cloudFunction),
21+
// Spread fields specific to this CloudEvent
1922
source,
2023
data: getBillingPlanAutomatedUpdateData(),
2124
};

src/cloudevent/partials/alerts/billing-on-plan-update-published.ts renamed to src/cloudevent/mocks/alerts/billing-on-plan-update-published.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
33
import { FirebaseAlertData } from 'firebase-functions/v2/alerts';
44
import {
55
BillingEvent,
66
PlanUpdatePayload,
77
} from 'firebase-functions/v2/alerts/billing';
8-
import { getEventFilters, getEventType, PROJECT_ID } from '../helpers';
8+
import {getBaseCloudEvent, getEventFilters, getEventType, PROJECT_ID} from '../helpers';
99

10-
export const alertsBillingOnPlanUpdatePublished: MockCloudEventPartials<BillingEvent<
10+
export const alertsBillingOnPlanUpdatePublished: MockCloudEventAbstractFactory<BillingEvent<
1111
PlanUpdatePayload
1212
>> = {
13-
generatePartial(
14-
_: CloudFunction<BillingEvent<PlanUpdatePayload>>
15-
): DeepPartial<BillingEvent<PlanUpdatePayload>> {
13+
generateMock(
14+
cloudFunction: CloudFunction<BillingEvent<PlanUpdatePayload>>
15+
): BillingEvent<PlanUpdatePayload> {
1616
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1717

1818
return {
19+
// Spread common fields
20+
...getBaseCloudEvent(cloudFunction),
21+
// Spread fields specific to this CloudEvent
1922
source,
2023
data: getBillingPlanUpdateData(),
2124
};

src/cloudevent/partials/alerts/crashlytics-on-new-anr-issue-published.ts renamed to src/cloudevent/mocks/alerts/crashlytics-on-new-anr-issue-published.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
3-
import { getEventFilters, getEventType, PROJECT_ID } from '../helpers';
3+
import {getBaseCloudEvent, getEventFilters, getEventType, PROJECT_ID} from '../helpers';
44
import {
55
CrashlyticsEvent,
66
NewAnrIssuePayload,
77
} from 'firebase-functions/v2/alerts/crashlytics';
88
import { FirebaseAlertData } from 'firebase-functions/v2/alerts';
99

10-
export const alertsCrashlyticsOnNewAnrIssuePublished: MockCloudEventPartials<CrashlyticsEvent<
10+
export const alertsCrashlyticsOnNewAnrIssuePublished: MockCloudEventAbstractFactory<CrashlyticsEvent<
1111
NewAnrIssuePayload
1212
>> = {
13-
generatePartial(
14-
_: CloudFunction<CrashlyticsEvent<NewAnrIssuePayload>>
15-
): DeepPartial<CrashlyticsEvent<NewAnrIssuePayload>> {
13+
generateMock(
14+
cloudFunction: CloudFunction<CrashlyticsEvent<NewAnrIssuePayload>>
15+
): CrashlyticsEvent<NewAnrIssuePayload> {
1616
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1717

1818
return {
19+
// Spread common fields
20+
...getBaseCloudEvent(cloudFunction),
21+
// Spread fields specific to this CloudEvent
1922
source,
2023
data: getCrashlyticsNewAnrIssueData(),
2124
};

src/cloudevent/partials/alerts/crashlytics-on-new-fatal-issue-published.ts renamed to src/cloudevent/mocks/alerts/crashlytics-on-new-fatal-issue-published.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
3-
import { getEventFilters, getEventType, PROJECT_ID } from '../helpers';
3+
import {getBaseCloudEvent, getEventFilters, getEventType, PROJECT_ID} from '../helpers';
44
import {
55
CrashlyticsEvent,
66
NewFatalIssuePayload,
77
} from 'firebase-functions/v2/alerts/crashlytics';
88
import { FirebaseAlertData } from 'firebase-functions/v2/alerts';
99

10-
export const alertsCrashlyticsOnNewFatalIssuePublished: MockCloudEventPartials<CrashlyticsEvent<
10+
export const alertsCrashlyticsOnNewFatalIssuePublished: MockCloudEventAbstractFactory<CrashlyticsEvent<
1111
NewFatalIssuePayload
1212
>> = {
13-
generatePartial(
14-
_: CloudFunction<CrashlyticsEvent<NewFatalIssuePayload>>
15-
): DeepPartial<CrashlyticsEvent<NewFatalIssuePayload>> {
13+
generateMock(
14+
cloudFunction: CloudFunction<CrashlyticsEvent<NewFatalIssuePayload>>
15+
): CrashlyticsEvent<NewFatalIssuePayload> {
1616
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1717

1818
return {
19+
// Spread common fields
20+
...getBaseCloudEvent(cloudFunction),
21+
// Spread fields specific to this CloudEvent
1922
source,
2023
data: getCrashlyticsNewFatalIssueData(),
2124
};

src/cloudevent/partials/alerts/crashlytics-on-new-nonfatal-issue-published.ts renamed to src/cloudevent/mocks/alerts/crashlytics-on-new-nonfatal-issue-published.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
3-
import { getEventFilters, getEventType, PROJECT_ID } from '../helpers';
3+
import {getBaseCloudEvent, getEventFilters, getEventType, PROJECT_ID} from '../helpers';
44
import {
55
CrashlyticsEvent,
66
NewNonfatalIssuePayload,
77
} from 'firebase-functions/v2/alerts/crashlytics';
88
import { FirebaseAlertData } from 'firebase-functions/v2/alerts';
99

10-
export const alertsCrashlyticsOnNewNonfatalIssuePublished: MockCloudEventPartials<CrashlyticsEvent<
10+
export const alertsCrashlyticsOnNewNonfatalIssuePublished: MockCloudEventAbstractFactory<CrashlyticsEvent<
1111
NewNonfatalIssuePayload
1212
>> = {
13-
generatePartial(
14-
_: CloudFunction<CrashlyticsEvent<NewNonfatalIssuePayload>>
15-
): DeepPartial<CrashlyticsEvent<NewNonfatalIssuePayload>> {
13+
generateMock(
14+
cloudFunction: CloudFunction<CrashlyticsEvent<NewNonfatalIssuePayload>>
15+
): CrashlyticsEvent<NewNonfatalIssuePayload> {
1616
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1717

1818
return {
19+
// Spread common fields
20+
...getBaseCloudEvent(cloudFunction),
21+
// Spread fields specific to this CloudEvent
1922
source,
2023
data: getCrashlyticsNewNonfatalIssueData(),
2124
};

src/cloudevent/partials/alerts/crashlytics-on-regression-alert-published.ts renamed to src/cloudevent/mocks/alerts/crashlytics-on-regression-alert-published.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
import { DeepPartial, MockCloudEventPartials } from '../../types';
1+
import { DeepPartial, MockCloudEventAbstractFactory } from '../../types';
22
import { CloudFunction } from 'firebase-functions/v2';
3-
import { getEventFilters, getEventType, PROJECT_ID } from '../helpers';
3+
import {getBaseCloudEvent, getEventFilters, getEventType, PROJECT_ID} from '../helpers';
44
import {
55
CrashlyticsEvent,
66
RegressionAlertPayload,
77
} from 'firebase-functions/v2/alerts/crashlytics';
88
import { FirebaseAlertData } from 'firebase-functions/v2/alerts';
99

10-
export const alertsCrashlyticsOnRegressionAlertPublished: MockCloudEventPartials<CrashlyticsEvent<
10+
export const alertsCrashlyticsOnRegressionAlertPublished: MockCloudEventAbstractFactory<CrashlyticsEvent<
1111
RegressionAlertPayload
1212
>> = {
13-
generatePartial(
14-
_: CloudFunction<CrashlyticsEvent<RegressionAlertPayload>>
15-
): DeepPartial<CrashlyticsEvent<RegressionAlertPayload>> {
13+
generateMock(
14+
cloudFunction: CloudFunction<CrashlyticsEvent<RegressionAlertPayload>>
15+
): CrashlyticsEvent<RegressionAlertPayload> {
1616
const source = `//firebasealerts.googleapis.com/projects/${PROJECT_ID}`;
1717

1818
return {
19+
// Spread common fields
20+
...getBaseCloudEvent(cloudFunction),
21+
// Spread fields specific to this CloudEvent
1922
source,
2023
data: getCrashlyticsRegressionAlertPayload(),
2124
};

0 commit comments

Comments
 (0)