Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OGUI-1455] Logging live mode filters/criteria #2684

Open
wants to merge 8 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions Framework/Backend/test/mocha-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/**
* @license
* Copyright 2019-2020 CERN and copyright holders of ALICE O2.
* See http://alice-o2.web.cern.ch/copyright for details of the copyright holders.
* All rights not expressly granted are reserved.
*
* This software is distributed under the terms of the GNU General Public
* License v3 (GPL Version 3), copied verbatim in the file "COPYING".
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/
const assert = require('assert');
const { minifyCriteria } = require('../utils/minifyCriteria.js');

const filters = {
timestamp: {
since: '13:02:30',
until: '13:02:40',
$since: '2024-12-02T12:02:30.000Z',
$until: '2024-12-02T12:02:40.000Z',
},
hostname: {
match: 'aldaqecs01-v1',
exclude: '',
$match: 'aldaqecs01-v1',
$exclude: null,
},
rolename: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
pid: {
match: '50990',
exclude: '',
$match: '50990',
$exclude: null,
},
username: {
match: 'alicedaq',
exclude: '',
$match: 'alicedaq',
$exclude: null,
},
system: {
match: 'DAQ',
exclude: '',
$match: 'DAQ',
$exclude: null,
},
facility: {
match: 'runControl',
exclude: '',
$match: 'runControl',
$exclude: null,
},
detector: {
match: 'TPC',
exclude: '',
$match: 'TPC',
$exclude: null,
},
partition: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
run: {
match: '248023',
exclude: '',
$match: '248023',
$exclude: null,
},
errcode: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
errline: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
errsource: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
message: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
severity: {
in: 'I F',
$in: [
'I',
'F',
],
},
level: {
max: null,
$max: null,
},
};

const minifiedFilters = '{"timestamp":{"since":"13:02:30","until":"13:02:40"},"hostname":{"match":"aldaqecs01-v1"},' +
'"pid":{"match":"50990"},"username":{"match":"alicedaq"},"system":{"match":"DAQ"},"facility":{"match":"runControl"},' +
'"detector":{"match":"TPC"},"run":{"match":"248023"},"severity":{"in":"I F"}}';

describe('Utils - minifyCriteria()', () => {
it('minifyCriteria() works as expected', (done) => {
const criterias = minifyCriteria(filters);
assert.strictEqual(JSON.stringify(criterias), minifiedFilters);
done();
});
});
160 changes: 147 additions & 13 deletions Framework/Backend/test/mocha-ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,151 @@
*/

const config = require('./../config-default.json');
const WebSocketClient = require('ws');
const { WebSocket: WsClient } = require('ws');
const assert = require('assert');
const WebSocket = require('./../websocket/server');
const HttpServer = require('./../http/server');
const O2TokenService = require('./../services/O2TokenService.js');
const sinon = require('sinon');
const WebSocketMessage = require('./../websocket/message.js');

process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

let http, ws, tokenService, token; // eslint-disable-line
let http, wss, tokenService, token; // eslint-disable-line

const filters = {
timestamp: {
since: '13:02:30',
until: '13:02:40',
$since: '2024-12-02T12:02:30.000Z',
$until: '2024-12-02T12:02:40.000Z',
},
hostname: {
match: 'aldaqecs01-v1',
exclude: '',
$match: 'aldaqecs01-v1',
$exclude: null,
},
rolename: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
pid: {
match: '50990',
exclude: '',
$match: '50990',
$exclude: null,
},
username: {
match: 'alicedaq',
exclude: '',
$match: 'alicedaq',
$exclude: null,
},
system: {
match: 'DAQ',
exclude: '',
$match: 'DAQ',
$exclude: null,
},
facility: {
match: 'runControl',
exclude: '',
$match: 'runControl',
$exclude: null,
},
detector: {
match: 'TPC',
exclude: '',
$match: 'TPC',
$exclude: null,
},
partition: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
run: {
match: '248023',
exclude: '',
$match: '248023',
$exclude: null,
},
errcode: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
errline: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
errsource: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
message: {
match: '',
exclude: '',
$match: null,
$exclude: null,
},
severity: {
in: 'I F',
$in: [
'I',
'F',
],
},
level: {
max: null,
$max: null,
},
};

const minifiedFilters = '{"timestamp":{"since":"13:02:30","until":"13:02:40"},"hostname":{"match":"aldaqecs01-v1"},' +
'"pid":{"match":"50990"},"username":{"match":"alicedaq"},"system":{"match":"DAQ"},"facility":{"match":"runControl"},' +
'"detector":{"match":"TPC"},"run":{"match":"248023"},"severity":{"in":"I F"}}';

