Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
140c555
fix: temporary setTimout fix for ObjectTree sort issue
May 5, 2025
1c9d33c
Merge branch 'dev' of github.com:AliceO2Group/WebUi into bug/QCG/OGUI…
May 13, 2025
a07103c
feat: rebuild ObjectTree sorting mechanism
May 12, 2025
abff6e1
feat: create ObjectTreeModel
May 13, 2025
d868c82
feat: create ObjectTreeComponent
May 13, 2025
92b3b68
feat: add objectPanel file.
May 13, 2025
a57351c
feat: create new objectTreePage.js
May 13, 2025
a6b04c7
feat: replace old objectTreePage with new objectTreePage
May 13, 2025
ea8445d
refractor: put objectTree item functions in their own file. and make …
May 14, 2025
199e85c
fix: pass object to statusbar left and right.
May 14, 2025
35b2cdc
feat(objectTreeSideBar): use objectTreeComponent
May 14, 2025
2e62457
chore: delete ObjectTree.class
May 14, 2025
19055f4
chore(ObjectTreeModel): add documentation and rename some variables.
May 14, 2025
52a7829
chore(ObjectTreeModel): change open status upon initialisation and re…
May 14, 2025
81cea1e
chore(objectTreeItem): swap out blue collored text for white collored…
May 14, 2025
2022447
chore: set key to path instead of name
May 15, 2025
5ed637d
refactor: remove unneeded object.name lookup
May 15, 2025
a8455e6
refactor: fix eslint issue and refactor objectTree items to no longer…
May 15, 2025
6aa0c58
refactor: change object function argument to qcObject
May 16, 2025
dd81502
feat: create QCObjectNameDto
May 16, 2025
51086c9
feat implement qcObjectNameDto in CcdbService::getObjectsTreeList
May 16, 2025
d96336c
fix: replace the sanitisation function of QCObjectNameDto with a excl…
May 16, 2025
279e70f
small refactor <= -> <
May 16, 2025
9558c5b
test: add test delay and fix the QCObjectNameTest
May 16, 2025
8fb4d42
feat: set development environment window exposion
May 19, 2025
ce5f474
test(object-tree-test): remove window.model dependency
May 20, 2025
3bc328e
fix(objectTreePage): fix object not showing up.
May 20, 2025
fe763bd
refactor(QCObjectNameDto): move false check to start of function
May 21, 2025
c03a10d
test(ObjectTreePath): add test for virtualTable
May 23, 2025
9d60af1
wip
May 25, 2025
02b9839
refractor: always exclude render messages from frontend
May 15, 2025
abe091f
fix about page and layout show
May 25, 2025
d2a4436
Merge branch 'bug/QCG/OGUI-1662/object-tree-breaking-when-sorting-by-…
May 25, 2025
42d0293
views: add id's and remove window.model dependencies.
May 25, 2025
80243e7
test: simplify and change tests to accomodate the removal of window.m…
May 25, 2025
1f5ffcf
feat: set debug to false if not in development mode.
May 25, 2025
92830fc
chore: set environment variables in package.json
May 25, 2025
6619c34
feat(Index.js): set public/index.js based on the NODE_ENV environment…
May 25, 2025
ad4411d
chore: put public/index.js in gitignore
May 25, 2025
63b6781
refactor: refactor changes based on feedback
May 26, 2025
571921f
Merge branch 'dev' of github.com:AliceO2Group/WebUi into bug/QCG/OGUI…
May 26, 2025
019cc15
refactor: initialise itemsInfo dynamically
May 26, 2025
215ccc2
refactor
May 26, 2025
6f7f7f7
experiment 1
May 26, 2025
b578bd9
style: move bootstrap alterations to app.css
May 26, 2025
315c0b8
Merge branch 'dev' of github.com:AliceO2Group/WebUi into bug/QCG/OGUI…
May 26, 2025
402f87d
Merge branch 'dev' of github.com:AliceO2Group/WebUi into bug/QCG/OGUI…
May 26, 2025
46a4cbb
Merge branch 'dev' of github.com:AliceO2Group/WebUi into improvement/…
May 27, 2025
d800a2f
Merge branch 'bug/QCG/OGUI-1662/object-tree-breaking-when-sorting-by-…
May 27, 2025
808bd6a
option stealth fix
May 27, 2025
c385f4d
chore: install cross-env dependency
May 27, 2025
d1bc9d0
Merge branch 'dev' into improvement/QCG/OGUI-1574/production-enabled-…
NarrowsProjects Jun 1, 2025
f414f2b
Merge branch 'dev' into improvement/QCG/OGUI-1574/production-enabled-…
NarrowsProjects Jun 3, 2025
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
1 change: 1 addition & 0 deletions QualityControl/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/config.js
public/config.cjs
public/index.js
test/setup/seeders/qcg-mock-data.json
*~
.DS_Store
Expand Down
47 changes: 47 additions & 0 deletions QualityControl/environmentSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { LogManager } from '@aliceo2/web-ui';
import { fileURLToPath } from 'url';
import { envIndexFiles } from './environments.js';
import { join, dirname } from 'path';
import fs from 'fs';

