From 9be0ed17426c6873b832f62780270733af656a7a Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 5.5" Date: Thu, 22 Oct 2026 15:52:24 +0200 Subject: [PATCH 1/7] [O2B-1623] Document the DPL process handlers The controller disabled the JSDoc rules for the whole file: its four handlers now describe their parameters and their result (and the documentation of the hosts handler, which read "the hosts that on which", is fixed). --- .../controllers/dplProcess.controller.js | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/lib/server/controllers/dplProcess.controller.js b/lib/server/controllers/dplProcess.controller.js index 0fa95e645a..938ebc7689 100644 --- a/lib/server/controllers/dplProcess.controller.js +++ b/lib/server/controllers/dplProcess.controller.js @@ -11,8 +11,6 @@ * or submit itself to any jurisdiction. */ -/* eslint-disable jsdoc/require-param */ - const { dtoValidator } = require('../utilities/index.js'); const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError.js'); const { DtoFactory } = require('../../domain/dtos/DtoFactory.js'); @@ -21,6 +19,12 @@ const { dplProcessService } = require('../services/dpl/DplProcessService.js'); /** * Route to fetch all the DPL detectors that have at least one executed process for a given run + * + * @param {Object} request The *request* object represents the HTTP request and has properties for the request query + * string, parameters, body, HTTP headers, and so on. + * @param {Object} response The *response* object represents the HTTP response that an Express app sends when it gets an + * HTTP request. + * @returns {Promise} resolves once the response has been sent */ const getAllDplDetectorsWithExecutedProcessesByRunHandler = async (request, response) => { const requestDto = await dtoValidator( @@ -40,6 +44,12 @@ const getAllDplDetectorsWithExecutedProcessesByRunHandler = async (request, resp /** * Route to fetch all the processes that have been executed at least once for a given run and detector + * + * @param {Object} request The *request* object represents the HTTP request and has properties for the request query + * string, parameters, body, HTTP headers, and so on. + * @param {Object} response The *response* object represents the HTTP response that an Express app sends when it gets an + * HTTP request. + * @returns {Promise} resolves once the response has been sent */ const getAllExecutedProcessesByRunAndDplDetectorHandler = async (request, response) => { const requestDto = await dtoValidator( @@ -64,7 +74,13 @@ const getAllExecutedProcessesByRunAndDplDetectorHandler = async (request, respon }; /** - * Route to fetch all the hosts that on which a given process has been executed for a given run and detector + * Route to fetch all the hosts on which a given process has been executed for a given run and detector + * + * @param {Object} request The *request* object represents the HTTP request and has properties for the request query + * string, parameters, body, HTTP headers, and so on. + * @param {Object} response The *response* object represents the HTTP response that an Express app sends when it gets an + * HTTP request. + * @returns {Promise} resolves once the response has been sent */ const getAllHostsWithExecutedProcessByRunAndDplDetectorHandler = async (request, response) => { const requestDto = await dtoValidator( @@ -92,6 +108,12 @@ const getAllHostsWithExecutedProcessByRunAndDplDetectorHandler = async (request, /** * Route to fetch all executions of a given process on a given host for a given run and detector + * + * @param {Object} request The *request* object represents the HTTP request and has properties for the request query + * string, parameters, body, HTTP headers, and so on. + * @param {Object} response The *response* object represents the HTTP response that an Express app sends when it gets an + * HTTP request. + * @returns {Promise} resolves once the response has been sent */ const getAllProcessExecutionByRunAndDplDetectorAndHostHandler = async (request, response) => { const requestDto = await dtoValidator( From b2d81a340b92ad9cf1649672ce98bb324d55a0da Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 5.5" Date: Thu, 22 Oct 2026 15:52:51 +0200 Subject: [PATCH 2/7] [O2B-1623] Validate the ids of the DPL process endpoints like the other entity ids The run number, the detector id, the process id and the host id of the DPL process endpoints were validated as any number (`0`, negative numbers and decimals were accepted and ended as a 404). They are validated with `EntityIdDto`, so that invalid values are rejected with a 400, and an API test checks one invalid value on each endpoint. --- .../controllers/dplProcess.controller.js | 22 +++++++++---------- test/api/dplProcess.test.js | 21 ++++++++++++++++++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/server/controllers/dplProcess.controller.js b/lib/server/controllers/dplProcess.controller.js index 938ebc7689..bc9c039e4b 100644 --- a/lib/server/controllers/dplProcess.controller.js +++ b/lib/server/controllers/dplProcess.controller.js @@ -14,7 +14,7 @@ const { dtoValidator } = require('../utilities/index.js'); const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError.js'); const { DtoFactory } = require('../../domain/dtos/DtoFactory.js'); -const Joi = require('joi'); +const EntityIdDto = require('../../domain/dtos/EntityIdDto.js'); const { dplProcessService } = require('../services/dpl/DplProcessService.js'); /** @@ -28,7 +28,7 @@ const { dplProcessService } = require('../services/dpl/DplProcessService.js'); */ const getAllDplDetectorsWithExecutedProcessesByRunHandler = async (request, response) => { const requestDto = await dtoValidator( - DtoFactory.queryOnly({ runNumber: Joi.number().required() }), + DtoFactory.queryOnly({ runNumber: EntityIdDto.required() }), request, response, ); @@ -54,8 +54,8 @@ const getAllDplDetectorsWithExecutedProcessesByRunHandler = async (request, resp const getAllExecutedProcessesByRunAndDplDetectorHandler = async (request, response) => { const requestDto = await dtoValidator( DtoFactory.queryOnly({ - runNumber: Joi.number().required(), - detectorId: Joi.number().required(), + runNumber: EntityIdDto.required(), + detectorId: EntityIdDto.required(), }), request, response, @@ -85,9 +85,9 @@ const getAllExecutedProcessesByRunAndDplDetectorHandler = async (request, respon const getAllHostsWithExecutedProcessByRunAndDplDetectorHandler = async (request, response) => { const requestDto = await dtoValidator( DtoFactory.queryOnly({ - runNumber: Joi.number().required(), - detectorId: Joi.number().required(), - processId: Joi.number().required(), + runNumber: EntityIdDto.required(), + detectorId: EntityIdDto.required(), + processId: EntityIdDto.required(), }), request, response, @@ -118,10 +118,10 @@ const getAllHostsWithExecutedProcessByRunAndDplDetectorHandler = async (request, const getAllProcessExecutionByRunAndDplDetectorAndHostHandler = async (request, response) => { const requestDto = await dtoValidator( DtoFactory.queryOnly({ - runNumber: Joi.number().required(), - detectorId: Joi.number().required(), - processId: Joi.number().required(), - hostId: Joi.number().required(), + runNumber: EntityIdDto.required(), + detectorId: EntityIdDto.required(), + processId: EntityIdDto.required(), + hostId: EntityIdDto.required(), }), request, response, diff --git a/test/api/dplProcess.test.js b/test/api/dplProcess.test.js index ddddd57c30..9c51481d85 100644 --- a/test/api/dplProcess.test.js +++ b/test/api/dplProcess.test.js @@ -492,4 +492,25 @@ module.exports = () => { expect(response.body.errors[0].detail).to.equal('"query.hostId" is required'); }); }); + + describe('Validation of the ids', () => { + it('Should return a 400 when an id is not a positive integer', async () => { + for (const [url, query, field, message] of [ + ['/api/dpl-process/detectors', { runNumber: 0 }, 'runNumber', 'must be a positive number'], + ['/api/dpl-process/processes', { runNumber: 106, detectorId: -1 }, 'detectorId', 'must be a positive number'], + ['/api/dpl-process/hosts', { runNumber: 106, detectorId: 1, processId: 0.5 }, 'processId', 'must be an integer'], + [ + '/api/dpl-process/executions', + { runNumber: 106, detectorId: 1, processId: 1, hostId: 0 }, + 'hostId', + 'must be a positive number', + ], + ]) { + const response = await request(server).get(buildUrl(url, query)); + + expect(response.status, url).to.equal(400); + expect(response.body.errors.map(({ detail }) => detail), url).to.eql([`"query.${field}" ${message}`]); + } + }); + }); }; From 4bcffa31df13647ec9b8b76a5986dd9e4314a4d1 Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 5.5" Date: Thu, 22 Oct 2026 15:53:29 +0200 Subject: [PATCH 3/7] [O2B-1623] Do not send createdAt and updatedAt of the DPL detectors, processes, hosts and executions Nothing uses the creation and update dates returned by the DPL process endpoints: the run details page only reads the names, the process type label, the host name and the arguments, and no test checks them. The adapters of the DPL detectors, processes, process types, process executions and hosts, only used by these endpoints (and the gRPC creation of an execution, whose response is only the id), stop returning them and their typedefs are updated. The (always empty) relations of these entities are left as they are, as the tests check them. --- lib/database/adapters/HostAdapter.js | 4 +--- lib/database/adapters/dpl/DplDetectorAdapter.js | 4 +--- lib/database/adapters/dpl/DplProcessAdapter.js | 4 +--- lib/database/adapters/dpl/DplProcessExecutionAdapter.js | 4 +--- lib/database/adapters/dpl/DplProcessTypeAdapter.js | 4 +--- lib/domain/entities/Host.js | 2 -- lib/domain/entities/dpl/DplDetector.js | 2 -- lib/domain/entities/dpl/DplProcess.js | 2 -- lib/domain/entities/dpl/DplProcessExecution.js | 2 -- lib/domain/entities/dpl/DplProcessType.js | 2 -- 10 files changed, 5 insertions(+), 25 deletions(-) diff --git a/lib/database/adapters/HostAdapter.js b/lib/database/adapters/HostAdapter.js index 487aa2cb6d..6ef6f07ce8 100644 --- a/lib/database/adapters/HostAdapter.js +++ b/lib/database/adapters/HostAdapter.js @@ -34,12 +34,10 @@ class HostAdapter { * @param {SequelizeHost} databaseObject Object to convert. * @returns {Host} Converted entity object. */ - toEntity({ id, hostname, processesExecutions, createdAt, updatedAt }) { + toEntity({ id, hostname, processesExecutions }) { return { id, hostname, - createdAt: createdAt ? new Date(createdAt).getTime() : createdAt, - updatedAt: updatedAt ? new Date(updatedAt).getTime() : updatedAt, processesExecutions: (processesExecutions ?? []).map(this.dplProcessExecutionAdapter.toEntity), }; } diff --git a/lib/database/adapters/dpl/DplDetectorAdapter.js b/lib/database/adapters/dpl/DplDetectorAdapter.js index 354aeeeea0..5090dbf6ed 100644 --- a/lib/database/adapters/dpl/DplDetectorAdapter.js +++ b/lib/database/adapters/dpl/DplDetectorAdapter.js @@ -35,12 +35,10 @@ class DplDetectorAdapter { * @returns {DplDetector} Converted entity object. */ toEntity(databaseObject) { - const { id, name, createdAt, updatedAt, processesExecutions } = databaseObject; + const { id, name, processesExecutions } = databaseObject; return { id, name, - createdAt: createdAt ? new Date(createdAt).getTime() : createdAt, - updatedAt: updatedAt ? new Date(updatedAt).getTime() : updatedAt, processesExecutions: (processesExecutions ?? []).map(this.dplProcessExecutionAdapter.toEntity), }; } diff --git a/lib/database/adapters/dpl/DplProcessAdapter.js b/lib/database/adapters/dpl/DplProcessAdapter.js index b170c6d09d..7c7a868b79 100644 --- a/lib/database/adapters/dpl/DplProcessAdapter.js +++ b/lib/database/adapters/dpl/DplProcessAdapter.js @@ -39,13 +39,11 @@ class DplProcessAdapter { * @param {SequelizeDplProcess} databaseObject Object to convert. * @returns {DplProcess} Converted entity object. */ - toEntity({ id, name, typeId, createdAt, updatedAt, type, processesExecutions }) { + toEntity({ id, name, typeId, type, processesExecutions }) { return { id, name, typeId, - createdAt: createdAt ? new Date(createdAt).getTime() : createdAt, - updatedAt: updatedAt ? new Date(updatedAt).getTime() : updatedAt, type: type ? this.dplProcessTypeAdapter.toEntity(type) : null, processesExecutions: (processesExecutions ?? []).map(this.dplProcessExecutionAdapter.toEntity), }; diff --git a/lib/database/adapters/dpl/DplProcessExecutionAdapter.js b/lib/database/adapters/dpl/DplProcessExecutionAdapter.js index 6ffe4b5ea3..c46019653b 100644 --- a/lib/database/adapters/dpl/DplProcessExecutionAdapter.js +++ b/lib/database/adapters/dpl/DplProcessExecutionAdapter.js @@ -50,7 +50,7 @@ class DplProcessExecutionAdapter { * @returns {DplProcessExecution} Converted entity object. */ toEntity(databaseObject) { - const { id, hostId, processId, detectorId, runNumber, args, createdAt, updatedAt, detector, host, process, run } = databaseObject; + const { id, hostId, processId, detectorId, runNumber, args, detector, host, process, run } = databaseObject; return { id, @@ -59,8 +59,6 @@ class DplProcessExecutionAdapter { detectorId, runNumber, args, - createdAt: createdAt ? new Date(createdAt).getTime() : createdAt, - updatedAt: updatedAt ? new Date(updatedAt).getTime() : updatedAt, detector: detector ? this.dplDetectorAdapter.toEntity(detector) : null, host: host ? this.hostAdapter.toEntity(host) : null, process: process ? this.dplProcessAdapter.toEntity(process) : null, diff --git a/lib/database/adapters/dpl/DplProcessTypeAdapter.js b/lib/database/adapters/dpl/DplProcessTypeAdapter.js index abc30be578..4ecd6ff209 100644 --- a/lib/database/adapters/dpl/DplProcessTypeAdapter.js +++ b/lib/database/adapters/dpl/DplProcessTypeAdapter.js @@ -29,12 +29,10 @@ class DplProcessTypeAdapter { * @param {SequelizeDplProcessType} databaseObject Object to convert. * @returns {DplProcessType} Converted entity object. */ - toEntity({ id, label, createdAt, updatedAt }) { + toEntity({ id, label }) { return { id, label, - createdAt: createdAt ? new Date(createdAt).getTime() : createdAt, - updatedAt: updatedAt ? new Date(updatedAt).getTime() : updatedAt, }; } diff --git a/lib/domain/entities/Host.js b/lib/domain/entities/Host.js index 2eca6bda74..786276ba66 100644 --- a/lib/domain/entities/Host.js +++ b/lib/domain/entities/Host.js @@ -15,8 +15,6 @@ * @typedef Host * @property {number} id * @property {string} hostname - * @property {number} createdAt - * @property {number} updatedAt * @property {DplProcessExecution[]} processesExecutions */ diff --git a/lib/domain/entities/dpl/DplDetector.js b/lib/domain/entities/dpl/DplDetector.js index dbf5a6ee39..546cb6d9b7 100644 --- a/lib/domain/entities/dpl/DplDetector.js +++ b/lib/domain/entities/dpl/DplDetector.js @@ -15,8 +15,6 @@ * @typedef DplDetector * @property {number} id * @property {string} name - * @property {number} createdAt - * @property {number} updatedAt * @property {DplProcessExecution[]} processesExecutions */ diff --git a/lib/domain/entities/dpl/DplProcess.js b/lib/domain/entities/dpl/DplProcess.js index 4ce2c18241..6f9d095f3c 100644 --- a/lib/domain/entities/dpl/DplProcess.js +++ b/lib/domain/entities/dpl/DplProcess.js @@ -16,8 +16,6 @@ * @property {number} id * @property {string} name * @property {number} typeId - * @property {number} createdAt - * @property {number} updatedAt * @property {DplProcessType|null} type * @property {DplProcessExecution[]} processesExecutions */ diff --git a/lib/domain/entities/dpl/DplProcessExecution.js b/lib/domain/entities/dpl/DplProcessExecution.js index 04e356820a..25f7971f81 100644 --- a/lib/domain/entities/dpl/DplProcessExecution.js +++ b/lib/domain/entities/dpl/DplProcessExecution.js @@ -19,8 +19,6 @@ * @property {string} processId * @property {string} hostId * @property {string} args - * @property {number} createdAt - * @property {number} updatedAt * @property {Run|null} run * @property {DplDetector|null} detector * @property {DplProcess|null} process diff --git a/lib/domain/entities/dpl/DplProcessType.js b/lib/domain/entities/dpl/DplProcessType.js index 0d38838267..d43cd4bee2 100644 --- a/lib/domain/entities/dpl/DplProcessType.js +++ b/lib/domain/entities/dpl/DplProcessType.js @@ -15,8 +15,6 @@ * @typedef DplProcessType * @property {number} id * @property {string} label - * @property {number} createdAt - * @property {number} updatedAt */ /** From 7be87a06cc7d94ab4ae0c2064e61952b5f2bdb1d Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 5.5" Date: Thu, 22 Oct 2026 15:55:47 +0200 Subject: [PATCH 4/7] [O2B-1623] Build the read methods of DplProcessService on small functions The four read methods of `DplProcessService` queried the DPL detectors, DPL processes, hosts and process executions repositories directly, while the creation of an execution already relies on small functions. They now use `getDplDetectorIdsWithExecutedProcessesInRun`, `getAllDplDetectors`, `getDplProcessIdsExecutedInRunByDetector`, `getAllDplProcesses`, `getHostIdsWithExecutedDplProcess`, `getAllHosts` and `getAllDplProcessExecutions`, each with a test. The two steps of the queries (a first one for the ids, a second one for the sorted entities) are kept as they were. Checked against the previous service with the SQL layer stubbed: the same queries are generated (up to redundant parentheses around conditions) and the same results are returned. --- lib/server/services/dpl/DplProcessService.js | 97 ++++------------ lib/server/services/dpl/getAllDplDetectors.js | 32 +++++ .../dpl/getAllDplProcessExecutions.js | 32 +++++ lib/server/services/dpl/getAllDplProcesses.js | 32 +++++ ...plDetectorIdsWithExecutedProcessesInRun.js | 36 ++++++ ...getDplProcessIdsExecutedInRunByDetector.js | 37 ++++++ .../dpl/getHostIdsWithExecutedDplProcess.js | 38 ++++++ lib/server/services/host/getAllHosts.js | 32 +++++ .../services/dpl/dplProcessQueries.test.js | 109 ++++++++++++++++++ test/lib/server/services/dpl/index.js | 2 + 10 files changed, 375 insertions(+), 72 deletions(-) create mode 100644 lib/server/services/dpl/getAllDplDetectors.js create mode 100644 lib/server/services/dpl/getAllDplProcessExecutions.js create mode 100644 lib/server/services/dpl/getAllDplProcesses.js create mode 100644 lib/server/services/dpl/getDplDetectorIdsWithExecutedProcessesInRun.js create mode 100644 lib/server/services/dpl/getDplProcessIdsExecutedInRunByDetector.js create mode 100644 lib/server/services/dpl/getHostIdsWithExecutedDplProcess.js create mode 100644 lib/server/services/host/getAllHosts.js create mode 100644 test/lib/server/services/dpl/dplProcessQueries.test.js diff --git a/lib/server/services/dpl/DplProcessService.js b/lib/server/services/dpl/DplProcessService.js index 589fba217a..024165812e 100644 --- a/lib/server/services/dpl/DplProcessService.js +++ b/lib/server/services/dpl/DplProcessService.js @@ -17,12 +17,6 @@ const { dplProcessExecutionAdapter, hostAdapter, } = require('../../../database/adapters/index.js'); -const { - DplDetectorRepository, - DplProcessRepository, - HostRepository, - DplProcessExecutionRepository, -} = require('../../../database/repositories/index.js'); const { getRunOrFail } = require('../run/getRunOrFail.js'); const { getDplDetectorOrFail } = require('./getDplDetectorOrFail.js'); const { getDplProcessOrFail } = require('./getDplProcessOrFail.js'); @@ -33,7 +27,13 @@ const { getOrCreateHostByHostname } = require('../host/getOrCreateHost.js'); const { createDplProcessExecution } = require('./createDplProcessExecution.js'); const { getOrCreateDplProcessTypeByLabel } = require('./getOrCreateDplProcessType.js'); const { getDplProcessExecution } = require('./getDplProcessExecution.js'); -const { Op } = require('sequelize'); +const { getAllDplDetectors } = require('./getAllDplDetectors.js'); +const { getAllDplProcessExecutions } = require('./getAllDplProcessExecutions.js'); +const { getAllDplProcesses } = require('./getAllDplProcesses.js'); +const { getAllHosts } = require('../host/getAllHosts.js'); +const { getDplDetectorIdsWithExecutedProcessesInRun } = require('./getDplDetectorIdsWithExecutedProcessesInRun.js'); +const { getDplProcessIdsExecutedInRunByDetector } = require('./getDplProcessIdsExecutedInRunByDetector.js'); +const { getHostIdsWithExecutedDplProcess } = require('./getHostIdsWithExecutedDplProcess.js'); const { cacheAsyncFunction } = require('../../../utilities/cacheAsyncFunction.js'); const cachedGetRunOrFail = cacheAsyncFunction((runIdentifier) => getRunOrFail(runIdentifier) @@ -65,25 +65,11 @@ class DplProcessService { const run = await getRunOrFail(runIdentifier); // Do the requests in two steps because some tests randomly fail, maybe because of a bug in sequelize? - const detectors = await DplDetectorRepository.findAll({ - include: { - association: 'processesExecutions', - where: { - runNumber: run.runNumber, - }, - required: true, - attributes: [], - }, - raw: true, - attributes: ['id'], - order: [['name', 'ASC']], - }); + const detectorIds = await getDplDetectorIdsWithExecutedProcessesInRun(run.runNumber); - const detectorIds = detectors.map(({ id }) => id); - - const sortedDetectors = await DplDetectorRepository.findAll({ - where: { id: { [Op.in]: detectorIds } }, - order: [['name', 'ASC']], + const sortedDetectors = await getAllDplDetectors((queryBuilder) => { + queryBuilder.where('id').oneOf(...detectorIds); + queryBuilder.orderBy('name', 'ASC'); }); return sortedDetectors.map(dplDetectorAdapter.toEntity); @@ -101,27 +87,12 @@ class DplProcessService { const detector = await getDplDetectorOrFail(detectorIdentifier); // Do the requests in two steps because some tests randomly fail, maybe because of a bug in sequelize? - const processes = await DplProcessRepository.findAll({ - include: { - association: 'processesExecutions', - where: { - runNumber: run.runNumber, - detectorId: detector.id, - }, - required: true, - attributes: [], - }, - raw: true, - attributes: ['id'], - order: [['name', 'ASC']], - }); + const processIds = await getDplProcessIdsExecutedInRunByDetector(run.runNumber, detector.id); - const processIds = processes.map(({ id }) => id); - - const sortedProcesses = await DplProcessRepository.findAll({ - where: { id: { [Op.in]: processIds } }, - include: 'type', - order: [['name', 'ASC']], + const sortedProcesses = await getAllDplProcesses((queryBuilder) => { + queryBuilder.where('id').oneOf(...processIds); + queryBuilder.include({ association: 'type' }); + queryBuilder.orderBy('name', 'ASC'); }); return sortedProcesses.map(dplProcessAdapter.toEntity); @@ -141,27 +112,11 @@ class DplProcessService { const process = await getDplProcessOrFail(processIdentifier); // Do the requests in two steps because some tests randomly fail, maybe because of a bug in sequelize? - const hosts = await HostRepository.findAll({ - include: { - association: 'processesExecutions', - where: { - runNumber: run.runNumber, - detectorId: detector.id, - processId: process.id, - }, - required: true, - attributes: [], - order: [['name', 'ASC']], - }, - raw: true, - attributes: ['id'], - }); - - const hostIds = hosts.map(({ id }) => id); + const hostIds = await getHostIdsWithExecutedDplProcess(run.runNumber, detector.id, process.id); - const sortedHosts = await HostRepository.findAll({ - where: { id: { [Op.in]: hostIds } }, - order: [['hostname', 'ASC']], + const sortedHosts = await getAllHosts((queryBuilder) => { + queryBuilder.where('id').oneOf(...hostIds); + queryBuilder.orderBy('hostname', 'ASC'); }); return sortedHosts.map(hostAdapter.toEntity); @@ -184,13 +139,11 @@ class DplProcessService { getHostOrFail(hostIdentifier), ]); - const processExecutions = await DplProcessExecutionRepository.findAll({ - where: { - runNumber: run.runNumber, - detectorId: detector.id, - processId: process.id, - hostId: host.id, - }, + const processExecutions = await getAllDplProcessExecutions((queryBuilder) => { + queryBuilder.where('runNumber').is(run.runNumber); + queryBuilder.where('detectorId').is(detector.id); + queryBuilder.where('processId').is(process.id); + queryBuilder.where('hostId').is(host.id); }); return processExecutions.map(dplProcessExecutionAdapter.toEntity); diff --git a/lib/server/services/dpl/getAllDplDetectors.js b/lib/server/services/dpl/getAllDplDetectors.js new file mode 100644 index 0000000000..57255f938f --- /dev/null +++ b/lib/server/services/dpl/getAllDplDetectors.js @@ -0,0 +1,32 @@ +/** + * @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. + */ + +const { dataSource } = require('../../../database/DataSource.js'); +const { DplDetectorRepository } = require('../../../database/repositories/index.js'); + +/** + * Find and return the DPL detectors + * + * @param {(function(QueryBuilder):void)|null} [qbConfiguration=null] function called with the find query builder as parameter to add specific + * configuration to the query + * @return {Promise} resolves with the DPL detectors found + */ +exports.getAllDplDetectors = (qbConfiguration = null) => { + const queryBuilder = dataSource.createQueryBuilder(); + + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + + return DplDetectorRepository.findAll(queryBuilder); +}; diff --git a/lib/server/services/dpl/getAllDplProcessExecutions.js b/lib/server/services/dpl/getAllDplProcessExecutions.js new file mode 100644 index 0000000000..1b7ae60e9d --- /dev/null +++ b/lib/server/services/dpl/getAllDplProcessExecutions.js @@ -0,0 +1,32 @@ +/** + * @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. + */ + +const { dataSource } = require('../../../database/DataSource.js'); +const { DplProcessExecutionRepository } = require('../../../database/repositories/index.js'); + +/** + * Find and return the DPL processes executions + * + * @param {(function(QueryBuilder):void)|null} [qbConfiguration=null] function called with the find query builder as parameter to add specific + * configuration to the query + * @return {Promise} resolves with the DPL processes executions found + */ +exports.getAllDplProcessExecutions = (qbConfiguration = null) => { + const queryBuilder = dataSource.createQueryBuilder(); + + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + + return DplProcessExecutionRepository.findAll(queryBuilder); +}; diff --git a/lib/server/services/dpl/getAllDplProcesses.js b/lib/server/services/dpl/getAllDplProcesses.js new file mode 100644 index 0000000000..108975c953 --- /dev/null +++ b/lib/server/services/dpl/getAllDplProcesses.js @@ -0,0 +1,32 @@ +/** + * @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. + */ + +const { dataSource } = require('../../../database/DataSource.js'); +const { DplProcessRepository } = require('../../../database/repositories/index.js'); + +/** + * Find and return the DPL processes + * + * @param {(function(QueryBuilder):void)|null} [qbConfiguration=null] function called with the find query builder as parameter to add specific + * configuration to the query + * @return {Promise} resolves with the DPL processes found + */ +exports.getAllDplProcesses = (qbConfiguration = null) => { + const queryBuilder = dataSource.createQueryBuilder(); + + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + + return DplProcessRepository.findAll(queryBuilder); +}; diff --git a/lib/server/services/dpl/getDplDetectorIdsWithExecutedProcessesInRun.js b/lib/server/services/dpl/getDplDetectorIdsWithExecutedProcessesInRun.js new file mode 100644 index 0000000000..c628a63241 --- /dev/null +++ b/lib/server/services/dpl/getDplDetectorIdsWithExecutedProcessesInRun.js @@ -0,0 +1,36 @@ +/** + * @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. + */ + +const { DplDetectorRepository } = require('../../../database/repositories/index.js'); + +/** + * Return the ids of the DPL detectors that have at least one executed process in a given run, sorted by detector name + * + * @param {number} runNumber the run number of the run + * @return {Promise} resolves with the ids of the DPL detectors + */ +exports.getDplDetectorIdsWithExecutedProcessesInRun = async (runNumber) => { + const detectors = await DplDetectorRepository.findAll({ + include: { + association: 'processesExecutions', + where: { runNumber }, + required: true, + attributes: [], + }, + raw: true, + attributes: ['id'], + order: [['name', 'ASC']], + }); + + return detectors.map(({ id }) => id); +}; diff --git a/lib/server/services/dpl/getDplProcessIdsExecutedInRunByDetector.js b/lib/server/services/dpl/getDplProcessIdsExecutedInRunByDetector.js new file mode 100644 index 0000000000..579ba5200b --- /dev/null +++ b/lib/server/services/dpl/getDplProcessIdsExecutedInRunByDetector.js @@ -0,0 +1,37 @@ +/** + * @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. + */ + +const { DplProcessRepository } = require('../../../database/repositories/index.js'); + +/** + * Return the ids of the DPL processes that have been executed at least once in a given run for a given DPL detector, sorted by process name + * + * @param {number} runNumber the run number of the run + * @param {number} detectorId the id of the DPL detector + * @return {Promise} resolves with the ids of the DPL processes + */ +exports.getDplProcessIdsExecutedInRunByDetector = async (runNumber, detectorId) => { + const processes = await DplProcessRepository.findAll({ + include: { + association: 'processesExecutions', + where: { runNumber, detectorId }, + required: true, + attributes: [], + }, + raw: true, + attributes: ['id'], + order: [['name', 'ASC']], + }); + + return processes.map(({ id }) => id); +}; diff --git a/lib/server/services/dpl/getHostIdsWithExecutedDplProcess.js b/lib/server/services/dpl/getHostIdsWithExecutedDplProcess.js new file mode 100644 index 0000000000..ae2aa5989d --- /dev/null +++ b/lib/server/services/dpl/getHostIdsWithExecutedDplProcess.js @@ -0,0 +1,38 @@ +/** + * @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. + */ + +const { HostRepository } = require('../../../database/repositories/index.js'); + +/** + * Return the ids of the hosts on which a given DPL process has been executed in a given run for a given DPL detector + * + * @param {number} runNumber the run number of the run + * @param {number} detectorId the id of the DPL detector + * @param {number} processId the id of the DPL process + * @return {Promise} resolves with the ids of the hosts + */ +exports.getHostIdsWithExecutedDplProcess = async (runNumber, detectorId, processId) => { + const hosts = await HostRepository.findAll({ + include: { + association: 'processesExecutions', + where: { runNumber, detectorId, processId }, + required: true, + attributes: [], + order: [['name', 'ASC']], + }, + raw: true, + attributes: ['id'], + }); + + return hosts.map(({ id }) => id); +}; diff --git a/lib/server/services/host/getAllHosts.js b/lib/server/services/host/getAllHosts.js new file mode 100644 index 0000000000..49ce1d8ee9 --- /dev/null +++ b/lib/server/services/host/getAllHosts.js @@ -0,0 +1,32 @@ +/** + * @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. + */ + +const { dataSource } = require('../../../database/DataSource.js'); +const { HostRepository } = require('../../../database/repositories/index.js'); + +/** + * Find and return the hosts + * + * @param {(function(QueryBuilder):void)|null} [qbConfiguration=null] function called with the find query builder as parameter to add specific + * configuration to the query + * @return {Promise} resolves with the hosts found + */ +exports.getAllHosts = (qbConfiguration = null) => { + const queryBuilder = dataSource.createQueryBuilder(); + + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + + return HostRepository.findAll(queryBuilder); +}; diff --git a/test/lib/server/services/dpl/dplProcessQueries.test.js b/test/lib/server/services/dpl/dplProcessQueries.test.js new file mode 100644 index 0000000000..03a5473acc --- /dev/null +++ b/test/lib/server/services/dpl/dplProcessQueries.test.js @@ -0,0 +1,109 @@ +/** + * @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. + */ + +const { expect } = require('chai'); +const { + getDplDetectorIdsWithExecutedProcessesInRun, +} = require('../../../../../lib/server/services/dpl/getDplDetectorIdsWithExecutedProcessesInRun.js'); +const { getAllDplDetectors } = require('../../../../../lib/server/services/dpl/getAllDplDetectors.js'); +const { + getDplProcessIdsExecutedInRunByDetector, +} = require('../../../../../lib/server/services/dpl/getDplProcessIdsExecutedInRunByDetector.js'); +const { getAllDplProcesses } = require('../../../../../lib/server/services/dpl/getAllDplProcesses.js'); +const { getHostIdsWithExecutedDplProcess } = require('../../../../../lib/server/services/dpl/getHostIdsWithExecutedDplProcess.js'); +const { getAllHosts } = require('../../../../../lib/server/services/host/getAllHosts.js'); +const { getAllDplProcessExecutions } = require('../../../../../lib/server/services/dpl/getAllDplProcessExecutions.js'); + +module.exports = () => { + describe('getDplDetectorIdsWithExecutedProcessesInRun', () => { + it('should return the ids of the detectors having an executed process in the run, sorted by name', async () => { + expect(await getDplDetectorIdsWithExecutedProcessesInRun(106)).to.eql([1, 2]); + }); + + it('should return an empty list for a run without executed process', async () => { + expect(await getDplDetectorIdsWithExecutedProcessesInRun(999)).to.eql([]); + }); + }); + + describe('getAllDplDetectors', () => { + it('should return the detectors matching the query builder configuration', async () => { + const detectors = await getAllDplDetectors((queryBuilder) => { + queryBuilder.where('id').oneOf(1, 2); + queryBuilder.orderBy('name', 'ASC'); + }); + + expect(detectors.map(({ id }) => id)).to.eql([1, 2]); + }); + + it('should return all the detectors without configuration', async () => { + expect((await getAllDplDetectors()).map(({ id }) => id)).to.include.members([1, 2]); + }); + }); + + describe('getDplProcessIdsExecutedInRunByDetector', () => { + it('should return the ids of the processes executed in the run for the detector, sorted by name', async () => { + expect(await getDplProcessIdsExecutedInRunByDetector(106, 1)).to.eql([1, 3, 2]); + }); + + it('should return an empty list when no process was executed for the detector', async () => { + expect(await getDplProcessIdsExecutedInRunByDetector(106, 999)).to.eql([]); + }); + }); + + describe('getAllDplProcesses', () => { + it('should return the processes matching the query builder configuration, with their type', async () => { + const processes = await getAllDplProcesses((queryBuilder) => { + queryBuilder.where('id').oneOf(1, 2, 3); + queryBuilder.include({ association: 'type' }); + queryBuilder.orderBy('name', 'ASC'); + }); + + expect(processes.map(({ id }) => id)).to.eql([1, 3, 2]); + expect(processes.every(({ type }) => type !== null && typeof type === 'object')).to.be.true; + }); + }); + + describe('getHostIdsWithExecutedDplProcess', () => { + it('should return the ids of the hosts on which the process was executed in the run for the detector', async () => { + expect(await getHostIdsWithExecutedDplProcess(106, 1, 1)).to.have.members([1, 2]); + }); + + it('should return an empty list when the process was not executed for the detector', async () => { + expect(await getHostIdsWithExecutedDplProcess(106, 999, 1)).to.eql([]); + }); + }); + + describe('getAllHosts', () => { + it('should return the hosts matching the query builder configuration, sorted as asked', async () => { + const getHostsIds = async (order) => (await getAllHosts((queryBuilder) => { + queryBuilder.where('id').oneOf(1, 2); + queryBuilder.orderBy('hostname', order); + })).map(({ id }) => id); + + const ascendingIds = await getHostsIds('ASC'); + expect(ascendingIds).to.have.members([1, 2]); + expect(ascendingIds).to.eql([...await getHostsIds('DESC')].reverse()); + }); + }); + + describe('getAllDplProcessExecutions', () => { + it('should return the executions matching the query builder configuration', async () => { + const executions = await getAllDplProcessExecutions((queryBuilder) => { + queryBuilder.where('runNumber').is(106); + }); + + expect(executions).to.not.be.empty; + expect(executions.every(({ runNumber }) => runNumber === 106)).to.be.true; + }); + }); +}; diff --git a/test/lib/server/services/dpl/index.js b/test/lib/server/services/dpl/index.js index fde22a3571..3ceb51fe1d 100644 --- a/test/lib/server/services/dpl/index.js +++ b/test/lib/server/services/dpl/index.js @@ -13,8 +13,10 @@ const { resetDatabaseContent } = require('../../../../utilities/resetDatabaseContent.js'); const DplProcessServiceTest = require('./DplProcessService.test.js'); +const DplProcessQueriesTest = require('./dplProcessQueries.test.js'); module.exports = () => { before(resetDatabaseContent); describe('DplProcessService', DplProcessServiceTest); + describe('DPL process small functions', DplProcessQueriesTest); }; From 6575eda3ae404f613b76400a416c45a8193d46c3 Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 5.5" Date: Thu, 22 Oct 2026 15:56:13 +0200 Subject: [PATCH 5/7] [O2B-1623] Fix the types of the DplProcessExecution typedef `processId` and `hostId` are integers (as the columns of the table) and `args` can be null, but the typedef declared strings. --- lib/domain/entities/dpl/DplProcessExecution.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/domain/entities/dpl/DplProcessExecution.js b/lib/domain/entities/dpl/DplProcessExecution.js index 25f7971f81..59380fb5c2 100644 --- a/lib/domain/entities/dpl/DplProcessExecution.js +++ b/lib/domain/entities/dpl/DplProcessExecution.js @@ -16,9 +16,9 @@ * @property {number} id * @property {number} runNumber * @property {number} detectorId - * @property {string} processId - * @property {string} hostId - * @property {string} args + * @property {number} processId + * @property {number} hostId + * @property {string|null} args * @property {Run|null} run * @property {DplDetector|null} detector * @property {DplProcess|null} process From 5084dfe909fd3e32bd72635da214cca7aebbc9c7 Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 5.5" Date: Thu, 22 Oct 2026 15:56:46 +0200 Subject: [PATCH 6/7] [O2B-1623] Document the DPL process endpoints in the HTTP API specification --- lib/server/api-docs/openapi.yaml | 195 +++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/lib/server/api-docs/openapi.yaml b/lib/server/api-docs/openapi.yaml index 4bae027099..ac5613c322 100644 --- a/lib/server/api-docs/openapi.yaml +++ b/lib/server/api-docs/openapi.yaml @@ -59,6 +59,8 @@ tags: description: End-of-shift (EoS) reports written by the shifters. - name: Attachments description: Files attached to log entries. + - name: DPL processes + description: Processes of the data processing layer (DPL) executed during the runs, by detector, process and host. paths: /tags: @@ -792,6 +794,100 @@ paths: example: errors: [{ status: 404, title: Not found, detail: Attachment with this id (999999999) could not be found }] + /dpl-process/detectors: + get: + tags: [DPL processes] + operationId: listDplDetectorsWithExecutedProcesses + summary: List the detectors having an executed process in a run + description: | + Returns the detectors that have at least one executed DPL process in the run, sorted by name. Not paginated. This is the first level of the + run details tree of DPL processes: detector, then process (`/dpl-process/processes`), then host (`/dpl-process/hosts`), then executions + (`/dpl-process/executions`). + parameters: + - $ref: '#/components/parameters/DplRunNumber' + responses: + '200': + description: The detectors. + content: + application/json: + schema: { $ref: '#/components/schemas/DplDetectorListResponse' } + example: + data: + - { id: 1, name: CPV, processesExecutions: [] } + - { id: 2, name: EMC, processesExecutions: [] } + '400': { $ref: '#/components/responses/ValidationError' } + '404': { $ref: '#/components/responses/DplNotFound' } + + /dpl-process/processes: + get: + tags: [DPL processes] + operationId: listDplProcessesExecutedForDetector + summary: List the processes executed for a detector in a run + description: Returns the DPL processes that have been executed at least once in the run for the detector, sorted by name, with their type. Not paginated. + parameters: + - $ref: '#/components/parameters/DplRunNumber' + - $ref: '#/components/parameters/DplDetectorId' + responses: + '200': + description: The processes. + content: + application/json: + schema: { $ref: '#/components/schemas/DplProcessListResponse' } + example: + data: + - { id: 1, name: PROCESS-1, typeId: 1, type: { id: 1, label: QcTask }, processesExecutions: [] } + '400': { $ref: '#/components/responses/ValidationError' } + '404': { $ref: '#/components/responses/DplNotFound' } + + /dpl-process/hosts: + get: + tags: [DPL processes] + operationId: listHostsExecutingDplProcess + summary: List the hosts on which a process was executed for a detector in a run + description: Returns the hosts on which the DPL process has been executed at least once in the run for the detector, sorted by host name. Not paginated. + parameters: + - $ref: '#/components/parameters/DplRunNumber' + - $ref: '#/components/parameters/DplDetectorId' + - $ref: '#/components/parameters/DplProcessId' + responses: + '200': + description: The hosts. + content: + application/json: + schema: { $ref: '#/components/schemas/HostListResponse' } + example: + data: + - { id: 1, hostname: flp001, processesExecutions: [] } + '400': { $ref: '#/components/responses/ValidationError' } + '404': { $ref: '#/components/responses/DplNotFound' } + + /dpl-process/executions: + get: + tags: [DPL processes] + operationId: listDplProcessExecutions + summary: List the executions of a process on a host for a detector in a run + description: Returns the executions of the DPL process on the host in the run for the detector. Not paginated. + parameters: + - $ref: '#/components/parameters/DplRunNumber' + - $ref: '#/components/parameters/DplDetectorId' + - $ref: '#/components/parameters/DplProcessId' + - name: hostId + in: query + required: true + description: Host identifier. + schema: { type: integer, minimum: 1, examples: [1] } + responses: + '200': + description: The executions. + content: + application/json: + schema: { $ref: '#/components/schemas/DplProcessExecutionListResponse' } + example: + data: + - { id: 1, hostId: 1, processId: 1, detectorId: 1, runNumber: 106, args: --id QcTask, detector: null, host: null, process: null, run: null } + '400': { $ref: '#/components/responses/ValidationError' } + '404': { $ref: '#/components/responses/DplNotFound' } + components: securitySchemes: jwtQuery: @@ -820,6 +916,25 @@ components: description: Number of items to skip. schema: { type: integer, minimum: 0, default: 0 } + DplRunNumber: + name: runNumber + in: query + required: true + description: Run number. + schema: { type: integer, minimum: 1, examples: [106] } + DplDetectorId: + name: detectorId + in: query + required: true + description: Identifier of the detector (the detectors of `GET /detectors`). + schema: { type: integer, minimum: 1, examples: [1] } + DplProcessId: + name: processId + in: query + required: true + description: Identifier of the DPL process. + schema: { type: integer, minimum: 1, examples: [1] } + responses: ValidationError: description: | @@ -849,6 +964,15 @@ components: example: errors: [{ status: '404', title: 'Tag with this id (999999999) could not be found' }] + DplNotFound: + description: The run, the detector, the process or the host does not exist. + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorResponse' } + example: + errors: + - { status: 404, title: Not found, detail: Run with this run number (999) could not be found } + schemas: Tag: type: object @@ -1231,3 +1355,74 @@ components: required: [data] properties: data: { $ref: '#/components/schemas/StoredAttachment' } + DplDetector: + type: object + required: [id, name] + properties: + id: { type: integer } + name: { type: string, description: 'Detector code, e.g. `ITS`.' } + processesExecutions: { type: array, description: Always empty here., items: { type: object, additionalProperties: true } } + DplDetectorListResponse: + type: object + required: [data] + properties: + data: + type: array + items: { $ref: '#/components/schemas/DplDetector' } + DplProcessType: + type: object + required: [id, label] + properties: + id: { type: integer } + label: { type: string, examples: [QcTask] } + DplProcess: + type: object + required: [id, name] + properties: + id: { type: integer } + name: { type: string } + typeId: { type: integer } + type: { $ref: '#/components/schemas/DplProcessType' } + processesExecutions: { type: array, description: Always empty here., items: { type: object, additionalProperties: true } } + DplProcessListResponse: + type: object + required: [data] + properties: + data: + type: array + items: { $ref: '#/components/schemas/DplProcess' } + Host: + type: object + required: [id, hostname] + properties: + id: { type: integer } + hostname: { type: string, examples: [flp001] } + processesExecutions: { type: array, description: Always empty here., items: { type: object, additionalProperties: true } } + HostListResponse: + type: object + required: [data] + properties: + data: + type: array + items: { $ref: '#/components/schemas/Host' } + DplProcessExecution: + type: object + required: [id, hostId, processId, detectorId, runNumber] + properties: + id: { type: integer } + hostId: { type: integer } + processId: { type: integer } + detectorId: { type: integer } + runNumber: { type: integer } + args: { type: [string, 'null'], description: Arguments of the process. } + detector: { type: 'null', description: Always null here. } + host: { type: 'null', description: Always null here. } + process: { type: 'null', description: Always null here. } + run: { type: 'null', description: Always null here. } + DplProcessExecutionListResponse: + type: object + required: [data] + properties: + data: + type: array + items: { $ref: '#/components/schemas/DplProcessExecution' } From defc87efb4449848af653ba168c7711f814a7bda Mon Sep 17 00:00:00 2001 From: "Claude Sonnet 5.5" Date: Thu, 22 Oct 2026 15:56:46 +0200 Subject: [PATCH 7/7] [O2B-1623] Regenerate the HTTP API documentation with the DPL process endpoints --- lib/server/api-docs/API.md | 92 +++++++++++++++++ lib/server/api-docs/index.html | 174 +++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) diff --git a/lib/server/api-docs/API.md b/lib/server/api-docs/API.md index 83227440ca..39a512754a 100644 --- a/lib/server/api-docs/API.md +++ b/lib/server/api-docs/API.md @@ -646,3 +646,95 @@ Responses: ```bash curl -g -OJ "$BOOKKEEPING_URL/api/attachments/1?token=$TOKEN" ``` + +## DPL processes + +Processes of the data processing layer (DPL) executed during the runs, by detector, process and host. + +### `GET /dpl-process/detectors` - List the detectors having an executed process in a run + +Operation id: `listDplDetectorsWithExecutedProcesses` + +Returns the detectors that have at least one executed DPL process in the run, sorted by name. Not paginated. This is the first level of the +run details tree of DPL processes: detector, then process (`/dpl-process/processes`), then host (`/dpl-process/hosts`), then executions +(`/dpl-process/executions`). + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `runNumber` | query | integer | yes | Run number. | + +Responses: + +- `200`: The detectors. +- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes//`. +- `404`: The run, the detector, the process or the host does not exist. + +```bash +curl -g "$BOOKKEEPING_URL/api/dpl-process/detectors?runNumber=106&token=$TOKEN" +``` + +### `GET /dpl-process/processes` - List the processes executed for a detector in a run + +Operation id: `listDplProcessesExecutedForDetector` + +Returns the DPL processes that have been executed at least once in the run for the detector, sorted by name, with their type. Not paginated. + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `runNumber` | query | integer | yes | Run number. | +| `detectorId` | query | integer | yes | Identifier of the detector (the detectors of `GET /detectors`). | + +Responses: + +- `200`: The processes. +- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes//`. +- `404`: The run, the detector, the process or the host does not exist. + +```bash +curl -g "$BOOKKEEPING_URL/api/dpl-process/processes?runNumber=106&detectorId=1&token=$TOKEN" +``` + +### `GET /dpl-process/hosts` - List the hosts on which a process was executed for a detector in a run + +Operation id: `listHostsExecutingDplProcess` + +Returns the hosts on which the DPL process has been executed at least once in the run for the detector, sorted by host name. Not paginated. + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `runNumber` | query | integer | yes | Run number. | +| `detectorId` | query | integer | yes | Identifier of the detector (the detectors of `GET /detectors`). | +| `processId` | query | integer | yes | Identifier of the DPL process. | + +Responses: + +- `200`: The hosts. +- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes//`. +- `404`: The run, the detector, the process or the host does not exist. + +```bash +curl -g "$BOOKKEEPING_URL/api/dpl-process/hosts?runNumber=106&detectorId=1&processId=1&token=$TOKEN" +``` + +### `GET /dpl-process/executions` - List the executions of a process on a host for a detector in a run + +Operation id: `listDplProcessExecutions` + +Returns the executions of the DPL process on the host in the run for the detector. Not paginated. + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `runNumber` | query | integer | yes | Run number. | +| `detectorId` | query | integer | yes | Identifier of the detector (the detectors of `GET /detectors`). | +| `processId` | query | integer | yes | Identifier of the DPL process. | +| `hostId` | query | integer | yes | Host identifier. | + +Responses: + +- `200`: The executions. +- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes//`. +- `404`: The run, the detector, the process or the host does not exist. + +```bash +curl -g "$BOOKKEEPING_URL/api/dpl-process/executions?runNumber=106&detectorId=1&processId=1&hostId=1&token=$TOKEN" +``` diff --git a/lib/server/api-docs/index.html b/lib/server/api-docs/index.html index 79ac65edd1..421ae26956 100644 --- a/lib/server/api-docs/index.html +++ b/lib/server/api-docs/index.html @@ -1160,6 +1160,180 @@

