Skip to content
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
2 changes: 2 additions & 0 deletions QualityControl/lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ export const setup = async (http, ws, eventEmitter) => {
http.get('/layouts', layoutController.getLayoutsHandler.bind(layoutController));
http.get('/layout/:id', layoutController.getLayoutHandler.bind(layoutController));
http.get('/layout', layoutController.getLayoutByNameHandler.bind(layoutController));
http.get('/download', layoutController.getDownloadHandler.bind(layoutController));
http.post('/layout', layoutController.postLayoutHandler.bind(layoutController));
http.post('/download', layoutController.postDownloadHandler.bind(layoutController));
http.put(
'/layout/:id',
layoutServiceMiddleware(jsonFileService),
Expand Down
47 changes: 47 additions & 0 deletions QualityControl/lib/controllers/LayoutController.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ import {
updateAndSendExpressResponseFromNativeError,
}
from '@aliceo2/web-ui';
import { parseRequestToConfig, parseRequestToLayout } from '../utils/download/configurator.js';
import { MapStorage } from '../utils/download/classes/domain/MapStorage.js';
import { download, saveDownloadData } from '../utils/download/downloadEngine.js';

/**
* @typedef {import('../repositories/LayoutRepository.js').LayoutRepository} LayoutRepository
*/

const logger = LogManager.getLogger(`${process.env.npm_config_log_label ?? 'qcg'}/layout-ctrl`);
const mapStorage = new MapStorage();

/**
* Gateway for all HTTP requests with regards to QCG Layouts
Expand Down Expand Up @@ -219,6 +223,49 @@ export class LayoutController {
}
}

/**
* Store layout data for later download request.
* @param {Request<import('../utils/download/configurator.js').Query>}req - request
* @param {Response} res - response
*/
async postDownloadHandler(req, res) {
try {
const downloadConfigDomain = parseRequestToConfig(req);
const downloadLayoutDomain = parseRequestToLayout(req);
// Note: if userId becomes 0 it will throw when creating the storagelayout.
const userId = Number(req.query.user_id ?? 0);
const key = saveDownloadData(mapStorage, downloadLayoutDomain, downloadConfigDomain, userId);
logger.infoMessage(`Saved layout key: ${key}`);
res.status(201).send(key);
} catch {
res.status(400).send('Could not save download data');
}
};

/**
* Download objects using key from post download request.
* @param {Request<import('../utils/download/configurator.js').Query>}req - request
* @param {Response} res - response
*/
async getDownloadHandler(req, res) {
const { key } = req.query;
if (key == '') {
res.status(400).send('Key not defined correctly');
}
try {
const downloadLayoutDomain = mapStorage.readRequest(key)?.[0].toSuper();
const downloadConfigDomain = mapStorage.readRequest(key)?.[1];
if (downloadLayoutDomain == undefined || downloadConfigDomain == undefined) {
throw new Error('Layout could not be found with key');
}
// start the download engine
await download(downloadLayoutDomain, downloadConfigDomain, 1234567, res);
} catch (error) {
logger.errorMessage(error?.message ?? error);
res.status(400).send('Could not download objects');
}
}

/**
* Patch a layout entity with information as per LayoutPatchDto.js
* @param {Request} req - HTTP request object with "params" and "body" information on layout
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @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 { DownloadConfigDomain } from '../classes/domain/DownloadConfigDomain.js';
import { NameTemplateOption } from '../enum/NameTemplateOption.js';
import { DownloadMode } from '../enum/DownloadMode.js';

/**
* map download config to domain model
* @param {string} downloadConfigData - DownloadconfigData to map
* @returns {DownloadConfigDomain} - mapped DownloadConfigDomain
*/
export function mapDownloadConfigToDomain(downloadConfigData) {
const archiveNameTemplateOptions = downloadConfigData.archiveNameTemplateOptions.map(mapNameTemplateOption);
const objectNameTemplateOptions = downloadConfigData.objectNameTemplateOptions.map(mapNameTemplateOption);
const downloadMode = mapDownloadMode(downloadConfigData.downloadMode);
// eslint-disable-next-line @stylistic/js/max-len
return new DownloadConfigDomain(downloadConfigData.tabIds, downloadConfigData.objectIds, archiveNameTemplateOptions, objectNameTemplateOptions, downloadMode, downloadConfigData.pathNameStructure);
}

/**
* map string to name template option
* @param {string} nameTemplateOption - string representation
* @returns {number} - name template option
*/
function mapNameTemplateOption(nameTemplateOption) {
if (typeof nameTemplateOption === 'string') {
nameTemplateOption = nameTemplateOption.trim();
const mappedNameTemplateOption = NameTemplateOption[nameTemplateOption];
if (mappedNameTemplateOption === undefined) {
throw new Error('Failed to map NameTemplateOption, perhaps an invalid option was passed?');
} else {
return mappedNameTemplateOption;
}
} else {
throw new Error('Failed to map NameTemplateOption, it should be a string');
}
}

/**
* map number to download mode.
* @param {string} downloadMode - download mode (tab/object/layout)
* @returns {number} - mapped downloadmode
*/
function mapDownloadMode(downloadMode) {
if (typeof downloadMode === 'string') {
downloadMode = downloadMode.trim();
downloadMode = downloadMode.toLowerCase();
const mappedDownloadMode = DownloadMode[downloadMode];
if (mappedDownloadMode === undefined) {
throw new Error('Failed to map DownloadMode, perhaps an invalid option was passed?');
} else {
return mappedDownloadMode;
}
} else {
throw new Error('Failed to map DownloadMode, it should be a string');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @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.
*/
/* eslint-disable jsdoc/reject-any-type */
import { DownloadMode } from '../../enum/DownloadMode.js';

export class DownloadConfigData {
/**
* constructor
* @param {string[]} tabIds - tabIds to download
* @param {string[]} objectIds - objectIds to download
* @param {string[]} archiveNameTemplateOptions - name options for the archive (*.tar.gz)
* @param {string[]} objectNameTemplateOptions - name options for the individual object files
* @param {string} downloadMode - download mode (layout/tab/object)
* @param {boolean} pathNameStructure - enable full pathname structure
*/
constructor(
tabIds, objectIds, archiveNameTemplateOptions,
objectNameTemplateOptions, downloadMode, pathNameStructure,
) {
this.tabIds = tabIds,
this.objectIds = objectIds,
this.archiveNameTemplateOptions = archiveNameTemplateOptions,
this.objectNameTemplateOptions = objectNameTemplateOptions,
this.downloadMode = downloadMode,
this.pathNameStructure = pathNameStructure;
}

tabIds;

objectIds;

archiveNameTemplateOptions;

objectNameTemplateOptions;

downloadMode;

pathNameStructure;

/**
* mapper from plain object to instance of DownloadConfigData.
* @static
* @param {any} downloadConfigPlain - plain object download config.
* @returns {DownloadConfigData} - mapped DownloadConfigData.
*/
static mapFromPlain(downloadConfigPlain) {
if (!downloadConfigPlain || typeof downloadConfigPlain !== 'object') {
throw new Error('invalid DownloadConfig');
}
return new DownloadConfigData(Array.isArray(downloadConfigPlain.tabIds) ?
downloadConfigPlain.tabIds : downloadConfigPlain.tabIds?.split(',') ??
[], Array.isArray(downloadConfigPlain.objectIds) ? downloadConfigPlain.objectIds :
downloadConfigPlain.objectIds?.split(',') ?? [], Array.isArray(downloadConfigPlain.archiveNameTemplateOptions) ?
downloadConfigPlain.archiveNameTemplateOptions : downloadConfigPlain.archiveNameTemplateOptions?.split(',')
?? [], Array.isArray(downloadConfigPlain.objectNameTemplateOptions) ?
downloadConfigPlain.objectNameTemplateOptions : downloadConfigPlain.objectNameTemplateOptions?.split(',')
?? [], downloadConfigPlain?.downloadMode ??
DownloadMode.OBJECT, downloadConfigPlain?.pathNameStructure == 'true' ? true : false);
}
}
82 changes: 82 additions & 0 deletions QualityControl/lib/utils/download/classes/data/LayoutData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* @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.
*/
/* eslint-disable jsdoc/reject-any-type */
/* eslint-disable jsdoc/require-param-description */
import { LayoutDomain } from '../../classes/domain/LayoutDomain.js';
import { TabData } from './TabData.js';
export class LayoutData {
/**
* constructor
* @param {string} id
* @param {string} name
* @param {number} owner_id
* @param {string} owner_name
* @param {TabData[]} tabs
* @param {any[]} collaborators
* @param {boolean} displayTimestamp
* @param {number} autoTabChange
* @param {boolean} isOfficial
*/
constructor(id, name, owner_id, owner_name, tabs, collaborators, displayTimestamp, autoTabChange, isOfficial) {
this.id = id;
this.name = name,
this.owner_id = owner_id,
this.owner_name = owner_name,
this.tabs = tabs,
this.collaborators = collaborators,
this.displayTimestamp = displayTimestamp,
this.autoTabChange = autoTabChange,
this.isOfficial = isOfficial;
}

id;

name;

owner_id;

owner_name;

tabs;

collaborators;

displayTimestamp;

autoTabChange;

isOfficial;

/**
* map to an instance of LayoutData from a plain object.
* @static
* @param {any} layoutPlain
* @returns {LayoutData} - mapped layoutData
*/
static mapFromPlain(layoutPlain) {
if (!layoutPlain || typeof layoutPlain !== 'object' || layoutPlain.id == undefined) {
throw new Error('invalid layout');
}
// eslint-disable-next-line @stylistic/js/max-len
return new LayoutData(layoutPlain.id, layoutPlain.name, Number(layoutPlain.owner_id), layoutPlain.owner_name, Array.isArray(layoutPlain.tabs) ? layoutPlain.tabs.map(TabData.mapFromPlain) : [], Array.isArray(layoutPlain.collaborators) ? layoutPlain.collaborators : [], Boolean(layoutPlain.displayTimestamp), Number(layoutPlain.autoTabChange), Boolean(layoutPlain.isOfficial));
}

/**
* mapper to Domain model
* @returns {LayoutDomain} Resulting LayoutDomain.
*/
mapToDomain() {
return new LayoutDomain(this.id, this.name, this.tabs.map((tab) => tab.mapToDomain()));
}
}
84 changes: 84 additions & 0 deletions QualityControl/lib/utils/download/classes/data/ObjectData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* @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.
*/
/* eslint-disable jsdoc/require-param-description */
/* eslint-disable jsdoc/reject-any-type */
import { ObjectDomain } from '../../classes/domain/ObjectDomain.js';
export class ObjectData {
/**
* constructor
* @param {string} id
* @param {number} x
* @param {number} y
* @param {number} h
* @param {number} w
* @param {string} name
* @param {string[]} options
* @param {boolean} autoSize
* @param {boolean} ignoreDefaults
*/
constructor(id, x, y, h, w, name, options, autoSize, ignoreDefaults) {
this.id = id,
this.x = x,
this.y = y,
this.h = h,
this.w = w,
this.name = name;
this.options = options,
this.autoSize = autoSize,
this.ignoreDefaults = ignoreDefaults;
}

id;

x;

y;

h;

w;

name;

options;

autoSize;

ignoreDefaults;

/**
* mapper to map from plain object to instance of ObjectData.
* @static
* @param {any} objectPlain - plain js object
* @returns {ObjectData} - mapped object data
*/
static mapFromPlain(objectPlain) {
if (!objectPlain || typeof objectPlain !== 'object') {
throw new Error('invalid object');
}
return new ObjectData(objectPlain.id, Number(objectPlain.x ??
0), Number(objectPlain.y ?? 0), Number(objectPlain.h ??
0), Number(objectPlain.w ??
0), objectPlain.name, Array.isArray(objectPlain.options) ? objectPlain.options :
[], Boolean(objectPlain.autoSize), Boolean(objectPlain.ignoreDefaults));
}

/**
* mapper to domain model.
* @returns {ObjectDomain} - mapped objectDomain model.
*/
mapToDomain() {
return new ObjectDomain(this.id, this.name);
}
}
Loading
Loading