diff --git a/lib/server/controllers/status.controller.js b/lib/server/controllers/status.controller.js index ebf0a53bf9..9bd2b52d80 100644 --- a/lib/server/controllers/status.controller.js +++ b/lib/server/controllers/status.controller.js @@ -11,11 +11,7 @@ * or submit itself to any jurisdiction. */ -const { - status: { - GetGuiStatusUseCase, - GetDatabaseStatusUseCase, - } } = require('../../usecases'); +const { statusService } = require('../services/status/StatusService.js'); /** * Show gui information. @@ -26,24 +22,10 @@ const { * * @returns {undefined} current gui information */ -const getGuiInformation = async (request, response) => { - const gui = await new GetGuiStatusUseCase() - .execute(request); - - if (gui === null) { - response.status(501).json({ - errors: [ - { - status: 501, - title: 'Something went wrong', - }, - ], - }); - } else { - response.status(200).json({ - data: gui, - }); - } +const getGuiInformation = (request, response) => { + response.status(200).json({ + data: statusService.getGuiStatus({ host: request.hostname, port: request.socket.localPort }), + }); }; /** @@ -56,23 +38,9 @@ const getGuiInformation = async (request, response) => { * @returns {undefined} current database information */ const getDatabaseInformation = async (request, response) => { - const information = await new GetDatabaseStatusUseCase() - .execute(); - - if (information === null) { - response.status(501).json({ - errors: [ - { - status: 501, - title: 'Something went wrong', - }, - ], - }); - } else { - response.status(200).json({ - data: information, - }); - } + response.status(200).json({ + data: await statusService.getDatabaseStatus(), + }); }; module.exports = { diff --git a/lib/server/controllers/tags.controller.js b/lib/server/controllers/tags.controller.js index 3b16d6013e..ebdcdd515e 100644 --- a/lib/server/controllers/tags.controller.js +++ b/lib/server/controllers/tags.controller.js @@ -11,22 +11,12 @@ * or submit itself to any jurisdiction. */ -const { - tag: { - CreateTagUseCase, - DeleteTagUseCase, - GetAllTagsUseCase, - GetLogsByTagUseCase, - GetTagUseCase, - GetTagByNameUseCase, - UpdateTagUseCase, - }, -} = require('../../usecases'); const { dtos: { CreateTagDto, GetAllTagsDto, GetTagDto, UpdateTagDto, GetTagByNameDto } } = require('../../domain'); +const GetAllLogsByTagDto = require('../../domain/dtos/GetAllLogsByTagDto.js'); const { dtoValidator } = require('../utilities'); const { updateExpressResponseFromNativeError } = require('../express/updateExpressResponseFromNativeError.js'); +const { tagService } = require('../services/tag/TagService.js'); const { ApiConfig } = require('../../config/index.js'); -const GetAllLogsByTagDto = require('../../domain/dtos/GetAllLogsByTagDto.js'); /** * Create a new tag. @@ -44,7 +34,7 @@ const createTag = async (request, response) => { } try { - const tag = await new CreateTagUseCase().execute(value); + const tag = await tagService.create(value.body, { userId: value.session?.id }); response.status(201).json({ data: tag }); } catch (error) { updateExpressResponseFromNativeError(response, error); @@ -68,8 +58,7 @@ const deleteTag = async (request, response, next) => { return; } - const tag = await new DeleteTagUseCase() - .execute(value); + const tag = await tagService.remove(value.params.tagId); if (tag === null) { response.status(404).json({ @@ -102,12 +91,12 @@ const getLogsByTagId = async (request, response) => { return; } - const { logs, count } = await new GetLogsByTagUseCase().execute(value); + const { query: { page = {} } } = value; + const { limit = ApiConfig.pagination.limit } = page; - const { query: { page: { limit = ApiConfig.pagination.limit } = {} } } = value; - const totalPages = Math.ceil(count / limit); + const result = await tagService.getLogs(value.params.tagId, page); - if (logs === null) { + if (result === null) { response.status(404).json({ errors: [ { @@ -117,11 +106,12 @@ const getLogsByTagId = async (request, response) => { ], }); } else { + const { logs, count } = result; response.status(200).json({ data: logs, meta: { page: { - pageCount: totalPages, + pageCount: Math.ceil(count / limit), totalCount: count, }, }, @@ -146,10 +136,9 @@ const listTags = async (request, response, next) => { return; } - const { count, tags } = await new GetAllTagsUseCase() - .execute(value); + const tags = await tagService.getAll({ filter: value.query.filter }); - response.status(count === 0 ? 204 : 200).json({ data: tags }); + response.status(tags.length === 0 ? 204 : 200).json({ data: tags }); }; /** @@ -169,8 +158,7 @@ const getTagById = async (request, response, next) => { return; } - const tag = await new GetTagUseCase() - .execute(value); + const tag = await tagService.getByIdentifier({ id: value.params.tagId }); if (tag === null) { response.status(404).json({ @@ -201,15 +189,15 @@ const getTagByName = async (request, response, next) => { return; } - const tag = await new GetTagByNameUseCase() - .execute(value); + const name = decodeURIComponent(value.query.name); + const tag = await tagService.getByIdentifier({ text: name }); if (tag === null) { response.status(404).json({ errors: [ { status: '404', - title: `Tag with this name (${decodeURIComponent(value.query.name)}) could not be found`, + title: `Tag with this name (${name}) could not be found`, }, ], }); @@ -235,7 +223,7 @@ const updateTagById = async (request, response) => { } try { - const tag = await new UpdateTagUseCase().execute(value); + const tag = await tagService.update(value.params.tagId, value.body, { userId: value.session?.id }); response.status(201).json({ data: tag }); } catch (error) { updateExpressResponseFromNativeError(response, error); diff --git a/lib/server/services/status/StatusService.js b/lib/server/services/status/StatusService.js new file mode 100644 index 0000000000..db570d0742 --- /dev/null +++ b/lib/server/services/status/StatusService.js @@ -0,0 +1,61 @@ +/** + * @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 { version } = require('../../../../package.json'); +const database = require('../../../database'); +const { DatabaseConfig: { host: databaseHost, port: databasePort } } = require('../../../config/index.js'); + +/** + * Service to read the status of the application and of its database + */ +class StatusService { + /** + * Return the status of the Bookkeeping application + * + * @param {object} server the address on which the application received the request + * @param {string} server.host the host + * @param {number} server.port the port + * @return {{host: string, port: number, version: string, status: {ok: boolean, configured: boolean}}} the application status + */ + getGuiStatus({ host, port }) { + return { + host, + port, + version, + status: { + ok: true, + configured: true, + }, + }; + } + + /** + * Return the status of the database connection + * + * @return {Promise<{host: string, port: number, status: {ok: boolean, configured: boolean}}>} the database status + */ + async getDatabaseStatus() { + return { + host: databaseHost, + port: databasePort, + status: { + ok: Boolean(await database.ping()), + configured: true, + }, + }; + } +} + +exports.StatusService = StatusService; + +exports.statusService = new StatusService(); diff --git a/lib/server/services/tag/TagService.js b/lib/server/services/tag/TagService.js new file mode 100644 index 0000000000..f942ee8e93 --- /dev/null +++ b/lib/server/services/tag/TagService.js @@ -0,0 +1,196 @@ +/** + * @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 { repositories: { LogRepository, TagRepository }, utilities: { QueryBuilder } } = require('../../../database'); +const { dataSource } = require('../../../database/DataSource.js'); +const { tagAdapter } = require('../../../database/adapters/index.js'); +const { ApiConfig } = require('../../../config/index.js'); +const { GetAllLogsUseCase } = require('../../../usecases/log'); +const { BadParameterError } = require('../../errors/BadParameterError.js'); +const { ConflictError } = require('../../errors/ConflictError.js'); +const { NotFoundError } = require('../../errors/NotFoundError.js'); +const { getUserOrFail } = require('../user/getUserOrFail.js'); +const { getAllTags } = require('./getAllTags.js'); +const { getTag } = require('./getTag.js'); + +/** + * Add the last editor of the tags (only its name) to a tags query + * + * @param {QueryBuilder} queryBuilder the query builder of the query + * @return {void} + */ +const includeLastEditor = (queryBuilder) => { + queryBuilder.include({ association: 'lastEditedBy', attributes: ['name'] }); +}; + +/** + * Global service to read and write tags + */ +class TagService { + /** + * Return the tags matching the given filter, non archived tags first then sorted by text + * + * The filter can contain the comma separated lists `ids`, `texts` (has priority over `partialText`), `emails` and `mattermosts`, + * and a `partialText` to keep the tags including this text + * + * @param {object} [options] the options of the search + * @param {object} [options.filter] the filter to apply + * @return {Promise} the tags + */ + async getAll({ filter = {} } = {}) { + const tags = await getAllTags((queryBuilder) => { + includeLastEditor(queryBuilder); + queryBuilder.orderBy((sequelize) => sequelize.literal('archived_at IS NULL'), 'DESC'); + queryBuilder.orderBy('text', 'asc'); + + if (filter) { + const { ids, texts, partialText, emails, mattermosts } = filter; + if (ids) { + queryBuilder.where('id').oneOf(...ids.split(',').filter((id) => id !== '')); + } + if (texts) { + queryBuilder.where('text').oneOf(...texts.split(',').filter((text) => text !== '')); + } else if (partialText) { + queryBuilder.where('text').substring(partialText); + } + if (emails) { + queryBuilder.where('email').oneOf(...emails.split(',').filter((email) => email !== '')); + } + if (mattermosts) { + queryBuilder.where('mattermost').oneOf(...mattermosts.split(',').filter((mattermost) => mattermost !== '')); + } + } + }); + + return tags.map(tagAdapter.toEntity); + } + + /** + * Return the tag matching the given identifier, or null if there is none + * + * @param {TagIdentifier} identifier the identifier of the tag to find + * @return {Promise} the tag found or null + */ + async getByIdentifier(identifier) { + const tag = await getTag(identifier, includeLastEditor); + return tag ? tagAdapter.toEntity(tag) : null; + } + + /** + * Create a tag + * + * @param {Pick} tag the tag to create + * @param {object} relations the relations of the creation + * @param {number} relations.userId the id of the user creating the tag + * @return {Promise} resolves with the created tag + * @throws {BadParameterError} if no user is provided + * @throws {NotFoundError} if the user does not exist + * @throws {ConflictError} if a tag with the same text already exists + */ + async create(tag, { userId }) { + if (userId === undefined || userId === null) { + throw new BadParameterError('A user is required to create a tag'); + } + + const createdTag = await dataSource.transaction(async () => { + const user = await getUserOrFail({ userId }); + + if (await getTag({ text: tag.text })) { + throw new ConflictError('The provided entity already exists'); + } + + return TagRepository.insert(tagAdapter.toDatabase({ ...tag, lastEditedByUserId: user.id })); + }); + + // Re-fetch the tag to return it with all its fields, including the last editor + return this.getByIdentifier({ id: createdTag.id }); + } + + /** + * Update a tag + * + * @param {number} tagId the id of the tag to update + * @param {Partial>} patch the new values of the tag + * @param {object} relations the relations of the update + * @param {number} relations.userId the id of the user updating the tag + * @return {Promise} resolves with the updated tag + * @throws {BadParameterError} if no user is provided + * @throws {NotFoundError} if the tag or the user does not exist + */ + async update(tagId, { description, email, mattermost, archivedAt, color }, { userId }) { + if (userId === undefined || userId === null) { + throw new BadParameterError('A user is required to update a tag'); + } + + await dataSource.transaction(async () => { + const tag = await getTag({ id: tagId }); + if (!tag) { + throw new NotFoundError(`Tag with this id (${tagId}) could not be found`); + } + + const user = await getUserOrFail({ userId }); + + tag.description = description; + tag.email = email; + tag.color = color; + tag.mattermost = mattermost; + tag.archivedAt = archivedAt; + tag.lastEditedByUserId = user.id; + await tag.save(); + }); + + return this.getByIdentifier({ id: tagId }); + } + + /** + * Remove a tag + * + * @param {number} tagId the id of the tag to remove + * @return {Promise} resolves with the removed tag, or null if there was no such tag + */ + async remove(tagId) { + const tag = await dataSource.transaction(() => TagRepository.removeOne(new QueryBuilder().where('id').is(tagId))); + return tag ? tagAdapter.toEntity(tag) : null; + } + + /** + * Return a page of the root logs carrying a given tag + * + * @param {number} tagId the id of the tag + * @param {object} [page] the page to return + * @param {number} [page.limit] the maximum number of logs to return (default to the maximum of the API) + * @param {number} [page.offset] the number of logs to skip + * @return {Promise<{logs: Log[], count: number}|null>} the logs and their total count, or null if the tag does not exist + */ + async getLogs(tagId, { limit = ApiConfig.pagination.limit, offset = 0 } = {}) { + return dataSource.transaction(async () => { + const tag = await getTag({ id: tagId }); + if (!tag) { + return null; + } + + const { logs, count } = await new GetAllLogsUseCase().execute({ + query: { + page: { offset, limit }, + filter: { tags: { values: [tag.text], operation: 'or' } }, + }, + }); + + return { logs: await LogRepository.addChildrenCountByLog(logs), count }; + }); + } +} + +exports.TagService = TagService; + +exports.tagService = new TagService(); diff --git a/lib/server/services/tag/getAllTags.js b/lib/server/services/tag/getAllTags.js new file mode 100644 index 0000000000..4f71508c2e --- /dev/null +++ b/lib/server/services/tag/getAllTags.js @@ -0,0 +1,28 @@ +/** + * @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 { repositories: { TagRepository }, utilities: { QueryBuilder } } = require('../../../database'); + +/** + * Return the list of all the tags + * + * @param {Function|null} [qbConfiguration] function called with the tags query builder as parameter to add specific configuration to the query + * @return {Promise} Promise resolving with the list of tags + */ +exports.getAllTags = (qbConfiguration = null) => { + const queryBuilder = new QueryBuilder(); + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + return TagRepository.findAll(queryBuilder); +}; diff --git a/lib/server/services/tag/getTag.js b/lib/server/services/tag/getTag.js new file mode 100644 index 0000000000..bffc5ef1af --- /dev/null +++ b/lib/server/services/tag/getTag.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 { repositories: { TagRepository }, utilities: { QueryBuilder } } = require('../../../database'); +const { BadParameterError } = require('../../errors/BadParameterError.js'); + +/** + * @typedef TagIdentifier object to uniquely identify a tag + * @property {string} [text] the text of the tag + * @property {number} [id] the id of the tag, ignored if text is present + */ + +/** + * Find and return a tag model by its text or id + * + * @param {TagIdentifier} identifier the criteria to find the tag + * @param {Function|null} [qbConfiguration] function called with the tag find query builder as parameter to add specific configuration to the + * query + * @return {Promise} the tag found or null + */ +exports.getTag = ({ text, id }, qbConfiguration = null) => { + const queryBuilder = new QueryBuilder(); + + if (text !== undefined) { + queryBuilder.where('text').is(text); + } else if (id !== undefined) { + queryBuilder.where('id').is(id); + } else { + throw new BadParameterError('Can not find without tag id or text'); + } + + if (qbConfiguration) { + qbConfiguration(queryBuilder); + } + return TagRepository.findOne(queryBuilder); +}; diff --git a/lib/usecases/index.js b/lib/usecases/index.js index 4c1009a9db..51f552cac2 100644 --- a/lib/usecases/index.js +++ b/lib/usecases/index.js @@ -18,8 +18,6 @@ const lhcFill = require('./lhcFill'); const log = require('./log'); const run = require('./run'); const server = require('./server'); -const status = require('./status'); -const tag = require('./tag'); module.exports = { attachment, @@ -29,6 +27,4 @@ module.exports = { log, run, server, - status, - tag, }; diff --git a/lib/usecases/status/GetDatabaseStatusUseCase.js b/lib/usecases/status/GetDatabaseStatusUseCase.js deleted file mode 100644 index 277049babd..0000000000 --- a/lib/usecases/status/GetDatabaseStatusUseCase.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @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 { - DatabaseConfig: { - host, - port, - } } = require('../../../lib/config'); -const database = require('../../database'); - -/** - * Database status use case - */ -class GetDatabaseStatusUseCase { - /** - * Executes this use case. - * - * @returns {Object} Object that represents the information/status of the server. - */ - async execute() { - const connectivity = await database.ping(); - if (connectivity) { - return { - host: host, - port: port, - status: { - ok: true, - configured: true, - }, - }; - } - return { - host: host, - port: port, - status: { - ok: false, - configured: true, - }, - }; - } -} - -module.exports = GetDatabaseStatusUseCase; diff --git a/lib/usecases/status/GetGuiStatusUseCase.js b/lib/usecases/status/GetGuiStatusUseCase.js deleted file mode 100644 index b4d6fc03a5..0000000000 --- a/lib/usecases/status/GetGuiStatusUseCase.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @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 { version } = require('../../../package.json'); - -/** - * GetGuiStatusUseCase - */ -class GetGuiStatusUseCase { - /** - * Executes this use case. - * @param {Object} request request object - * @returns {Promise} Promise object represents the result of this use case. - */ - execute(request) { - return { - host: request.hostname, - port: request.socket.localPort, - version: version, - status: { - ok: true, - configured: true, - }, - }; - } -} - -module.exports = GetGuiStatusUseCase; diff --git a/lib/usecases/tag/CreateTagUseCase.js b/lib/usecases/tag/CreateTagUseCase.js deleted file mode 100644 index 6dfd91b6d9..0000000000 --- a/lib/usecases/tag/CreateTagUseCase.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @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 { - repositories: { - TagRepository, - }, - utilities: { - QueryBuilder, - TransactionHelper, - }, -} = require('../../database'); -const { tagAdapter } = require('../../database/adapters/index.js'); -const GetTagUseCase = require('./GetTagUseCase'); -const { BadParameterError } = require('../../server/errors/BadParameterError.js'); -const { ConflictError } = require('../../server/errors/ConflictError.js'); -const { getUserOrFail } = require('../../server/services/user/getUserOrFail.js'); - -/** - * CreateTagUseCase - */ -class CreateTagUseCase { - /** - * Executes this use case. - * - * @param {Object} dto The CreateTagDto containing all data. - * @returns {Promise} resolves with the created tag - * @throws {BadParameterError} if no user is provided in the session - * @throws {NotFoundError} if the session user does not exist - * @throws {ConflictError} if a tag with the same text already exists - */ - async execute(dto) { - const { body } = dto; - const userId = dto?.session?.id; - if (userId === undefined || userId === null) { - throw new BadParameterError('A user is required to create a tag'); - } - - const createdTag = await TransactionHelper.provide(async () => { - const user = await getUserOrFail({ userId }); - body.lastEditedByUserId = user.id; - - const existingTag = await TagRepository.findOne(new QueryBuilder().where('text').is(body.text)); - if (existingTag) { - throw new ConflictError('The provided entity already exists'); - } - - return TagRepository.insert(tagAdapter.toDatabase(body)); - }); - - // Re-fetch the tag to return it with all its fields, including the last editor - return new GetTagUseCase().execute({ params: { tagId: createdTag.id } }); - } -} - -module.exports = CreateTagUseCase; diff --git a/lib/usecases/tag/DeleteTagUseCase.js b/lib/usecases/tag/DeleteTagUseCase.js deleted file mode 100644 index eb116ef2e9..0000000000 --- a/lib/usecases/tag/DeleteTagUseCase.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @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 { - repositories: { - TagRepository, - }, - utilities: { - QueryBuilder, - TransactionHelper, - }, -} = require('../../database'); -const { tagAdapter } = require('../../database/adapters/index.js'); - -/** - * DeleteTagUseCase - */ -class DeleteTagUseCase { - /** - * Executes this use case. - * - * @param {Object} dto The GetAllLogs DTO which contains all request data. - * @returns {Promise} Promise object represents the result of this use case. - */ - async execute(dto = {}) { - const { params } = dto; - const { tagId } = params; - - const tag = await TransactionHelper.provide(async () => { - const queryBuilder = new QueryBuilder() - .where('id').is(tagId); - - return TagRepository.removeOne(queryBuilder); - }); - - return tag ? tagAdapter.toEntity(tag) : null; - } -} - -module.exports = DeleteTagUseCase; diff --git a/lib/usecases/tag/GetAllTagsUseCase.js b/lib/usecases/tag/GetAllTagsUseCase.js deleted file mode 100644 index fae6ea973b..0000000000 --- a/lib/usecases/tag/GetAllTagsUseCase.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @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 { - repositories: { - TagRepository, - }, - utilities: { - QueryBuilder, - TransactionHelper, - }, -} = require('../../database'); -const { tagAdapter } = require('../../database/adapters/index.js'); - -/** - * GetAllTagsUseCase - */ -class GetAllTagsUseCase { - /** - * Executes this use case. - * - * @param {Object} dto The GetAllLogs DTO which contains all request data. - * @returns {Promise} Promise object represents the result of this use case. - */ - async execute(dto = {}) { - const queryBuilder = new QueryBuilder().include({ association: 'lastEditedBy', attributes: ['name'] }); - const { query = {} } = dto; - const { filter = {} } = query; - - queryBuilder.orderBy((sequelize) => sequelize.literal('archived_at IS NULL'), 'DESC'); - queryBuilder.orderBy('text', 'asc'); - - const { - count, - rows, - } = await TransactionHelper.provide(async () => { - if (filter) { - const { ids, texts, partialText, emails, mattermosts } = filter; - if (ids) { - const idList = ids.split(',').filter((id) => id !== ''); - queryBuilder.where('id').oneOf(...idList); - } - if (texts) { - const textList = texts.split(',').filter((text) => text !== ''); - queryBuilder.where('text').oneOf(...textList); - } else if (partialText) { - queryBuilder.where('text').substring(partialText); - } - if (emails) { - const emailList = emails.split(',').filter((email) => email !== ''); - queryBuilder.where('email').oneOf(...emailList); - } - if (mattermosts) { - const mattermostList = mattermosts.split(',').filter((mattermost) => mattermost !== ''); - queryBuilder.where('mattermost').oneOf(...mattermostList); - } - } - - return TagRepository.findAndCountAll(queryBuilder); - }); - - return { - count, - tags: rows.map(tagAdapter.toEntity), - }; - } -} - -module.exports = GetAllTagsUseCase; diff --git a/lib/usecases/tag/GetLogsByTagUseCase.js b/lib/usecases/tag/GetLogsByTagUseCase.js deleted file mode 100644 index 3a69840fb9..0000000000 --- a/lib/usecases/tag/GetLogsByTagUseCase.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * @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 { - repositories: { - LogRepository, - TagRepository, - }, - utilities: { - QueryBuilder, - TransactionHelper, - }, -} = require('../../database'); -const { ApiConfig } = require('../../config/index.js'); - -const { GetAllLogsUseCase } = require('../log'); - -/** - * GetLogsByTagUseCase - */ -class GetLogsByTagUseCase { - /** - * Executes this use case. - * - * @param {Object} dto The GetTagDto containing all data. - * @returns {Promise} Promise object represents the result of this use case. - */ - async execute(dto) { - const { params } = dto; - const { tagId } = params; - const { query = {} } = dto; - const { page = {} } = query; - const { limit = ApiConfig.pagination.limit, offset = 0 } = page; - - return TransactionHelper.provide(async () => { - const queryBuilder = new QueryBuilder().where('id').is(tagId); - const tag = await TagRepository.findOne(queryBuilder); - if (!tag) { - return { logs: null, count: 0 }; - } - - const { logs, count } = await new GetAllLogsUseCase().execute({ - query: { - page: { - offset: offset, - limit: limit, - }, - filter: { - tags: { - values: [tag.text], operation: 'or', - }, - }, - }, - }); - - const logsWithCount = await LogRepository.addChildrenCountByLog(logs); - - return { logs: logsWithCount, count }; - }); - } -} - -module.exports = GetLogsByTagUseCase; diff --git a/lib/usecases/tag/GetTagByNameUseCase.js b/lib/usecases/tag/GetTagByNameUseCase.js deleted file mode 100644 index 56b12591dd..0000000000 --- a/lib/usecases/tag/GetTagByNameUseCase.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @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 { - repositories: { - TagRepository, - }, - utilities: { - QueryBuilder, - TransactionHelper, - }, -} = require('../../database'); -const { tagAdapter } = require('../../database/adapters/index.js'); - -/** - * GetTagByNameUseCase - */ -class GetTagByNameUseCase { - /** - * Executes this use case. - * - * @param {Object} dto The GetTagDto containing all data. - * @returns {Promise} Promise object represents the result of this use case. - */ - async execute(dto) { - const { query } = dto; - let { name } = query; - name = decodeURIComponent(name); - const tag = await TransactionHelper.provide(async () => { - const queryBuilder = new QueryBuilder() - .where('text').is(name) - .include({ association: 'lastEditedBy', attributes: ['name'] }); - - return TagRepository.findOne(queryBuilder); - }); - return tag ? tagAdapter.toEntity(tag) : null; - } -} - -module.exports = GetTagByNameUseCase; diff --git a/lib/usecases/tag/GetTagUseCase.js b/lib/usecases/tag/GetTagUseCase.js deleted file mode 100644 index b2bc9bb604..0000000000 --- a/lib/usecases/tag/GetTagUseCase.js +++ /dev/null @@ -1,51 +0,0 @@ -/** - * @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 { - repositories: { - TagRepository, - }, - utilities: { - QueryBuilder, - TransactionHelper, - }, -} = require('../../database'); -const { tagAdapter } = require('../../database/adapters/index.js'); - -/** - * GetTagUseCase - */ -class GetTagUseCase { - /** - * Executes this use case. - * - * @param {Object} dto The GetTagDto containing all data. - * @returns {Promise} Promise object represents the result of this use case. - */ - async execute(dto) { - const { params } = dto; - const { tagId } = params; - - const tag = await TransactionHelper.provide(async () => { - const queryBuilder = new QueryBuilder() - .where('id').is(tagId) - .include({ association: 'lastEditedBy', attributes: ['name'] }); - - return TagRepository.findOne(queryBuilder); - }); - - return tag ? tagAdapter.toEntity(tag) : null; - } -} - -module.exports = GetTagUseCase; diff --git a/lib/usecases/tag/UpdateTagUseCase.js b/lib/usecases/tag/UpdateTagUseCase.js deleted file mode 100644 index b1017674f5..0000000000 --- a/lib/usecases/tag/UpdateTagUseCase.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @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 { - repositories: { - TagRepository, - }, - utilities: { - QueryBuilder, - TransactionHelper, - }, -} = require('../../database'); -const GetTagUseCase = require('./GetTagUseCase'); -const { BadParameterError } = require('../../server/errors/BadParameterError.js'); -const { NotFoundError } = require('../../server/errors/NotFoundError.js'); -const { getUserOrFail } = require('../../server/services/user/getUserOrFail.js'); - -/** - * Update tag use case - */ -class UpdateTagUseCase { - /** - * Executes this use case - * - * @param {Object} dto The UpdateTagDto containing the values that needs to be updated. - * @returns {Promise} resolves with the updated tag - * @throws {BadParameterError} if no user is provided in the session - * @throws {NotFoundError} if the tag or the session user does not exist - */ - async execute(dto) { - const { body, params } = dto; - const { tagId } = params; - const { description, email, mattermost, archivedAt, color } = body; - const userId = dto?.session?.id; - if (userId === undefined || userId === null) { - throw new BadParameterError('A user is required to update a tag'); - } - - await TransactionHelper.provide(async () => { - const tag = await TagRepository.findOne(new QueryBuilder().where('id').is(tagId)); - if (!tag) { - throw new NotFoundError(`Tag with this id (${tagId}) could not be found`); - } - - const user = await getUserOrFail({ userId }); - - tag.description = description; - tag.email = email; - tag.color = color; - tag.mattermost = mattermost; - tag.archivedAt = archivedAt; - tag.lastEditedByUserId = user.id; - await tag.save(); - }); - - return new GetTagUseCase().execute({ params: { tagId } }); - } -} - -module.exports = UpdateTagUseCase; diff --git a/lib/usecases/tag/index.js b/lib/usecases/tag/index.js deleted file mode 100644 index 4e10b048d6..0000000000 --- a/lib/usecases/tag/index.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @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 CreateTagUseCase = require('./CreateTagUseCase'); -const DeleteTagUseCase = require('./DeleteTagUseCase'); -const GetAllTagsUseCase = require('./GetAllTagsUseCase'); -const GetLogsByTagUseCase = require('./GetLogsByTagUseCase'); -const GetTagUseCase = require('./GetTagUseCase'); -const UpdateTagUseCase = require('./UpdateTagUseCase'); -const GetTagByNameUseCase = require('./GetTagByNameUseCase'); -const { GetTagsByTextsUseCase } = require('../../server/services/tag/getTagsByText.js'); - -module.exports = { - CreateTagUseCase, - DeleteTagUseCase, - GetAllTagsUseCase, - GetLogsByTagUseCase, - GetTagUseCase, - UpdateTagUseCase, - GetTagByNameUseCase, - GetTagsByTextsUseCase, -}; diff --git a/test/api/tags.test.js b/test/api/tags.test.js index 073da4a64d..396c5d668e 100644 --- a/test/api/tags.test.js +++ b/test/api/tags.test.js @@ -13,8 +13,7 @@ const { expect } = require('chai'); const request = require('supertest'); -const { tag: { CreateTagUseCase } } = require('../../lib/usecases'); -const { dtos: { CreateTagDto } } = require('../../lib/domain'); +const { tagService } = require('../../lib/server/services/tag/TagService.js'); const { beforeEach } = require('mocha'); const { resetDatabaseContent } = require('../utilities/resetDatabaseContent.js'); @@ -307,15 +306,7 @@ module.exports = () => { let createdTag; beforeEach(async () => { - const createTagDto = await CreateTagDto.validateAsync({ - body: { - text: `TAG#${new Date().getTime()}`, - }, - }); - - createTagDto.session = { id: 1, externalId: 1, name: 'John Doe' }; - createdTag = await new CreateTagUseCase() - .execute(createTagDto); + createdTag = await tagService.create({ text: `TAG#${new Date().getTime()}` }, { userId: 1 }); }); it('should return 400 if the tag id is not a number', (done) => { @@ -556,14 +547,7 @@ module.exports = () => { let createdTag; beforeEach(async () => { - const createTagDto = await CreateTagDto.validateAsync({ - body: { - text: `TAG#${new Date().getTime()}`, - }, - }); - createTagDto.session = { id: 1, externalId: 1, name: 'John Doe' }; - createdTag = await new CreateTagUseCase() - .execute(createTagDto); + createdTag = await tagService.create({ text: `TAG#${new Date().getTime()}` }, { userId: 1 }); }); it('should return 201 if no text is provided', (done) => { diff --git a/test/lib/server/services/index.js b/test/lib/server/services/index.js index 7ec4e2d9c6..c9e7871aa2 100644 --- a/test/lib/server/services/index.js +++ b/test/lib/server/services/index.js @@ -27,6 +27,8 @@ const RunSuite = require('./run/index.js'); const RunTypeSuite = require('./runType/index.js'); const ShiftSuite = require('./shift/index.js'); const StatisticsSuite = require('./statistics/index.js'); +const StatusSuite = require('./status/index.js'); +const TagSuite = require('./tag/index.js'); const LhcPeriodSuite = require('./lhcPeriod'); const DataPassesSuite = require('./dataPasses/index.js'); const UserSuite = require('./user/index.js'); @@ -55,6 +57,8 @@ module.exports = () => { describe('Run', RunSuite); describe('Shift', ShiftSuite); describe('Statistics', StatisticsSuite); + describe('Status', StatusSuite); + describe('Tag', TagSuite); describe('LhcPeriod', LhcPeriodSuite); describe('User', UserSuite); describe('DataPasses', DataPassesSuite); diff --git a/test/lib/server/services/status/StatusService.test.js b/test/lib/server/services/status/StatusService.test.js new file mode 100644 index 0000000000..4c4c653f73 --- /dev/null +++ b/test/lib/server/services/status/StatusService.test.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. + */ + +const { statusService } = require('../../../../../lib/server/services/status/StatusService.js'); +const { expect } = require('chai'); +const sinon = require('sinon'); +const database = require('../../../../../lib/database/index.js'); +const { DatabaseConfig: { host, port } } = require('../../../../../lib/config/index.js'); + +module.exports = () => { + it('should give current gui data', () => { + const data = statusService.getGuiStatus({ host: 'test', port: 4000 }); + + expect(data.status.ok).to.equal(true); + expect(data.status.configured).to.equal(true); + expect(data.host).to.equal('test'); + expect(data.port).to.equal(4000); + expect(data.version).to.be.a('string'); + }); + + it('should give current database data', async () => { + const data = await statusService.getDatabaseStatus(); + + expect(data.status.ok).to.equal(true); + expect(data.host).to.equal(host); + expect(data.port).to.equal(port); + }); + + it('should return the database as not ok when it cannot be reached', async () => { + const stub = sinon.stub(database, 'ping').returns(false); + try { + const data = await statusService.getDatabaseStatus(); + + expect(stub.called).to.be.true; + expect(data.status.ok).to.equal(false); + } finally { + stub.restore(); + } + }); +}; diff --git a/lib/usecases/status/index.js b/test/lib/server/services/status/index.js similarity index 70% rename from lib/usecases/status/index.js rename to test/lib/server/services/status/index.js index f46513cc52..af7980be3c 100644 --- a/lib/usecases/status/index.js +++ b/test/lib/server/services/status/index.js @@ -11,10 +11,8 @@ * or submit itself to any jurisdiction. */ -const GetGuiStatusUseCase = require('./GetGuiStatusUseCase'); -const GetDatabaseStatusUseCase = require('./GetDatabaseStatusUseCase'); +const StatusServiceSuite = require('./StatusService.test.js'); -module.exports = { - GetGuiStatusUseCase, - GetDatabaseStatusUseCase, +module.exports = () => { + describe('StatusService', StatusServiceSuite); }; diff --git a/test/lib/server/services/tag/TagService.test.js b/test/lib/server/services/tag/TagService.test.js new file mode 100644 index 0000000000..8f8a9e434d --- /dev/null +++ b/test/lib/server/services/tag/TagService.test.js @@ -0,0 +1,340 @@ +/** + * @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 { repositories: { TagRepository }, utilities: { QueryBuilder } } = require('../../../../../lib/database/index.js'); +const { tagService } = require('../../../../../lib/server/services/tag/TagService.js'); +const assert = require('assert'); +const { expect } = require('chai'); +const { BadParameterError } = require('../../../../../lib/server/errors/BadParameterError.js'); +const { NotFoundError } = require('../../../../../lib/server/errors/NotFoundError.js'); +const { ConflictError } = require('../../../../../lib/server/errors/ConflictError.js'); + +module.exports = () => { + describe('getAll', () => { + it('should successfully return an array with tags', async () => { + expect(await tagService.getAll()).to.be.an('array'); + }); + + it('should return the user who last edited each tag', async () => { + for (const tag of await tagService.getAll()) { + expect(tag).to.have.property('lastEditedBy'); + expect(tag).to.not.have.property('lastEditedName'); + expect(tag).to.not.have.property('lastEditedByUserId'); + if (tag.lastEditedBy !== null) { + expect(tag.lastEditedBy).to.have.all.keys('name'); + } + } + }); + + it('should return tags sorted by text, archived tags last', async () => { + const tags = await tagService.getAll(); + + const sorted = Array.from(tags); + sorted.sort((a, b) => { + if (a.archived === b.archived) { + return a.text.localeCompare(b.text); + } else { + return a.archived - b.archived; + } + }); + + expect(tags).to.eql(sorted); + }); + + it('should successfully return an array with tags with specified ids', async () => { + const tags = await tagService.getAll({ filter: { ids: '1,2' } }); + + expect(tags).to.have.lengthOf(2); + expect(tags[0].id).to.equal(1); + expect(tags[0].text).to.equal('FOOD'); + expect(tags[1].id).to.equal(2); + expect(tags[1].text).to.equal('RUN'); + }); + + it('should return an empty array if no tag was found with specified ids', async () => { + expect(await tagService.getAll({ filter: { ids: '5002' } })).to.have.lengthOf(0); + }); + + it('should successfully return an array with tags with specified texts', async () => { + const tags = await tagService.getAll({ filter: { texts: 'FOOD,SL' } }); + + expect(tags).to.have.lengthOf(2); + expect(tags[0].id).to.equal(1); + expect(tags[0].text).to.equal('FOOD'); + expect(tags[1].id).to.equal(8); + expect(tags[1].text).to.equal('SL'); + }); + + it('should successfully return an array with tags containing given text search', async () => { + let tags = await tagService.getAll({ filter: { partialText: 'MAI' } }); + expect(tags).to.have.lengthOf(1); + expect(tags[0].id).to.equal(3); + expect(tags[0].text).to.equal('MAINTENANCE'); + + tags = await tagService.getAll({ filter: { partialText: 'ANCE' } }); + expect(tags).to.have.lengthOf(1); + expect(tags[0].id).to.equal(3); + expect(tags[0].text).to.equal('MAINTENANCE'); + + tags = await tagService.getAll({ filter: { partialText: '-TAG-' } }); + expect(tags).to.have.lengthOf(31); + expect(tags.every((tag) => tag.text.includes('-TAG-'))).to.be.true; + + tags = await tagService.getAll({ filter: { partialText: 'DO-NOT-EXISTS' } }); + expect(tags).to.have.lengthOf(0); + }); + + it('should successfully return an array with tags with specified emails', async () => { + const tags = await tagService.getAll({ filter: { emails: 'other-group@cern.ch' } }); + + expect(tags).to.have.lengthOf(1); + expect(tags[0].id).to.equal(8); + expect(tags[0].text).to.equal('SL'); + }); + + it('should successfully return an array with tags with specified mattermosts', async () => { + const tags = await tagService.getAll({ filter: { mattermosts: 'marathon,,food' } }); + + expect(tags).to.have.lengthOf(2); + expect(tags[0].id).to.equal(1); + expect(tags[0].text).to.equal('FOOD'); + expect(tags[1].id).to.equal(2); + expect(tags[1].text).to.equal('RUN'); + }); + }); + + describe('getByIdentifier', () => { + /** + * Ids of the tags created by a test, removed after each test even if it fails + * @type {number[]} + */ + let createdTagsIds = []; + + afterEach(async () => { + if (createdTagsIds.length > 0) { + await TagRepository.removeAll(new QueryBuilder().where('id').oneOf(...createdTagsIds)); + createdTagsIds = []; + } + }); + + it('should return a tag by id', async () => { + const result = await tagService.getByIdentifier({ id: 1 }); + + expect(result).to.have.ownProperty('id'); + expect(result.id).to.equal(1); + }); + + it('should return the user who last edited the tag', async () => { + const createdTag = await TagRepository.insert({ text: `TAG-LAST-EDITED-${Date.now()}`, lastEditedByUserId: 2 }); + createdTagsIds.push(createdTag.id); + + expect((await tagService.getByIdentifier({ id: createdTag.id })).lastEditedBy).to.deep.equal({ name: 'Jan Jansen' }); + }); + + it('should return null last editor for a tag that was never edited', async () => { + const createdTag = await TagRepository.insert({ text: `TAG-NEVER-EDITED-${Date.now()}` }); + createdTagsIds.push(createdTag.id); + + expect((await tagService.getByIdentifier({ id: createdTag.id })).lastEditedBy).to.be.null; + }); + + it('should return a tag by text with its email and mattermost fields', async () => { + const result = await tagService.getByIdentifier({ text: 'FOOD' }); + + expect(result.mattermost).to.equal('food'); + expect(result.email).to.equal('food-group@cern.ch'); + expect(result.description).to.equal('The food\'s related tag'); + }); + + it('should return null when no tag matches', async () => { + expect(await tagService.getByIdentifier({ text: '9999999999' })).to.be.null; + expect(await tagService.getByIdentifier({ id: 9999999 })).to.be.null; + }); + + it('should reject when no id or text is given', async () => { + await assert.rejects(() => tagService.getByIdentifier({}), new BadParameterError('Can not find without tag id or text')); + }); + }); + + describe('create', () => { + let tag; + const userRelation = { userId: 1 }; + + beforeEach(() => { + tag = { text: `TAG#${new Date().getTime()}` }; + }); + + it('should insert a new Tag', async () => { + const nTagsBefore = await TagRepository.count(); + + await tagService.create(tag, userRelation); + + expect(await TagRepository.count()).to.be.greaterThan(nTagsBefore); + }); + + it('should insert a new Tag with the same title as provided', async () => { + tag.text = `Tag #${Math.round(Math.random() * 1000)}`; + + expect((await tagService.create(tag, userRelation)).text).to.equal(tag.text); + }); + + it('should reject with a ConflictError if we are trying to create the same tag again', async () => { + const nTagsBefore = await TagRepository.count(); + + await tagService.create(tag, userRelation); + + const nTagsAfter = await TagRepository.count(); + expect(nTagsAfter).to.be.greaterThan(nTagsBefore); + + await assert.rejects(() => tagService.create(tag, userRelation), new ConflictError('The provided entity already exists')); + expect(await TagRepository.count()).to.equal(nTagsAfter); + }); + + it('should store the id of the user creating the tag', async () => { + const createdTag = await tagService.create(tag, userRelation); + expect(createdTag.lastEditedBy).to.deep.equal({ name: 'John Doe' }); + expect(createdTag).to.not.have.property('lastEditedName'); + + const storedTag = await TagRepository.findOne({ where: { id: createdTag.id } }); + expect(storedTag.lastEditedByUserId).to.equal(1); + }); + + it('should not modify the given tag', async () => { + const given = { ...tag }; + await tagService.create(tag, userRelation); + expect(tag).to.eql(given); + }); + + it('should reject the creation if no user is provided', async () => { + const nTagsBefore = await TagRepository.count(); + + await assert.rejects(() => tagService.create(tag, {}), new BadParameterError('A user is required to create a tag')); + expect(await TagRepository.count()).to.equal(nTagsBefore); + }); + + it('should reject the creation if the user does not exist', async () => { + const nTagsBefore = await TagRepository.count(); + + await assert.rejects( + () => tagService.create(tag, { userId: 9999 }), + new NotFoundError('User with this id (9999) could not be found'), + ); + expect(await TagRepository.count()).to.equal(nTagsBefore); + }); + + it('should successfully create a new tag with a description', async () => { + tag.description = 'A description'; + + const createdTag = await tagService.create(tag, userRelation); + expect(createdTag).to.be.an('object'); + expect(createdTag.description).to.equal('A description'); + }); + }); + + describe('update', () => { + let patch; + + beforeEach(() => { + patch = { + mattermost: 'tag,tag,tag', + email: 'cern@tag.ch,cern@othertag.ch', + description: 'The new tag\'s description', + archivedAt: Date.now(), + }; + }); + + it('should save the correct values', async () => { + const result = await tagService.update(3, patch, { userId: 1 }); + + expect(result.mattermost).to.equal('tag,tag,tag'); + expect(result.lastEditedBy).to.deep.equal({ name: 'John Doe' }); + expect(result).to.not.have.property('lastEditedName'); + expect(result.email).to.equal('cern@tag.ch,cern@othertag.ch'); + expect(result.description).to.equal('The new tag\'s description'); + expect(result.archived).to.be.true; + }); + + it('should store the id of the user performing the update', async () => { + const result = await tagService.update(3, patch, { userId: 2 }); + expect(result.lastEditedBy).to.deep.equal({ name: 'Jan Jansen' }); + }); + + it('should reject when no user is provided', async () => { + await assert.rejects(() => tagService.update(3, patch, {}), new BadParameterError('A user is required to update a tag')); + }); + + it('should reject when the user does not exist', async () => { + await assert.rejects( + () => tagService.update(3, patch, { userId: 9999 }), + new NotFoundError('User with this id (9999) could not be found'), + ); + }); + + it('should reject when the tag does not exist', async () => { + await assert.rejects( + () => tagService.update(9999, patch, { userId: 1 }), + new NotFoundError('Tag with this id (9999) could not be found'), + ); + }); + }); + + describe('remove', () => { + let createdTag; + + beforeEach(async () => { + createdTag = await tagService.create({ text: `TAG#${new Date().getTime()}` }, { userId: 1 }); + }); + + it('should remove a tag', async () => { + const nTagsBefore = await TagRepository.count(); + + await tagService.remove(createdTag.id); + + expect(await TagRepository.count()).to.be.lessThan(nTagsBefore); + }); + + it('should return the removed tag', async () => { + const result = await tagService.remove(createdTag.id); + + expect(result.id).to.equal(createdTag.id); + expect(result.text).to.equal(createdTag.text); + }); + + it('should return null if the tag does not exist', async () => { + expect(await tagService.remove(9999999)).to.be.null; + }); + }); + + describe('getLogs', () => { + it('should return the logs carrying the tag with their total count', async () => { + const { logs, count } = await tagService.getLogs(3); + + expect(logs).to.be.an('array'); + expect(count).to.be.greaterThan(0); + // Log 1 carries the tag 3 + expect(logs.map(({ id }) => id)).to.include(1); + }); + + it('should apply the given pagination but still return the total count', async () => { + const { count: totalCount } = await tagService.getLogs(3); + const { logs, count } = await tagService.getLogs(3, { limit: 1, offset: 0 }); + + expect(logs).to.have.lengthOf(1); + expect(count).to.equal(totalCount); + }); + + it('should return null if the tag does not exist', async () => { + expect(await tagService.getLogs(9999999)).to.be.null; + }); + }); +}; diff --git a/test/lib/usecases/status/index.js b/test/lib/server/services/tag/index.js similarity index 65% rename from test/lib/usecases/status/index.js rename to test/lib/server/services/tag/index.js index f6a9b8840f..4a2711f077 100644 --- a/test/lib/usecases/status/index.js +++ b/test/lib/server/services/tag/index.js @@ -11,10 +11,8 @@ * or submit itself to any jurisdiction. */ -const GetDatabaseStatusUseCase = require('./GetDatabaseStatusUseCase.test.js'); -const GetGuiStatusUseCase = require('./GetGuiStatusUseCase.test.js'); +const TagServiceSuite = require('./TagService.test.js'); module.exports = () => { - describe('GetDatabaseStatusUseCase', GetDatabaseStatusUseCase); - describe('GetDatabaseStatusUseCase', GetGuiStatusUseCase); + describe('TagService', TagServiceSuite); }; diff --git a/test/lib/usecases/index.js b/test/lib/usecases/index.js index bab1e09d0a..d6f5835ed1 100644 --- a/test/lib/usecases/index.js +++ b/test/lib/usecases/index.js @@ -12,23 +12,19 @@ */ const EnvironmentSuite = require('./environment/index.js'); -const StatusSuite = require('./status/index.js'); const LhcFillSuite = require('./lhcFill/index.js'); const LogSuite = require('./log/index.js'); const RunSuite = require('./run/index.js'); const FlpSuite = require('./flp/index.js'); const ServerSuite = require('./server/index.js'); -const TagSuite = require('./tag/index.js'); const { resetDatabaseContent } = require('../../utilities/resetDatabaseContent.js'); module.exports = () => { before(resetDatabaseContent); - describe('Status use-case', StatusSuite); describe('Log use-case', LogSuite); describe('LhcFill use-case', LhcFillSuite); describe('Run use-case', RunSuite); describe('Flp use-case', FlpSuite); describe('Server use-case', ServerSuite); - describe('Tag use-case', TagSuite); describe('Environment use-case', EnvironmentSuite); }; diff --git a/test/lib/usecases/status/GetDatabaseStatusUseCase.test.js b/test/lib/usecases/status/GetDatabaseStatusUseCase.test.js deleted file mode 100644 index 75421c4524..0000000000 --- a/test/lib/usecases/status/GetDatabaseStatusUseCase.test.js +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable no-trailing-spaces */ -/** - * @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 { status: { GetDatabaseStatusUseCase } } = require('../../../../lib/usecases/index.js'); -const chai = require('chai'); -const sinon = require('sinon'); -const database = require('../../../../lib/database/index.js'); -const { - DatabaseConfig: { - host, - port, - } } = require('../../../../lib/config/index.js'); - -const { expect } = chai; - -module.exports = () => { - it('should give current database data', async () => { - const data = await new GetDatabaseStatusUseCase() - .execute(); - expect(data.status.ok).to.equal(true); - expect(data.host).to.equal(host); - expect(data.port).to.equal(port); - }); - it('should return the error message when the user cannot connect', async () =>{ - const stub = sinon.stub(database, 'ping').returns(false); - const data = await new GetDatabaseStatusUseCase() - .execute(); - - // Validate stub is called and extra data - expect(stub.called); - expect(data.status.ok).to.equal(false); - stub.resetBehavior(); - }); -}; diff --git a/test/lib/usecases/status/GetGuiStatusUseCase.test.js b/test/lib/usecases/status/GetGuiStatusUseCase.test.js deleted file mode 100644 index d40db730f7..0000000000 --- a/test/lib/usecases/status/GetGuiStatusUseCase.test.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @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 { status: { GetGuiStatusUseCase } } = require('../../../../lib/usecases/index.js'); -const chai = require('chai'); - -const { expect } = chai; - -module.exports = () => { - it('should give current gui data', async () => { - const request = { - hostname: 'test', - socket: { - localPort: 4000, - }, - }; - const data = await new GetGuiStatusUseCase() - .execute(request); - expect(data.status.ok).to.equal(true); - expect(data.status.configured).to.equal(true); - expect(data.host).to.equal(request.hostname); - expect(data.port).to.equal(request.socket.localPort); - }); -}; diff --git a/test/lib/usecases/tag/CreateTagUseCase.test.js b/test/lib/usecases/tag/CreateTagUseCase.test.js deleted file mode 100644 index a1ba167e81..0000000000 --- a/test/lib/usecases/tag/CreateTagUseCase.test.js +++ /dev/null @@ -1,110 +0,0 @@ -/** - * @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 { repositories: { TagRepository } } = require('../../../../lib/database/index.js'); -const { tag: { CreateTagUseCase } } = require('../../../../lib/usecases/index.js'); -const { dtos: { CreateTagDto } } = require('../../../../lib/domain/index.js'); -const chai = require('chai'); -const assert = require('assert'); -const { BadParameterError } = require('../../../../lib/server/errors/BadParameterError.js'); -const { NotFoundError } = require('../../../../lib/server/errors/NotFoundError.js'); -const { ConflictError } = require('../../../../lib/server/errors/ConflictError.js'); - -const { expect } = chai; - -module.exports = () => { - let createTagDto; - - beforeEach(async () => { - createTagDto = await CreateTagDto.validateAsync({ - body: { - text: `TAG#${new Date().getTime()}`, - }, - }); - createTagDto.session = { id: 1, externalId: 1, name: 'John Doe' }; - }); - - it('should insert a new Tag', async () => { - const nTagsBefore = await TagRepository.count(); - - await new CreateTagUseCase() - .execute(createTagDto); - - const nTagsAfter = await TagRepository.count(); - expect(nTagsAfter).to.be.greaterThan(nTagsBefore); - }); - - it('should insert a new Tag with the same title as provided', async () => { - const expectedTitle = `Tag #${Math.round(Math.random() * 1000)}`; - - createTagDto.body.text = expectedTitle; - const result = await new CreateTagUseCase() - .execute(createTagDto); - - expect(result.text).to.equal(expectedTitle); - }); - - it('should reject with a ConflictError if we are trying to create the same tag again', async () => { - const nTagsBefore = await TagRepository.count(); - - await new CreateTagUseCase() - .execute(createTagDto); - - const nTagsAfter = await TagRepository.count(); - expect(nTagsAfter).to.be.greaterThan(nTagsBefore); - - await assert.rejects( - () => new CreateTagUseCase().execute(createTagDto), - new ConflictError('The provided entity already exists'), - ); - expect(await TagRepository.count()).to.equal(nTagsAfter); - }); - - it('should store the id of the user creating the tag', async () => { - const tag = await new CreateTagUseCase().execute(createTagDto); - expect(tag.lastEditedBy).to.deep.equal({ name: 'John Doe' }); - expect(tag).to.not.have.property('lastEditedName'); - - const storedTag = await TagRepository.findOne({ where: { id: tag.id } }); - expect(storedTag.lastEditedByUserId).to.equal(1); - }); - - it('should reject the creation if no user is provided', async () => { - delete createTagDto.session; - const nTagsBefore = await TagRepository.count(); - - await assert.rejects( - () => new CreateTagUseCase().execute(createTagDto), - new BadParameterError('A user is required to create a tag'), - ); - expect(await TagRepository.count()).to.equal(nTagsBefore); - }); - - it('should reject the creation if the user does not exist', async () => { - createTagDto.session = { id: 9999, externalId: 9999, name: 'Ghost' }; - const nTagsBefore = await TagRepository.count(); - - await assert.rejects( - () => new CreateTagUseCase().execute(createTagDto), - new NotFoundError('User with this id (9999) could not be found'), - ); - expect(await TagRepository.count()).to.equal(nTagsBefore); - }); - - it('should successfully create a new tag with a description', async () => { - createTagDto.body.description = 'A description'; - const tag = await new CreateTagUseCase().execute(createTagDto); - expect(tag).to.be.an('object'); - expect(tag.description).to.equal('A description'); - }); -}; diff --git a/test/lib/usecases/tag/DeleteTagUseCase.test.js b/test/lib/usecases/tag/DeleteTagUseCase.test.js deleted file mode 100644 index 336b38818e..0000000000 --- a/test/lib/usecases/tag/DeleteTagUseCase.test.js +++ /dev/null @@ -1,66 +0,0 @@ -/** - * @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 { repositories: { TagRepository } } = require('../../../../lib/database/index.js'); -const { tag: { CreateTagUseCase, DeleteTagUseCase } } = require('../../../../lib/usecases/index.js'); -const { dtos: { CreateTagDto } } = require('../../../../lib/domain/index.js'); -const chai = require('chai'); - -const { expect } = chai; - -module.exports = () => { - let createdTag; - - beforeEach(async () => { - const createTagDto = await CreateTagDto.validateAsync({ - body: { - text: `TAG#${new Date().getTime()}`, - }, - }); - - createTagDto.session = { id: 1, externalId: 1, name: 'John Doe' }; - createdTag = await new CreateTagUseCase() - .execute(createTagDto); - }); - - it('should remove a tag', async () => { - const nTagsBefore = await TagRepository.count(); - - await new DeleteTagUseCase() - .execute({ - params: { - tagId: createdTag.id, - }, - }); - - const nTagsAfter = await TagRepository.count(); - expect(nTagsAfter).to.be.lessThan(nTagsBefore); - }); - - it('should return the removed tag', async () => { - const nTagsBefore = await TagRepository.count(); - - const result = await new DeleteTagUseCase() - .execute({ - params: { - tagId: createdTag.id, - }, - }); - - const nTagsAfter = await TagRepository.count(); - expect(nTagsAfter).to.be.lessThan(nTagsBefore); - - expect(result.id).to.equal(createdTag.id); - expect(result.text).to.equal(createdTag.text); - }); -}; diff --git a/test/lib/usecases/tag/GetAllTagsUseCase.test.js b/test/lib/usecases/tag/GetAllTagsUseCase.test.js deleted file mode 100644 index a9c5882c24..0000000000 --- a/test/lib/usecases/tag/GetAllTagsUseCase.test.js +++ /dev/null @@ -1,140 +0,0 @@ -/** - * @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 { tag: { GetAllTagsUseCase } } = require('../../../../lib/usecases/index.js'); -const { dtos: { GetAllTagsDto } } = require('../../../../lib/domain/index.js'); -const chai = require('chai'); - -const { expect } = chai; - -module.exports = () => { - let getAllTagsDto; - - beforeEach(async () => { - getAllTagsDto = await GetAllTagsDto.validateAsync({}); - }); - it('should successfully return an array with tags', async () => { - const { tags } = await new GetAllTagsUseCase().execute(); - - expect(tags).to.be.an('array'); - }); - - it('should return the user who last edited each tag', async () => { - const { tags } = await new GetAllTagsUseCase().execute(); - - for (const tag of tags) { - expect(tag).to.have.property('lastEditedBy'); - expect(tag).to.not.have.property('lastEditedName'); - expect(tag).to.not.have.property('lastEditedByUserId'); - if (tag.lastEditedBy !== null) { - expect(tag.lastEditedBy).to.have.all.keys('name'); - } - } - }); - it('should return tags sorted by text', async () => { - const { tags } = await new GetAllTagsUseCase().execute(); - - const sorted = Array.from(tags); - sorted.sort((a, b) => { - if (a.archived === b.archived) { - return a.text.localeCompare(b.text); - } else { - return a.archived - b.archived; - } - }); - - expect(tags).to.eql(sorted); - }); - it('should successfully return an array with tags with specified ids', async () => { - getAllTagsDto.query = { filter: { ids: '1,2' } }; - const { tags } = await new GetAllTagsUseCase().execute(getAllTagsDto); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(2); - expect(tags[0].id).to.equal(1); - expect(tags[0].text).to.equal('FOOD'); - expect(tags[1].id).to.equal(2); - expect(tags[1].text).to.equal('RUN'); - }); - it('should successfully return 204 status if no tag was found with specified ids', async () => { - getAllTagsDto.query = { filter: { ids: '5002' } }; - const { tags } = await new GetAllTagsUseCase().execute(getAllTagsDto); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(0); - }); - it('should successfully return an array with tags with specified texts', async () => { - getAllTagsDto.query = { filter: { texts: 'FOOD,SL' } }; - const { tags } = await new GetAllTagsUseCase().execute(getAllTagsDto); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(2); - expect(tags[0].id).to.equal(1); - expect(tags[0].text).to.equal('FOOD'); - expect(tags[1].id).to.equal(8); - expect(tags[1].text).to.equal('SL'); - }); - it('should successfully return an array with tags containing given text search', async () => { - getAllTagsDto.query = { filter: { partialText: 'MAI' } }; - let { tags } = await new GetAllTagsUseCase().execute(getAllTagsDto); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(1); - expect(tags[0].id).to.equal(3); - expect(tags[0].text).to.equal('MAINTENANCE'); - - getAllTagsDto.query = { filter: { partialText: 'ANCE' } }; - - ({ tags } = await new GetAllTagsUseCase().execute(getAllTagsDto)); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(1); - expect(tags[0].id).to.equal(3); - expect(tags[0].text).to.equal('MAINTENANCE'); - - getAllTagsDto.query = { filter: { partialText: '-TAG-' } }; - - ({ tags } = await new GetAllTagsUseCase().execute(getAllTagsDto)); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(31); - expect(tags.every((tag) => tag.text.includes('-TAG-'))).to.be.true; - - getAllTagsDto.query = { filter: { partialText: 'DO-NOT-EXISTS' } }; - - ({ tags } = await new GetAllTagsUseCase().execute(getAllTagsDto)); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(0); - }); - it('should successfully return an array with tags with specified emails', async () => { - getAllTagsDto.query = { filter: { emails: 'other-group@cern.ch' } }; - const { tags } = await new GetAllTagsUseCase().execute(getAllTagsDto); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(1); - expect(tags[0].id).to.equal(8); - expect(tags[0].text).to.equal('SL'); - }); - it('should successfully return an array with tags with specified mattermosts', async () => { - getAllTagsDto.query = { filter: { mattermosts: 'marathon,,food' } }; - const { tags } = await new GetAllTagsUseCase().execute(getAllTagsDto); - - expect(tags).to.be.an('array'); - expect(tags).to.have.lengthOf(2); - expect(tags[0].id).to.equal(1); - expect(tags[0].text).to.equal('FOOD'); - expect(tags[1].id).to.equal(2); - expect(tags[1].text).to.equal('RUN'); - }); -}; diff --git a/test/lib/usecases/tag/GetTagByNameUseCase.test.js b/test/lib/usecases/tag/GetTagByNameUseCase.test.js deleted file mode 100644 index bcb97e7ca2..0000000000 --- a/test/lib/usecases/tag/GetTagByNameUseCase.test.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * @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 { tag: { GetTagByNameUseCase } } = require('../../../../lib/usecases/index.js'); -const { dtos: { GetTagByNameDto } } = require('../../../../lib/domain/index.js'); -const chai = require('chai'); - -const { expect } = chai; - -module.exports = () => { - let getLogDto; - - beforeEach(async () => { - getLogDto = await GetTagByNameDto.validateAsync({ - query: { - name: 'FOOD', - }, - }); - }); - - it('should return an object that has a valid email and mattermost fields', async () => { - const result = await new GetTagByNameUseCase() - .execute(getLogDto); - - expect(result.mattermost).to.equal('food'); - expect(result.email).to.equal('food-group@cern.ch'); - expect(result.description).to.equal('The food\'s related tag'); - }); - it('should return null when invalid data is given', async () => { - getLogDto = await GetTagByNameDto.validateAsync({ - query: { - name: '9999999999', - }, - }); - const result = await new GetTagByNameUseCase() - .execute(getLogDto); - - expect(result).to.be.null; - }); - it('should return an object when the value is URI encoded', async () => { - getLogDto = await GetTagByNameDto.validateAsync({ - query: { - name: 'DCS', - }, - }); - const result = await new GetTagByNameUseCase() - .execute(getLogDto); - - expect(result.mattermost).to.equal('cake'); - expect(result.email).to.equal('cake@cern.ch'); - expect(result.description).to.equal('The first test tag'); - }); -}; diff --git a/test/lib/usecases/tag/GetTagUseCase.test.js b/test/lib/usecases/tag/GetTagUseCase.test.js deleted file mode 100644 index 688b35309d..0000000000 --- a/test/lib/usecases/tag/GetTagUseCase.test.js +++ /dev/null @@ -1,68 +0,0 @@ -/** - * @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 { tag: { GetTagUseCase } } = require('../../../../lib/usecases/index.js'); -const { repositories: { TagRepository }, utilities: { QueryBuilder } } = require('../../../../lib/database/index.js'); -const { dtos: { GetTagDto } } = require('../../../../lib/domain/index.js'); -const chai = require('chai'); - -const { expect } = chai; - -module.exports = () => { - let getLogDto; - - /** - * Ids of the tags created by a test, removed after each test even if it fails - * @type {number[]} - */ - let createdTagsIds = []; - - beforeEach(async () => { - getLogDto = await GetTagDto.validateAsync({ - params: { - tagId: 1, - }, - }); - }); - - afterEach(async () => { - if (createdTagsIds.length > 0) { - await TagRepository.removeAll(new QueryBuilder().where('id').oneOf(...createdTagsIds)); - createdTagsIds = []; - } - }); - - it('should return an object that has the `id` property', async () => { - const result = await new GetTagUseCase() - .execute(getLogDto); - - expect(result).to.have.ownProperty('id'); - expect(result.id).to.equal(1); - }); - - it('should return the user who last edited the tag', async () => { - const createdTag = await TagRepository.insert({ text: `TAG-LAST-EDITED-${Date.now()}`, lastEditedByUserId: 2 }); - createdTagsIds.push(createdTag.id); - const result = await new GetTagUseCase().execute({ params: { tagId: createdTag.id } }); - - expect(result.lastEditedBy).to.deep.equal({ name: 'Jan Jansen' }); - }); - - it('should return null last editor for a tag that was never edited', async () => { - const createdTag = await TagRepository.insert({ text: `TAG-NEVER-EDITED-${Date.now()}` }); - createdTagsIds.push(createdTag.id); - const result = await new GetTagUseCase().execute({ params: { tagId: createdTag.id } }); - - expect(result.lastEditedBy).to.be.null; - }); -}; diff --git a/test/lib/usecases/tag/UpdateTagUseCase.test.js b/test/lib/usecases/tag/UpdateTagUseCase.test.js deleted file mode 100644 index 0ef90e6267..0000000000 --- a/test/lib/usecases/tag/UpdateTagUseCase.test.js +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @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 { tag: { UpdateTagUseCase } } = require('../../../../lib/usecases/index.js'); -const { dtos: { UpdateTagDto } } = require('../../../../lib/domain/index.js'); -const chai = require('chai'); -const assert = require('assert'); -const { BadParameterError } = require('../../../../lib/server/errors/BadParameterError.js'); -const { NotFoundError } = require('../../../../lib/server/errors/NotFoundError.js'); - -const { expect } = chai; - -module.exports = () => { - let updateTagDto; - - beforeEach(async () => { - updateTagDto = await UpdateTagDto.validateAsync({ - body: { - mattermost: 'tag,tag,tag', - email: 'cern@tag.ch,cern@othertag.ch', - description: 'The new tag\'s description', - archivedAt: Date.now(), - }, - params: { - tagId: 3, - }, - }); - updateTagDto.session = { - personid: 1, - id: 1, - name: 'John Doe', - }; - }); - it('should save the correct values', async () => { - const result = await new UpdateTagUseCase() - .execute(updateTagDto); - expect(result.mattermost).to.equal('tag,tag,tag'); - expect(result.lastEditedBy).to.deep.equal({ name: 'John Doe' }); - expect(result).to.not.have.property('lastEditedName'); - expect(result.email).to.equal('cern@tag.ch,cern@othertag.ch'); - expect(result.description).to.equal('The new tag\'s description'); - expect(result.archived).to.be.true; - }); - - it('should store the id of the user performing the update', async () => { - updateTagDto.session = { - personid: 456, - id: 2, - name: 'Jan Jansen', - }; - const result = await new UpdateTagUseCase() - .execute(updateTagDto); - expect(result.lastEditedBy).to.deep.equal({ name: 'Jan Jansen' }); - }); - - it('should reject when no user is provided in the session', async () => { - delete updateTagDto.session; - await assert.rejects( - () => new UpdateTagUseCase().execute(updateTagDto), - new BadParameterError('A user is required to update a tag'), - ); - }); - - it('should reject when the session user does not exist', async () => { - updateTagDto.session = { id: 9999, externalId: 9999, name: 'Ghost' }; - await assert.rejects( - () => new UpdateTagUseCase().execute(updateTagDto), - new NotFoundError('User with this id (9999) could not be found'), - ); - }); - - it('should reject when the tag does not exist', async () => { - updateTagDto.params.tagId = 9999; - await assert.rejects( - () => new UpdateTagUseCase().execute(updateTagDto), - new NotFoundError('Tag with this id (9999) could not be found'), - ); - }); -}; diff --git a/test/lib/usecases/tag/index.js b/test/lib/usecases/tag/index.js deleted file mode 100644 index 9ffa789eeb..0000000000 --- a/test/lib/usecases/tag/index.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @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 CreateTagUseCase = require('./CreateTagUseCase.test.js'); -const DeleteTagUseCase = require('./DeleteTagUseCase.test.js'); -const GetAllTagsUseCase = require('./GetAllTagsUseCase.test.js'); -const GetTagUseCase = require('./GetTagUseCase.test.js'); -const GetTagByNameUseCase = require('./GetTagByNameUseCase.test.js'); -const UpdateTagUseCase = require('./UpdateTagUseCase.test.js'); - -module.exports = () => { - describe('CreateTagUseCase', CreateTagUseCase); - describe('DeleteTagUseCase', DeleteTagUseCase); - describe('GetAllTagsUseCase', GetAllTagsUseCase); - describe('GetTagUseCase', GetTagUseCase); - describe('GetTagByNameUseCase', GetTagByNameUseCase); - describe('UpdateTagsUseCase', UpdateTagUseCase); -}; diff --git a/test/public/runs/detail.test.js b/test/public/runs/detail.test.js index a39302db34..d6c0fef087 100644 --- a/test/public/runs/detail.test.js +++ b/test/public/runs/detail.test.js @@ -31,7 +31,7 @@ const { const { RunCalibrationStatus } = require('../../../lib/domain/enums/RunCalibrationStatus.js'); const { runService } = require('../../../lib/server/services/run/RunService'); const { resetDatabaseContent } = require('../../utilities/resetDatabaseContent.js'); -const { tag: { UpdateTagUseCase } } = require('../../../lib/usecases/index.js'); +const { tagService } = require('../../../lib/server/services/tag/TagService.js'); const { expect } = chai; @@ -605,21 +605,7 @@ module.exports = () => { expect(badges[0]).to.equal(expectedBgColorBefore); - const updateTagDto = { - body: { - color: '#FF0000', //Red - }, - params: { - tagId: 1, - }, - session: { - personid: 1, - id: 1, - name: 'John Doe', - }, - }; - await new UpdateTagUseCase() - .execute(updateTagDto); + await tagService.update(1, { color: '#FF0000' /* Red */ }, { userId: 1 }); await goToRunDetails(page, 106); badges = await getRunTagsBadges();