From 1ebf1a15723799b64e26b42fffa5b9211078b633 Mon Sep 17 00:00:00 2001 From: Eliezer Castro Date: Wed, 15 Oct 2025 19:18:09 -0300 Subject: [PATCH] feat(tags): add tags to test cases (#299) --- .../migrations/20251009180204-create-tags.js | 35 +++++++++++ .../20251009191250-create-caseTags.js | 40 ++++++++++++ backend/models/caseTags.js | 36 +++++++++++ backend/models/cases.js | 5 ++ backend/models/tags.js | 34 ++++++++++ backend/routes/caseTags/delete.js | 41 ++++++++++++ backend/routes/caseTags/new.js | 58 +++++++++++++++++ backend/routes/cases/index.js | 13 ++++ backend/routes/cases/index.test.js | 27 +++++++- backend/routes/cases/indexByProjectId.js | 9 +++ backend/routes/cases/show.js | 9 +++ backend/routes/tags/delete.js | 45 +++++++++++++ backend/routes/tags/edit.js | 63 +++++++++++++++++++ backend/routes/tags/index.js | 38 +++++++++++ backend/routes/tags/new.js | 54 ++++++++++++++++ backend/routes/tags/show.js | 43 +++++++++++++ backend/server.js | 18 ++++++ 17 files changed, 567 insertions(+), 1 deletion(-) create mode 100644 backend/migrations/20251009180204-create-tags.js create mode 100644 backend/migrations/20251009191250-create-caseTags.js create mode 100644 backend/models/caseTags.js create mode 100644 backend/models/tags.js create mode 100644 backend/routes/caseTags/delete.js create mode 100644 backend/routes/caseTags/new.js create mode 100644 backend/routes/tags/delete.js create mode 100644 backend/routes/tags/edit.js create mode 100644 backend/routes/tags/index.js create mode 100644 backend/routes/tags/new.js create mode 100644 backend/routes/tags/show.js diff --git a/backend/migrations/20251009180204-create-tags.js b/backend/migrations/20251009180204-create-tags.js new file mode 100644 index 0000000..8cc67da --- /dev/null +++ b/backend/migrations/20251009180204-create-tags.js @@ -0,0 +1,35 @@ +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('tags', { + id: { + type: Sequelize.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false, + }, + name: { + type: Sequelize.STRING, + allowNull: false, + }, + projectId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'projects', + key: 'id', + }, + onDelete: 'CASCADE', + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); +} + +export async function down(queryInterface) { + await queryInterface.dropTable('tags'); +} diff --git a/backend/migrations/20251009191250-create-caseTags.js b/backend/migrations/20251009191250-create-caseTags.js new file mode 100644 index 0000000..4ffcb8b --- /dev/null +++ b/backend/migrations/20251009191250-create-caseTags.js @@ -0,0 +1,40 @@ +export async function up(queryInterface, Sequelize) { + await queryInterface.createTable('caseTags', { + id: { + type: Sequelize.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false, + }, + caseId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'cases', + key: 'id', + }, + onDelete: 'CASCADE', + }, + tagId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'tags', + key: 'id', + }, + onDelete: 'CASCADE', + }, + createdAt: { + type: Sequelize.DATE, + allowNull: false, + }, + updatedAt: { + type: Sequelize.DATE, + allowNull: false, + }, + }); +} + +export async function down(queryInterface) { + await queryInterface.dropTable('caseTags'); +} diff --git a/backend/models/caseTags.js b/backend/models/caseTags.js new file mode 100644 index 0000000..e62e630 --- /dev/null +++ b/backend/models/caseTags.js @@ -0,0 +1,36 @@ +function definecaseTags(sequelize, DataTypes) { + const caseTags = sequelize.define('caseTags', { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + caseId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'cases', + key: 'id', + }, + onDelete: 'CASCADE', + }, + tagId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'tags', + key: 'id', + }, + onDelete: 'CASCADE', + }, + }); + + caseTags.associate = (models) => { + caseTags.belongsTo(models.Case, { foreignKey: 'caseId', onDelete: 'CASCADE' }); + caseTags.belongsTo(models.Tags, { foreignKey: 'tagId', onDelete: 'CASCADE' }); + }; + + return caseTags; +} + +export default definecaseTags; diff --git a/backend/models/cases.js b/backend/models/cases.js index 684bdd4..49586d2 100644 --- a/backend/models/cases.js +++ b/backend/models/cases.js @@ -55,6 +55,11 @@ function defineCase(sequelize, DataTypes) { Case.belongsToMany(models.Step, { through: 'caseSteps', }); + Case.belongsToMany(models.Tags, { + through: 'caseTags', + foreignKey: 'caseId', + otherKey: 'tagId', + }); }; return Case; diff --git a/backend/models/tags.js b/backend/models/tags.js new file mode 100644 index 0000000..51d0c42 --- /dev/null +++ b/backend/models/tags.js @@ -0,0 +1,34 @@ +function defineTag(sequelize, DataTypes) { + const Tags = sequelize.define('Tags', { + name: { + type: DataTypes.STRING, + allowNull: false, + }, + projectId: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'projects', + key: 'id', + }, + onDelete: 'CASCADE', + }, + }); + + Tags.associate = (models) => { + Tags.belongsTo(models.Project, { + foreignKey: 'projectId', + onDelete: 'CASCADE', + }); + + Tags.belongsToMany(models.Case, { + through: 'caseTags', + foreignKey: 'tagId', + otherKey: 'caseId', + }); + }; + + return Tags; +} + +export default defineTag; diff --git a/backend/routes/caseTags/delete.js b/backend/routes/caseTags/delete.js new file mode 100644 index 0000000..18caac6 --- /dev/null +++ b/backend/routes/caseTags/delete.js @@ -0,0 +1,41 @@ +import express from 'express'; +const router = express.Router(); +import { DataTypes } from 'sequelize'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; +import definecaseTags from '../../models/caseTags.js'; + +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromCaseId } = editableMiddleware(sequelize); + const CaseTag = definecaseTags(sequelize, DataTypes); + + router.delete('/:id', verifySignedIn, verifyProjectDeveloperFromCaseId, async (req, res) => { + const { id } = req.params; + + if (!id) { + return res.status(400).json({ + error: 'id is required', + }); + } + + try { + const deletedCaseTag = await CaseTag.destroy({ + where: { + id: id, + }, + }); + + if (!deletedCaseTag) { + return res.status(404).json({ error: 'Case-tag association not found' }); + } + + res.status(204).send(); + } catch (error) { + console.error('Error deleting case-tag association:', error); + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} diff --git a/backend/routes/caseTags/new.js b/backend/routes/caseTags/new.js new file mode 100644 index 0000000..19b0898 --- /dev/null +++ b/backend/routes/caseTags/new.js @@ -0,0 +1,58 @@ +import express from 'express'; +const router = express.Router(); +import { DataTypes } from 'sequelize'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; +import definecaseTags from '../../models/caseTags.js'; +import defineCase from '../../models/cases.js'; +import defineTag from '../../models/tags.js'; + +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromCaseId } = editableMiddleware(sequelize); + const CaseTag = definecaseTags(sequelize, DataTypes); + const Case = defineCase(sequelize, DataTypes); + const Tags = defineTag(sequelize, DataTypes); + + router.post('/', verifySignedIn, verifyProjectDeveloperFromCaseId, async (req, res) => { + const { caseId, tagId } = req.body; + + if (!caseId || !tagId) { + return res.status(400).json({ + error: 'caseId and tagId are required', + }); + } + + try { + const caseExists = await Case.findByPk(caseId); + if (!caseExists) { + return res.status(404).json({ error: 'Case not found' }); + } + + const tagExists = await Tags.findByPk(tagId); + if (!tagExists) { + return res.status(404).json({ error: 'Tag not found' }); + } + + const existingAssociation = await CaseTag.findOne({ + where: { caseId, tagId }, + }); + + if (existingAssociation) { + return res.status(409).json({ error: 'Tag is already associated with this case' }); + } + + const newCaseTag = await CaseTag.create({ + caseId, + tagId, + }); + + res.status(201).json(newCaseTag); + } catch (error) { + console.error('Error creating case-tag association:', error); + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} diff --git a/backend/routes/cases/index.js b/backend/routes/cases/index.js index 16fc54e..b64e51a 100644 --- a/backend/routes/cases/index.js +++ b/backend/routes/cases/index.js @@ -2,6 +2,8 @@ import express from 'express'; const router = express.Router(); import { DataTypes, Op } from 'sequelize'; import defineCase from '../../models/cases.js'; +import defineTag from '../../models/tags.js'; + import authMiddleware from '../../middleware/auth.js'; import visibilityMiddleware from '../../middleware/verifyVisible.js'; @@ -9,6 +11,10 @@ export default function (sequelize) { const { verifySignedIn } = authMiddleware(sequelize); const { verifyProjectVisibleFromFolderId } = visibilityMiddleware(sequelize); const Case = defineCase(sequelize, DataTypes); + const Tags = defineTag(sequelize, DataTypes); + + Case.belongsToMany(Tags, { through: 'caseTags', foreignKey: 'caseId', otherKey: 'tagId' }); + Tags.belongsToMany(Case, { through: 'caseTags', foreignKey: 'tagId', otherKey: 'caseId' }); router.get('/', verifySignedIn, verifyProjectVisibleFromFolderId, async (req, res) => { const { folderId, title, priority, type } = req.query; @@ -56,6 +62,13 @@ export default function (sequelize) { const cases = await Case.findAll({ where: whereClause, + include: [ + { + model: Tags, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], }); res.json(cases); } catch (error) { diff --git a/backend/routes/cases/index.test.js b/backend/routes/cases/index.test.js index ce1181f..7eb1bcc 100644 --- a/backend/routes/cases/index.test.js +++ b/backend/routes/cases/index.test.js @@ -24,11 +24,20 @@ vi.mock('../../middleware/verifyVisible.js', () => ({ // mock defineCase const mockCase = { findAll: vi.fn(), + belongsToMany: vi.fn(), }; + vi.mock('../../models/cases.js', () => ({ default: () => mockCase, })); +const mockTags = { + belongsToMany: vi.fn(), +}; +vi.mock('../../models/tags.js', () => ({ + default: () => mockTags, +})); + describe('GET /cases', () => { let app; const sequelize = new Sequelize({ @@ -54,7 +63,16 @@ describe('GET /cases', () => { mockCase.findAll.mockResolvedValue([{ id: 1 }]); const res = await request(app).get('/cases?folderId=1'); expect(res.status).toBe(200); - expect(mockCase.findAll).toHaveBeenCalledWith({ where: { folderId: '1' } }); + expect(mockCase.findAll).toHaveBeenCalledWith({ + where: { folderId: '1' }, + include: [ + { + model: mockTags, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], + }); expect(res.body).toEqual([{ id: 1 }]); }); @@ -69,6 +87,13 @@ describe('GET /cases', () => { priority: { [Op.in]: [1, 2] }, type: { [Op.in]: [3] }, }, + include: [ + { + model: mockTags, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], }); }); }); diff --git a/backend/routes/cases/indexByProjectId.js b/backend/routes/cases/indexByProjectId.js index aa57e35..4bc883c 100644 --- a/backend/routes/cases/indexByProjectId.js +++ b/backend/routes/cases/indexByProjectId.js @@ -4,6 +4,7 @@ import { DataTypes } from 'sequelize'; import defineProject from '../../models/projects.js'; import defineFolder from '../../models/folders.js'; import defineCase from '../../models/cases.js'; +import defineTag from '../../models/tags.js'; import defineRunCase from '../../models/runCases.js'; import authMiddleware from '../../middleware/auth.js'; import visibilityMiddleware from '../../middleware/verifyVisible.js'; @@ -13,11 +14,14 @@ export default function (sequelize) { const Folder = defineFolder(sequelize, DataTypes); const Case = defineCase(sequelize, DataTypes); const RunCase = defineRunCase(sequelize, DataTypes); + const Tags = defineTag(sequelize, DataTypes); Project.hasMany(Folder, { foreignKey: 'projectId' }); Folder.hasMany(Case, { foreignKey: 'folderId' }); Folder.belongsTo(Project, { foreignKey: 'projectId' }); Case.belongsTo(Folder, { foreignKey: 'folderId' }); Case.hasMany(RunCase, { foreignKey: 'caseId' }); + Case.belongsToMany(Tags, { through: 'caseTags', foreignKey: 'caseId', otherKey: 'tagId' }); + Tags.belongsToMany(Case, { through: 'caseTags', foreignKey: 'tagId', otherKey: 'caseId' }); RunCase.belongsTo(Case, { foreignKey: 'caseId' }); const { verifySignedIn } = authMiddleware(sequelize); const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); @@ -43,6 +47,11 @@ export default function (sequelize) { model: RunCase, attributes: ['id', 'runId', 'status'], }, + { + model: Tags, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, ], }); res.json(cases); diff --git a/backend/routes/cases/show.js b/backend/routes/cases/show.js index eef32f9..ac25f60 100644 --- a/backend/routes/cases/show.js +++ b/backend/routes/cases/show.js @@ -3,6 +3,7 @@ const router = express.Router(); import { DataTypes } from 'sequelize'; import defineCase from '../../models/cases.js'; import defineStep from '../../models/steps.js'; +import defineTag from '../../models/tags.js'; import defineAttachment from '../../models/attachments.js'; import authMiddleware from '../../middleware/auth.js'; import visibilityMiddleware from '../../middleware/verifyVisible.js'; @@ -10,11 +11,14 @@ import visibilityMiddleware from '../../middleware/verifyVisible.js'; export default function (sequelize) { const Case = defineCase(sequelize, DataTypes); const Step = defineStep(sequelize, DataTypes); + const Tags = defineTag(sequelize, DataTypes); const Attachment = defineAttachment(sequelize, DataTypes); Case.belongsToMany(Step, { through: 'caseSteps' }); Step.belongsToMany(Case, { through: 'caseSteps' }); Case.belongsToMany(Attachment, { through: 'caseAttachments' }); Attachment.belongsToMany(Case, { through: 'caseAttachments' }); + Case.belongsToMany(Tags, { through: 'caseTags', foreignKey: 'caseId', otherKey: 'tagId' }); + Tags.belongsToMany(Case, { through: 'caseTags', foreignKey: 'tagId', otherKey: 'caseId' }); const { verifySignedIn } = authMiddleware(sequelize); const { verifyProjectVisibleFromCaseId } = visibilityMiddleware(sequelize); @@ -35,6 +39,11 @@ export default function (sequelize) { { model: Attachment, }, + { + model: Tags, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, ], }); return res.json(testcase); diff --git a/backend/routes/tags/delete.js b/backend/routes/tags/delete.js new file mode 100644 index 0000000..0658da8 --- /dev/null +++ b/backend/routes/tags/delete.js @@ -0,0 +1,45 @@ +import express from 'express'; +const router = express.Router(); +import { DataTypes } from 'sequelize'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; + +import defineTag from '../../models/tags.js'; + +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromProjectId } = editableMiddleware(sequelize); + + const Tags = defineTag(sequelize, DataTypes); + + router.delete('/:tagId', verifySignedIn, verifyProjectDeveloperFromProjectId, async (req, res) => { + const { tagId } = req.params; + const projectId = req.query.projectId; + + if (!tagId || !projectId) { + return res.status(400).json({ + error: 'tagId and projectId are required', + }); + } + + try { + const deletedTag = await Tags.destroy({ + where: { + id: tagId, + projectId, + }, + }); + + if (!deletedTag) { + return res.status(404).json({ error: 'Tag not found' }); + } + + res.status(204).send(); + } catch (error) { + console.error('Error deleting tag:', error); + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} diff --git a/backend/routes/tags/edit.js b/backend/routes/tags/edit.js new file mode 100644 index 0000000..cccaeee --- /dev/null +++ b/backend/routes/tags/edit.js @@ -0,0 +1,63 @@ +import express from 'express'; +const router = express.Router(); +import { DataTypes, Op } from 'sequelize'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; + +import defineTag from '../../models/tags.js'; + +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromProjectId } = editableMiddleware(sequelize); + + const Tags = defineTag(sequelize, DataTypes); + + router.put('/:tagId', verifySignedIn, verifyProjectDeveloperFromProjectId, async (req, res) => { + const { tagId } = req.params; + const projectId = req.query.projectId; + + if (!tagId || !projectId) { + return res.status(400).json({ + error: 'tagId and projectId are required', + }); + } + + const { name } = req.body; + + if (!name) { + return res.status(400).json({ + error: 'name is required', + }); + } + + const trimmedName = name.trim(); + + if (trimmedName.length < 3 || trimmedName.length > 20) { + return res.status(400).json({ + error: 'name must be between 3 and 20 characters long', + }); + } + + try { + const existingTag = await Tags.findOne({ + where: { name: trimmedName, projectId, id: { [Op.ne]: tagId } }, + attributes: ['id'], + }); + + if (existingTag) { + return res.status(409).json({ error: 'Tag name must be unique' }); + } + + const [updated, [updatedTag]] = await Tags.update({ name: trimmedName }, { where: { id: tagId, projectId } }); + + if (updated === 0) return res.status(404).json({ error: 'Tag not found' }); + + res.status(200).json(updatedTag); + } catch (error) { + console.error('Error updating tag:', error); + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} diff --git a/backend/routes/tags/index.js b/backend/routes/tags/index.js new file mode 100644 index 0000000..cf986dd --- /dev/null +++ b/backend/routes/tags/index.js @@ -0,0 +1,38 @@ +import express from 'express'; +const router = express.Router(); +import { DataTypes } from 'sequelize'; +import authMiddleware from '../../middleware/auth.js'; +import defineTag from '../../models/tags.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; + +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); + + const Tags = defineTag(sequelize, DataTypes); + + router.get('/', verifySignedIn, verifyProjectVisibleFromProjectId, async (req, res) => { + const projectId = req.query.projectId; + + if (!projectId) { + return res.status(400).json({ + error: 'projectId is required', + }); + } + + try { + const tags = await Tags.findAll({ + where: { + projectId: projectId, + }, + order: [['name', 'ASC']], + }); + res.json(tags); + } catch (error) { + console.error('Error fetching tags:', error); + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} diff --git a/backend/routes/tags/new.js b/backend/routes/tags/new.js new file mode 100644 index 0000000..4de7cee --- /dev/null +++ b/backend/routes/tags/new.js @@ -0,0 +1,54 @@ +import express from 'express'; +const router = express.Router(); +import { DataTypes } from 'sequelize'; +import authMiddleware from '../../middleware/auth.js'; +import editableMiddleware from '../../middleware/verifyEditable.js'; + +import defineTag from '../../models/tags.js'; + +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectDeveloperFromProjectId } = editableMiddleware(sequelize); + + const Tags = defineTag(sequelize, DataTypes); + + router.post('/', verifySignedIn, verifyProjectDeveloperFromProjectId, async (req, res) => { + const { name } = req.body; + const projectId = req.query.projectId; + + if (!name || !projectId) { + return res.status(400).json({ + error: 'name and projectId are required', + }); + } + const trimmedName = name.trim(); + + if (trimmedName.length < 3 || trimmedName.length > 20) { + return res.status(400).json({ + error: 'name must be between 3 and 20 characters long', + }); + } + + try { + if (await Tags.findOne({ where: { name: trimmedName, projectId } })) { + return res.status(409).json({ error: 'Tag name must be unique' }); + } + + const newTag = await Tags.create({ + name: trimmedName, + projectId, + }); + res.status(201).json(newTag); + } catch (error) { + console.error('Error creating new tag:', error); + + if (error.name === 'SequelizeUniqueConstraintError') { + return res.status(409).json({ error: 'Tag name must be unique' }); + } + + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} diff --git a/backend/routes/tags/show.js b/backend/routes/tags/show.js new file mode 100644 index 0000000..cfc686f --- /dev/null +++ b/backend/routes/tags/show.js @@ -0,0 +1,43 @@ +import express from 'express'; +const router = express.Router(); +import { DataTypes } from 'sequelize'; +import authMiddleware from '../../middleware/auth.js'; +import defineTag from '../../models/tags.js'; +import visibilityMiddleware from '../../middleware/verifyVisible.js'; + +export default function (sequelize) { + const { verifySignedIn } = authMiddleware(sequelize); + const { verifyProjectVisibleFromProjectId } = visibilityMiddleware(sequelize); + + const Tags = defineTag(sequelize, DataTypes); + + router.get('/:tagId', verifySignedIn, verifyProjectVisibleFromProjectId, async (req, res) => { + const tagId = req.params.tagId; + const projectId = req.query.projectId; + + if (!tagId || !projectId) { + return res.status(400).json({ + error: 'tagId and projectId are required', + }); + } + + try { + const tag = await Tags.findOne({ + where: { + projectId: projectId, + id: tagId, + }, + }); + + if (!tag) { + return res.status(404).json({ error: 'Tag not found' }); + } + res.json(tag); + } catch (error) { + console.error('Error fetching tags:', error); + res.status(500).json({ error: 'Internal server error' }); + } + }); + + return router; +} diff --git a/backend/server.js b/backend/server.js index ea94d65..da1c355 100644 --- a/backend/server.js +++ b/backend/server.js @@ -145,6 +145,24 @@ app.use('/members', membersEditRoute(sequelize)); app.use('/members', membersDeleteRoute(sequelize)); app.use('/members', membersCheckRoute(sequelize)); +// "/tags" +import tagsNewRoute from './routes/tags/new.js'; +import tagsIndexRoute from './routes/tags/index.js'; +import tagsEditRoute from './routes/tags/edit.js'; +import tagsDeleteRoute from './routes/tags/delete.js'; +import tagsShowRoute from './routes/tags/show.js'; +app.use('/tags', tagsNewRoute(sequelize)); +app.use('/tags', tagsIndexRoute(sequelize)); +app.use('/tags', tagsShowRoute(sequelize)); +app.use('/tags', tagsDeleteRoute(sequelize)); +app.use('/tags', tagsEditRoute(sequelize)); + +// "/casetags" +import caseTagsNewRoute from './routes/casetags/new.js'; +import caseTagsDeleteRoute from './routes/casetags/delete.js'; +app.use('/casetags', caseTagsNewRoute(sequelize)); +app.use('/casetags', caseTagsDeleteRoute(sequelize)); + // "/home" import homeIndexRoute from './routes/home/index.js'; app.use('/home', homeIndexRoute(sequelize));