Skip to content

Commit

Permalink
Merge pull request #96 from Ferlab-Ste-Justine/feat/clin-3507
Browse files Browse the repository at this point in the history
feat: CLIN-3507 manage add and update note and list variants with notes
  • Loading branch information
meek0 authored Nov 19, 2024
2 parents 8d23e0c + 9c793c0 commit 81dd514
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/scan.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: build
on:
pull_request:

jobs:
build:
name: Build
Expand Down
2 changes: 2 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# node-pkg, not fixed as of 2024-11-19
CVE-2024-21538
72 changes: 56 additions & 16 deletions src/db/dal/variant.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,55 @@
import { Op } from 'sequelize';

import VariantModel from '../models/Variant';

export const addNewEntry = async function (uniqueId: string, organizationId: string, authorId: string, properties: any) {
export const addNewEntry = async function (
uniqueId: string,
organizationId: string,
authorId: string,
properties: any,
) {
const variantFound = await VariantModel.findOne({
order: [['timestamp', 'DESC']],
where: {
unique_id: {
[Op.eq]: uniqueId,
},
author_id: {
[Op.eq]: authorId,
},
},
});

const combinedProperties = { ...(variantFound?.properties || {}), ...properties };

return await VariantModel.create({
unique_id: uniqueId,
organization_id: organizationId,
author_id: authorId,
properties,
timestamp: new Date()
properties: combinedProperties,
timestamp: new Date(),
});
}
};

export const getEntriesByUniqueIdsAndOrganizations = async function (uniqueIds: string[], organizationIds: string[]) {
return await VariantModel.findAll({
order: [['timestamp', 'DESC']],
where: {
unique_id: {
[Op.in]: uniqueIds
[Op.in]: uniqueIds,
},
organization_id: {
[Op.in]: organizationIds
}
}
});
}

export const getEntriesByPropertiesFlags = async function (flags: string[], organizationIds: string[], uniqueIdParam: string) {
const flagsWhere = flags.map(f => {
return `properties @> '{"flags": ["${f}"]}'`;
[Op.in]: organizationIds,
},
},
});
};