Attachments

curl -g -OJ "$BOOKKEEPING_URL/api/attachments/1?token=$TOKEN"
 
+

DPL processes

+

Processes of the data processing layer (DPL) executed during the runs, by detector, process and host.

+
+

GET /dpl-process/detectors - List the detectors having an executed process in a run

+ +

Operation id: listDplDetectorsWithExecutedProcesses

+

Returns the detectors that have at least one executed DPL process in the run, sorted by name. Not paginated. This is the first level of the +run details tree of DPL processes: detector, then process (/dpl-process/processes), then host (/dpl-process/hosts), then executions +(/dpl-process/executions).

+ + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
runNumberqueryintegeryesRun number.
+

Responses:

+
    +
  • 200: The detectors.
  • +
  • 400: The request failed validation. HTTP status is 400 although each error's status field is "422". source.pointer has the form /data/attributes/<query|params|body>/<field>.
  • +
  • 404: The run, the detector, the process or the host does not exist.
  • +
+
curl -g "$BOOKKEEPING_URL/api/dpl-process/detectors?runNumber=106&token=$TOKEN"
+
+
+
+

GET /dpl-process/processes - List the processes executed for a detector in a run

+ +

Operation id: listDplProcessesExecutedForDetector

+

Returns the DPL processes that have been executed at least once in the run for the detector, sorted by name, with their type. Not paginated.

+ + + + + + + + + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
runNumberqueryintegeryesRun number.
detectorIdqueryintegeryesIdentifier of the detector (the detectors of GET /detectors).
+

Responses:

+
    +
  • 200: The processes.
  • +
  • 400: The request failed validation. HTTP status is 400 although each error's status field is "422". source.pointer has the form /data/attributes/<query|params|body>/<field>.
  • +
  • 404: The run, the detector, the process or the host does not exist.
  • +
+
curl -g "$BOOKKEEPING_URL/api/dpl-process/processes?runNumber=106&detectorId=1&token=$TOKEN"
+
+
+
+

GET /dpl-process/hosts - List the hosts on which a process was executed for a detector in a run

+ +

Operation id: listHostsExecutingDplProcess

+

Returns the hosts on which the DPL process has been executed at least once in the run for the detector, sorted by host name. Not paginated.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
runNumberqueryintegeryesRun number.
detectorIdqueryintegeryesIdentifier of the detector (the detectors of GET /detectors).
processIdqueryintegeryesIdentifier of the DPL process.
+

Responses:

+
    +
  • 200: The hosts.
  • +
  • 400: The request failed validation. HTTP status is 400 although each error's status field is "422". source.pointer has the form /data/attributes/<query|params|body>/<field>.
  • +
  • 404: The run, the detector, the process or the host does not exist.
  • +
+
curl -g "$BOOKKEEPING_URL/api/dpl-process/hosts?runNumber=106&detectorId=1&processId=1&token=$TOKEN"
+
+
+
+

GET /dpl-process/executions - List the executions of a process on a host for a detector in a run

+ +

Operation id: listDplProcessExecutions

+

Returns the executions of the DPL process on the host in the run for the detector. Not paginated.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
runNumberqueryintegeryesRun number.
detectorIdqueryintegeryesIdentifier of the detector (the detectors of GET /detectors).
processIdqueryintegeryesIdentifier of the DPL process.
hostIdqueryintegeryesHost identifier.
+

Responses:

+
    +
  • 200: The executions.
  • +
  • 400: The request failed validation. HTTP status is 400 although each error's status field is "422". source.pointer has the form /data/attributes/<query|params|body>/<field>.
  • +
  • 404: The run, the detector, the process or the host does not exist.
  • +
+
curl -g "$BOOKKEEPING_URL/api/dpl-process/executions?runNumber=106&detectorId=1&processId=1&hostId=1&token=$TOKEN"
+
+