import merge from 'lodash.merge'; import { flattenMessageData } from '../../support/flattenMessage.js'; import { getConnectivity, getURLAndGroup, toSafeKey } from '../../support/tsdbUtil.js'; function getAdditionalTags(key, type) { let tags = {}; const keyArray = key.split('.'); if (/(^contentTypes)/.test(key)) { // contentTypes.favicon.requests.mean // contentTypes.favicon.requests // contentTypes.css.transferSize tags.contentType = keyArray[1]; } else if (/(^pageTimings|^visualMetrics)/.test(key)) { // pageTimings.serverResponseTime.max // visualMetrics.SpeedIndex.median tags.timings = keyArray[0]; } else switch (type) { case 'browsertime.pageSummary': { // statistics.timings.pageTimings.backEndTime.median // statistics.timings.userTimings.marks.logoTime.median // statistics.visualMetrics.SpeedIndex.median tags[keyArray[0]] = keyArray[1]; if (keyArray.length >= 5) { tags[keyArray[2]] = keyArray[3]; } if (key.includes('cpu.categories')) { tags.cpu = 'category'; } else if (key.includes('cpu.events')) { tags.cpu = 'event'; } else if (key.includes('cpu.longTasks')) { tags.cpu = 'longTask'; } break; } case 'browsertime.summary': { // firstPaint.median // userTimings.marks.logoTime.median if (key.includes('userTimings')) { tags[keyArray[0]] = keyArray[1]; } break; } case 'axe.pageSummary': { tags.axeType = keyArray[0]; break; } case 'coach.pageSummary': { // advice.score // advice.performance.score if (keyArray.length > 2) { tags.advice = keyArray[1]; } // set the actual advice name // advice.performance.adviceList.cacheHeaders.score if (keyArray.length > 4) { tags.adviceName = keyArray[3]; } break; } case 'coach.summary': { // score.max // performance.score.median if (keyArray.length === 3) { tags.advice = keyArray[0]; } break; } case 'pagexray.summary': { // firstParty.requests.min pagexray.summary // requests.median // responseCodes.307.max pagexray.summary // requests.min pagexray.summary if (key.includes('responseCodes')) { tags.responseCodes = 'response'; } if (key.includes('firstParty') || key.includes('thirdParty')) { tags.party = keyArray[0]; } break; } case 'pagexray.pageSummary': { // thirdParty.contentTypes.json.requests pagexray.pageSummary // thirdParty.requests pagexray.pageSummary // firstParty.cookieStats.max pagexray.pageSummary // responseCodes.200 pagexray.pageSummary // expireStats.max pagexray.pageSummary // totalDomains pagexray.pageSummary if (key.includes('firstParty') || key.includes('thirdParty')) { tags.party = keyArray[0]; } if (key.includes('responseCodes')) { tags.responseCodes = 'response'; } if (key.includes('contentTypes')) { tags.contentType = keyArray[2]; } break; } case 'thirdparty.pageSummary': { tags.thirdPartyCategory = keyArray[1]; tags.thirdPartyType = keyArray[2]; break; } case 'lighthouse.pageSummary': { // categories.seo.score // categories.performance.score if (key.includes('score')) { tags.audit = keyArray[1]; } if (key.includes('audits')) { tags.audit = keyArray[1]; } break; } case 'crux.pageSummary': { tags.experience = keyArray[0]; tags.formFactor = keyArray[1]; tags.metric = keyArray[2]; break; } case 'gpsi.pageSummary': { if (key.includes('googleWebVitals')) { tags.testType = 'googleWebVitals'; } else if (key.includes('score')) { tags.testType = 'score'; } else if (key.includes('loadingExperience')) { tags.experience = keyArray[0]; tags.metric = keyArray[1]; tags.testType = 'crux'; } break; } default: // console.log('Missed added tags to ' + key + ' ' + type); } return tags; } function getFieldAndSeriesName(key) { const functions = [ 'min', 'p10', 'median', 'mean', 'avg', 'max', 'p90', 'p99', 'mdev', 'stddev', 'rsd' ]; const keyArray = key.split('.'); const end = keyArray.pop(); if (functions.includes(end)) { return { field: end, seriesName: keyArray.pop() }; } return { field: 'value', seriesName: end }; } export class InfluxDBDataGenerator { constructor(includeQueryParameters, options) { this.includeQueryParams = !!includeQueryParameters; this.options = options; this.defaultTags = {}; for (let row of options.influxdb.tags.split(',')) { const keyAndValue = row.split('='); this.defaultTags[keyAndValue[0]] = keyAndValue[1]; } } dataFromMessage(message, time, alias) { function getTagsFromMessage( message, includeQueryParameters, options, defaultTags ) { const tags = merge({}, defaultTags); let typeParts = message.type.split('.'); tags.origin = typeParts[0]; typeParts.push(typeParts.shift()); tags.summaryType = typeParts[0]; // always have browser and connectivity in Browsertime and related tools if ( /(^pagexray|^coach|^browsertime|^thirdparty|^axe|^sustainable)/.test( message.type ) ) { // if we have a friendly name for your connectivity, use that! let connectivity = getConnectivity(options); tags.connectivity = connectivity; tags.browser = options.browser; } else if (/(^gpsi)/.test(message.type)) { tags.strategy = options.mobile ? 'mobile' : 'desktop'; } // if we get a URL type, add the URL if (message.url) { const urlAndGroup = getURLAndGroup( options, message.group, message.url, includeQueryParameters, alias ).split('.'); tags.page = urlAndGroup[1]; tags.group = urlAndGroup[0]; } else if (message.group) { // add the group of the summary message tags.group = toSafeKey(message.group, options.influxdb.groupSeparator); } if (options.slug) { tags.testName = options.slug; } return tags; } return Object.entries(flattenMessageData(message)).reduce( (entries, [key, value]) => { const fieldAndSeriesName = getFieldAndSeriesName(key); let tags = getTagsFromMessage( message, this.includeQueryParams, this.options, this.defaultTags ); tags = { ...getAdditionalTags(key, message.type), ...tags }; const point = { time: time.valueOf(), [fieldAndSeriesName.field]: value }; entries.push({ tags, seriesName: fieldAndSeriesName.seriesName, point }); return entries; }, [] ); } }