/**
* Initializes environment-specific JavaScript file and logging configuration.
* - Copies the appropriate environment-specific index.js file (dev/prod/test) to index.js
* - Sets up test environment mocks when in test mode
* @async
* @function initializeEnvironment
* @returns {Promise<Logger>} logger instance
* @description
* This function performs environment setup tasks:
* 1. Determines correct environment-specific JS file based on NODE_ENV
* 2. Copies the environment-specific file to index.js in public directory
* 3. Initializes test mocks when in test environment
* 4. Returns a configured logger instance
*/
export default async function () {
const { NODE_ENV, npm_config_log_label } = process.env;
const logger = LogManager.getLogger(`${npm_config_log_label ?? 'qcg'}/index`);

const __dirname = dirname(fileURLToPath(import.meta.url));

const envIndexFile = envIndexFiles[NODE_ENV];

const publicDir = join(__dirname, 'public');
const sourceJsPath =
join(publicDir, envIndexFiles[NODE_ENV]); // These files will have different properties per environment
const targetJsPath = join(publicDir, 'index.js'); // the former file will be overwrite this file.

fs.copyFileSync(sourceJsPath, targetJsPath);
logger.infoMessage(`Using ${envIndexFile} as index.js for environment: ${process.env.NODE_ENV}`);

if (NODE_ENV === 'test') {
// Initialize nock for CCDB and Bookkeeping only if we are in test environment
const { initializeNockForCcdb } = await import('./test/setup/testSetupForCcdb.js');
const { initializeNockForBkp } = await import('./test/setup/testSetupForBkp.js');

initializeNockForCcdb();
initializeNockForBkp();
}

return logger;
}
5 changes: 5 additions & 0 deletions QualityControl/environments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const envIndexFiles = Object.freeze({
development: 'index.dev.js',
test: 'index.test.js',
production: 'index.production.js',
});
45 changes: 16 additions & 29 deletions QualityControl/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,49 +12,36 @@
* or submit itself to any jurisdiction.
*/

import { LogManager, HttpServer, WebSocket } from '@aliceo2/web-ui';
const logger = LogManager.getLogger(`${process.env.npm_config_log_label ?? 'qcg'}/index`);
import path from 'path';
import { HttpServer, WebSocket } from '@aliceo2/web-ui';
import { join, dirname } from 'path';
import { setup } from './lib/api.js';

// Reading config file
import { fileURLToPath } from 'url';
import { config } from './lib/config/configProvider.js';
import { createRequire } from 'module';
import environmentSetup from './environmentSetup.js';

// Quick check config at start

