Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions spec/v2/providers/alerts/appDistribution.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,89 @@ describe('appDistribution', () => {
});
});

describe('onInAppfeedbackPublished', () => {
it('should create a function with alertType & appId', () => {
const func = appDistribution.onInAppFeedbackPublished(APPID, myHandler);

expect(func.__endpoint).to.deep.equal({
platform: 'gcfv2',
labels: {},
eventTrigger: {
eventType: alerts.eventType,
eventFilters: {
...APP_EVENT_FILTER,
alerttype: appDistribution.inAppFeedbackAlert,
},
retry: false,
},
});
});

it('should create a function with opts', () => {
const func = appDistribution.onInAppFeedbackPublished(
{ ...FULL_OPTIONS },
myHandler
);

expect(func.__endpoint).to.deep.equal({
...FULL_ENDPOINT,
eventTrigger: {
eventType: alerts.eventType,
eventFilters: {
alerttype: appDistribution.inAppFeedbackAlert,
},
retry: false,
},
});
});

it('should create a function with appid in opts', () => {
const func = appDistribution.onInAppFeedbackPublished(
{ ...FULL_OPTIONS, appId: APPID },
myHandler
);

expect(func.__endpoint).to.deep.equal({
...FULL_ENDPOINT,
eventTrigger: {
eventType: alerts.eventType,
eventFilters: {
...APP_EVENT_FILTER,
alerttype: appDistribution.inAppFeedbackAlert,
},
retry: false,
},
});
});

it('should create a function without opts or appId', () => {
const func = appDistribution.onInAppFeedbackPublished(myHandler);

expect(func.__endpoint).to.deep.equal({
platform: 'gcfv2',
labels: {},
eventTrigger: {
eventType: alerts.eventType,
eventFilters: {
alerttype: appDistribution.inAppFeedbackAlert,
},
retry: false,
},
});
});

it('should create a function with a run method', () => {
const func = appDistribution.onInAppFeedbackPublished(
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);
Expand Down
1 change: 1 addition & 0 deletions src/v2/providers/alerts/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export type AlertType =
| 'billing.planUpdate'
| 'billing.automatedPlanUpdate'
| 'appDistribution.newTesterIosDevice'
| 'appDistribution.inAppFeedback'
| string;

/**
Expand Down
105 changes: 105 additions & 0 deletions src/v2/providers/alerts/appDistribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,36 @@ export interface NewTesterDevicePayload {
testerDeviceIdentifier: string;
}

/**
* The internal payload object for receiving in-app feedback from a tester.
* Payload is wrapped inside a `FirebaseAlertData` object.
*/
export interface InAppFeedbackPayload {
['@type']: 'type.googleapis.com/google.events.firebase.firebasealerts.v1.AppDistroInAppFeedbackPayload';
/** Resource name. Format: `projects/{project_number}/apps/{app_id}/releases/{release_id}/feedbackReports/{feedback_id}` */
feedbackReport: string;
/** Name of the tester */
testerName?: string;
/** Email of the tester */
testerEmail: string;
/**
* Display version of the release. For an Android release, the display version
* is the `versionName`. For an iOS release, the display version is the
* `CFBundleShortVersionString`.
*/
displayVersion: string;
/**
* Build version of the release. For an Android release, the build version
* is the `versionCode`. For an iOS release, the build version is the
* `CFBundleVersion`.
*/
buildVersion: string;
/** Text entered by the tester */
text: string;
/** URIs to download screenshot(s) */
screenshotUris?: string[];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this non-optional and always provide the empty array if necessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the SDK, correct?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi there, I'm on Kai's team and working on this while he's on vacation.

#1187 includes a fix for this feedback.

Copy link
Contributor

@jladieu jladieu Aug 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update on this...I put the fix for making screenshotUris non-optional into a different PR: #1188 because I don't know what I'm doing yet and don't want to couple the other changes in the originally-linked PR with this one :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries. I helped out in that PR.

}

/**
* A custom CloudEvent for Firebase Alerts (with custom extension attributes).
* @typeParam T - the data type for app distribution alerts that is wrapped in a `FirebaseAlertData` object.
Expand All @@ -59,6 +89,8 @@ export interface AppDistributionEvent<T>

/** @internal */
export const newTesterIosDeviceAlert = 'appDistribution.newTesterIosDevice';
/** @internal */
export const inAppFeedbackAlert = 'appDistribution.inAppFeedback';

/**
* Configuration for app distribution functions.
Expand Down Expand Up @@ -234,6 +266,79 @@ export function onNewTesterIosDevicePublished(
return func;
}

/**
* Declares a function that can handle receiving new in-app feedback from a tester.
* @param handler - Event handler which is run every time new feedback is received.
* @returns A function that you can export and deploy.
*/
export function onInAppFeedbackPublished(
handler: (
event: AppDistributionEvent<InAppFeedbackPayload>
) => any | Promise<any>
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>>;

/**
* Declares a function that can handle receiving new in-app feedback from a tester.
* @param appId - A specific application the handler will trigger on.
* @param handler - Event handler which is run every time new feedback is received.
* @returns A function that you can export and deploy.
*/
export function onInAppFeedbackPublished(
appId: string,
handler: (
event: AppDistributionEvent<InAppFeedbackPayload>
) => any | Promise<any>
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>>;

/**
* Declares a function that can handle receiving new in-app feedback from a tester.
* @param opts - Options that can be set on the function.
* @param handler - Event handler which is run every time new feedback is received.
* @returns A function that you can export and deploy.
*/
export function onInAppFeedbackPublished(
opts: AppDistributionOptions,
handler: (
event: AppDistributionEvent<InAppFeedbackPayload>
) => any | Promise<any>
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>>;

/**
* Declares a function that can handle receiving new in-app feedback from a tester.
* @param appIdOrOptsOrHandler - A specific application, options, or an event-handling function.
* @param handler - Event handler which is run every time new feedback is received.
* @returns A function that you can export and deploy.
*/
export function onInAppFeedbackPublished(
appIdOrOptsOrHandler:
| string
| AppDistributionOptions
| ((
event: AppDistributionEvent<InAppFeedbackPayload>
) => any | Promise<any>),
handler?: (
event: AppDistributionEvent<InAppFeedbackPayload>
) => any | Promise<any>
): CloudFunction<AppDistributionEvent<InAppFeedbackPayload>> {
if (typeof appIdOrOptsOrHandler === 'function') {
handler = appIdOrOptsOrHandler as (
event: AppDistributionEvent<InAppFeedbackPayload>
) => any | Promise<any>;
appIdOrOptsOrHandler = {};
}

const [opts, appId] = getOptsAndApp(appIdOrOptsOrHandler);

const func = (raw: CloudEvent<unknown>) => {
return handler(raw as AppDistributionEvent<InAppFeedbackPayload>);
};

func.run = handler;
func.__endpoint = getEndpointAnnotation(opts, inAppFeedbackAlert, appId);

return func;
}

/**
* Helper function to parse the function opts and appId.
* @internal
Expand Down