UNPKG

firebase-tools

Version:
123 lines (122 loc) • 5.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NOTIFICATION_CHANNELS_MASK_PATH = exports.EMAIL_CHANNEL_TYPE = exports.FIREBASE_EMAIL_CHANNEL_DISPLAY_NAME_SUFFIX = exports.FIREBASE_CHANNEL_LABEL = void 0; exports.generateAlertPolicy = generateAlertPolicy; exports.fetchFirebaseEmailChannel = fetchFirebaseEmailChannel; exports.createEmailChannel = createEmailChannel; exports.resolveEmailNotificationChannel = resolveEmailNotificationChannel; exports.addChannelToPolicy = addChannelToPolicy; exports.enableAlert = enableAlert; exports.enableAlerts = enableAlerts; const error_1 = require("../error"); const cloudmonitoring_1 = require("../gcp/cloudmonitoring"); const logger_1 = require("../logger"); const utils_1 = require("./utils"); exports.FIREBASE_CHANNEL_LABEL = "is_firebase_channel"; exports.FIREBASE_EMAIL_CHANNEL_DISPLAY_NAME_SUFFIX = " - for Firebase alerts"; exports.EMAIL_CHANNEL_TYPE = "email"; exports.NOTIFICATION_CHANNELS_MASK_PATH = "notification_channels"; async function generateAlertPolicy(projectId, appId, alertType) { logger_1.logger.debug(`[crashlytics] generateAlertPolicy called with projectId: ${projectId}, appId: ${appId}, alertType: ${alertType}`); try { const response = await utils_1.CRASHLYTICS_API_CLIENT.request({ method: "POST", headers: { "Content-Type": "application/json", }, path: `/projects/${projectId}/apps/${appId}:generateAlertPolicy`, body: { alertType }, timeout: utils_1.TIMEOUT, }); return response.body.alertPolicy; } catch (err) { const status = (0, error_1.getErrStatus)(err); const msg = (0, error_1.getErrMsg)(err); throw new error_1.FirebaseError(`Failed to generate Crashlytics alert policy for ${alertType} on app ${appId} (status ${status}): ${msg}`, { original: (0, error_1.getError)(err), status }); } } async function fetchFirebaseEmailChannel(projectId, userEmail) { const escapedEmail = userEmail.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); const filter = `type = "${exports.EMAIL_CHANNEL_TYPE}" AND ` + `labels.email_address = "${escapedEmail}" AND ` + `user_labels.${exports.FIREBASE_CHANNEL_LABEL} = "true"`; const channels = await (0, cloudmonitoring_1.listNotificationChannels)(projectId, filter); return channels[0]; } async function createEmailChannel(projectId, userEmail) { return await (0, cloudmonitoring_1.createNotificationChannel)(projectId, { type: exports.EMAIL_CHANNEL_TYPE, displayName: `${userEmail}${exports.FIREBASE_EMAIL_CHANNEL_DISPLAY_NAME_SUFFIX}`, labels: { email_address: userEmail, }, userLabels: { [exports.FIREBASE_CHANNEL_LABEL]: "true", }, }); } async function resolveEmailNotificationChannel(projectId, userEmail) { const existingChannel = await fetchFirebaseEmailChannel(projectId, userEmail); return existingChannel ?? (await createEmailChannel(projectId, userEmail)); } function addChannelToPolicy(policy, channelName) { const currentChannels = policy.notificationChannels ?? []; if (currentChannels.includes(channelName)) { return policy; } return { ...policy, notificationChannels: [...currentChannels, channelName], }; } async function enableAlert(projectId, appId, alertType, target) { let channel; if (!target.channelName && target.userEmail) { channel = await resolveEmailNotificationChannel(projectId, target.userEmail); } else if (!target.channelName) { throw new error_1.FirebaseError("Either channelName or userEmail must be specified to enable a Crashlytics alert.", { exit: 1 }); } const channelName = target.channelName ?? channel?.name; if (!channelName) { throw new error_1.FirebaseError("Resolved notification channel is missing a resource name.", { exit: 1, }); } const policyResourceName = await generateAlertPolicy(projectId, appId, alertType); const targetPolicy = await (0, cloudmonitoring_1.getAlertPolicy)(policyResourceName); const policyWithChannel = addChannelToPolicy(targetPolicy, channelName); const updatedPolicy = policyWithChannel === targetPolicy ? targetPolicy : await (0, cloudmonitoring_1.updateAlertPolicy)(policyWithChannel, exports.NOTIFICATION_CHANNELS_MASK_PATH); return { channel, policy: updatedPolicy }; } async function enableAlerts(projectId, appId, alertTypes, userEmail) { if (alertTypes.length === 0) { return []; } const channel = await resolveEmailNotificationChannel(projectId, userEmail); const channelName = channel.name; if (!channelName) { throw new error_1.FirebaseError("Notification channel for Crashlytics alerts is missing a name.", { exit: 1, }); } const results = await Promise.allSettled(alertTypes.map(async (alertType) => { const { policy } = await enableAlert(projectId, appId, alertType, { channelName, }); return policy; })); const policies = []; for (const res of results) { if (res.status === "fulfilled") { policies.push(res.value); } else { logger_1.logger.debug(`[crashlytics] Failed to enable alert: ${(0, error_1.getErrMsg)(res.reason)}`); } } return policies; }