Move InfluxDB to it own standalone plugin (#4451)

This commit is contained in:
Peter Hedenskog 2025-02-28 15:22:28 +01:00 committed by GitHub
parent b0a98cc973
commit e78200da3e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 18 additions and 2336 deletions

View File

@ -70,10 +70,6 @@ jobs:
run: bin/sitespeed.js http://127.0.0.1:3001/simple/ -n 1 --graphite.host 127.0.0.1 --xvfb
- name: Run test without a CLI
run: xvfb-run node test/runWithoutCli.js
- name: Run test with Influx 1.8
run: bin/sitespeed.js http://127.0.0.1:3001/simple/ -n 1 --influxdb.host 127.0.0.1 --xvfb --logToFile --resultBaseUrl https://result.sitespeed.io --influxdb.annotationScreenshot=true
- name: Run test with Influx 2.6.1
run: bin/sitespeed.js http://127.0.0.1:3001/simple/ -n 1 --influxdb.host 127.0.0.1 --influxdb.port 8087 --influxdb.version 2 --influxdb.organisation sitespeed --influxdb.token sitespeed --xvfb --resultBaseUrl https://result.sitespeed.io --influxdb.annotationScreenshot=true
- name: Run Chrome test with config
run: node bin/sitespeed.js --config test/exampleConfig.json http://127.0.0.1:3001/simple/ --xvfb
- name: Run Chrome test using compare plugin

View File

@ -421,7 +421,7 @@ export async function parseCommandLine() {
})
.option('browsertime.script', {
describe:
'Add custom Javascript that collect metrics and run after the page has finished loading. Note that --script can be passed multiple times if you want to collect multiple metrics. The metrics will automatically be pushed to the summary/detailed summary and each individual page + sent to Graphite/InfluxDB.',
'Add custom Javascript that collect metrics and run after the page has finished loading. Note that --script can be passed multiple times if you want to collect multiple metrics. The metrics will automatically be pushed to the summary/detailed summary and each individual page + sent to Graphite',
alias: ['script'],
group: 'Browser'
})
@ -1346,78 +1346,6 @@ export async function parseCommandLine() {
describe:
'Define which messages to send to Graphite. By default we do not send data per run, but you can change that by adding run as one of the options',
group: 'Graphite'
})
.option('influxdb.protocol', {
describe: 'The protocol used to store connect to the InfluxDB host.',
default: 'http',
group: 'InfluxDB'
})
.option('influxdb.host', {
describe: 'The InfluxDB host used to store captured metrics.',
group: 'InfluxDB'
})
.option('influxdb.port', {
default: 8086,
describe: 'The InfluxDB port used to store captured metrics.',
group: 'InfluxDB'
})
.option('influxdb.username', {
describe:
'The InfluxDB username for your InfluxDB instance (only for InfluxDB v1)',
group: 'InfluxDB'
})
.option('influxdb.password', {
describe:
'The InfluxDB password for your InfluxDB instance (only for InfluxDB v1).',
group: 'InfluxDB'
})
.option('influxdb.organisation', {
describe:
'The InfluxDB organisation for your InfluxDB instance (only for InfluxDB v2)',
group: 'InfluxDB'
})
.option('influxdb.token', {
describe:
'The InfluxDB token for your InfluxDB instance (only for InfluxDB v2)',
group: 'InfluxDB'
})
.option('influxdb.version', {
default: 1,
describe: 'The InfluxDB version of your InfluxDB instance.',
type: 'integer',
group: 'InfluxDB'
})
.option('influxdb.database', {
default: 'sitespeed',
describe: 'The database name used to store captured metrics.',
group: 'InfluxDB'
})
.option('influxdb.tags', {
default: 'category=default',
describe:
'A comma separated list of tags and values added to each metric',
group: 'InfluxDB'
})
.option('influxdb.includeQueryParams', {
default: false,
describe:
'Whether to include query parameters from the URL in the InfluxDB keys or not',
type: 'boolean',
group: 'InfluxDB'
})
.option('influxdb.groupSeparator', {
default: '_',
describe:
'Choose which character that will separate a group/domain. Default is underscore, set it to a dot if you wanna keep the original domain name.',
group: 'InfluxDB'
})
.option('influxdb.annotationScreenshot', {
default: false,
type: 'boolean',
describe:
'Include screenshot (from Browsertime) in the annotation. You need to specify a --resultBaseURL for this to work.',
group: 'InfluxDB'
});
parsed
@ -1527,10 +1455,6 @@ export async function parseCommandLine() {
default: browsertimeConfig.screenshotParams.maxSize,
group: 'Screenshot'
});
/**
InfluxDB cli option
*/
parsed
// Metrics
.option('metrics.list', {
@ -2028,12 +1952,12 @@ export async function parseCommandLine() {
})
.option('urlAlias', {
describe:
'Use an alias for the URL (if you feed URLs from a file you can instead have the alias in the file). You need to pass on the same amount of alias as URLs. The alias is used as the name of the URL on the HTML report and in Graphite/InfluxDB. Pass on multiple --urlAlias for multiple alias/URLs. This will override alias in a file.',
'Use an alias for the URL (if you feed URLs from a file you can instead have the alias in the file). You need to pass on the same amount of alias as URLs. The alias is used as the name of the URL on the HTML report and in Graphite. Pass on multiple --urlAlias for multiple alias/URLs. This will override alias in a file.',
type: 'string'
})
.option('groupAlias', {
describe:
'Use an alias for the group/domain. You need to pass on the same amount of alias as URLs. The alias is used as the name of the group in Graphite/InfluxDB. Pass on multiple --groupAlias for multiple alias/groups. This do not work for scripting at the moment.',
'Use an alias for the group/domain. You need to pass on the same amount of alias as URLs. The alias is used as the name of the group in Graphite. Pass on multiple --groupAlias for multiple alias/groups. This do not work for scripting at the moment.',
type: 'string'
})
.option('utc', {

View File

@ -41,9 +41,7 @@ export function send(
options,
group,
url,
tsdbType === 'graphite'
? options.graphite.includeQueryParams
: options.influxdb.includeQueryParams,
options.graphite.includeQueryParams,
alias
).split('.');

View File

@ -1,262 +0,0 @@
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;
},
[]
);
}
}