const logger = await environmentSetup();
// Reading config file
if (config.http.tls) {
logger.info(`HTTPS endpoint: https://${config.http.hostname}:${config.http.portSecure}`);
logger.infoMessage(`HTTPS endpoint: https://${config.http.hostname}:${config.http.portSecure}`);
}
logger.info(`HTTP endpoint: http://${config.http.hostname}:${config.http.port}`);
logger.infoMessage(`HTTP endpoint: http://${config.http.hostname}:${config.http.port}`);
if (typeof config.demoData != 'undefined' && config.demoData) {
logger.info('Using demo data');
logger.infoMessage('Using demo data');
} else {
config.demoData = false;
}

import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));
// Start servers
const http = new HttpServer(config.http, config.jwt, config.openId);
http.addStaticPath(path.join(__dirname, 'common'));
http.addStaticPath(path.join(__dirname, 'public'));

import { createRequire } from 'module';

const require = createRequire(import.meta.url);
const pathName = require.resolve('jsroot');
http.addStaticPath(path.join(pathName, '../..'), 'jsroot');

const ws = new WebSocket(http);
// Start servers
const http = new HttpServer(config.http, config.jwt, config.openId);
http.addStaticPath(join(__dirname, 'common'));
http.addStaticPath(join(__dirname, 'public'));
http.addStaticPath(join(pathName, '../..'), 'jsroot');

if (process.env.NODE_ENV === 'test') {
// Initialize nock for CCDB and Bookkeeping only if we are in test environment
const { initializeNockForCcdb } = await import('./test/setup/testSetupForCcdb.js');
const { initializeNockForBkp } = await import('./test/setup/testSetupForBkp.js');
const ws = new WebSocket(http);

initializeNockForCcdb();
initializeNockForBkp();
}
setup(http, ws);
80 changes: 80 additions & 0 deletions QualityControl/lib/dtos/QCObjectNameDto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* @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.
*/

import Joi from 'joi';
import { LogManager } from '@aliceo2/web-ui';

const logger = LogManager.getLogger(`${process.env.npm_config_log_label ?? 'qcg'}/qcObj-Dto`);

/**
* Validates if a path segment is valid (non-empty and no whitespace)
* @param {string} path - The path to validate
* @returns {boolean} True if the path is valid
*/
function isValidPath(path) {
if (typeof path !== 'string' || path === '') {
logger.debugMessage(`Invalid path: Path is empty or not a string (${path})`);
return false;
}

Check warning on line 29 in QualityControl/lib/dtos/QCObjectNameDto.js

View check run for this annotation

Codecov / codecov/patch

QualityControl/lib/dtos/QCObjectNameDto.js#L27-L29

Added lines #L27 - L29 were not covered by tests

if (path.endsWith('/')) {
logger.debugMessage(`Invalid path: Path ends with a slash (${path})`);
return false;
}

let segmentStart = 0;
for (let i = 0; i <= path.length; i++) {
const char = path[i];

if (i === path.length || char === '/') {
const segment = path.slice(segmentStart, i);
const hasWhitespace = /\s/.test(segment);
if (segment.length === 0) {
logger.debugMessage(`Invalid path: Empty segment found in path (${path})`);
return false;
}
if (hasWhitespace) {
logger.debugMessage(`Invalid path: Segment contains whitespace (${segment}) in path (${path})`);
return false;
}
segmentStart = i + 1;
}
}
return true;
}

/**
* Joi schema that validates the path
*/
export const qcObjectNameDto = Joi.string()
.custom((value) => {
if (!isValidPath(value)) {
logger.debugMessage(`Found invalid QC object name: ${value}`);
return value;
}
return value;
}, 'QC Object Name Validator');

export const qcObjectNameArrayDto = Joi.array()
.items(qcObjectNameDto)
.custom((value) => {
const filtered = value.filter((item) => isValidPath(item));

if (filtered.length !== value.length) {
logger.debugMessage(`Filtered ${value.length - filtered.length} invalid QC object names from array`);
}

return filtered;
}, 'QC Object Array Validator')
.options({ stripUnknown: { arrays: true } });
6 changes: 4 additions & 2 deletions QualityControl/lib/services/ccdb/CcdbService.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { httpHeadJson, httpGetJson } from '../../utils/httpRequests.js';
import {
CCDB_MONITOR, CCDB_VERSION_KEY, CCDB_RESPONSE_BODY_KEYS, CCDB_FILTER_FIELDS, CCDB_RESPONSE_HEADER_KEYS,
} from './CcdbConstants.js';
import { qcObjectNameArrayDto } from '../../dtos/QCObjectNameDto.js';

