sitespeed.io/lib/support/flattenMessage.js

168 lines
4.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { getLogger } from '@sitespeed.io/log';
const log = getLogger('sitespeedio');
function joinNonEmpty(strings, delimeter) {
return strings.filter(Boolean).join(delimeter);
}
function toSafeKey(key) {
// U+2013 : EN DASH as used on https://en.wikipedia.org/wiki/201920_coronavirus_pandemic
return key.replaceAll(/[ %&()+,./:?|~]|%7C/g, '_');
}
export function keypathFromUrl(
urlString,
includeQueryParameters,
useHash,
group
) {
function flattenQueryParameters(parameters) {
return Object.keys(parameters).reduce(
(result, key) => joinNonEmpty([result, key, parameters[key]], '_'),
''
);
}
const url = new URL(urlString);
let path = toSafeKey(url.pathname);
if (includeQueryParameters) {
const parameters = {};
for (const [key, value] of url.searchParams) {
parameters[key] = value;
}
path = joinNonEmpty(
[path, toSafeKey(flattenQueryParameters(parameters))],
'_'
);
}
if (useHash && url.hash) {
path = joinNonEmpty([path, toSafeKey(url.hash)], '_');
}
const keys = [toSafeKey(group || url.hostname), path];
return joinNonEmpty(keys, '.');
}
function isNumericString(n) {
// eslint-disable-next-line unicorn/prefer-number-properties
return !isNaN(Number.parseFloat(n)) && isFinite(n);
}
export function flattenMessageData({ data, type }) {
function recursiveFlatten(target, keyPrefix, value) {
// super simple version to avoid flatten HAR and screenshot data
if (/(screenshots\.|har\.)/.test(keyPrefix)) {
return;
}
// Google is overloading User Timing marks
// See https://github.com/sitespeedio/browsertime/issues/257
if (keyPrefix.includes('userTimings.marks.goog_')) {
return;
}
// Google is overloading User Timing marks = the same using WebPageTest
// See https://github.com/sitespeedio/browsertime/issues/257
if (keyPrefix.includes('userTimes.goog_')) {
return;
}
// Hack to remove visual progress from default metrics
if (keyPrefix.includes('visualMetrics.VisualProgress')) {
return;
}
if (keyPrefix.includes('visualMetrics.videoRecordingStart')) {
return;
}
const valueType = typeof value;
switch (valueType) {
case 'number': {
{
if (Number.isFinite(value)) {
target[keyPrefix] = value;
} else {
log.warn(
`Non-finite number '${value}' found at path '${keyPrefix}' for '${type}' message (url = ${data.url})`
);
}
}
break;
}
case 'object': {
{
if (value === null) {
break;
}
for (const key of Object.keys(value)) {
// Hey are you coming to the future from 1980s? Please don't
// look at this code, it's a ugly hack to make sure we can send assets
// to Graphite and don't send them with array position, instead
// use the url to generate the key
if (type === 'pagexray.pageSummary' && keyPrefix === 'assets') {
recursiveFlatten(
target,
joinNonEmpty(
[keyPrefix, toSafeKey(value[key].url || key)],
'.'
),
value[key]
);
} else {
recursiveFlatten(
target,
joinNonEmpty([keyPrefix, toSafeKey(key)], '.'),
value[key]
);
}
}
}
break;
}
case 'string': {
{
if (isNumericString(value)) {
target[keyPrefix] = Number.parseFloat(value);
}
}
break;
}
case 'boolean': {
{
target[keyPrefix] = value ? 1 : 0;
}
break;
}
case 'undefined': {
{
log.debug(
`Undefined value found at path '${keyPrefix}' for '${type}' message (url = ${data.url})`
);
}
break;
}
default: {
throw new Error(
'Unhandled value type ' +
valueType +
' found when flattening data for prefix ' +
keyPrefix
);
}
}
}
let returnValueValue = {};
recursiveFlatten(returnValueValue, '', data);
return returnValueValue;
}