View File

@ -1,176 +0,0 @@
import { getLogger } from '@sitespeed.io/log';
import dayjs from 'dayjs';
import { SitespeedioPlugin } from '@sitespeed.io/plugin';
import { InfluxDBSender as Sender } from './sender.js';
import { InfluxDB2Sender as SenderV2 } from './senderV2.js';
import { sendV1 } from './send-annotation.js';
import { sendV2 } from './send-annotationV2.js';
import { InfluxDBDataGenerator as DataGenerator } from './data-generator.js';
import { throwIfMissing, isEmpty } from '../../support/util.js';
const log = getLogger('sitespeedio.plugin.influxdb');
export default class InfluxDBPlugin extends SitespeedioPlugin {
constructor(options, context, queue) {
super({ name: 'influxdb', options, context, queue });
}
open(context, options) {
throwIfMissing(options.influxdb, ['host', 'database'], 'influxdb');
this.filterRegistry = context.filterRegistry;
log.debug(
'Setup InfluxDB host %s and database %s',
options.influxdb.host,
options.influxdb.database
);
const options_ = options.influxdb;
this.options = options;
this.sender =
options_.version == 1 ? new Sender(options_) : new SenderV2(options_);
this.timestamp = context.timestamp;
this.resultUrls = context.resultUrls;
this.dataGenerator = new DataGenerator(
options_.includeQueryParams,
options
);
this.messageTypesToFireAnnotations = [];
this.receivedTypesThatFireAnnotations = {};
this.make = context.messageMaker('influxdb').make;
this.sendAnnotation = true;
this.alias = {};
}
processMessage(message, queue) {
const filterRegistry = this.filterRegistry;
// First catch if we are running Browsertime and/or WebPageTest
switch (message.type) {
case 'browsertime.setup': {
this.messageTypesToFireAnnotations.push('browsertime.pageSummary');
this.usingBrowsertime = true;
break;
}
case 'browsertime.config': {
if (message.data.screenshot) {
this.useScreenshots = message.data.screenshot;
this.screenshotType = message.data.screenshotType;
}
break;
}
case 'sitespeedio.setup': {
// Let other plugins know that the InfluxDB plugin is alive
queue.postMessage(this.make('influxdb.setup'));
break;
}
case 'grafana.setup': {
this.sendAnnotation = false;
break;
}
// No default
}
if (message.type === 'browsertime.alias') {
this.alias[message.url] = message.data;
}
if (
!(
message.type.endsWith('.summary') ||
message.type.endsWith('.pageSummary')
)
) {
return;
}
if (this.messageTypesToFireAnnotations.includes(message.type)) {
this.receivedTypesThatFireAnnotations[message.url]
? this.receivedTypesThatFireAnnotations[message.url]++
: (this.receivedTypesThatFireAnnotations[message.url] = 1);
}
// Let us skip this for a while and concentrate on the real deal
if (
/(^largestassets|^slowestassets|^aggregateassets|^domains)/.test(
message.type
)
)
return;
// we only sends individual groups to Influx, not the
// total of all groups (you can calculate that yourself)
if (message.group === 'total') {
return;
}
message = filterRegistry.filterMessage(message);
if (isEmpty(message.data)) return;
let data = this.dataGenerator.dataFromMessage(
message,
message.type === 'browsertime.pageSummary'
? dayjs(message.runTime)
: this.timestamp,
this.alias
);
if (data.length > 0) {
log.debug('Send the following data to InfluxDB: %:2j', data);
return this.sender.send(data).then(() => {
// Make sure we only send the annotation once per URL:
// If we run browsertime, always send on browsertime.pageSummary
// If we run WebPageTest standalone, send on webPageTestSummary
// when we configured a base url
if (
this.receivedTypesThatFireAnnotations[message.url] ===
this.messageTypesToFireAnnotations.length &&
this.resultUrls.hasBaseUrl() &&
this.sendAnnotation
) {
const absolutePagePath = this.resultUrls.absoluteSummaryPagePath(
message.url,
this.alias[message.url]
);
this.receivedTypesThatFireAnnotations[message.url] = 0;
return this.options.influxdb.version == 2
? sendV2(
message.url,
message.group,
absolutePagePath,
this.useScreenshots,
this.screenshotType,
// Browsertime pass on when the first run was done for that URL
message.runTime,
this.alias,
this.usingBrowsertime,
this.options
)
: sendV1(
message.url,
message.group,
absolutePagePath,
this.useScreenshots,
this.screenshotType,
// Browsertime pass on when the first run was done for that URL
message.runTime,
this.alias,
this.usingBrowsertime,
this.options
);
}
});
} else {
return Promise.reject(
new Error(
'No data to send to influxdb for message:\n' +
JSON.stringify(message, undefined, 2)
)
);
}
}
}