describe('websocket', () => {
before(() => {
tokenService = new O2TokenService(config.jwt);
token = tokenService.generateToken(0, 'test', 'Test', 'admin');

http = new HttpServer(config.http, config.jwt);
ws = new WebSocket(http, config.jwt, 'localhost');
ws.bind('test', (message) => {
wss = new WebSocket(http, config.jwt, 'localhost');

wss.bind('test', (message) => {
const res = new WebSocketMessage().setCommand(message.getCommand());
return res;
});

ws.bind('fail', () => ({ test: 'test' }));
wss.bind('fail', () => ({ test: 'test' }));

ws.bind('broadcast', (message) => {
wss.bind('broadcast', (message) => {
const res = new WebSocketMessage().setCommand(message.getCommand()).setBroadcast();
return res;
});
});

it('Drop connection due to invalid JWT token', (done) => {
const connection = new WebSocketClient(`ws://localhost:${config.http.port}`);
const connection = new WsClient(`ws://localhost:${config.http.port}`);
connection.on('close', () => {
connection.terminate();
done();
});
});

it('Connect send, and receive a message', (done) => {
const connection = new WebSocketClient(`ws://localhost:${config.http.port}/?token=${token}`);
const connection = new WsClient(`ws://localhost:${config.http.port}/?token=${token}`);

connection.on('open', () => {
const message = { command: 'test', token: token };
Expand All @@ -71,7 +175,7 @@ describe('websocket', () => {
});

it('Reject message with misformatted fields', (done) => {
const connection = new WebSocketClient(`ws://localhost:${config.http.port}/?token=${token}`);
const connection = new WsClient(`ws://localhost:${config.http.port}/?token=${token}`);

connection.on('open', () => {
const message = { command: '', token: token };
Expand All @@ -89,7 +193,7 @@ describe('websocket', () => {
});

it('Reject message with 500', (done) => {
const connection = new WebSocketClient(`ws://localhost:${config.http.port}/?token=${token}`);
const connection = new WsClient(`ws://localhost:${config.http.port}/?token=${token}`);

connection.on('open', () => {
const message = { command: 'fail', token: token };
Expand All @@ -107,7 +211,7 @@ describe('websocket', () => {
});

it('Accept filter with 200', (done) => {
const connection = new WebSocketClient(`ws://localhost:${config.http.port}/?token=${token}`);
const connection = new WsClient(`ws://localhost:${config.http.port}/?token=${token}`);

connection.on('open', () => {
const message = { command: 'filter',
Expand All @@ -130,8 +234,38 @@ describe('websocket', () => {
});
});

it('Live filter changes are logged', (done) => {
const wsClient = new WsClient(`ws://localhost:${config.http.port}/?token=${token}`);
wss.logger = {
debugMessage: sinon.spy(),
};

// eslint-disable-next-line jsdoc/require-jsdoc
function filterFunction(message, returnCriteriasOnly = false) {
if (returnCriteriasOnly) {
return 'PLACE_HOLDER';
}
return 'Dont check me pls';
};
let filterFunctionAsString = filterFunction.toString();
filterFunctionAsString = filterFunctionAsString.replace('\'PLACE_HOLDER\'', JSON.stringify(filters));

const theMessage = {
command: 'filter',
token: 'token',
payload: filterFunctionAsString,
};
wsClient.on('open', () => {
wsClient.send(JSON.stringify(theMessage));
wsClient.ping();
}).on('pong', () => {
assert.ok(wss.logger.debugMessage.calledWith(`New live filter applied: ${minifiedFilters}`));
done();
});
});

it('Request message broadcast with 200', (done) => {
const connection = new WebSocketClient(`ws://localhost:${config.http.port}/?token=${token}`);
const connection = new WsClient(`ws://localhost:${config.http.port}/?token=${token}`);

connection.on('open', () => {
const message = { command: 'broadcast', token: token };
Expand All @@ -151,7 +285,7 @@ describe('websocket', () => {
});

after(() => {
ws.shutdown();
wss.shutdown();
http.close();
});
});
37 changes: 37 additions & 0 deletions Framework/Backend/utils/minifyCriteria.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Make criteria more readable.
* This code is a close copy of InfoLogger/public/logFilter/LogFilter.js LN 101 toObject()
* @param {object} criteria - criteria to be minified
* @returns {object} minimal filter object
*/
function minifyCriteria(criteria) {
// Copy everything
const criterias = JSON.parse(JSON.stringify(criteria));

// Clean-up the whole structure

for (const field in criterias) {
for (const operator in criterias[field]) {
// Remote parsed properties (generated with fromJSON)
if (operator.includes('$')) {
delete criterias[field][operator];
}

// Remote empty inputs
if (!criterias[field][operator]) {
delete criterias[field][operator];
} else if (operator === 'match' || operator === 'exclude') {
// Encode potential breaking characters and escape double quotes as are used by browser by default
criterias[field][operator] = encodeURI(criterias[field][operator].replace(/["]+/g, '\\"'));
}

// Remove empty fields
if (!Object.keys(criterias[field]).length) {
delete criterias[field];
}
}
}
return criterias;
}

module.exports.minifyCriteria = minifyCriteria;
Loading
Loading