const getEntriesByProperties = async function (
whereClause: string,
organizationIds: string[],
uniqueIdParam: string
) {
const uniqueIdWhere = uniqueIdParam.length > 0 ? ` AND unique_id LIKE '%${uniqueIdParam}%'` : '';

const result = await VariantModel.sequelize.query(`
Expand All @@ -43,7 +63,27 @@ export const getEntriesByPropertiesFlags = async function (flags: string[], orga
FROM variants
WHERE organization_id IN ('${organizationIds.join("', '")}') ${uniqueIdWhere}
) s
WHERE rnk = 1 AND (${flagsWhere.join(' OR ')});`);
WHERE rnk = 1 AND (${whereClause});`);

return result;
}
}

export const getEntriesByPropertiesFlags = async function (
flags: string[],
organizationIds: string[],
uniqueIdParam: string
) {
const flagsWhere = flags.map((f) => `properties @> '{"flags": ["${f}"]}'`);

return await getEntriesByProperties(flagsWhere.join(' OR '), organizationIds, uniqueIdParam);
};

export const getEntriesByPropertiesNote = async function (
hasNote: boolean,
organizationIds: string[],
uniqueIdParam: string
) {
const notesWhere = `properties ->> 'note' IS ${hasNote ? 'NOT NULL' : 'NULL'}`;

return await getEntriesByProperties(notesWhere, organizationIds, uniqueIdParam);
}
15 changes: 8 additions & 7 deletions src/db/models/Variant.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CreationOptional, DataTypes, Model, Optional } from 'sequelize';
import { CreationOptional, DataTypes, Model, Optional } from 'sequelize';

import sequelizeConnection from '../config';

interface IVariantAttributes {
Expand All @@ -7,7 +8,7 @@ interface IVariantAttributes {
author_id: string;
organization_id: string;
timestamp: Date;
properties: Object;
properties: object;
}

type VariantCreationAttributes = Optional<IVariantAttributes, 'id'>;
Expand Down Expand Up @@ -50,19 +51,19 @@ VariantModel.init(
defaultValue: new Date(),
validate: {
isDate: true,
}
},
},
properties: {
type: DataTypes.JSONB,
allowNull: false,
defaultValue: {},
}
},
},
{
sequelize: sequelizeConnection,
tableName: 'variants',
timestamps: false
}
timestamps: false,
},
);

export default VariantModel;
export default VariantModel;
50 changes: 35 additions & 15 deletions src/routes/variant.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { Request, Router } from 'express';
import { StatusCodes } from 'http-status-codes';

import Realm from '../config/realm';
import { keycloakRealm } from '../config/env';
import { addNewEntry, getEntriesByUniqueIdsAndOrganizations, getEntriesByPropertiesFlags } from '../db/dal/variant';
import Realm from '../config/realm';
import { addNewEntry, getEntriesByPropertiesFlags, getEntriesByPropertiesNote, getEntriesByUniqueIdsAndOrganizations } from '../db/dal/variant';

const CLIN_GENETICIAN_ROLE = 'clin_genetician';

const EMPTY_PROPERTIES = { };
const EMPTY_PROPERTIES = {};

interface UserInfo {
authorId: string,
userRoles: string[],
userOrganizations: string[]
authorId: string;
userRoles: string[];
userOrganizations: string[];
}

function getUserInfo(request: Request): UserInfo {
Expand All @@ -23,7 +23,10 @@ function getUserInfo(request: Request): UserInfo {
}

function validateCreate(userInfo: UserInfo, organization_id: string) {
return userInfo.userRoles.indexOf(CLIN_GENETICIAN_ROLE) > -1 && userInfo.userOrganizations.indexOf(organization_id) > -1;
return (
userInfo.userRoles.indexOf(CLIN_GENETICIAN_ROLE) > -1 &&
userInfo.userOrganizations.indexOf(organization_id) > -1
);
}

function validateGet(userInfo: UserInfo) {
Expand All @@ -42,7 +45,12 @@ variantRouter.post('/:unique_id/:organization_id', async (req, res, next) => {
const canCreate = validateCreate(userInfo, req?.params?.organization_id);

if (canCreate) {
const dbResponse = await addNewEntry(req?.params?.unique_id, req?.params?.organization_id, userInfo.authorId, req?.body || EMPTY_PROPERTIES);
const dbResponse = await addNewEntry(
req?.params?.unique_id,
req?.params?.organization_id,
userInfo.authorId,
req?.body || EMPTY_PROPERTIES,
);
return res.status(StatusCodes.CREATED).send(dbResponse);
} else {
return res.sendStatus(StatusCodes.FORBIDDEN);
Expand All @@ -62,7 +70,7 @@ variantRouter.get('/', async (req, res, next) => {
const canGet = validateGet(userInfo);

let dbResponse = [];
let uniqueIds = [];
const uniqueIds = [];

if (Array.isArray(req.query?.unique_id)) {
uniqueIds.push(...req.query.unique_id);
Expand All @@ -78,7 +86,6 @@ variantRouter.get('/', async (req, res, next) => {
} else {
return res.sendStatus(StatusCodes.BAD_REQUEST);
}

} catch (e) {
next(e);
}
Expand All @@ -94,7 +101,8 @@ variantRouter.get('/filter', async (req, res, next) => {
const canGet = validateGet(userInfo);

let dbResponse = [];
let flags = [];
const flags = [];
let hasNote = null;

const flagsParam = req.query?.flag;
if (Array.isArray(flagsParam)) {
Expand All @@ -103,11 +111,23 @@ variantRouter.get('/filter', async (req, res, next) => {
flags.push(flagsParam);
}

let uniqueIdFilterParam = (req.query?.unique_id || '').toString().trim();
const noteParams = req.query?.note;
if (typeof noteParams === 'string') {
if (noteParams === 'true') {
hasNote = true;
} else if (noteParams === 'false') {
hasNote = false;
}
}

const uniqueIdFilterParam = (req.query?.unique_id || '').toString().trim();

if (canGet && flags.length > 0) {
if (canGet && hasNote !== null) {
dbResponse = await getEntriesByPropertiesNote(hasNote, userInfo.userOrganizations, uniqueIdFilterParam);
return res.status(StatusCodes.OK).send(dbResponse[0].map((r) => r.unique_id));
} else if (canGet && flags.length > 0) {
dbResponse = await getEntriesByPropertiesFlags(flags, userInfo.userOrganizations, uniqueIdFilterParam);
return res.status(StatusCodes.OK).send(dbResponse[0].map(r => r.unique_id));
return res.status(StatusCodes.OK).send(dbResponse[0].map((r) => r.unique_id));
} else if (!canGet) {
return res.sendStatus(StatusCodes.FORBIDDEN);
} else {
Expand All @@ -118,4 +138,4 @@ variantRouter.get('/filter', async (req, res, next) => {
}
});

export default variantRouter;
export default variantRouter;

0 comments on commit 81dd514

Please sign in to comment.