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/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/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/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 })])), +}; 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/api-docs/API.md b/lib/server/api-docs/API.md index 70a34d614f..4ddf660eb7 100644 --- a/lib/server/api-docs/API.md +++ b/lib/server/api-docs/API.md @@ -436,3 +436,41 @@ Responses: ```bash curl -g "$BOOKKEEPING_URL/api/simulationPasses/1?token=$TOKEN" ``` + +## Templates + +Presentation templates, each an ordered list of content modules, read by the ALICE Insights generation service. + +### `GET /templates` - List presentation templates + +Operation id: `listTemplates` + +Returns the presentation templates ordered by title, without their modules. Not paginated: there are few templates. Pagination parameters +are rejected. + +Responses: + +- `200`: The templates. +- `400`: The request failed validation. HTTP status is 400 although each error's `status` field is `"422"`. `source.pointer` has the form `/data/attributes//`. + +```bash +curl -g "$BOOKKEEPING_URL/api/templates?token=$TOKEN" +``` + +### `GET /templates/{templateId}` - Get a presentation template by id, with its modules + +Operation id: `getTemplate` + +| Parameter | In | Type | Required | Notes | +| --- | --- | --- | --- | --- | +| `templateId` | path | integer | yes | Template identifier. | + +Responses: + +- `200`: The template, with its modules in presentation order. +- `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`: No template has this id. + +```bash +curl -g "$BOOKKEEPING_URL/api/templates/1?token=$TOKEN" +``` diff --git a/lib/server/api-docs/index.html b/lib/server/api-docs/index.html index cb869dc1c8..e3b16d49a8 100644 --- a/lib/server/api-docs/index.html +++ b/lib/server/api-docs/index.html @@ -759,6 +759,53 @@

Simulation passes

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

Templates

+

Presentation templates, each an ordered list of content modules, read by the ALICE Insights generation service.

+
+

GET /templates - List presentation templates

+ +

Operation id: listTemplates

+

Returns the presentation templates ordered by title, without their modules. Not paginated: there are few templates. Pagination parameters +are rejected.

+

Responses:

+
    +
  • 200: The templates.
  • +
  • 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>.
  • +
+
curl -g "$BOOKKEEPING_URL/api/templates?token=$TOKEN"
+
+
+
+

GET /templates/{templateId} - Get a presentation template by id, with its modules

+ +

Operation id: getTemplate

+ + + + + + + + + + + + + + + + + +
ParameterInTypeRequiredNotes
templateIdpathintegeryesTemplate identifier.
+

Responses:

+
    +
  • 200: The template, with its modules in presentation order.
  • +
  • 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: No template has this id.
  • +
+
curl -g "$BOOKKEEPING_URL/api/templates/1?token=$TOKEN"
+
+
Generated from openapi.yaml by npm run docs:api · ALICE Bookkeeping · internal use only
diff --git a/lib/server/api-docs/openapi.yaml b/lib/server/api-docs/openapi.yaml index 4a7fc919b2..7fc3a2ba30 100644 --- a/lib/server/api-docs/openapi.yaml +++ b/lib/server/api-docs/openapi.yaml @@ -51,6 +51,8 @@ tags: description: Data used to fill end-of-shift reports. - name: Simulation passes description: Monte Carlo simulation productions. + - name: Templates + description: Presentation templates, each an ordered list of content modules, read by the ALICE Insights generation service. paths: /tags: @@ -522,6 +524,65 @@ paths: errors: - { status: 404, title: Not found, detail: Simulation pass with this id (99999) could not be found } + /templates: + get: + tags: [Templates] + operationId: listTemplates + summary: List presentation templates + description: | + Returns the presentation templates ordered by title, without their modules. Not paginated: there are few templates. Pagination parameters + are rejected. + responses: + '200': + description: The templates. + content: + application/json: + schema: { $ref: '#/components/schemas/TemplateListResponse' } + example: + data: + - 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. + '400': { $ref: '#/components/responses/ValidationError' } + + /templates/{templateId}: + get: + tags: [Templates] + operationId: getTemplate + summary: Get a presentation template by id, with its modules + parameters: + - name: templateId + in: path + required: true + description: Template identifier. + schema: { type: integer, minimum: 1 } + responses: + '200': + description: The template, with its modules in presentation order. + content: + application/json: + schema: { $ref: '#/components/schemas/TemplateResponse' } + example: + data: + 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. + modules: + - { position: 0, type: title-slide, title: LHC Fill Summary, options: {} } + - { position: 1, type: fill-calendar, title: Fills and runs, options: { run_definitions: [PHYSICS] } } + - { position: 2, type: fill-efficiency, title: Fill efficiencies, options: { run_definitions: [PHYSICS] } } + '400': { $ref: '#/components/responses/ValidationError' } + '404': + description: No template has this id. + content: + application/json: + schema: { $ref: '#/components/schemas/ErrorResponse' } + example: + errors: + - { status: 404, title: Not found, detail: Template with this id (999999) could not be found } + components: securitySchemes: jwtQuery: @@ -819,3 +880,35 @@ components: type: array items: { $ref: '#/components/schemas/SimulationPass' } meta: { $ref: '#/components/schemas/PageMeta' } + TemplateModule: + type: object + required: [position, type, title, options] + properties: + position: { type: integer, description: 'Position of the module in the presentation, starting at 0.' } + type: { type: string, description: 'Type of the module, as known by the generation service, e.g. `fill-efficiency`.' } + title: { type: string, nullable: true, description: Title of the module's slides. } + options: { type: object, additionalProperties: true, description: Settings of the module. } + Template: + type: object + required: [id, name, title, description] + properties: + id: { type: integer } + name: { type: string, description: 'Unique name of the template, e.g. `run-manager-weekly-report`.' } + title: { type: string, description: Title of the presentations made with the template. } + description: { type: string, nullable: true } + modules: + type: array + description: Modules in presentation order. Only returned when getting a single template. + items: { $ref: '#/components/schemas/TemplateModule' } + TemplateResponse: + type: object + required: [data] + properties: + data: { $ref: '#/components/schemas/Template' } + TemplateListResponse: + type: object + required: [data] + properties: + data: + type: array + items: { $ref: '#/components/schemas/Template' } 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