From aadb06768b938973c0042a6a2455cfc7c3bce99d Mon Sep 17 00:00:00 2001 From: Kay van der Meer Date: Wed, 28 Oct 2026 21:08:56 +0100 Subject: [PATCH 1/3] Add tables and example data for presentation templates The templates table holds the name, title and description of a template. The template_modules table holds its modules in presentation order, each with a module type, an optional slide title and its options as JSON. Deleting a template deletes its modules, and each position is used once per template. The seeder adds two example templates for the tests and for development. --- .../v1/20261028100000-create-templates.js | 105 ++++++++++++++++++ lib/database/models/index.js | 4 + lib/database/models/template.js | 41 +++++++ lib/database/models/templateModule.js | 48 ++++++++ .../models/typedefs/SequelizeTemplate.js | 24 ++++ .../typedefs/SequelizeTemplateModule.js | 25 +++++ .../seeders/20261028100000-templates.js | 50 +++++++++ 7 files changed, 297 insertions(+) create mode 100644 lib/database/migrations/v1/20261028100000-create-templates.js create mode 100644 lib/database/models/template.js create mode 100644 lib/database/models/templateModule.js create mode 100644 lib/database/models/typedefs/SequelizeTemplate.js create mode 100644 lib/database/models/typedefs/SequelizeTemplateModule.js create mode 100644 lib/database/seeders/20261028100000-templates.js diff --git a/lib/database/migrations/v1/20261028100000-create-templates.js b/lib/database/migrations/v1/20261028100000-create-templates.js new file mode 100644 index 0000000000..02c729236f --- /dev/null +++ b/lib/database/migrations/v1/20261028100000-create-templates.js @@ -0,0 +1,105 @@ +/** + * @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. + */ + +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async (queryInterface, Sequelize) => queryInterface.sequelize.transaction(async (transaction) => { + await queryInterface.createTable('templates', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + name: { + allowNull: false, + unique: true, + type: Sequelize.STRING(64), + }, + title: { + allowNull: false, + type: Sequelize.STRING(255), + }, + description: { + allowNull: true, + type: Sequelize.TEXT, + }, + created_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'), + }, + }, { transaction }); + + await queryInterface.createTable('template_modules', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + template_id: { + allowNull: false, + type: Sequelize.INTEGER, + references: { model: 'templates', key: 'id' }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + position: { + allowNull: false, + type: Sequelize.INTEGER, + }, + type: { + allowNull: false, + type: Sequelize.STRING(64), + }, + title: { + allowNull: true, + type: Sequelize.STRING(255), + }, + options: { + allowNull: false, + type: Sequelize.JSON, + }, + created_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'), + }, + }, { transaction }); + + await queryInterface.addConstraint('template_modules', { + fields: ['template_id', 'position'], + type: 'unique', + name: 'template_modules_template_id_position', + transaction, + }); + }), + + down: async (queryInterface) => queryInterface.sequelize.transaction(async (transaction) => { + await queryInterface.dropTable('template_modules', { transaction }); + await queryInterface.dropTable('templates', { transaction }); + }), +}; diff --git a/lib/database/models/index.js b/lib/database/models/index.js index 87d793fac3..394c47074e 100644 --- a/lib/database/models/index.js +++ b/lib/database/models/index.js @@ -45,6 +45,8 @@ const SimulationPass = require('./simulationPass.js'); const SimulationPassQcFlag = require('./simulationPassQcFlag.js'); const StableBeamRun = require('./stableBeamsRun.js'); const Tag = require('./tag'); +const Template = require('./template.js'); +const TemplateModule = require('./templateModule.js'); const CtpTriggerCounters = require('./ctpTriggerCounters'); const User = require('./user'); @@ -84,6 +86,8 @@ module.exports = (sequelize) => { SimulationPassQcFlag: SimulationPassQcFlag(sequelize), StableBeamRun: StableBeamRun(sequelize), Tag: Tag(sequelize), + Template: Template(sequelize), + TemplateModule: TemplateModule(sequelize), CtpTriggerCounters: CtpTriggerCounters(sequelize), User: User(sequelize), }; diff --git a/lib/database/models/template.js b/lib/database/models/template.js new file mode 100644 index 0000000000..db7f2b96ea --- /dev/null +++ b/lib/database/models/template.js @@ -0,0 +1,41 @@ +/** + * @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 Sequelize = require('sequelize'); + +module.exports = (sequelize) => { + const Template = sequelize.define('Template', { + name: { + allowNull: false, + unique: true, + type: Sequelize.STRING(64), + }, + title: { + allowNull: false, + type: Sequelize.STRING(255), + }, + description: { + allowNull: true, + type: Sequelize.TEXT, + }, + }); + + Template.associate = (models) => { + Template.hasMany(models.TemplateModule, { + as: 'modules', + foreignKey: 'templateId', + }); + }; + + return Template; +}; diff --git a/lib/database/models/templateModule.js b/lib/database/models/templateModule.js new file mode 100644 index 0000000000..84c66e8b21 --- /dev/null +++ b/lib/database/models/templateModule.js @@ -0,0 +1,48 @@ +/** + * @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 Sequelize = require('sequelize'); + +module.exports = (sequelize) => { + const TemplateModule = sequelize.define('TemplateModule', { + templateId: { + allowNull: false, + type: Sequelize.INTEGER, + }, + position: { + allowNull: false, + type: Sequelize.INTEGER, + }, + type: { + allowNull: false, + type: Sequelize.STRING(64), + }, + title: { + allowNull: true, + type: Sequelize.STRING(255), + }, + options: { + allowNull: false, + type: Sequelize.JSON, + }, + }); + + TemplateModule.associate = (models) => { + TemplateModule.belongsTo(models.Template, { + as: 'template', + foreignKey: 'templateId', + }); + }; + + return TemplateModule; +}; diff --git a/lib/database/models/typedefs/SequelizeTemplate.js b/lib/database/models/typedefs/SequelizeTemplate.js new file mode 100644 index 0000000000..7a9d7e3e40 --- /dev/null +++ b/lib/database/models/typedefs/SequelizeTemplate.js @@ -0,0 +1,24 @@ +/** + * @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 SequelizeTemplate + * + * @property {number} id + * @property {string} name + * @property {string} title + * @property {string|null} description + * @property {SequelizeTemplateModule[]} [modules] + * @property {string} createdAt + * @property {string} updatedAt + */ diff --git a/lib/database/models/typedefs/SequelizeTemplateModule.js b/lib/database/models/typedefs/SequelizeTemplateModule.js new file mode 100644 index 0000000000..2f0068997e --- /dev/null +++ b/lib/database/models/typedefs/SequelizeTemplateModule.js @@ -0,0 +1,25 @@ +/** + * @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 SequelizeTemplateModule + * + * @property {number} id + * @property {number} templateId + * @property {number} position + * @property {string} type + * @property {string|null} title + * @property {Object} options + * @property {string} createdAt + * @property {string} updatedAt + */ diff --git a/lib/database/seeders/20261028100000-templates.js b/lib/database/seeders/20261028100000-templates.js new file mode 100644 index 0000000000..926d4ff406 --- /dev/null +++ b/lib/database/seeders/20261028100000-templates.js @@ -0,0 +1,50 @@ +/** + * @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. + */ +'use strict'; + +const PHYSICS_ONLY = JSON.stringify({ run_definitions: ['PHYSICS'] }); +const NO_OPTIONS = JSON.stringify({}); + +module.exports = { + up: async (queryInterface) => + queryInterface.sequelize.transaction(async (transaction) => { + await queryInterface.bulkInsert('templates', [ + { + id: 1, + name: 'run-manager-weekly-report', + title: 'Run Manager Weekly Report', + description: 'The weekly report of the run manager for the Run Coordination meeting.', + }, + { + id: 2, + name: 'lhc-fill-summary', + title: 'LHC Fill Summary', + description: 'A short summary of the LHC fills of the period and how efficiently their stable beams were used.', + }, + ], { transaction }); + + await queryInterface.bulkInsert('template_modules', [ + { template_id: 1, position: 0, type: 'title-slide', title: 'Run Manager Weekly Report', options: NO_OPTIONS }, + { template_id: 1, position: 1, type: 'fill-calendar', title: 'Fills and runs', options: PHYSICS_ONLY }, + { template_id: 1, position: 2, type: 'fill-efficiency', title: 'Fill efficiencies', options: PHYSICS_ONLY }, + { template_id: 1, position: 3, type: 'running-time', title: 'Running time', options: PHYSICS_ONLY }, + { template_id: 1, position: 4, type: 'eor-reasons', title: 'End of Run reasons', options: PHYSICS_ONLY }, + { template_id: 2, position: 0, type: 'title-slide', title: 'LHC Fill Summary', options: NO_OPTIONS }, + { template_id: 2, position: 1, type: 'fill-calendar', title: 'Fills and runs', options: PHYSICS_ONLY }, + { template_id: 2, position: 2, type: 'fill-efficiency', title: 'Fill efficiencies', options: PHYSICS_ONLY }, + ], { transaction }); + }), + down: async (queryInterface) => + queryInterface.sequelize.transaction((transaction) => + Promise.all([queryInterface.bulkDelete('templates', null, { transaction })])), +}; From 6307710ed3489ca1fe1b5ed5a5aa67eeb25d2adf Mon Sep 17 00:00:00 2001 From: Kay van der Meer Date: Wed, 28 Oct 2026 21:08:56 +0100 Subject: [PATCH 2/3] Add endpoints to read presentation templates GET /api/templates lists the templates sorted by title, without their modules. GET /api/templates/:templateId returns one template with its modules in presentation order, or a 404 naming the id. The endpoints follow the layers of the run types endpoints. --- lib/database/adapters/TemplateAdapter.js | 51 +++++++++++++ .../adapters/TemplateModuleAdapter.js | 40 ++++++++++ lib/database/adapters/index.js | 7 ++ .../repositories/TemplateRepository.js | 33 +++++++++ lib/database/repositories/index.js | 2 + lib/domain/dtos/GetAllTemplatesDto.js | 26 +++++++ lib/domain/dtos/GetTemplateDto.js | 31 ++++++++ lib/domain/dtos/index.js | 4 + lib/domain/entities/Template.js | 31 ++++++++ lib/server/controllers/index.js | 2 + .../controllers/templates.controller.js | 66 +++++++++++++++++ lib/server/routers/index.js | 2 + lib/server/routers/templates.router.js | 33 +++++++++ .../services/template/TemplateService.js | 46 ++++++++++++ .../services/template/getAllTemplates.js | 26 +++++++ lib/server/services/template/getTemplate.js | 31 ++++++++ .../services/template/getTemplateOrFail.js | 30 ++++++++ test/api/index.js | 2 + test/api/templates.test.js | 74 +++++++++++++++++++ test/lib/server/services/index.js | 2 + .../services/template/TemplateService.test.js | 38 ++++++++++ test/lib/server/services/template/index.js | 18 +++++ 22 files changed, 595 insertions(+) create mode 100644 lib/database/adapters/TemplateAdapter.js create mode 100644 lib/database/adapters/TemplateModuleAdapter.js create mode 100644 lib/database/repositories/TemplateRepository.js create mode 100644 lib/domain/dtos/GetAllTemplatesDto.js create mode 100644 lib/domain/dtos/GetTemplateDto.js create mode 100644 lib/domain/entities/Template.js create mode 100644 lib/server/controllers/templates.controller.js create mode 100644 lib/server/routers/templates.router.js create mode 100644 lib/server/services/template/TemplateService.js create mode 100644 lib/server/services/template/getAllTemplates.js create mode 100644 lib/server/services/template/getTemplate.js create mode 100644 lib/server/services/template/getTemplateOrFail.js create mode 100644 test/api/templates.test.js create mode 100644 test/lib/server/services/template/TemplateService.test.js create mode 100644 test/lib/server/services/template/index.js diff --git a/lib/database/adapters/TemplateAdapter.js b/lib/database/adapters/TemplateAdapter.js new file mode 100644 index 0000000000..961b188884 --- /dev/null +++ b/lib/database/adapters/TemplateAdapter.js @@ -0,0 +1,51 @@ +/** + * @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. + */ +/** + * Adapter for templates + */ +class TemplateAdapter { + /** + * Constructor + */ + constructor() { + this.toEntity = this.toEntity.bind(this); + + /** + * @type {TemplateModuleAdapter|null} + */ + this.templateModuleAdapter = null; + } + + /** + * Converts the given database object to an entity object. + * + * @param {SequelizeTemplate} databaseObject Object to convert. + * @returns {Template} Converted entity object. + */ + toEntity({ id, name, title, description, modules }) { + const entityObject = { + id, + name, + title, + description, + }; + + if (modules) { + entityObject.modules = modules.map(this.templateModuleAdapter.toEntity); + } + + return entityObject; + } +} + +module.exports = TemplateAdapter; diff --git a/lib/database/adapters/TemplateModuleAdapter.js b/lib/database/adapters/TemplateModuleAdapter.js new file mode 100644 index 0000000000..71639e7aeb --- /dev/null +++ b/lib/database/adapters/TemplateModuleAdapter.js @@ -0,0 +1,40 @@ +/** + * @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. + */ +/** + * Adapter for template modules + */ +class TemplateModuleAdapter { + /** + * Constructor + */ + constructor() { + this.toEntity = this.toEntity.bind(this); + } + + /** + * Converts the given database object to an entity object. + * + * @param {SequelizeTemplateModule} databaseObject Object to convert. + * @returns {TemplateModule} Converted entity object. + */ + toEntity({ position, type, title, options }) { + return { + position, + type, + title, + options, + }; + } +} + +module.exports = TemplateModuleAdapter; diff --git a/lib/database/adapters/index.js b/lib/database/adapters/index.js index 433a4febeb..470cf6dfb9 100644 --- a/lib/database/adapters/index.js +++ b/lib/database/adapters/index.js @@ -45,6 +45,8 @@ const RunTypeAdapter = require('./RunTypeAdapter'); const SimulationPassAdapter = require('./SimulationPassAdapter.js'); const { SimulationPassQcFlagAdapter } = require('./SimulationPassQcFlagAdapter'); const TagAdapter = require('./TagAdapter'); +const TemplateAdapter = require('./TemplateAdapter.js'); +const TemplateModuleAdapter = require('./TemplateModuleAdapter.js'); const { CtpTriggerCountersAdapter } = require('./CtpTriggerCountersAdapter'); const UserAdapter = require('./UserAdapter'); @@ -82,6 +84,8 @@ const runTypeAdapter = new RunTypeAdapter(); const simulationPassAdapter = new SimulationPassAdapter(); const simulationPassQcFlagAdapter = new SimulationPassQcFlagAdapter(); const tagAdapter = new TagAdapter(); +const templateAdapter = new TemplateAdapter(); +const templateModuleAdapter = new TemplateModuleAdapter(); const ctpTriggerCountersAdapter = new CtpTriggerCountersAdapter(); const userAdapter = new UserAdapter(); @@ -144,6 +148,7 @@ runAdapter.tagAdapter = tagAdapter; runAdapter.userAdapter = userAdapter; tagAdapter.userAdapter = userAdapter; +templateAdapter.templateModuleAdapter = templateModuleAdapter; runAdapter.qcFlagAdapter = qcFlagAdapter; simulationPassQcFlagAdapter.simulationPassAdapter = simulationPassAdapter; @@ -183,6 +188,8 @@ module.exports = { simulationPassAdapter, simulationPassQcFlagAdapter, tagAdapter, + templateAdapter, + templateModuleAdapter, ctpTriggerCountersAdapter, userAdapter, }; diff --git a/lib/database/repositories/TemplateRepository.js b/lib/database/repositories/TemplateRepository.js new file mode 100644 index 0000000000..90603b533c --- /dev/null +++ b/lib/database/repositories/TemplateRepository.js @@ -0,0 +1,33 @@ +/** + * @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 { + models: { + Template, + }, +} = require('../'); +const Repository = require('./Repository'); + +/** + * Sequelize implementation of the TemplateRepository. + */ +class TemplateRepository extends Repository { + /** + * Creates a new `TemplateRepository` instance. + */ + constructor() { + super(Template); + } +} + +module.exports = new TemplateRepository(); diff --git a/lib/database/repositories/index.js b/lib/database/repositories/index.js index 53673cc9c1..9e37b8e010 100644 --- a/lib/database/repositories/index.js +++ b/lib/database/repositories/index.js @@ -52,6 +52,7 @@ const SimulationPassRepository = require('./SimulationPassRepository.js'); const SimulationPassQcFlagRepository = require('./SimulationPassQcFlagRepository.js'); const StableBeamRunsRepository = require('./StableBeamRunsRepository.js'); const TagRepository = require('./TagRepository'); +const TemplateRepository = require('./TemplateRepository.js'); const CtpTriggerCountersRepository = require('./CtpTriggerCountersRepository'); const UserRepository = require('./UserRepository'); @@ -97,6 +98,7 @@ module.exports = { SimulationPassQcFlagRepository, StableBeamRunsRepository, TagRepository, + TemplateRepository, CtpTriggerCountersRepository, UserRepository, }; diff --git a/lib/domain/dtos/GetAllTemplatesDto.js b/lib/domain/dtos/GetAllTemplatesDto.js new file mode 100644 index 0000000000..0fc614d222 --- /dev/null +++ b/lib/domain/dtos/GetAllTemplatesDto.js @@ -0,0 +1,26 @@ +/** + * @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 Joi = require('joi'); + +const QueryDto = Joi.object({ + token: Joi.string(), +}); + +const GetAllTemplatesDto = Joi.object({ + body: Joi.object({}), + params: Joi.object({}), + query: QueryDto, +}); + +module.exports = GetAllTemplatesDto; diff --git a/lib/domain/dtos/GetTemplateDto.js b/lib/domain/dtos/GetTemplateDto.js new file mode 100644 index 0000000000..6b796037c8 --- /dev/null +++ b/lib/domain/dtos/GetTemplateDto.js @@ -0,0 +1,31 @@ +/** + * @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 Joi = require('joi'); +const EntityIdDto = require('./EntityIdDto'); + +const ParamsDto = Joi.object({ + templateId: EntityIdDto, +}); + +const QueryDto = Joi.object({ + token: Joi.string(), +}); + +const GetTemplateDto = Joi.object({ + body: Joi.object({}), + params: ParamsDto, + query: QueryDto, +}); + +module.exports = GetTemplateDto; diff --git a/lib/domain/dtos/index.js b/lib/domain/dtos/index.js index 01eb348934..54714b10ee 100644 --- a/lib/domain/dtos/index.js +++ b/lib/domain/dtos/index.js @@ -25,6 +25,7 @@ const GetAllLogsDto = require('./GetAllLogsDto'); const GetAllRunsDto = require('./GetAllRunsDto'); const GetAllRunTypesDto = require('./GetAllRunTypesDto'); const GetAllTagsDto = require('./GetAllTagsDto'); +const GetAllTemplatesDto = require('./GetAllTemplatesDto.js'); const GetAllFlpsDto = require('./GetAllFlpsDto'); const GetAttachmentDto = require('./GetAttachmentDto'); const GetEnvironmentDto = require('./GetEnvironmentDto'); @@ -37,6 +38,7 @@ const GetRunTypeDto = require('./GetRunTypeDto'); const StartRunDto = require('./StartRunDto'); const EndRunDto = require('./EndRunDto'); const GetTagDto = require('./GetTagDto'); +const GetTemplateDto = require('./GetTemplateDto.js'); const GetFlpDto = require('./GetFlpDto'); const PaginationDto = require('./PaginationDto'); const UpdateEnvironmentDto = require('./UpdateEnvironmentDto'); @@ -63,6 +65,7 @@ module.exports = { GetAllRunsDto, GetAllRunTypesDto, GetAllTagsDto, + GetAllTemplatesDto, GetAllFlpsDto, GetAttachmentDto, GetEnvironmentDto, @@ -75,6 +78,7 @@ module.exports = { StartRunDto, GetTagByNameDto, GetTagDto, + GetTemplateDto, GetFlpDto, PaginationDto, UpdateEnvironmentDto, diff --git a/lib/domain/entities/Template.js b/lib/domain/entities/Template.js new file mode 100644 index 0000000000..9ba9b2b49d --- /dev/null +++ b/lib/domain/entities/Template.js @@ -0,0 +1,31 @@ +/** + * @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 TemplateModule + * + * @property {number} position the position of the module in the presentation, starting at 0 + * @property {string} type the type of the module, as known by the generation service + * @property {string|null} title the title of the module's slides + * @property {Object} options the settings of the module + */ + +/** + * @typedef Template + * + * @property {number} id + * @property {string} name the unique name of the template + * @property {string} title the title of the presentations made with the template + * @property {string|null} description + * @property {TemplateModule[]} [modules] the modules of the template, in presentation order + */ diff --git a/lib/server/controllers/index.js b/lib/server/controllers/index.js index 48184378b2..18b29d2430 100644 --- a/lib/server/controllers/index.js +++ b/lib/server/controllers/index.js @@ -22,6 +22,7 @@ const RunTypesController = require('./runTypes.controller'); const RunsController = require('./runs.controller'); const StatusController = require('./status.controller'); const TagsController = require('./tags.controller'); +const TemplatesController = require('./templates.controller.js'); const CtpTriggerCountersController = require('./ctpTriggerCounters.controller'); module.exports = { @@ -36,5 +37,6 @@ module.exports = { RunsController, StatusController, TagsController, + TemplatesController, CtpTriggerCountersController, }; diff --git a/lib/server/controllers/templates.controller.js b/lib/server/controllers/templates.controller.js new file mode 100644 index 0000000000..66da847091 --- /dev/null +++ b/lib/server/controllers/templates.controller.js @@ -0,0 +1,66 @@ +/** + * @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 { dtos: { GetAllTemplatesDto, GetTemplateDto } } = require('../../domain'); +const { dtoValidator } = require('../utilities'); +const { templateService } = require('../services/template/TemplateService.js'); +const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError.js'); + +/** + * Get all templates. + * + * @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 {undefined} + */ +const listTemplates = async (request, response) => { + const value = await dtoValidator(GetAllTemplatesDto, request, response); + if (!value) { + return; + } + + try { + response.status(200).json({ data: await templateService.getAll() }); + } catch (error) { + updateExpressResponseFromNativeError(response, error); + } +}; + +/** + * Get a template with its modules. + * + * @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 {undefined} + */ +const getTemplateById = async (request, response) => { + const value = await dtoValidator(GetTemplateDto, request, response); + if (!value) { + return; + } + + try { + response.status(200).json({ data: await templateService.getOneOrFail(value.params.templateId) }); + } catch (error) { + updateExpressResponseFromNativeError(response, error); + } +}; + +module.exports = { + listTemplates, + getTemplateById, +}; diff --git a/lib/server/routers/index.js b/lib/server/routers/index.js index b3952aa86a..4dd7c0f3c1 100644 --- a/lib/server/routers/index.js +++ b/lib/server/routers/index.js @@ -29,6 +29,7 @@ const { shiftRouter } = require('./shift.router.js'); const { statisticsRouter } = require('./statistics.router.js'); const statusRoute = require('./status.router'); const tagsRoute = require('./tags.router'); +const templatesRoute = require('./templates.router.js'); const { lhcPeriodsRouter } = require('./lhcPeriodsStatistics.router.js'); const { isInTestMode } = require('../../utilities/env-utils'); const { dataPassesRouter } = require('./dataPasses.router'); @@ -60,6 +61,7 @@ const routes = [ statisticsRouter, statusRoute, tagsRoute, + templatesRoute, ctpTriggerCountersRouter, ]; diff --git a/lib/server/routers/templates.router.js b/lib/server/routers/templates.router.js new file mode 100644 index 0000000000..9b5a5310d9 --- /dev/null +++ b/lib/server/routers/templates.router.js @@ -0,0 +1,33 @@ +/** + * @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 { TemplatesController } = require('../controllers'); + +module.exports = { + path: '/templates', + children: [ + { + method: 'get', + controller: TemplatesController.listTemplates, + }, + { + path: ':templateId', + children: [ + { + method: 'get', + controller: TemplatesController.getTemplateById, + }, + ], + }, + ], +}; diff --git a/lib/server/services/template/TemplateService.js b/lib/server/services/template/TemplateService.js new file mode 100644 index 0000000000..6a5c9798ef --- /dev/null +++ b/lib/server/services/template/TemplateService.js @@ -0,0 +1,46 @@ +/** + * @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 { templateAdapter } = require('../../../database/adapters/index.js'); +const { getAllTemplates } = require('./getAllTemplates.js'); +const { getTemplateOrFail } = require('./getTemplateOrFail.js'); + +/** + * Global service to read presentation templates + */ +class TemplateService { + /** + * Return the templates sorted by title, without their modules + * + * Templates are not paginated, as there are only a few of them + * + * @return {Promise} the templates + */ + async getAll() { + return (await getAllTemplates()).map(templateAdapter.toEntity); + } + + /** + * Return the template with the given id, with its modules in presentation order, or reject if there is none + * + * @param {number} id the id of the template to find + * @return {Promise