View File

@ -1,111 +0,0 @@
import http from 'node:http';
import https from 'node:https';
import { stringify } from 'node:querystring';
import { getLogger } from '@sitespeed.io/log';
import dayjs from 'dayjs';
import { getConnectivity, getURLAndGroup } from '../../support/tsdbUtil.js';
import {
getAnnotationMessage,
getTagsAsString
} from '../../support/annotationsHelper.js';
const log = getLogger('sitespeedio.plugin.influxdb');
export function sendV1(
url,
group,
absolutePagePath,
screenShotsEnabledInBrowsertime,
screenshotType,
runTime,
alias,
usingBrowsertime,
options
) {
// The tags make it possible for the dashboard to use the
// templates to choose which annotations that will be showed.
// That's why we need to send tags that matches the template
// variables in Grafana.
const connectivity = getConnectivity(options);
const browser = options.browser;
const urlAndGroup = getURLAndGroup(
options,
group,
url,
options.influxdb.includeQueryParams,
alias
).split('.');
let tags = [connectivity, browser, urlAndGroup[0], urlAndGroup[1]];
if (options.slug) {
tags.push(options.slug);
}
const message = getAnnotationMessage(
absolutePagePath,
screenShotsEnabledInBrowsertime,
screenshotType,
undefined,
usingBrowsertime,
options
);
const timestamp = runTime
? Math.round(dayjs(runTime) / 1000)
: Math.round(dayjs() / 1000);
// if we have a category, let us send that category too
if (options.influxdb.tags) {
for (let row of options.influxdb.tags.split(',')) {
const keyAndValue = row.split('=');
tags.push(keyAndValue[1]);
}
}
const influxDBTags = getTagsAsString(tags);
const postData = `events title="Sitespeed.io",text="${message}",tags=${influxDBTags} ${timestamp}`;
const postOptions = {
hostname: options.influxdb.host,
port: options.influxdb.port,
path: '/write?db=' + options.influxdb.database + '&precision=s',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
};
if (options.influxdb.username) {
postOptions.path =
postOptions.path +
'&' +
stringify({
u: options.influxdb.username,
p: options.influxdb.password
});
}
return new Promise((resolve, reject) => {
log.debug('Send annotation to Influx: %j', postData);
// not perfect but maybe work for us
const library = options.influxdb.protocol === 'https' ? https : http;
const request = library.request(postOptions, res => {
if (res.statusCode === 204) {
res.setEncoding('utf8');
log.debug('Sent annotation to InfluxDB');
resolve();
} else {
const e = new Error(
`Got ${res.statusCode} from InfluxDB when sending annotation ${res.statusMessage}`
);
log.warn(e.message);
reject(e);
}
});
request.on('error', error => {
log.error('Got error from InfluxDB when sending annotation', error);
reject(error);
});
request.write(postData);
request.end();
});
}