const {
LAST_MODIFIED, VALID_FROM, VALID_UNTIL, CREATED, PATH, SIZE, FILE_NAME, METADATA, ID,
Expand Down Expand Up @@ -107,12 +108,13 @@ export class CcdbService {
* @rejects {Error}
*/
async getObjectsTreeList(prefix = this._PREFIX) {
const { subfolders } = await httpGetJson(this._hostname, this._port, `/tree/${prefix}.*`);
let { subfolders } = await httpGetJson(this._hostname, this._port, `/tree/${prefix}.*`);

if (!Array.isArray(subfolders)) {
throw new FailedDependencyError('Invalid response format from server - expected subfolders array');
}
// console.log(await this.getObjectsLatestVersionList(prefix));

subfolders = await qcObjectNameArrayDto.validateAsync(subfolders);

return subfolders.map((folder) => ({ path: folder }));
}
Expand Down
25 changes: 19 additions & 6 deletions QualityControl/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions QualityControl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
"type": "module",
"homepage": "https://alice-o2-project.web.cern.ch/",
"scripts": {
"start": "node index.js",
"dev": "nodemon --watch index.js --watch lib --watch config.js index.js",
"start": "cross-env NODE_ENV=production node index.js",
"dev": "cross-env NODE_ENV=development nodemon --watch index.js --watch lib --watch config.js index.js",
"lint": "./node_modules/.bin/eslint --config eslint.config.js lib public",
"coverage-lcov": "node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info test/test-index.js",
"coverage-local": "node --test --experimental-test-coverage --test-reporter=spec test/test-index.js",
Expand All @@ -38,6 +38,7 @@
],
"dependencies": {
"@aliceo2/web-ui": "2.8.4",
"cross-env": "^7.0.3",
"joi": "17.13.3",
"jsroot": "7.9.0",
"mariadb": "^3.4.1",
Expand Down
43 changes: 42 additions & 1 deletion QualityControl/public/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
100% { opacity: 1; }
}

.object-selectable { cursor: pointer; text-decoration: none; }
.object-selectable {
cursor: pointer;
text-decoration: none;
display: block;
padding: 0.3em;
}
.object-selectable:hover { cursor: pointer; background-color: var(--color-gray-dark) !important; color: var(--color-gray-lighter); }

.layout-selectable { border: 0.0em solid var(--color-primary); transition: border 0.1s; }
Expand Down Expand Up @@ -62,6 +67,30 @@
height: 100%;
}

.heading {
font-weight: bold;
padding: 0.3em;
background-color: var(--color-gray-light);
}

.root-tree {
list-style-type: none;
margin: 0;
padding-left: 0.1em;
}

ul.object-tree-list {
list-style-type: none;
margin: 0;
padding-left: 1em;
}

li.object-tree-branch, ul.object-tree-list {
display: flex;
flex-direction: column;
}


.folderHeader, .cardHeader {
border-radius: .5rem .5rem 0 0;
}
Expand All @@ -75,6 +104,14 @@
.jsroot-container {}
.jsroot-container pre { background-color: initial; }
.jsrootdiv:hover + .resize-element, .resize-element:hover{ display: flex !important; }
.jsrootdiv {
z-index: 90;
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}


.resize-button { position: absolute; right: 0%; z-index: 100 }

Expand All @@ -100,3 +137,7 @@
border: 1px solid #ddd;
background: var(--color-gray-light);
}

/* Standardised styles to potentially be moved to bootstrap */
.ph0 { padding-left: 0; padding-right: 0; }
.pv0 { padding-top: 0; padding-bottom: 0; }
Loading
Loading