From 036793b4321be6727cba284c91f87f4a83441bdb Mon Sep 17 00:00:00 2001 From: sshugsc Date: Thu, 30 Jul 2026 16:29:38 -0700 Subject: [PATCH 1/4] add endpoint for therapeutic targets --- app/middleware/acl.js | 4 ++++ app/routes/index.js | 4 ++++ app/routes/therapeuticTargets.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 app/routes/therapeuticTargets.js diff --git a/app/middleware/acl.js b/app/middleware/acl.js index e0da9b607..c288ba4ec 100644 --- a/app/middleware/acl.js +++ b/app/middleware/acl.js @@ -94,6 +94,10 @@ const SPECIAL_CASES = [ PUT: [{name: 'admin'}, {name: 'manager'}, {name: 'appendix edit access'}], DELETE: [{name: 'admin'}, {name: 'manager'}, {name: 'appendix edit access'}], }, + { + path: pathToRegexp('/api/therapeutic-targets'), + GET: [{name: 'admin'}], + }, { path: pathToRegexp('/api/project'), POST: [{name: 'admin'}, {name: 'manager'}, {name: 'create project access'}], diff --git a/app/routes/index.js b/app/routes/index.js index c1176134f..95143379b 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -20,6 +20,7 @@ const variantTextRoute = require('./variantText'); const templateRoute = require('./template'); const appendixRoute = require('./appendix'); const legendRoute = require('./legend'); +const therapeuticTargetsRoute = require('./therapeuticTargets'); // Get module route files const RouterInterface = require('./routingInterface'); @@ -104,6 +105,9 @@ class Routing extends RouterInterface { // Global legend routes this.router.use('/legend', legendRoute); + + // Get therapeutic targets routes (all reports) + this.router.use('/therapeutic-targets', therapeuticTargetsRoute); return true; } diff --git a/app/routes/therapeuticTargets.js b/app/routes/therapeuticTargets.js new file mode 100644 index 000000000..7223d2b5b --- /dev/null +++ b/app/routes/therapeuticTargets.js @@ -0,0 +1,30 @@ +const HTTP_STATUS = require('http-status-codes'); +const express = require('express'); + +const db = require('../models'); +const logger = require('../log'); + +const router = express.Router({mergeParams: true}); + +// Get all therapeutic targets across all reports (admin only, enforced in ACL middleware) +router.route('/') + .get(async (req, res) => { + try { + const results = await db.models.therapeuticTarget.scope('public').findAll({ + order: [['rank', 'ASC']], + include: [ + { + model: db.models.report, + as: 'report', + attributes: ['ident'], + }, + ], + }); + return res.json(results); + } catch (error) { + logger.error(`Unable to retrieve therapeutic targets ${error}`); + return res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({error: {message: 'Unable to retrieve therapeutic targets'}}); + } + }); + +module.exports = router; From 77552e7668d71e504ca0c7653ae350805d89eff3 Mon Sep 17 00:00:00 2001 From: sshugsc Date: Thu, 30 Jul 2026 16:33:13 -0700 Subject: [PATCH 2/4] lint --- app/routes/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/index.js b/app/routes/index.js index 95143379b..2664c476c 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -105,7 +105,7 @@ class Routing extends RouterInterface { // Global legend routes this.router.use('/legend', legendRoute); - + // Get therapeutic targets routes (all reports) this.router.use('/therapeutic-targets', therapeuticTargetsRoute); From 20b6bbcf206352a55f7ffcb8fbd173b2a1898d14 Mon Sep 17 00:00:00 2001 From: sshugsc Date: Fri, 31 Jul 2026 14:54:41 -0700 Subject: [PATCH 3/4] add therapeutic target endpoint to swagger --- app/routes/swagger/swagger.json | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/app/routes/swagger/swagger.json b/app/routes/swagger/swagger.json index 5edea12f4..1480ae6d3 100644 --- a/app/routes/swagger/swagger.json +++ b/app/routes/swagger/swagger.json @@ -2187,6 +2187,41 @@ } } }, + "/therapeutic-targets": { + "get": { + "summary": "Get All Therapeutic Targets", + "description": "Returns all therapeutic targets across all reports. Admin only.", + "tags": [ + "Therapeutic Targets" + ], + "security": [ + { + "basicAuth": [] + } + ], + "responses": { + "200": { + "description": "Returns all therapeutic targets across all reports", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/therapeuticTargetAssociations" + } + } + } + } + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "description": "User does not have permission to access this endpoint (admin only)" + } + } + } + }, "/project/{project}/therapeutic-targets": { "get": { "summary": "Get All Therapeutic Targets For a Project", From 2ffe50c7b58ea1b58f141f13c2b19fa6522f14eb Mon Sep 17 00:00:00 2001 From: sshugsc Date: Wed, 5 Aug 2026 15:47:50 -0700 Subject: [PATCH 4/4] order by reportId to avoid interleave rows from different reports --- app/routes/therapeuticTargets.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/routes/therapeuticTargets.js b/app/routes/therapeuticTargets.js index 7223d2b5b..e77f0cd2a 100644 --- a/app/routes/therapeuticTargets.js +++ b/app/routes/therapeuticTargets.js @@ -11,7 +11,7 @@ router.route('/') .get(async (req, res) => { try { const results = await db.models.therapeuticTarget.scope('public').findAll({ - order: [['rank', 'ASC']], + order: [['reportId', 'ASC'], ['rank', 'ASC']], include: [ { model: db.models.report, @@ -23,7 +23,9 @@ router.route('/') return res.json(results); } catch (error) { logger.error(`Unable to retrieve therapeutic targets ${error}`); - return res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({error: {message: 'Unable to retrieve therapeutic targets'}}); + return res.status(HTTP_STATUS.INTERNAL_SERVER_ERROR).json({ + error: {message: 'Unable to retrieve therapeutic targets'}, + }); } });