View File

@ -1,106 +0,0 @@
import http from 'node:http';
import https from 'node:https';
import { getLogger } from '@sitespeed.io/log';
import dayjs from 'dayjs';
import { getConnectivity, getURLAndGroup } from '../../support/tsdbUtil.js';
import {
getAnnotationMessage,
getTagsAsString
} from '../../support/annotationsHelper.js';
const log = getLogger('sitespeedio.plugin.influxdb');
export function sendV2(
url,
group,
absolutePagePath,
screenShotsEnabledInBrowsertime,
screenshotType,
runTime,
alias,
usingBrowsertime,
options
) {
// The tags make it possible for the dashboard to use the
// templates to choose which annotations that will be showed.
// That's why we need to send tags that matches the template
// variables in Grafana.
const connectivity = getConnectivity(options);
const browser = options.browser;
const urlAndGroup = getURLAndGroup(
options,
group,
url,
options.influxdb.includeQueryParams,
alias
).split('.');
let tags = [
`connectivity=${connectivity}`,
`browser=${browser}`,
`group=${urlAndGroup[0]}`,
`page=${urlAndGroup[1]}`
];
if (options.slug) {
tags.push(`slug=${options.slug}`);
}
const message = getAnnotationMessage(
absolutePagePath,
screenShotsEnabledInBrowsertime,
screenshotType,
undefined,
usingBrowsertime,
options
);
const timestamp = runTime
? Math.round(dayjs(runTime) / 1000)
: Math.round(dayjs() / 1000);
// if we have a category, let us send that category too
if (options.influxdb.tags) {
for (const tag of options.influxdb.tags.split(',')) {
tags.push(tag);
}
}
const influxDBTags = tags.join(',');
const grafanaTags = getTagsAsString(tags.map(pair => pair.split('=')[1]));
const postData = `annotations,${influxDBTags} title="Sitespeed.io",text="${message}",tags=${grafanaTags} ${timestamp}`;
const postOptions = {
hostname: options.influxdb.host,
port: options.influxdb.port,
path: `/api/v2/write?org=${options.influxdb.organisation}&bucket=${options.influxdb.database}&precision=s`,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData),
Authorization: `Token ${options.influxdb.token}`
}
};
return new Promise((resolve, reject) => {
log.debug('Send annotation to Influx: %j', postData);
// not perfect but maybe work for us
const library = options.influxdb.protocol === 'https' ? https : http;
const request = library.request(postOptions, res => {
if (res.statusCode === 204) {
res.setEncoding('utf8');
log.debug('Sent annotation to InfluxDB');
resolve();
} else {
const e = new Error(
`Got ${res.statusCode} from InfluxDB when sending annotation ${res.statusMessage}`
);
log.warn(e.message);
reject(e);
}
});
request.on('error', error => {
log.error('Got error from InfluxDB when sending annotation', error);
reject(error);
});
request.write(postData);
request.end();
});
}

View File

@ -1,26 +0,0 @@
import { InfluxDB } from 'influx';
export class InfluxDBSender {
constructor({ protocol, host, port, database, username, password }) {
this.client = new InfluxDB({
protocol,
host,
port,
database,
username,
password
});
}
send(data) {
const points = [];
for (let point of data) {
points.push({
tags: point.tags,
measurement: point.seriesName,
fields: point.point
});
}
return this.client.writePoints(points);
}
}

View File

@ -1,38 +0,0 @@
import { InfluxDB, Point, HttpError } from '@influxdata/influxdb-client';
export class InfluxDB2Sender {
constructor({ protocol, host, port, database, organisation, token }) {
this.client = new InfluxDB({
url: `${protocol}://${host}:${port}`,
token
}).getWriteApi(organisation, database);
this.database = database;
}
send(data) {
const points = [];
for (let point of data) {
const influxPoint = new Point(point.seriesName);
for (const key of Object.keys(point.tags)) {
influxPoint.tag(key, point.tags[key]);
}
for (const key of Object.keys(point.point)) {
if (key === 'time') {
influxPoint.timestamp(new Date(point.point[key]));
} else {
influxPoint.floatField(key, point.point[key]);
}
}
points.push(influxPoint);
}
this.client.writePoints(points);
return this.client.flush().catch(error => {
if (error instanceof HttpError && error.statusCode === 401) {
throw new Error(
`The InfluxDB database: ${this.database} doesn't exist.`
);
}
throw new Error('Writing to influx failed');
});
}
}

