Skip to content

Commit

Permalink
Merge pull request #47 from tidepool-org/pdf_generation_jhb
Browse files Browse the repository at this point in the history
PDF Generation
  • Loading branch information
krystophv authored Jul 11, 2023
2 parents 2e8e811 + 4c368a2 commit cb449f2
Show file tree
Hide file tree
Showing 18 changed files with 36,504 additions and 396 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"ignorePatterns": ["pdfkit.js"],
"extends": "airbnb",
"parser": "babel-eslint",
"plugins": ["lodash"],
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
16.20.1
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ sudo: false
language: node_js

node_js:
- 12.14.0
- 16.20.1
- node

cache: yarn
Expand All @@ -29,6 +29,7 @@ services:
script:
- yarn run lint
- ./artifact.sh
- yarn test

matrix:
allow_failures:
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
### Stage 0 - Base image
FROM node:12.14.0-alpine as base
FROM node:16.20.1-alpine as base
WORKDIR /app
RUN apk --no-cache update && \
apk --no-cache upgrade && \
apk add --no-cache --virtual .build-dependencies python make g++ && \
apk add --no-cache --virtual .build-dependencies python3 make g++ && \
mkdir -p node_modules && chown -R node:node .


Expand Down
27 changes: 12 additions & 15 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
/* eslint no-restricted-syntax: [0, "ForInStatement"] */

import _ from 'lodash';
import fs from 'fs';
import http from 'http';
import https from 'https';
import express from 'express';
import bodyParser from 'body-parser';

import userDataHandler from './lib/userDataHandler';
import userReportHandler from './lib/userReportHandler';
import { exportTimeout, register, logMaker } from './lib/utils';
const _ = require('lodash');
const fs = require('fs');
const http = require('http');
const https = require('https');
const express = require('express');
const bodyParser = require('body-parser');
const { createTerminus } = require('@godaddy/terminus');
const { exportTimeout, register, logMaker } = require('./lib/utils');
const handlers = require('./lib/handlers');

export const log = logMaker('app.js', {
level: process.env.DEBUG_LEVEL || 'info',
});

const { createTerminus } = require('@godaddy/terminus');

function maybeReplaceWithContentsOfFile(obj, field) {
const potentialFile = obj[field];
if (potentialFile != null && fs.existsSync(potentialFile)) {
Expand Down Expand Up @@ -54,9 +51,9 @@ app.use(
}),
);

app.get('/export/:userid', userDataHandler());

app.post('/export/report/:userid', userReportHandler());
app.get('/export/:userid', handlers.getUserData());
app.get('/export/report/:userid', handlers.getUserReport());
app.post('/export/report/:userid', handlers.postUserReport());

