Skip to content
Open
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