First run for Snowplow Vue event dictionary entries (shorter filenames)
What does this MR do and why?
Related to #333993 (closed)
This covers all Snowplow events emitted from frontend using the Vue mixin until date. Any missing event will be added on a follow-up pass.
This is the 2nd version of this MR, as the first one introduced an infrastructure issue and was reverted. The issue was long filenames, so this iteration both removes parts of the filename and also slices it at 95 chars, although there are no cases like this.
Generator used
/* eslint-disable import/no-commonjs */
/* eslint-disable no-use-before-define */
/* eslint-disable no-restricted-syntax */
const fs = require('fs');
const csv = require('csv-parser');
const SOURCE_FILE_PATH = './events.csv';
const FOLDER_PATH = './event_dictionary_generator_output';
const ARRAY_SEPARATOR = ';';
// extra_properties
const baseTemplate = `description:
category:
action:
label_description:
property_description:
value_description:
extra_properties:
identifiers:
product_section:
product_stage:
product_group:
product_category:
milestone:
introduced_by_url:
distributions:
tiers:
`;
createFolderIfNeeded();
iterateRows();
function createFolderIfNeeded() {
if (!fs.existsSync(FOLDER_PATH)) {
fs.mkdirSync(FOLDER_PATH);
}
}
function iterateRows() {
let fileIndex = 0;
fs.createReadStream(SOURCE_FILE_PATH)
.pipe(csv())
.on('data', (chunk) => {
createFileFromRow(chunk, fileIndex++)
});
}
function createFileFromRow(row, fileIndex) {
let template = String(baseTemplate);
for (const [name, rawValue] of Object.entries(row)) {
const label = `${name}:`;
let value = rawValue;
const isArray = value.includes(ARRAY_SEPARATOR) || ['distributions', 'tiers'].includes(name);
if (name === 'category') {
value = value || 'default';
}
if (
[
'description',
'milestone',
'label_description',
'property_description',
'value_description',
].includes(name)
) {
value = `"${value}"`;
}
if (isArray) {
const items = value.split(ARRAY_SEPARATOR).join('\n- ');
template = template.replace(label, `${label}\n- ${items}`);
} else {
const variableValue = value.replace(/^`(.+?)`$/, '"`$1`"');
template = template.replace(label, `${label} ${variableValue}`);
}
}
// Timestamp
const pad = (number, at = 2) => `0${number}`.slice(at * -1);
const now = new Date(Date.now() + (fileIndex * 1000));
const timestamp = [
now.getFullYear(),
pad(now.getMonth() + 1),
pad(now.getDate()),
pad(now.getHours()),
pad(now.getMinutes()),
pad(now.getSeconds()),
].join('');
// Custom slug logic attempt
let filename = `${timestamp}_${row.category || 'default'}_${row.action}`
.replace(/[`\[\]]/g, '')
.replace(/(?:\||:)/g, '_')
.replace(/\s+/g, '_')
.toLowerCase()
.trim()
.replace(/_{2,}/, '_');
// Make sure filename length is lower than 100
filename = filename.slice(0, 95);
const path = `${FOLDER_PATH}/${filename}.yml`;
// Create file
fs.writeFileSync(path, template);
console.info(path, 'created');
}
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.
Edited by Axel García