function beforeShutdown() {
return new Promise((resolve) => {
Expand Down
75 changes: 75 additions & 0 deletions lib/handlers/getUserReportsHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const fs = require('fs');
const {
getSessionHeader,
createCounter,
exportTimeout,
logMaker,
} = require('../utils');

const { generateReport } = require('../reportUtils');

const reportStatusCount = createCounter(
'tidepool_get_report_status_count',
'The number of errors for each status code.',
['status_code', 'report_params'],
);

const log = logMaker('getUserReportsHandler.js', {
level: process.env.DEBUG_LEVEL || 'info',
});

module.exports = function getUserReportsHandler() {
return async (req, res) => {
// Set the timeout for the request. Make it 10 seconds longer than
// our configured timeout to give the service time to cancel the API data
// request, and close the outgoing data stream cleanly.
req.setTimeout(exportTimeout + 10000);

try {
const userId = req.params.userid;
const {
dob,
fullName,
mrn,
bgUnits,
tzName,
restricted_token: restrictedToken,
reports,
} = req.query;

const timer = setTimeout(() => {
res.emit('timeout', exportTimeout);
}, exportTimeout);

const pdfReport = await generateReport(
log,
{
userId,
fullName,
dob,
mrn,
},
{ tzName, bgUnits, reports },
{ token: restrictedToken, sessionHeader: getSessionHeader(req) },
);

res.setHeader('Content-Disposition', 'attachment: filename="report.pdf"');
res.setHeader('Content-Type', 'application/octet-stream');
const blobArrayBuffer = await pdfReport.blob.arrayBuffer();
const pdfBuffer = Buffer.from(blobArrayBuffer);
fs.writeFileSync('test.pdf', pdfBuffer);
res.send(pdfBuffer);
clearTimeout(timer);
} catch (error) {
if (error.response && error.response.status === 403) {
reportStatusCount.inc({ status_code: 403, report_params: req.query });
res.status(403);
log.error(`403: ${error}`);
} else {
reportStatusCount.inc({ status_code: 500, report_params: req.query });
res.status(500);
log.error(`500: ${error}`);
}
}
};
};
9 changes: 9 additions & 0 deletions lib/handlers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const getUserReportsHandler = require('./getUserReportsHandler');
const postUserReportsHandler = require('./postUserReportsHandler');
const userDataHandler = require('./userDataHandler');

module.exports = {
postUserReport: postUserReportsHandler,
getUserReport: getUserReportsHandler,
getUserData: userDataHandler,
};
63 changes: 63 additions & 0 deletions lib/handlers/postUserReportsHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const fs = require('fs');
const {
getSessionHeader,
createCounter,
exportTimeout,
logMaker,
} = require('../utils');

const { generateReport } = require('../reportUtils');

const reportStatusCount = createCounter(
'tidepool_post_report_status_count',
'The number of errors for each status code.',
['status_code', 'report_params'],
);

const log = logMaker('postUserReportsHandler.js', {
level: process.env.DEBUG_LEVEL || 'info',
});

module.exports = function postUserReportsHandler() {
return async (req, res) => {
// Set the timeout for the request. Make it 10 seconds longer than
// our configured timeout to give the service time to cancel the API data
// request, and close the outgoing data stream cleanly.
req.setTimeout(exportTimeout + 10000);

try {
const userId = req.params.userid;
// TODO: which token will we use here?
const restrictedToken = '';
const { userDetail, reportDetail } = req.body;

userDetail.userId = userId;
const timer = setTimeout(() => {
res.emit('timeout', exportTimeout);
}, exportTimeout);

const pdfReport = await generateReport(log, userDetail, reportDetail, {
token: restrictedToken,
sessionHeader: getSessionHeader(req),
});

res.setHeader('Content-Disposition', 'attachment: filename="report.pdf"');
res.setHeader('Content-Type', 'application/octet-stream');
const blobArrayBuffer = await pdfReport.blob.arrayBuffer();
const pdfBuffer = Buffer.from(blobArrayBuffer);
fs.writeFileSync('test.pdf', pdfBuffer);
res.send(pdfBuffer);
clearTimeout(timer);
} catch (error) {
if (error.response && error.response.status === 403) {
reportStatusCount.inc({ status_code: 403, report_params: req.query });
res.status(403);
log.error(`403: ${error}`);
} else {
reportStatusCount.inc({ status_code: 500, report_params: req.query });
res.status(500);
log.error(`500: ${error}`);
}
}
};
};
26 changes: 15 additions & 11 deletions lib/userDataHandler.js → lib/handlers/userDataHandler.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import axios from 'axios';
import queryString from 'query-string';
import dataTools from '@tidepool/data-tools';
import {
buildHeaders, createCounter, exportTimeout, logMaker,
} from './utils';
const axios = require('axios');
const queryString = require('query-string');
const dataTools = require('@tidepool/data-tools');
const {
getSessionHeader,
createCounter,
exportTimeout,
logMaker,
mmolLUnits,
} = require('../utils');

const dataStatusCount = createCounter(
'tidepool_export_status_count',
Expand All @@ -15,7 +19,7 @@ const log = logMaker('userDataHandler.js', {
level: process.env.DEBUG_LEVEL || 'info',
});

export default function userDataHandler() {
module.exports = function userDataHandler() {
return async (req, res) => {
// Set the timeout for the request. Make it 10 seconds longer than
// our configured timeout to give the service time to cancel the API data
Expand All @@ -30,7 +34,7 @@ export default function userDataHandler() {
}
if (req.query.startDate) {
queryData.startDate = req.query.startDate;
logString += ` from ${req.query.startDate}`;
logString += ` = require(${req.query.startDate}`;
}
if (req.query.endDate) {
queryData.endDate = req.query.endDate;
Expand All @@ -47,7 +51,7 @@ export default function userDataHandler() {
try {
const cancelRequest = axios.CancelToken.source();

const requestConfig = buildHeaders(req);
const requestConfig = getSessionHeader(req);
requestConfig.responseType = 'stream';
requestConfig.cancelToken = cancelRequest.token;
const dataResponse = await axios.get(
Expand All @@ -58,7 +62,7 @@ export default function userDataHandler() {
);
log.debug(`Downloading data for User ${req.params.userid}...`);

const processorConfig = { bgUnits: req.query.bgUnits || 'mmol/L' };
const processorConfig = { bgUnits: req.query.bgUnits || mmolLUnits };

let writeStream = null;

Expand Down Expand Up @@ -136,4 +140,4 @@ export default function userDataHandler() {
}
}
};
}
};
Loading

0 comments on commit cb449f2

Please sign in to comment.