1144
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -83,7 +83,6 @@
"dependencies": {
"@aws-sdk/client-s3": "3.717.0",
"@google-cloud/storage": "7.14.0",
"@influxdata/influxdb-client": "1.33.2",
"@sitespeed.io/log": "0.2.6",
"@sitespeed.io/plugin": "1.0.0",
"@tgwf/co2": "0.16.4",
@ -95,7 +94,6 @@
"fast-crc32c": "2.0.0",
"fast-stats": "0.0.7",
"import-global": "1.1.1",
"influx": "5.9.3",
"lodash.get": "4.4.2",
"lodash.merge": "4.6.2",
"lodash.set": "4.3.2",

View File

@ -4,21 +4,4 @@ services:
image: sitespeedio/graphite:1.1.5-12
ports:
- "2003:2003"
- "8080:80"
influxdb_v1.8:
image: influxdb:1.8
ports:
- '8086:8086'
environment:
- INFLUXDB_DB=sitespeed
influxdb_v2.6:
image: influxdb:2.6.1
ports:
- '8087:8086'
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=sitespeed
- DOCKER_INFLUXDB_INIT_PASSWORD=sitespeed
- DOCKER_INFLUXDB_INIT_ORG=sitespeed
- DOCKER_INFLUXDB_INIT_BUCKET=sitespeed
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=sitespeed
- "8080:80"

View File

@ -1,359 +0,0 @@
import dayjs from 'dayjs';
import test from 'ava';
import { InfluxDBDataGenerator as DataGenerator } from '../lib/plugins/influxdb/data-generator.js';
test(`Test influxdb dataGenerator`, t => {
const message = {
uuid: '33774328-e781-4152-babe-a367cee27153',
type: 'coach.summary',
timestamp: '2017-04-04T09:55:59+02:00',
source: 'coach',
data: {
score: {
median: '96',
mean: '96',
min: '96',
p90: '96',
max: '96'
},
accessibility: {
score: {
median: '95',
mean: '95',
min: '95',
p90: '95',
max: '95'
},
altImages: {
median: '80',
mean: '80',
min: '80',
p90: '80',
max: '80'
},
headings: {
median: '90',
mean: '90',
min: '90',
p90: '90',
max: '90'
},
labelOnInput: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
landmarks: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
neverSuppressZoom: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
sections: {
median: '0',
mean: '0',
min: '0',
p90: '0',
max: '0'
},
table: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
}
},
bestpractice: {
score: {
median: '85',
mean: '85',
min: '85',
p90: '85',
max: '85'
},
charset: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
doctype: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
https: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
httpsH2: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
language: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
metaDescription: {
median: '50',
mean: '50',
min: '50',
p90: '50',
max: '50'
},
optimizely: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
pageTitle: {
median: '50',
mean: '50',
min: '50',
p90: '50',
max: '50'
},
spdy: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
url: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
}
},
performance: {
score: {
median: '98',
mean: '98',
min: '98',
p90: '98',
max: '98'
},
avoidScalingImages: {
median: '50',
mean: '50',
min: '50',
p90: '50',
max: '50'
},
cssPrint: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
fastRender: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
inlineCss: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
jquery: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
spof: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
thirdPartyAsyncJs: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
assetsRedirects: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
cacheHeaders: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
cacheHeadersLong: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
compressAssets: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
connectionKeepAlive: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
cssSize: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
documentRedirect: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
favicon: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
fewFonts: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
fewRequestsPerDomain: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
headerSize: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
imageSize: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
javascriptSize: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
mimeTypes: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
optimalCssSize: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
pageSize: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
privateAssets: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
},
responseOk: {
median: '100',
mean: '100',
min: '100',
p90: '100',
max: '100'
}
}
},
group: 'www.sitespeed.io'
};
let generator = new DataGenerator(false, {
_: ['filename'],
browser: 'chrome',
connectivity: 'cable',
influxdb: {
tags: 'tool=sitespeed.io'
}
});
const data = generator.dataFromMessage(message, dayjs());
const seriesName = data[0].seriesName;
const numberOfTags = Object.keys(data[0].tags).length;
t.is(seriesName, 'score');
t.is(numberOfTags, 6);
});

View File

@ -1,21 +0,0 @@
version: '2'
services:
grafana:
image: grafana/grafana
depends_on:
- influxdb
links:
- influxdb
ports:
- "3000:3000"
influxdb:
image: tutum/influxdb
environment:
- PRE_CREATE_DB="sitespeed"
- ADMIN_USER="root"
- INFLUXDB_INIT_PWD="root"
ports:
- "8083:8083"
- "8086:8086"
- "8090:8090"
- "8099:8099"