Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,9 @@ node_modules
.env.production.local
.vscode/
cms/env
cms/variant-migrations/config.js

npm-debug.log*
yarn-debug.log*
yarn-error.log*

docker/mongo
docker/elasticsearch/data

docker/
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ To run the required migrations:

```
cd cms/variant-migrations
../../node_modules/.bin/migrate-mongo up -f migrate-mongo-config.js
../../node_modules/.bin/migrate-mongo up -f config.js
```

### Quickstart
Expand All @@ -59,7 +59,7 @@ yarn

```
cd cms/variant-migrations
../../node_modules/.bin/migrate-mongo up -f migrate-mongo-config.js
../../node_modules/.bin/migrate-mongo up -f config.js
```

4. Initialize ElasticSearch:
Expand Down
6 changes: 4 additions & 2 deletions api/.env.schema
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
ES_URL=
ES_USER=
ES_PASS=
ES_HOST=
PROJECT_ID=
PORT=
ES_UPDATE_INDEX=
ENABLE_ADMIN=
MONGODB_URI=mongodb://user@pass:localhost:27017/hcmi

LOG_LEVEL=
NODE_ENV=
SEARCH_CLIENT_TYPE=
2 changes: 1 addition & 1 deletion api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ require('@babel/register')({
],
});

require('./src/index');
require('./src/index.ts');
11 changes: 7 additions & 4 deletions api/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@hcmi-portal/api",
"version": "0.0.1",
"description": "``` npm i @overture-stack/arranger-server ```",
"description": "``` npm i @overture-stack/arranger-graphql-router ```",
"private": true,
"main": "index.js",
"scripts": {
Expand All @@ -11,8 +11,11 @@
"author": "",
"license": "ISC",
"dependencies": {
"@elastic/elasticsearch": "~7.17.0",
"@overture-stack/arranger-server": "^3.0.0",
"@aws-sdk/credential-provider-node": "^3.972.66",
"@opensearch-project/opensearch": "^3.6.0",
"@overture-stack/arranger-graphql-router": "1.0.0-rc.4",
"@overture-stack/arranger-types": "1.0.0-rc.3",
"@overture-stack/sqon-builder": "^1.1.0",
"JSONStream": "^1.3.5",
"babel-polyfill": "^6.26.0",
"dotenv": "^5.0.1",
Expand All @@ -21,7 +24,7 @@
"helmet": "^3.15.0",
"jszip": "^3.7.0",
"map-stream": "^0.0.7",
"mongoose": "7.8.7",
"mongoose": "^8.23.0",
"pino": "^9.13.1",
"pino-http": "^10.5.0",
"sharp": "0.34.4"
Expand Down
13 changes: 9 additions & 4 deletions api/src/dataExport.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import express from 'express';
import bodyParser from 'body-parser';
import expressSanitizer from 'express-sanitizer';
import { dataStream } from '@overture-stack/arranger-server/dist/download';
import getAllData from '@overture-stack/arranger-server/dist/utils/getAllData';
import { dataStream } from '@overture-stack/arranger-graphql-router/download';
import { getAllData } from '@overture-stack/arranger-graphql-router/utils';
import JSZip from 'jszip';
import map from 'map-stream';
import through2 from 'through2';
import getLogger from './logger';

import getLogger from './logger.js';

const logger = getLogger('dataExport');

Expand Down Expand Up @@ -48,7 +49,11 @@ dataExportRouter.post('/models', async (req, res) => {
* We can get a stream from arranger using the getAllData method and then can store the variant data for each model
* for future processing.
*/
const allDataStream = await getAllData({ sqon, maxRows: 100, mock: {}, ctx: req.context });
const allDataStream = await getAllData({
sqon,
maxRows: 100,
ctx: req.context,
});
const collectVariantData = through2.obj(function ({ hits }, enc, callback) {
hits.forEach((model) => {
if (model?.genomic_variants && model?.genomic_variants.length > 0) {
Expand Down
11 changes: 4 additions & 7 deletions api/src/health.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import express from 'express';
import elasticsearch from '@elastic/elasticsearch';

import _ from 'lodash';

const startTime = Date.now();

const client = new elasticsearch.Client({
node: process.env.ES_URL,
});
import pm2 from './pm2.ts';
import getClient from './services/searchClient.ts';

const startTime = Date.now();
const healthRouter = express.Router();

healthRouter.get('/', async (req, res) => {
Expand All @@ -18,6 +14,7 @@ healthRouter.get('/', async (req, res) => {

healthRouter.get('/es', async (req, res) => {
try {
const client = getClient(pm2);
const status = await client.ping();
if (_.get(status, 'statusCode') === 200) {
const response = _.omit(status, 'meta');
Expand Down
53 changes: 0 additions & 53 deletions api/src/index.js

This file was deleted.

85 changes: 85 additions & 0 deletions api/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import 'babel-polyfill';
import bodyParser from 'body-parser';
import cors from 'cors';
import express from 'express';
import expressSanitizer from 'express-sanitizer';
import helmet from 'helmet';
import { Server } from 'http';
import ArrangerRouter, { type ArrangerBaseContext } from '@overture-stack/arranger-graphql-router';
import type { ConfigsObject, DisplayType, ExtendedConfigs, FacetsConfigs, MatchBoxConfigs, TableConfigs } from '@overture-stack/arranger-types/configs';
import * as path from 'path';

import baseConfig from '../../elasticsearch/arranger_metadata/base.json' with { type: 'json' };
import extendedConfigFile from '../../elasticsearch/arranger_metadata/extended.json' with { type: 'json' };
import facetsConfigFile from '../../elasticsearch/arranger_metadata/facets.json' with { type: 'json' };
import matchboxConfigFile from '../../elasticsearch/arranger_metadata/matchbox.json' with { type: 'json' };
import tableConfigFile from '../../elasticsearch/arranger_metadata/table.json' with { type: 'json' };

import dataExportRouter from './dataExport.js';
import getClient from './services/searchClient.ts';
import getLogger from './logger.js';
import healthRouter from './health.js';
import lastUpdatedRouter from './lastUpdated.js';
import pm2 from './pm2.ts';
import searchRouter from './search.js';

const logger = getLogger('root');
const port = process.env.PORT || 5050;
const app = express();
const http = new Server(app);

app.use(helmet());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSanitizer()); // each route is responsible for sanitization
app.use(cors());

// Swagger
app.use('/docs', (req, res) => {
res.sendFile(path.join(__dirname, '../redoc.html'));
});
app.use('/swagger', (req, res) => {
res.sendFile(path.join(__dirname, '../swagger.json'));
});

// Arranger Configs
const displayTypeValues: DisplayType[] = ['all' , 'bits' , 'boolean' , 'bytes' , 'date' , 'list' , 'nested' , 'number'];

const extendedConfigs: ExtendedConfigs[] = extendedConfigFile['extended'].map(configRecord => {
const {type: stringType} = configRecord;
const displayType = displayTypeValues.find(type => stringType === type) || 'all';
return {...configRecord, type: displayType};
});
const facetConfigs: FacetsConfigs = facetsConfigFile['facets'];
const matchboxConfigs: MatchBoxConfigs[] = matchboxConfigFile['matchbox'];
const tableConfigs: TableConfigs = tableConfigFile['table'];

const disableGraphQLIntrospection = process.env.DISABLE_GRAPHQL_INTROSPECTION === 'true' || pm2.DISABLE_GRAPHQL_INTROSPECTION || false;
const esHost = process.env.ES_HOST || pm2.ES_URL || 'http://localhost:9200';

const configs: Partial<ConfigsObject<ArrangerBaseContext>> = {
...baseConfig,
disableGraphQLIntrospection,
esHost,
esIndex: 'hcmi',
extended: extendedConfigs,
enableDebug: true,
facets: facetConfigs,
matchbox: matchboxConfigs,
maxDepth: 10,
table: tableConfigs
};

const esClient = getClient(pm2);

ArrangerRouter({ configs, esClient }).then((router) => {
app.use(router);
app.use('/last-updated', lastUpdatedRouter);
app.use('/health', healthRouter);
app.use('/search', searchRouter);
app.use('/export', dataExportRouter);

http.listen(port, async () => {
logger.info({ port }, 'API Started!');
});
});
14 changes: 10 additions & 4 deletions api/src/lastUpdated.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
// @ts-check

import express from 'express';
import esClient from './services/elasticsearch';

import pm2 from './pm2.ts';
import getClient from './services/searchClient.ts';
import getLogger from './logger.js';

const lastUpdatedRouter = express.Router();
const logger = getLogger('lastUpdated Router');

lastUpdatedRouter.get('/', async (req, res) => {
const searchClient = getClient(pm2);
try {
const response = await esClient.search({
index: process.env.ES_UPDATE_INDEX,
const response = await searchClient?.search({
index: process.env.ES_UPDATE_INDEX || pm2.ES_UPDATE_INDEX || 'hcmi-update',
body: {
query: {
match_all: {},
Expand All @@ -23,8 +28,9 @@ lastUpdatedRouter.get('/', async (req, res) => {
],
},
});
return res.json(response.body.hits.hits[0]._source);
return res.json(response?.body?.hits?.hits[0]?._source);
} catch (error) {
logger.error(`Error retrieving last updated date from Arranger: ${error}`);
return res.status(500).json({
error: error,
});
Expand Down
21 changes: 21 additions & 0 deletions api/src/pm2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import pm2Config from './../pm2.config.js';

// PM2 Env Setup
type pm2EnvValues = 'dev' | 'prd' | 'staging';

const pm2EnvValues = ['dev', 'prd', 'staging'];
const pm2Env: pm2EnvValues =
process.env.ENV && pm2EnvValues.includes(process.env.ENV)
? (process.env.ENV as pm2EnvValues)
: 'dev';

const pm2ConfigGeneric =
(pm2Config && pm2Config.apps && pm2Config.apps[0] && pm2Config.apps[0].env) || {};
const pm2ConfigForEnv =
(pm2Config && pm2Config.apps && pm2Config.apps[0] && pm2Config.apps[0][`env_${pm2Env}`]) || {};

const pm2 = { ...pm2ConfigGeneric, ...pm2ConfigForEnv };

export type PM2Config = typeof pm2;

export default pm2;
16 changes: 10 additions & 6 deletions api/src/search.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import express from 'express';
import esClient from './services/elasticsearch';
import { get } from 'lodash';
import _ from 'lodash';

import pm2 from './pm2.ts';
import getClient from './services/searchClient.ts';

const GENES_INDEX = 'genes';
const VARIANTS_INDEX = 'genomic_variants';
Expand Down Expand Up @@ -28,11 +30,12 @@ geneSearchRouter.get('/gene', async (req, res) => {
},
};

const response = await esClient.search({
const searchClient = getClient(pm2);
const response = await searchClient.search({
index: GENES_INDEX,
body: { query },
});
const genes = get(response, 'body.hits.hits', []).map((i) => i._source);
const genes = _.get(response, 'body.hits.hits', []).map((i) => i._source);
res.status(200).json({ genes });
} catch (err) {
console.log('Failure performing gene search:', err);
Expand All @@ -57,11 +60,12 @@ geneSearchRouter.get('/variant', async (req, res) => {
},
};

const response = await esClient.search({
const searchClient = getClient(pm2);
const response = await searchClient.search({
index: VARIANTS_INDEX,
body: { query },
});
const genes = get(response, 'body.hits.hits', []).map((i) => i._source);
const genes = _.get(response, 'body.hits.hits', []).map((i) => i._source);
res.status(200).json({ genes });
} catch (err) {
console.log('Failure performing genomic variants search:', err);
Expand Down
7 changes: 0 additions & 7 deletions api/src/services/elasticsearch.js

This file was deleted.

Loading