ui/models/browsertimeAlertsExtraData.js (116 lines of code) (raw):

import { summaryStatusMap } from '../perfherder/perf-helpers/constants'; import { addResultsLink, getFrameworkName, } from '../perfherder/perf-helpers/helpers'; import { getArtifactsUrl } from '../helpers/url'; import JobModel from './job'; import RepositoryModel from './repository'; export default class BrowsertimeAlertsExtraData { constructor(alertSummary, frameworks) { this.alertSummary = alertSummary; this.framework = getFrameworkName(frameworks, alertSummary.framework); } async enrichAndRetrieveAlerts() { let alerts = []; if (this.framework === 'browsertime') { alerts = await this.enrichSummaryAlerts( this.alertSummary, this.alertSummary.repository, this.alertSummary.push_id, this.alertSummary.prev_push_id, ); } return alerts; } async enrichSummaryAlerts(alertSummary, repo, pushId, prevPushId) { const [jobList, prevJobList] = await Promise.all([ JobModel.getList({ repo, push_id: pushId }, { fetchAll: true }), JobModel.getList({ repo, push_id: prevPushId }, { fetchAll: true }), ]); if (this.anyAlertWithVideoResults(this.alertSummary)) { // add task ids for current rev and previous rev to every relevant alert item this.enrichWithLinks(alertSummary, jobList); this.enrichWithLinks(alertSummary, prevJobList); } const alertsRepo = await this.getAlertsRepo(); await this.enrichWithProfileLinks(alertSummary, alertsRepo, jobList); await this.enrichWithProfileLinks(alertSummary, alertsRepo, prevJobList); return alertSummary.alerts; } anyAlertWithVideoResults(alertSummary) { // For the moment the browsertime vismet are separate from the pageload ones // that's why we need to filter them out. Also, we're retrieving the video results // for regressions only const any = alertSummary.alerts.filter((alert) => this.shouldHaveVideoLinks(alert), ); return any.length > 0; } shouldHaveVideoLinks(alert) { return ( alert.status !== summaryStatusMap.reassigned && alert.status !== summaryStatusMap.downstream && alert.status !== summaryStatusMap.invalid && alert.is_regression === true ); } enrichWithLinks(alertSummary, jobList) { alertSummary.alerts.forEach((alert) => { if (this.shouldHaveVideoLinks(alert)) { const job = jobList.data.find( (j) => j.searchStr.includes(alert.series_signature.suite) && j.searchStr.includes(alert.series_signature.machine_platform) && j.resultStatus === 'success', ); if (job) { if (alertSummary.revision === job.push_revision) { alert.results_link = addResultsLink(job.task_id); } if (alertSummary.prev_push_revision === job.push_revision) { alert.prev_results_link = addResultsLink(job.task_id); } } } }); } async getAlertsRepo() { const repos = await RepositoryModel.getList(); return RepositoryModel.getRepo(this.alertSummary.repository, repos); } async enrichWithProfileLinks(alertSummary, repo, jobList) { let alertLinks = alertSummary.alerts.map((alert) => { const job = jobList.data.find( (j) => j.searchStr.includes(alert.series_signature.suite) && j.searchStr.includes(alert.series_signature.machine_platform) && j.resultStatus === 'success', ); if (job) { const { suite } = alert.series_signature; const url = getArtifactsUrl({ taskId: job.task_id, run: job.retry_id, rootUrl: repo.tc_root_url, artifactPath: `public/test_info/profile_${suite}.zip`, }); // We check the artifacts with `method: 'HEAD'` which means that it // doesn't download the whole file and only gets the headers. We can see // if artifact is available or not using the headers. const promise = fetch(url, { method: 'HEAD', }); return { alert, job, promise, url }; } return null; }); // Keep only the alerts that have a job and url. Filter out the nulls. alertLinks = alertLinks.filter((a) => a); // We don't await in the loop above because we don't want to block the loop // on each iteration. Promise.all will properly parallelize the requests. const promiseResults = await Promise.all(alertLinks.map((a) => a.promise)); // Now we know if the artifacts are available or not, we can add the links // to the alerts. for (let i = 0; i < alertLinks.length; i++) { const { alert, job, url } = alertLinks[i]; const promiseResult = promiseResults[i]; if (promiseResult.ok) { // Add profile urls to the alert only if the artifact is present. if (job.push_revision === alertSummary.revision) { alert.profile_url = url; } if (job.push_revision === alertSummary.prev_push_revision) { alert.prev_profile_url = url; } } } } }