Skip to content

Commit

Permalink
Merge pull request #295 from bcgsc/release/v7.19.0
Browse files Browse the repository at this point in the history
Release/v7.19.0
  • Loading branch information
Nithriel authored Jan 31, 2024
2 parents 1a3f74b + bef4c7d commit b96c08b
Show file tree
Hide file tree
Showing 31 changed files with 5,344 additions and 96 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/npm-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [18, 20]
node: [16, 18, 20]
# TODO: readd node versions: node: [14, 16, 18, 20]
name: node-${{ matrix.node }}
services:
Expand Down Expand Up @@ -40,8 +40,8 @@ jobs:
# Maps port 6379 on service container to the host
- 6379:6379
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: bash ./demo/restore_iprdb_dump.sh
Expand Down Expand Up @@ -73,11 +73,11 @@ jobs:
IPR_TESTING_PASSWORD: iprdemo
IPR_TESTING_USERNAME: iprdemo
IPR_REDIS_HOST: localhost
- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v3
with:
yml: codecov.yml
token: ${{ secrets.CODECOV_TOKEN }}
fail_if_ci_error: true
fail_ci_if_error: true
if: matrix.node == 16
- uses: EnricoMi/[email protected]
with:
Expand All @@ -88,7 +88,7 @@ jobs:
runs-on: ubuntu-latest
name: docker build
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: build the API docker container
run: |
docker build --file Dockerfile --tag pori/ipr-api .
5 changes: 4 additions & 1 deletion app/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ const DEFAULTS = {
: 'iprdevdb.bcgsc.ca',
port: 5432,
name: DEFAULT_DB_NAME,
maxConn: 30,
poolMax: 20,
poolMin: 0,
poolAcquire: 60000,
poolIdle: 5000,
},
redis: {
host: ENV === 'production'
Expand Down
9 changes: 7 additions & 2 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ const fetchRoutes = (initialRouter) => {

const listen = async (port = null) => {
const app = express(); // define app using express
logger.info(`starting http server on port ${port || conf.get('web:port')}`);
const server = http.createServer(app).listen(port || conf.get('web:port'));
port = process.env.PORT || conf.get('web:port');

if (process.env.NODE_ENV === 'test') {
port = 0;
}
logger.info(`starting http server on port ${port}`);
const server = http.createServer(app).listen(port);
// TODO: https://www.bcgsc.ca/jira/browse/DEVSU-985 reduce when images are a separate upload
app.use(express.json({limit: '100mb'}));
app.use(boolParser());
Expand Down
21 changes: 17 additions & 4 deletions app/libs/helperFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ const hasAccessToNonProdReports = (user) => {
return user.groups.some((group) => {
return group.name.toLowerCase() === 'admin'
|| group.name.toLowerCase() === 'manager'
|| group.name.toLowerCase() === 'non-production access'
|| group.name.toLowerCase() === 'demo';
|| group.name.toLowerCase() === 'non-production access';
});
};

Expand All @@ -97,8 +96,21 @@ const hasAccessToUnreviewedReports = (user) => {
return user.groups.some((group) => {
return group.name.toLowerCase() === 'admin'
|| group.name.toLowerCase() === 'manager'
|| group.name.toLowerCase() === 'unreviewed access'
|| group.name.toLowerCase() === 'demo';
|| group.name.toLowerCase() === 'unreviewed access';
});
};

/**
* Checks if user has access to germline repots
*
* @param {object} user - Sequelize user model
* @returns {boolean} - Returns a boolean indicating if the user has access to germline reports
*/
const hasAccessToGermlineReports = (user) => {
return user.groups.some((group) => {
return group.name.toLowerCase() === 'admin'
|| group.name.toLowerCase() === 'manager'
|| group.name.toLowerCase() === 'germline access';
});
};

Expand Down Expand Up @@ -164,6 +176,7 @@ module.exports = {
hasAccess,
hasAccessToNonProdReports,
hasAccessToUnreviewedReports,
hasAccessToGermlineReports,
hasMasterAccess,
projectAccess,
isIntersectionBy,
Expand Down
29 changes: 28 additions & 1 deletion app/middleware/acl.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const {FORBIDDEN} = require('http-status-codes');
const {pathToRegexp} = require('path-to-regexp');
const {
isAdmin, isIntersectionBy, hasAccess, hasMasterAccess, projectAccess,
isAdmin, isIntersectionBy, hasAccess, hasMasterAccess, projectAccess, hasAccessToGermlineReports,
} = require('../libs/helperFunctions');
const {MASTER_REPORT_ACCESS, UPDATE_METHODS} = require('../constants');
const logger = require('../log');
Expand Down Expand Up @@ -62,6 +62,14 @@ module.exports = async (req, res, next) => {
await req.user.update({lastLoginAt: new Date()});
}

try {
if (req.query.clinician_view && hasMasterAccess(req.user)) {
req.user.groups = [{name: 'Clinician'}];
}
} catch {
logger.error('Clinician View error: Using users normal group');
}

// Check if user is an admin
if (isAdmin(req.user)) {
return next();
Expand All @@ -70,6 +78,13 @@ module.exports = async (req, res, next) => {
// Get route
const [route] = req.originalUrl.split('?');

if (!hasAccessToGermlineReports(req.user) && route.includes('/germline-small-mutation-reports')) {
logger.error('User does not have germline access');
return res.status(
FORBIDDEN,
).json({error: {message: 'User does not have access to Germline reports'}});
}

if (req.report) {
// check if user is bound to report depending on report type
let boundUser;
Expand All @@ -93,6 +108,18 @@ module.exports = async (req, res, next) => {
error: {message: 'You do not have the correct permissions to access this'},
});
}

// If user is trying to make an update and the report is completed
// and they dont have update permissions, throw an error
if (UPDATE_METHODS.includes(req.method)
&& req.report.state === 'completed'
&& !(hasAccess(req.user, MASTER_REPORT_ACCESS))
) {
logger.error(`User: ${req.user.username} is trying to make a ${req.method} request to ${req.originalUrl} - Report is marked as complete`);
return res.status(FORBIDDEN).json({
error: {message: 'Report is marked as completed and update has been restricted'},
});
}
} else {
// See if route exists in special cases
const spCase = SPECIAL_CASES.find((value) => {
Expand Down
2 changes: 1 addition & 1 deletion app/middleware/germlineSmallMutation/reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ module.exports = async (req, res, next, ident) => {
return res.status(HTTP_STATUS.FORBIDDEN).json({error: {message: 'User does not have access to Non-Production reports'}});
}

if (!hasAccessToUnreviewedReports(req.user) && (result.state !== 'reviewed' && result.state !== 'archived')) {
if (!hasAccessToUnreviewedReports(req.user) && (result.state !== 'reviewed' && result.state !== 'completed')) {
logger.error(`User does not have unreviewed access to ${ident}`);
return res.status(HTTP_STATUS.FORBIDDEN).json({error: {message: 'User does not have access to Unreviewed reports'}});
}
Expand Down
2 changes: 1 addition & 1 deletion app/middleware/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ module.exports = async (req, res, next, ident) => {
return res.status(HTTP_STATUS.NOT_FOUND).json({error: {message: 'Unable to find the requested report'}});
}

if (!hasAccessToUnreviewedReports(req.user) && (result.state !== 'reviewed' && result.state !== 'archived')) {
if (!hasAccessToUnreviewedReports(req.user) && (result.state !== 'reviewed' && result.state !== 'completed')) {
logger.error(`User does not have unreviewed access to ${ident}`);
return res.status(HTTP_STATUS.FORBIDDEN).json({error: {message: 'User does not have access to Unreviewed reports'}});
}
Expand Down
14 changes: 14 additions & 0 deletions app/models/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,20 @@ const DEFAULT_REPORT_OPTIONS = {
transaction: options.transaction,
userId: options.userId,
}),
// If report is state "reviewed" change to "ready"
(!changed || includesAll(updateExclude, changed)) ? Promise.resolve(true)
: instance.sequelize.models.report.update({
state: 'ready',
}, {
where: {
id: (modelName === 'report') ? instance.id : instance.reportId,
state: 'reviewed',
},
individualHooks: true,
paranoid: true,
transaction: options.transaction,
userId: options.userId,
}),
]);
},
afterCreate: async (instance, options = {}) => {
Expand Down
2 changes: 1 addition & 1 deletion app/models/germlineSmallMutation/reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ module.exports = (sequelize, Sq) => {
defaultValue: false,
},
state: {
type: Sq.ENUM('ready', 'active', 'uploaded', 'signedoff', 'archived', 'reviewed', 'nonproduction', null),
type: Sq.ENUM('ready', 'active', 'uploaded', 'signedoff', 'completed', 'reviewed', 'nonproduction', null),
defaultValue: 'uploaded',
},
}, {
Expand Down
5 changes: 4 additions & 1 deletion app/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ const sequelize = new Sq(
schema: dbSettings.schema,
logging: null,
pool: {
max: dbSettings.maxConn,
max: dbSettings.poolMax,
min: dbSettings.poolMin,
acquire: dbSettings.poolAcquire,
idle: dbSettings.poolIdle,
},
},
);
Expand Down
12 changes: 6 additions & 6 deletions app/models/reports/genes.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ module.exports = (sequelize, Sq) => {
type: Sq.BOOLEAN,
defaultValue: false,
},
cancerGene: {
name: 'cancerGene',
field: 'cancer_gene',
cancerGeneListMatch: {
name: 'cancerGeneListMatch',
field: 'cancer_gene_list_match',
type: Sq.BOOLEAN,
},
cancerRelated: {
name: 'cancerRelated',
field: 'cancer_related',
kbStatementRelated: {
name: 'kbStatementRelated',
field: 'kb_statement_related',
type: Sq.BOOLEAN,
defaultValue: false,
},
Expand Down
5 changes: 4 additions & 1 deletion app/models/reports/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ module.exports = (sequelize, Sq) => {
config: {
type: Sq.TEXT,
},
appendix: {
type: Sq.TEXT,
},
reportVersion: {
type: Sq.STRING,
defaultValue: null,
Expand All @@ -97,7 +100,7 @@ module.exports = (sequelize, Sq) => {
defaultValue: null,
},
state: {
type: Sq.ENUM('ready', 'active', 'uploaded', 'signedoff', 'archived', 'reviewed', 'nonproduction'),
type: Sq.ENUM('ready', 'active', 'uploaded', 'signedoff', 'completed', 'reviewed', 'nonproduction'),
defaultValue: 'ready',
allowNull: false,
},
Expand Down
2 changes: 1 addition & 1 deletion app/routes/germlineSmallMutation/export.download.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ router.get('/batch/download', async (req, res) => {
}),
},
state: {
[Op.in]: ['reviewed', 'active', 'archived'],
[Op.in]: ['reviewed', 'active', 'completed'],
},
},
required: true,
Expand Down
2 changes: 1 addition & 1 deletion app/routes/germlineSmallMutation/reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ router.route('/')
if (!hasAccessToUnreviewedReports(req.user)) {
opts.where = {
...opts.where,
state: ['reviewed', 'archived'],
state: ['reviewed', 'completed'],
};
}

Expand Down
21 changes: 0 additions & 21 deletions app/routes/report/kbMatches.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ const router = express.Router({mergeParams: true});

const db = require('../../models');
const logger = require('../../log');
const cache = require('../../cache');

const {generateKey} = require('../../libs/cacheFunctions');
const {KB_PIVOT_MAPPING} = require('../../constants');

// Middleware for kbMatches
Expand Down Expand Up @@ -61,20 +58,6 @@ router.route('/')
.get(async (req, res) => {
const {query: {matchedCancer, approvedTherapy, category, iprEvidenceLevel}} = req;

// Check cache
const key = generateKey(`/reports/${req.report.ident}/kb-matches`, req.query);

try {
const cacheResults = await cache.get(key);

if (cacheResults) {
res.type('json');
return res.send(cacheResults);
}
} catch (error) {
logger.error(`Error while checking cache for kb matches ${error}`);
}

try {
const results = await db.models.kbMatches.scope('public').findAll({
where: {
Expand All @@ -87,10 +70,6 @@ router.route('/')
order: [['variantType', 'ASC'], ['variantId', 'ASC']],
});

if (key) {
cache.set(key, JSON.stringify(results), 'EX', 14400);
}

return res.json(results);
} catch (error) {
logger.error(`Unable to get kb matches ${error}`);
Expand Down
2 changes: 1 addition & 1 deletion app/routes/report/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ router.route('/')
if (!hasAccessToUnreviewedReports(req.user)) {
opts.where = {
...opts.where,
state: ['reviewed', 'archived'],
state: ['reviewed', 'completed'],
};
}

Expand Down
2 changes: 1 addition & 1 deletion app/routes/report/variants.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const unknownSignificanceIncludes = ['mut'];
const signatureVariant = ['tmb', 'msi'];

const unknownSignificanceGeneFilter = {
[Op.or]: [{oncogene: true}, {tumourSuppressor: true}, {cancerGene: true}],
[Op.or]: [{oncogene: true}, {tumourSuppressor: true}, {cancerGeneListMatch: true}],
};

const getRapidReportVariants = async (tableName, variantType, reportId, rapidTable) => {
Expand Down
Loading

0 comments on commit b96c08b

Please sign in to comment.