Skip to content

Commit 724a8b0

Browse files
authored
Add support for App Distribution in-app Feedback (#1167)
Add `InAppFeedbackPayload` and `onNewInAppFeedbackPublished()`.
1 parent 7d2c8dc commit 724a8b0

File tree

3 files changed

+189
-0
lines changed

3 files changed

+189
-0
lines changed

spec/v2/providers/alerts/appDistribution.spec.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,89 @@ describe('appDistribution', () => {
9797
});
9898
});
9999

100+
describe('onInAppfeedbackPublished', () => {
101+
it('should create a function with alertType & appId', () => {
102+
const func = appDistribution.onInAppFeedbackPublished(APPID, myHandler);
103+
104+
expect(func.__endpoint).to.deep.equal({
105+
platform: 'gcfv2',
106+
labels: {},
107+
eventTrigger: {
108+
eventType: alerts.eventType,
109+
eventFilters: {
110+
...APP_EVENT_FILTER,
111+
alerttype: appDistribution.inAppFeedbackAlert,
112+
},
113+
retry: false,
114+
},
115+
});
116+
});
117+
118+
it('should create a function with opts', () => {
119+
const func = appDistribution.onInAppFeedbackPublished(
120+
{ ...FULL_OPTIONS },
121+
myHandler
122+
);
123+
124+
expect(func.__endpoint).to.deep.equal({
125+
...FULL_ENDPOINT,
126+
eventTrigger: {
127+
eventType: alerts.eventType,
128+
eventFilters: {
129+
alerttype: appDistribution.inAppFeedbackAlert,
130+
},
131+
retry: false,
132+
},
133+
});
134+
});
135+
136+
it('should create a function with appid in opts', () => {
137+
const func = appDistribution.onInAppFeedbackPublished(
138+
{ ...FULL_OPTIONS, appId: APPID },
139+
myHandler
140+
);
141+
142+
expect(func.__endpoint).to.deep.equal({
143+
...FULL_ENDPOINT,
144+
eventTrigger: {
145+
eventType: alerts.eventType,
146+
eventFilters: {
147+
...APP_EVENT_FILTER,
148+
alerttype: appDistribution.inAppFeedbackAlert,
149+
},
150+
retry: false,
151+
},
152+
});
153+
});
154+
155+
it('should create a function without opts or appId', () => {
156+
const func = appDistribution.onInAppFeedbackPublished(myHandler);
157+
158+
expect(func.__endpoint).to.deep.equal({
159+
platform: 'gcfv2',
160+
labels: {},
161+
eventTrigger: {
162+
eventType: alerts.eventType,
163+
eventFilters: {
164+
alerttype: appDistribution.inAppFeedbackAlert,
165+
},
166+
retry: false,
167+
},
168+
});
169+
});
170+
171+
it('should create a function with a run method', () => {
172+
const func = appDistribution.onInAppFeedbackPublished(
173+
APPID,
174+
(event) => event
175+
);
176+
177+
const res = func.run('input' as any);
178+
179+
expect(res).to.equal('input');
180+
});
181+
});
182+
100183
describe('getOptsAndApp', () => {
101184
it('should parse a string', () => {
102185
const [opts, appId] = appDistribution.getOptsAndApp(APPID);

src/v2/providers/alerts/alerts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export type AlertType =
6868
| 'billing.planUpdate'
6969
| 'billing.automatedPlanUpdate'
7070
| 'appDistribution.newTesterIosDevice'
71+
| 'appDistribution.inAppFeedback'
7172
| string;
7273

7374
/**

src/v2/providers/alerts/appDistribution.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,36 @@ export interface NewTesterDevicePayload {
4545
testerDeviceIdentifier: string;
4646
}
4747

48+
/**
49+
* The internal payload object for receiving in-app feedback from a tester.
50+
* Payload is wrapped inside a `FirebaseAlertData` object.
51+
*/
52+
export interface InAppFeedbackPayload {
53+
['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroInAppFeedbackPayload';
54+
/** Resource name. Format: `projects/{project_number}/apps/{app_id}/releases/{release_id}/feedbackReports/{feedback_id}` */
55+
feedbackReport: string;
56+
/** Name of the tester */
57+
testerName?: string;
58+
/** Email of the tester */
59+
testerEmail: string;
60+
/**
61+
* Display version of the release. For an Android release, the display version
62+
* is the `versionName`. For an iOS release, the display version is the
63+
* `CFBundleShortVersionString`.
64+
*/
65+
displayVersion: string;
66+
/**
67+
* Build version of the release. For an Android release, the build version
68+
* is the `versionCode`. For an iOS release, the build version is the
69+
* `CFBundleVersion`.
70+
*/
71+
buildVersion: string;
72+
/** Text entered by the tester */
73+
text: string;
74+
/** URIs to download screenshot(s) */
75+
screenshotUris?: string[];
76+
}
77+
4878
/**
4979
* A custom CloudEvent for Firebase Alerts (with custom extension attributes).
5080
* @typeParam T - the data type for app distribution alerts that is wrapped in a `FirebaseAlertData` object.
@@ -59,6 +89,8 @@ export interface AppDistributionEvent<T>
5989

6090
/** @internal */
6191
export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice';
92+
/** @internal */
93+
export const inAppFeedbackAlert = 'appDistribution.inAppFeedback';
6294

6395
/**
6496
* Configuration for app distribution functions.
@@ -234,6 +266,79 @@ export function onNewTesterIosDevicePublished(
234266
return func;
235267
}
236268

269+
/**
270+
* Declares a function that can handle receiving new in-app feedback from a tester.
271+
* @param handler - Event handler which is run every time new feedback is received.
272+
* @returns A function that you can export and deploy.
273+
*/
274+
export function onInAppFeedbackPublished(
275+
handler: (
276+
event: AppDistributionEvent<InAppFeedbackPayload>
277+
) => any | Promise<any>
278+
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>>;
279+
280+
/**
281+
* Declares a function that can handle receiving new in-app feedback from a tester.
282+
* @param appId - A specific application the handler will trigger on.
283+
* @param handler - Event handler which is run every time new feedback is received.
284+
* @returns A function that you can export and deploy.
285+
*/
286+
export function onInAppFeedbackPublished(
287+
appId: string,
288+
handler: (
289+
event: AppDistributionEvent<InAppFeedbackPayload>
290+
) => any | Promise<any>
291+
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>>;
292+
293+
/**
294+
* Declares a function that can handle receiving new in-app feedback from a tester.
295+
* @param opts - Options that can be set on the function.
296+
* @param handler - Event handler which is run every time new feedback is received.
297+
* @returns A function that you can export and deploy.
298+
*/
299+
export function onInAppFeedbackPublished(
300+
opts: AppDistributionOptions,
301+
handler: (
302+
event: AppDistributionEvent<InAppFeedbackPayload>
303+
) => any | Promise<any>
304+
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>>;
305+
306+
/**
307+
* Declares a function that can handle receiving new in-app feedback from a tester.
308+
* @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function.
309+
* @param handler - Event handler which is run every time new feedback is received.
310+
* @returns A function that you can export and deploy.
311+
*/
312+
export function onInAppFeedbackPublished(
313+
appIdOrOptsOrHandler:
314+
| string
315+
| AppDistributionOptions
316+
| ((
317+
event: AppDistributionEvent<InAppFeedbackPayload>
318+
) => any | Promise<any>),
319+
handler?: (
320+
event: AppDistributionEvent<InAppFeedbackPayload>
321+
) => any | Promise<any>
322+
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>> {
323+
if (typeof appIdOrOptsOrHandler === 'function') {
324+
handler = appIdOrOptsOrHandler as (
325+
event: AppDistributionEvent<InAppFeedbackPayload>
326+
) => any | Promise<any>;
327+
appIdOrOptsOrHandler = {};
328+
}
329+
330+
const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler);
331+
332+
const func = (raw: CloudEvent<unknown>) => {
333+
return handler(raw as AppDistributionEvent<InAppFeedbackPayload>);
334+
};
335+
336+
func.run = handler;
337+
func.__endpoint = getEndpointAnnotation(opts, inAppFeedbackAlert, appId);
338+
339+
return func;
340+
}
341+
237342
/**
238343
* Helper function to parse the function opts and appId.
239344
* @internal

0 commit comments

Comments
 (0)