refs #5563 config check fixes, options added
gitea/mylogger/pipeline/head This commit looks good
Details
gitea/mylogger/pipeline/head This commit looks good
Details
New options: logId, logRelation, showRelation and logFields
This commit is contained in:
parent
512bd56a2c
commit
ab775da88a
|
@ -1,3 +1,6 @@
|
||||||
|
logId: true
|
||||||
|
logRelation: true
|
||||||
|
logMainShowField: false
|
||||||
upperCaseTable: true
|
upperCaseTable: true
|
||||||
userField: editorFk
|
userField: editorFk
|
||||||
rowExcludeField: logExclude
|
rowExcludeField: logExclude
|
||||||
|
@ -19,5 +22,7 @@ logs:
|
||||||
- itemTag
|
- itemTag
|
||||||
- name: item
|
- name: item
|
||||||
showField: name
|
showField: name
|
||||||
|
logFields:
|
||||||
|
- size
|
||||||
exclude:
|
exclude:
|
||||||
- image
|
- image
|
||||||
|
|
|
@ -2,6 +2,9 @@ const path = require('path');
|
||||||
const {loadConfig, toUpperCamelCase} = require('./util');
|
const {loadConfig, toUpperCamelCase} = require('./util');
|
||||||
const MultiMap = require('./multi-map');
|
const MultiMap = require('./multi-map');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads model configuration.
|
||||||
|
*/
|
||||||
module.exports = class ModelLoader {
|
module.exports = class ModelLoader {
|
||||||
init(logger) {
|
init(logger) {
|
||||||
const configDir = path.join(__dirname, '..');
|
const configDir = path.join(__dirname, '..');
|
||||||
|
@ -54,7 +57,7 @@ module.exports = class ModelLoader {
|
||||||
if (!tableInfo) {
|
if (!tableInfo) {
|
||||||
tableInfo = {
|
tableInfo = {
|
||||||
conf: tableConf,
|
conf: tableConf,
|
||||||
log: logInfo
|
logInfo
|
||||||
};
|
};
|
||||||
schemaMap.set(table.schema, table.name, tableInfo);
|
schemaMap.set(table.schema, table.name, tableInfo);
|
||||||
}
|
}
|
||||||
|
@ -85,10 +88,11 @@ module.exports = class ModelLoader {
|
||||||
? new RegExp(conf.excludeRegex) : null;
|
? new RegExp(conf.excludeRegex) : null;
|
||||||
|
|
||||||
const localProps = [
|
const localProps = [
|
||||||
'idName'
|
'idName',
|
||||||
|
'showField',
|
||||||
|
'logFields'
|
||||||
];
|
];
|
||||||
const globalProps = [
|
const globalProps = [
|
||||||
'showField',
|
|
||||||
'userField',
|
'userField',
|
||||||
'rowExcludeField'
|
'rowExcludeField'
|
||||||
];
|
];
|
||||||
|
@ -106,9 +110,10 @@ module.exports = class ModelLoader {
|
||||||
|
|
||||||
// Fetch columns & types
|
// Fetch columns & types
|
||||||
|
|
||||||
|
const columns = new Set();
|
||||||
Object.assign (tableInfo, {
|
Object.assign (tableInfo, {
|
||||||
castTypes: new Map(),
|
castTypes: new Map(),
|
||||||
columns: new Map()
|
columns
|
||||||
});
|
});
|
||||||
|
|
||||||
if (tableConf.types)
|
if (tableConf.types)
|
||||||
|
@ -118,8 +123,7 @@ module.exports = class ModelLoader {
|
||||||
const [dbCols] = await db.query(
|
const [dbCols] = await db.query(
|
||||||
`SELECT
|
`SELECT
|
||||||
COLUMN_NAME \`col\`,
|
COLUMN_NAME \`col\`,
|
||||||
DATA_TYPE \`type\`,
|
DATA_TYPE \`type\`
|
||||||
COLUMN_DEFAULT \`def\`
|
|
||||||
FROM information_schema.\`COLUMNS\`
|
FROM information_schema.\`COLUMNS\`
|
||||||
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?`,
|
WHERE TABLE_NAME = ? AND TABLE_SCHEMA = ?`,
|
||||||
[table, schema]
|
[table, schema]
|
||||||
|
@ -128,14 +132,14 @@ module.exports = class ModelLoader {
|
||||||
const exclude = new Set(tableConf.exclude);
|
const exclude = new Set(tableConf.exclude);
|
||||||
exclude.add(tableInfo.userField);
|
exclude.add(tableInfo.userField);
|
||||||
|
|
||||||
for (const {col, type, def} of dbCols) {
|
for (const {col, type} of dbCols) {
|
||||||
const isExcluded =
|
const isExcluded =
|
||||||
excludeFields.has(col)
|
excludeFields.has(col)
|
||||||
|| (excludeRegex && excludeRegex.test(col))
|
|| (excludeRegex && excludeRegex.test(col))
|
||||||
|| exclude.has(col);
|
|| exclude.has(col);
|
||||||
|
|
||||||
if (!isExcluded)
|
if (!isExcluded)
|
||||||
tableInfo.columns.set(col, {type, def});
|
columns.add(col);
|
||||||
|
|
||||||
const castType = conf.castTypes[type];
|
const castType = conf.castTypes[type];
|
||||||
if (castType && !tableInfo.castTypes.has(col))
|
if (castType && !tableInfo.castTypes.has(col))
|
||||||
|
@ -144,9 +148,10 @@ module.exports = class ModelLoader {
|
||||||
|
|
||||||
// Fetch primary key
|
// Fetch primary key
|
||||||
|
|
||||||
if (!tableInfo.idName) {
|
const {idName} = tableInfo;
|
||||||
const [dbPks] = await db.query(
|
if (!idName) {
|
||||||
`SELECT COLUMN_NAME idName
|
const [pks] = await db.query(
|
||||||
|
`SELECT COLUMN_NAME pk
|
||||||
FROM information_schema.KEY_COLUMN_USAGE
|
FROM information_schema.KEY_COLUMN_USAGE
|
||||||
WHERE CONSTRAINT_NAME = 'PRIMARY'
|
WHERE CONSTRAINT_NAME = 'PRIMARY'
|
||||||
AND TABLE_NAME = ?
|
AND TABLE_NAME = ?
|
||||||
|
@ -154,38 +159,39 @@ module.exports = class ModelLoader {
|
||||||
[table, schema]
|
[table, schema]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!dbPks.length)
|
if (!pks.length)
|
||||||
throw new Error(`Primary not found for table: ${schema}.${table}`);
|
throw new Error(`Primary key not found: ${schema}.${table}`);
|
||||||
if (dbPks.length > 1)
|
if (pks.length > 1)
|
||||||
throw new Error(`Only one column primary is supported: ${schema}.${table}`);
|
throw new Error(`Only one column primary is supported: ${schema}.${table}`);
|
||||||
|
|
||||||
for (const {idName} of dbPks)
|
const [{pk}] = pks;
|
||||||
tableInfo.idName = idName;
|
tableInfo.idName = pk;
|
||||||
}
|
} else if (!columns.has(idName))
|
||||||
|
throw new Error(`Primary column not found: ${schema}.${table}.${idName}`);
|
||||||
|
|
||||||
// Get show field
|
// Get show field
|
||||||
|
|
||||||
const {showField} = tableInfo;
|
const {showField} = tableInfo;
|
||||||
if (showField !== null && !tableInfo.isMain) {
|
if (showField !== null) {
|
||||||
if (showField === undefined) {
|
if (showField === undefined) {
|
||||||
|
if (!tableInfo.isMain || conf.logMainShowField)
|
||||||
for (const field of conf.showFields) {
|
for (const field of conf.showFields) {
|
||||||
if (tableInfo.columns.has(field)) {
|
if (columns.has(field)) {
|
||||||
tableInfo.showField = field;
|
tableInfo.showField = field;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!columns.has(showField))
|
||||||
const match = showField.match(/(^.*)\$$/);
|
throw new Error(`Show column not found: ${schema}.${table}.${showField}`);
|
||||||
if (match) tableInfo.showRelation = match[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch relation to main table
|
|
||||||
|
|
||||||
for (const [schema, table, tableInfo] of schemaMap) {
|
for (const [schema, table, tableInfo] of schemaMap) {
|
||||||
|
|
||||||
|
// Fetch relation to main table
|
||||||
|
|
||||||
if (!tableInfo.conf.relation && !tableInfo.isMain) {
|
if (!tableInfo.conf.relation && !tableInfo.isMain) {
|
||||||
const mainTable = tableInfo.log.mainTable;
|
const mainTable = tableInfo.logInfo.mainTable;
|
||||||
const mainInfo = schemaMap.get(mainTable.schema, mainTable.name);
|
const mainInfo = schemaMap.get(mainTable.schema, mainTable.name);
|
||||||
|
|
||||||
const [mainRelations] = await db.query(
|
const [mainRelations] = await db.query(
|
||||||
|
@ -213,6 +219,16 @@ module.exports = class ModelLoader {
|
||||||
for (const {relation} of mainRelations)
|
for (const {relation} of mainRelations)
|
||||||
tableInfo.relation = relation;
|
tableInfo.relation = relation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set instance columns
|
||||||
|
|
||||||
|
const {columns} = tableInfo;
|
||||||
|
const cols = new Set(columns);
|
||||||
|
if (!conf.logId)
|
||||||
|
cols.delete(tableInfo.idName);
|
||||||
|
if (!conf.logRelation)
|
||||||
|
cols.delete(tableInfo.relation);
|
||||||
|
tableInfo.instanceColumns = [...cols.keys()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const MultiMap = require("./multi-map");
|
const MultiMap = require("./multi-map");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: #5563 Fetch relations and show values in fronted
|
* Caches and sets user friendly values for instaces and relations.
|
||||||
*/
|
*/
|
||||||
module.exports = class ShowDb {
|
module.exports = class ShowDb {
|
||||||
init(logger) {
|
init(logger) {
|
||||||
|
@ -107,7 +107,7 @@ module.exports = class ShowDb {
|
||||||
if (save) tableInfo.showField = col;
|
if (save) tableInfo.showField = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean tables and relations without required information
|
// Remove tables without showable columns and prepare SELECT statements
|
||||||
|
|
||||||
for (const [schema, table] of relatedList) {
|
for (const [schema, table] of relatedList) {
|
||||||
const tableInfo = tables.get(schema, table);
|
const tableInfo = tables.get(schema, table);
|
||||||
|
@ -127,10 +127,26 @@ module.exports = class ShowDb {
|
||||||
WHERE ${sqlIdName} IN (?)`;
|
WHERE ${sqlIdName} IN (?)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tableInfo of schemaMap.values())
|
// Remove relations without showable tables and set show configuration
|
||||||
for (const [col, relation] of tableInfo.relations) {
|
|
||||||
if (!tables.has(relation.schema, relation.table))
|
for (const [schema, table, tableInfo] of schemaMap) {
|
||||||
tableInfo.relations.delete(col);
|
const {relations} = tableInfo;
|
||||||
|
|
||||||
|
for (const [col, relation] of relations) {
|
||||||
|
if (!tables.has(relation.schema, relation.table))
|
||||||
|
relations.delete(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
const {showRelation} = tableInfo.conf;
|
||||||
|
if (showRelation) {
|
||||||
|
if (!relations.has(showRelation))
|
||||||
|
throw new Error(`Relation not found: ${schema}.${table}.${showRelation}`);
|
||||||
|
|
||||||
|
Object.assign(tableInfo, {
|
||||||
|
showField: showRelation +'$',
|
||||||
|
showRelation
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
34
mylogger.js
34
mylogger.js
|
@ -313,10 +313,7 @@ module.exports = class MyLogger {
|
||||||
if (!tableInfo) return;
|
if (!tableInfo) return;
|
||||||
|
|
||||||
const action = actions[eventName];
|
const action = actions[eventName];
|
||||||
const {
|
const {rowExcludeField} = tableInfo;
|
||||||
columns,
|
|
||||||
rowExcludeField
|
|
||||||
} = tableInfo;
|
|
||||||
const changes = [];
|
const changes = [];
|
||||||
|
|
||||||
function isExcluded(row) {
|
function isExcluded(row) {
|
||||||
|
@ -354,6 +351,8 @@ module.exports = class MyLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == 'update') {
|
if (action == 'update') {
|
||||||
|
const cols = tableInfo.columns;
|
||||||
|
|
||||||
for (const row of evt.rows) {
|
for (const row of evt.rows) {
|
||||||
const after = row.after;
|
const after = row.after;
|
||||||
if (isExcluded(after)) continue;
|
if (isExcluded(after)) continue;
|
||||||
|
@ -364,10 +363,9 @@ module.exports = class MyLogger {
|
||||||
let nColsChanged = 0;
|
let nColsChanged = 0;
|
||||||
|
|
||||||
for (const col in before) {
|
for (const col in before) {
|
||||||
if (columns.has(col)
|
if (cols.has(col)
|
||||||
&& !equals(after[col], before[col])) {
|
&& !equals(after[col], before[col])) {
|
||||||
if (before[col] !== null)
|
oldI[col] = castValue(col, before[col]);
|
||||||
oldI[col] = castValue(col, before[col]);
|
|
||||||
newI[col] = castValue(col, after[col]);
|
newI[col] = castValue(col, after[col]);
|
||||||
nColsChanged++;
|
nColsChanged++;
|
||||||
}
|
}
|
||||||
|
@ -376,7 +374,7 @@ module.exports = class MyLogger {
|
||||||
changes.push({row: after, oldI, newI});
|
changes.push({row: after, oldI, newI});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const cols = columns.keys();
|
const cols = tableInfo.instanceColumns;
|
||||||
|
|
||||||
for (const row of evt.rows) {
|
for (const row of evt.rows) {
|
||||||
if (isExcluded(row)) continue;
|
if (isExcluded(row)) continue;
|
||||||
|
@ -474,13 +472,16 @@ module.exports = class MyLogger {
|
||||||
evt,
|
evt,
|
||||||
changes
|
changes
|
||||||
} = op;
|
} = op;
|
||||||
|
const {
|
||||||
|
logInfo,
|
||||||
|
isMain,
|
||||||
|
relation,
|
||||||
|
modelName,
|
||||||
|
logFields
|
||||||
|
} = tableInfo;
|
||||||
|
|
||||||
const logInfo = tableInfo.log;
|
|
||||||
const isDelete = action == 'delete';
|
const isDelete = action == 'delete';
|
||||||
const isUpdate = action == 'update';
|
const isUpdate = action == 'update';
|
||||||
const isSecondary = !tableInfo.isMain;
|
|
||||||
const relation = tableInfo.relation;
|
|
||||||
const modelName = tableInfo.modelName;
|
|
||||||
const created = new Date(evt.timestamp);
|
const created = new Date(evt.timestamp);
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
|
@ -491,6 +492,11 @@ module.exports = class MyLogger {
|
||||||
case 'update':
|
case 'update':
|
||||||
newI = change.newI;
|
newI = change.newI;
|
||||||
oldI = change.oldI;
|
oldI = change.oldI;
|
||||||
|
if (logFields) {
|
||||||
|
for (const field of logFields)
|
||||||
|
if (newI[field] === undefined)
|
||||||
|
newI[field] = row[field];
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'insert':
|
case 'insert':
|
||||||
newI = change.instance;
|
newI = change.instance;
|
||||||
|
@ -503,8 +509,8 @@ module.exports = class MyLogger {
|
||||||
const modelId = row[tableInfo.idName];
|
const modelId = row[tableInfo.idName];
|
||||||
const modelValue = change.modelValue ?? null;
|
const modelValue = change.modelValue ?? null;
|
||||||
const oldInstance = oldI ? JSON.stringify(oldI) : null;
|
const oldInstance = oldI ? JSON.stringify(oldI) : null;
|
||||||
const originFk = isSecondary ? row[relation] : modelId;
|
const originFk = !isMain ? row[relation] : modelId;
|
||||||
const originChanged = isUpdate && isSecondary
|
const originChanged = isUpdate && !isMain
|
||||||
&& newI[relation] !== undefined;
|
&& newI[relation] !== undefined;
|
||||||
|
|
||||||
let deleteRow;
|
let deleteRow;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "mylogger",
|
"name": "mylogger",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mylogger",
|
"name": "mylogger",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mylogger",
|
"name": "mylogger",
|
||||||
"version": "1.0.0",
|
"version": "1.1.0",
|
||||||
"author": "Verdnatura Levante SL",
|
"author": "Verdnatura Levante SL",
|
||||||
"description": "MySQL and MariaDB logger using binary log",
|
"description": "MySQL and MariaDB logger using binary log",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
|
|
Loading…
Reference in New Issue