-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[OGUI-1409] Add bkp service for past runs retrieval (#2117)
* adds a new HTTP based service to retrieve per run information from Bookkeeping based on provided definition, type and detector * adds a new HTTP based service to retrieve the run type maps with name associated to ID from Bookkeeping
- Loading branch information
Showing
6 changed files
with
356 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/** | ||
* @license | ||
* Copyright CERN and copyright holders of ALICE O2. This software is | ||
* distributed under the terms of the GNU General Public License v3 (GPL | ||
* Version 3), copied verbatim in the file "COPYING". | ||
* | ||
* See http://alice-o2.web.cern.ch/license for full licensing information. | ||
* | ||
* 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. | ||
*/ | ||
|
||
/** | ||
* RunSummaryAdapter - Given an object with RUN information as per Bookkeeping Database (https://github.com/AliceO2Group/Bookkeeping/blob/main/lib/domain/entities/Run.js), | ||
* return a minified version of it with only the summary | ||
*/ | ||
class RunSummaryAdapter { | ||
/** | ||
* RunSummaryAdapter | ||
*/ | ||
constructor() {} | ||
|
||
/** | ||
* Converts the given object to an entity object. | ||
* | ||
* @param {Object} run - Run Entity as per Bookkeeping https://github.com/AliceO2Group/Bookkeeping/blob/main/lib/domain/entities/Run.js | ||
* @returns {RunSummary} entity of a task with needed information | ||
*/ | ||
static toEntity(run) { | ||
const { | ||
runNumber, | ||
environmentId, | ||
definition, | ||
calibrationStatus, | ||
runType, | ||
startTime, | ||
endTime, | ||
detectors = [], | ||
} = run; | ||
return { | ||
runNumber, | ||
environmentId, | ||
definition, | ||
calibrationStatus, | ||
runType: runType?.name, | ||
startTime, | ||
detectors: detectors.sort(), | ||
endTime, | ||
}; | ||
} | ||
} | ||
|
||
module.exports = RunSummaryAdapter; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* @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 {Log} = require('@aliceo2/web-ui'); | ||
const {httpGetJson} = require('./../utils.js'); | ||
const RunSummaryAdapter = require('./../adapters/RunSummaryAdapter.js'); | ||
|
||
/** | ||
* BookkeepingService class to be used to retrieve data from Bookkeeping | ||
*/ | ||
class BookkeepingService { | ||
/** | ||
* Constructor for configuring the service to retrieve data via Bookkeeping HTTP API | ||
* @param {Object} config = {url: string, token: string} - configuration for using BKP service | ||
*/ | ||
constructor({url = '', token = ''}) { | ||
this._url = url; | ||
const {protocol, hostname, port} = new URL(this._url); | ||
this._hostname = hostname; | ||
this._port = port; | ||
this._protocol = protocol; | ||
|
||
this._token = token; | ||
|
||
this._runTypes = {}; // in-memory object which is filled with runTypes on server start | ||
this._logger = new Log(`${process.env.npm_config_log_label ?? 'cog'}/bkp-service`); | ||
} | ||
|
||
/** | ||
* Method to initialize the run service with static data such as runTypes | ||
* @return {void} | ||
*/ | ||
async init() { | ||
this._runTypes = await this._getRunTypes(); | ||
} | ||
|
||
/** | ||
* Given a definition, a type of a run and a detector, fetch from Bookkeeping the last RUN matching the parameters | ||
* @param {String} definition - definition of the run to query | ||
* @param {String} type - type of the run to query | ||
* @param {String} detector - detector which contained the run | ||
* @return {RunSummary|{}} - run object from Bookkeeping | ||
*/ | ||
async getRun(definition, type, detector) { | ||
if (this._runTypes[type]) { | ||
let filter = `filter[definitions]=${definition}&filter[runTypes]=${this._runTypes[type]}&page[limit]=1&`; | ||
filter += `filter[detectors][operator]=and&filter[detectors][values]=${detector}` | ||
try { | ||
const {data} = await httpGetJson(this._hostname, this._port, `/api/runs?${filter}&token=${this._token}`, { | ||
protocol: this._protocol, | ||
rejectUnauthorized: false, | ||
}); | ||
if (data?.length > 0) { | ||
return RunSummaryAdapter.toEntity(data[0]); | ||
} | ||
} catch (error) { | ||
this._logger.debug(error); | ||
} | ||
} | ||
return {}; | ||
} | ||
|
||
/** | ||
* Method to fetch run types from Bookkeeping and build a map of types to IDs as needed for filtering in RUNs API | ||
* @returns {Object<String, Number>} - map of runtypes to their ID | ||
*/ | ||
async _getRunTypes() { | ||
try { | ||
const runTypesMap = {}; | ||
const {data} = await httpGetJson(this._hostname, this._port, `/api/runTypes?token=${this._token}`, { | ||
protocol: this._protocol, | ||
rejectUnauthorized: false, | ||
}); | ||
for (const type of data) { | ||
runTypesMap[type.name] = type.id; | ||
} | ||
return runTypesMap; | ||
} catch (error) { | ||
this._logger.debug(error); | ||
} | ||
return {}; | ||
} | ||
|
||
/** | ||
* Getters/Setters | ||
*/ | ||
|
||
/** | ||
* Return the object storing run types by their name with ID | ||
* @return {Object<String, Number>} | ||
*/ | ||
get runTypes() { | ||
return this._runTypes; | ||
} | ||
} | ||
|
||
module.exports = {BookkeepingService}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* @license | ||
* Copyright CERN and copyright holders of ALICE O2. This software is | ||
* distributed under the terms of the GNU General Public License v3 (GPL | ||
* Version 3), copied verbatim in the file "COPYING". | ||
* | ||
* See http://alice-o2.web.cern.ch/license for full licensing information. | ||
* | ||
* 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. | ||
*/ | ||
|
||
/** | ||
* @typedef RunSummary | ||
* | ||
* RunSummary is an object which contains just a summary of an entire run entity: https://github.com/AliceO2Group/Bookkeeping/blob/main/lib/domain/entities/Run.js | ||
* | ||
* @property {Number} runNumber | ||
* @property {String} environmentId | ||
* @property {String} definition | ||
* @property {String} calibrationStatus | ||
* @property {String} runType | ||
* @property {Number} startTime | ||
* @property {Number} endTime | ||
* @property {Array<String>} detectors | ||
*/ |
155 changes: 155 additions & 0 deletions
155
Control/test/lib/services/mocha-bookkeeping.service.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/** | ||
* @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 max-len */ | ||
|
||
const assert = require('assert'); | ||
const nock = require('nock'); | ||
|
||
const {BookkeepingService} = require('../../../lib/services/Bookkeeping.service'); | ||
|
||
describe('BookkeepingService test suite', () => { | ||
const url = 'http://bkp-test.cern.ch:8888'; | ||
let bkp = new BookkeepingService({url, token: ''}); | ||
|
||
describe(`'init' test suite`, async () => { | ||
before(() => { | ||
bkp = new BookkeepingService({url, token: ''}); | ||
nock(url) | ||
.get('/api/runTypes?token=') | ||
.reply(200, { | ||
data: [ | ||
{name: 'NOISE', id: 1}, {name: 'PHYSICS', id: 2}, {name: 'SYNTHETIC', id: 3} | ||
] | ||
}); | ||
nock(url) | ||
.get('/api/runTypes?token=no-data') | ||
.reply(200, {data: []}); | ||
nock(url) | ||
.get('/api/runTypes?token=error') | ||
.replyWithError('Unable to connect'); | ||
}); | ||
after(() => nock.cleanAll()); | ||
|
||
it('should successfully load runTypes from bookkeeping', async () => { | ||
await bkp.init(); | ||
assert.deepStrictEqual(bkp.runTypes, {NOISE: 1, PHYSICS: 2, SYNTHETIC: 3}); | ||
}); | ||
|
||
it('should successfully load an empty object if no runTypes are provided', async () => { | ||
bkp._token = 'no-data'; | ||
await bkp.init(); | ||
assert.deepStrictEqual(bkp.runTypes, {}); | ||
}); | ||
|
||
it('should successfully load an empty object even if bookkeeping returned an error', async () => { | ||
bkp._token = 'error'; | ||
await bkp.init(); | ||
assert.deepStrictEqual(bkp.runTypes, {}); | ||
}); | ||
}); | ||
|
||
describe(`'_getRunTypes' test suite`, async () => { | ||
before(() => { | ||
bkp = new BookkeepingService({url, token: ''}); | ||
nock(url) | ||
.get('/api/runTypes?token=') | ||
.reply(200, { | ||
data: [ | ||
{name: 'NOISE', id: 1}, {name: 'PHYSICS', id: 2}, {name: 'SYNTHETIC', id: 3} | ||
] | ||
}); | ||
nock(url) | ||
.get('/api/runTypes?token=no-data') | ||
.reply(200, {data: []}); | ||
nock(url) | ||
.get('/api/runTypes?token=error') | ||
.replyWithError('Unable to connect'); | ||
}); | ||
after(() => nock.cleanAll()); | ||
|
||
it('should successfully return runTypes as object from bookkeeping', async () => { | ||
const runTypes = await bkp._getRunTypes(); | ||
assert.deepStrictEqual(runTypes, {NOISE: 1, PHYSICS: 2, SYNTHETIC: 3}); | ||
}); | ||
|
||
it('should successfully load an empty object if no runTypes are provided', async () => { | ||
bkp._token = 'no-data'; | ||
const runTypes = await bkp._getRunTypes(); | ||
assert.deepStrictEqual(runTypes, {}); | ||
}); | ||
|
||
it('should successfully load an empty object even if bookkeeping returned an error', async () => { | ||
bkp._token = 'error'; | ||
const runTypes = await bkp._getRunTypes(); | ||
assert.deepStrictEqual(runTypes, {}); | ||
}); | ||
}); | ||
|
||
describe(`'getRun' test suite`, async () => { | ||
let runToReturn = { | ||
runNumber: 123, | ||
environmentId: 'abc', | ||
definition: 'CALIBRATION', | ||
calibrationStatus: 'good', | ||
runType: {name: 'NOISE', id: 1}, | ||
detectors: ['TPC'], | ||
startTime: Date.now() - 100, | ||
endTime: Date.now(), | ||
extraField: '', | ||
someOther: 1234 | ||
}; | ||
before(() => { | ||
bkp = new BookkeepingService({url, token: ''}); | ||
bkp._runTypes = {NOISE: 1, PHYSICS: 2, SYNTHETIC: 3}; | ||
nock(url) | ||
.get('/api/runs?filter[definitions]=CALIBRATION&filter[runTypes]=1&page[limit]=1&' | ||
+ 'filter[detectors][operator]=and&filter[detectors][values]=TPC&token=') | ||
.reply(200, { | ||
data: [runToReturn] | ||
}); | ||
|
||
nock(url) | ||
.get('/api/runs?filter[definitions]=CALIBRATION&filter[runTypes]=2&page[limit]=1&' | ||
+ 'filter[detectors][operator]=and&filter[detectors][values]=TPC&token=') | ||
.reply(200, { | ||
data: [] | ||
}); | ||
|
||
nock(url) | ||
.get('/api/runs?filter[definitions]=CALIBRATION&filter[runTypes]=3&page[limit]=1&' | ||
+ 'filter[detectors][operator]=and&filter[detectors][values]=TPC&token=') | ||
.replyWithError('Unable'); | ||
}); | ||
after(() => nock.cleanAll()); | ||
|
||
it('should successfully return a run based on existing runType and provided def, type and detector', async () => { | ||
const run = await bkp.getRun('CALIBRATION', 'NOISE', 'TPC'); | ||
const runInfo = JSON.parse(JSON.stringify(runToReturn)); | ||
runInfo.runType = runInfo.runType.name; | ||
delete runInfo.extraField; | ||
delete runInfo.someOther; | ||
assert.deepStrictEqual(run, runInfo); | ||
}); | ||
|
||
it('should successfully return an empty run if none was found', async () => { | ||
const run = await bkp.getRun('CALIBRATION', 'PHYSICS', 'TPC'); | ||
assert.deepStrictEqual(run, {}); | ||
}); | ||
|
||
it('should successfully return an empty run even if bkp service throws error', async () => { | ||
const run = await bkp.getRun('CALIBRATION', 'SYNTHETIC', 'TPC'); | ||
assert.deepStrictEqual(run, {}); | ||
}); | ||
}); | ||
}); |