1// Copyright 2023 The Chromium Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5import {assert} from 'chrome://resources/js/assert.js'; 6 7/** 8 * Key-Value pair for the summary and metrics table. 9 */ 10interface KeyValue { 11 key: string; 12 value: number|string|boolean; 13} 14 15/** 16 * Sequence information for an event. 17 */ 18interface SequenceMetadata { 19 id: string; 20 systemUptimeMs: string; 21 resetCounter: string; 22} 23 24/** 25 * An event and its data. This includes metadata about the event and sequence 26 * information if applicable. 27 */ 28export interface StructuredMetricEvent { 29 project: string; 30 event: string; 31 type: string; 32 sequenceMetadata?: SequenceMetadata; 33 metrics: KeyValue[]; 34} 35 36/** 37 * Summary about Structured Metrics service. 38 */ 39export interface StructuredMetricsSummary { 40 enabled: boolean; 41 flags: KeyValue[]; 42 crosDeviceId: string; 43} 44 45/** 46 * Updates the Summary table with new information. 47 * 48 * @param summaryBody Body of the summary table. 49 * @param summary Summary object to populate the table. 50 * @param template Key-Value pair HTML template. 51 */ 52export function updateStructuredMetricsSummary( 53 summaryBody: HTMLElement, summary: StructuredMetricsSummary, 54 template: HTMLTemplateElement): void { 55 // Clear the table first. 56 summaryBody.replaceChildren(); 57 58 const enabled = 59 buildKeyValueRow('Enabled', summary.enabled.toString(), template); 60 summaryBody.append(enabled); 61 62 const crosDeviceId = 63 buildKeyValueRow('CrOS Device Id', summary.crosDeviceId || '-', template); 64 summaryBody.append(crosDeviceId); 65} 66 67/** 68 * Updates the events table with the events recorded by the client. 69 * 70 * @param eventBody Body of the event table. 71 * @param events List of events to populate the table. 72 * @param template HTML template for the event table row. 73 * @param kvTemplate Key-Value pair HTML template. 74 */ 75export function updateStructuredMetricsEvents( 76 eventBody: HTMLElement, events: StructuredMetricEvent[], 77 eventTemplate: HTMLTemplateElement, detailsTemplate: HTMLTemplateElement, 78 kvTemplate: HTMLTemplateElement): void { 79 eventBody.replaceChildren(); 80 81 for (const event of events) { 82 const row = eventTemplate.content.cloneNode(true) as HTMLElement; 83 const [project, evn, type, uptime] = row.querySelectorAll('td'); 84 85 assert(project); 86 project.textContent = event.project; 87 88 assert(evn); 89 evn.textContent = event.event; 90 91 assert(type); 92 type.textContent = event.type; 93 94 assert(uptime); 95 uptime.textContent = event.sequenceMetadata?.systemUptimeMs ?? '-'; 96 97 const detailsRow = detailsTemplate.content.cloneNode(true) as HTMLElement; 98 const metricsRow = detailsRow.querySelector('#metrics-row') as HTMLElement; 99 assert(metricsRow); 100 101 const [details, metrics] = detailsRow.querySelectorAll('tbody'); 102 assert(details); 103 assert(metrics); 104 105 updateEventDetailsTable(details, event, kvTemplate); 106 updateEventMetricsTable(metrics, event, kvTemplate); 107 108 const eventRow = row.querySelector('#event-row') as HTMLElement; 109 eventRow.addEventListener('click', () => { 110 if (metricsRow.style.display === 'none') { 111 metricsRow.style.display = 'table-row'; 112 } else { 113 metricsRow.style.display = 'none'; 114 } 115 }, false); 116 117 eventBody.append(row); 118 eventBody.append(detailsRow); 119 } 120} 121 122function updateEventDetailsTable( 123 detailTable: HTMLElement, event: StructuredMetricEvent, 124 template: HTMLTemplateElement): void { 125 detailTable.replaceChildren(); 126 127 const resetCounter = event.sequenceMetadata?.resetCounter ?? '-'; 128 const systemUptime = event.sequenceMetadata?.systemUptimeMs ?? '-'; 129 const eventId = event.sequenceMetadata?.id ?? '-'; 130 131 const resetCounterRow = buildKeyValueRow('Reset Id', resetCounter, template); 132 const systemUptimeRow = 133 buildKeyValueRow('System Uptime', systemUptime, template); 134 const eventIdRow = buildKeyValueRow('Event Id', eventId, template); 135 136 detailTable.append(resetCounterRow); 137 detailTable.append(systemUptimeRow); 138 detailTable.append(eventIdRow); 139} 140 141function updateEventMetricsTable( 142 metricsTable: HTMLElement, event: StructuredMetricEvent, 143 template: HTMLTemplateElement): void { 144 metricsTable.replaceChildren(); 145 for (const metric of event.metrics) { 146 const metricRow = 147 buildKeyValueRow(metric.key, metric.value.toString(), template); 148 metricsTable.append(metricRow); 149 } 150} 151 152function buildKeyValueRow( 153 key: string, value: string, template: HTMLTemplateElement): HTMLElement { 154 const kvRow = template.content.cloneNode(true) as HTMLElement; 155 156 const [k, v] = kvRow.querySelectorAll('td'); 157 assert(k); 158 k.textContent = key; 159 assert(v); 160 v.textContent = value; 161 162 return kvRow; 163} 164