Skip to content

Commit

Permalink
Merge pull request #953 from varadeth/DIV-8225
Browse files Browse the repository at this point in the history
DIV-8225 | EU_Vaccine_Proph, EU_Vaccine_Code, EU_Vaccine_Manuf as ETCD
  • Loading branch information
egov-joy authored Feb 16, 2022
2 parents fb1a80c + 017eff4 commit 1b2c3bb
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 196 deletions.
64 changes: 8 additions & 56 deletions backend/certificate_api/configs/constants.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,18 @@
const EU_DISEASE = {"covid-19": "840539006"};
const EU_VACCINE_PROPH = {
"covaxin": "J07BX03",
"covishield": "J07BX03",
"sputnik": "J07BX03",
"pfizer-biontech": "J07BX03",
"comirnaty": "J07BX03",
"janssen": "J07BX03",
"moderna": "J07BX03",
"spikevax": "J07BX03",
"astrazeneca": "J07BX03",
"vaxzevria": "J07BX03",
"sinovac": "J07BX03",
"coronavac": "J07BX03",
"bbibp-corv": "J07BX03",
"sinopharm": "J07BX03",
"convidecia": "J07BX03",
"novavax": "J07BX03",
"covovax": "J07BX03"
};
const EU_VACCINE_CODE = {
"covaxin": "Covaxin",
"covishield": "Covishield",
"sputnik": "Sputnik-V",
"pfizer-biontech": "EU/1/20/1528",
"comirnaty": "EU/1/20/1528",
"janssen": "EU/1/20/1525",
"moderna": "EU/1/20/1507",
"spikevax": "EU/1/20/1507",
"astrazeneca": "EU/1/21/1529",
"vaxzevria": "EU/1/21/1529",
"sinovac": "CoronaVac",
"coronavac": "CoronaVac",
"bbibp-corv": "BBIBP- CorV",
"sinopharm": "BBIBP- CorV",
"convidecia": "Convidecia",
"novavax": "NVX - CoV2373",
"covovax": "NVX - CoV2373"
}
const VACCINE_MANUF = {
"bharat": "Bharat-Biotech",
"serum": "ORG-100001981",
"gamaleya": "Gamaleya-Research-Institute",
"biontech": "ORG-100030215",
"janssen": "ORG-100001417",
"moderna": "ORG-100031184",
"astrazeneca": "ORG-100001699",
"sinovac": "Sinovac- Biontech",
"sinopharm": "ORG-100020693",
"canSino": "ORG-100013793",
"novavax": "ORG-100032020"
};

const TEMPLATES = {
VACCINATION_CERTIFICATE: "vaccineCertificateTemplate",
TEST_CERTIFICATE: "testCertificateTemplate"
};
Object.freeze(TEMPLATES);

const EU_VACCINE_CONFIG_KEYS = {
PROPHYLAXIS_TYPE: "euVaccineProph",
VACCINE_CODE: "euVaccineCode",
MANUFACTURER: "euVaccineManuf"
}

module.exports = {
EU_DISEASE,
EU_VACCINE_PROPH,
EU_VACCINE_CODE,
VACCINE_MANUF,
TEMPLATES
TEMPLATES,
EU_VACCINE_CONFIG_KEYS
}
28 changes: 21 additions & 7 deletions backend/certificate_api/src/routes/certificate_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ const config = require('../../configs/config');
const dcc = require("@pathcheck/dcc-sdk");
const shc = require("@pathcheck/shc-sdk");
const keyUtils = require("../services/key_utils");
const { CertificateTemplate, init } = require('../services/etcd_configuration_service');
const { ConfigurationService, init } = require('../services/configuration_service');
const {TEMPLATES} = require('../../configs/constants');
const QR_TYPE = "qrcode";

init();
let shcKeyPair = [];
const certificateTemplate = new CertificateTemplate();
const configurationService = new ConfigurationService();
function getNumberWithOrdinal(n) {
const s = ["th", "st", "nd", "rd"],
v = n % 100;
Expand Down Expand Up @@ -184,7 +184,7 @@ async function createCertificatePDF(certificateResp, res, source) {
const dataURL = await getQRCodeData(certificateRaw.certificate, true);
let doseToVaccinationDetailsMap = getVaccineDetailsOfPreviousDoses(certificateResp);
const certificateData = prepareDataForVaccineCertificateTemplate(certificateRaw, dataURL, doseToVaccinationDetailsMap);
const htmlData = await certificateTemplate.getCertificateTemplate(TEMPLATES.VACCINATION_CERTIFICATE);
const htmlData = await configurationService.getCertificateTemplate(TEMPLATES.VACCINATION_CERTIFICATE);
let pdfBuffer;
try {
pdfBuffer = await createPDF(htmlData, certificateData);
Expand Down Expand Up @@ -263,7 +263,7 @@ async function createTestCertificatePDF(certificateResp, res, source) {
qrCode: dataURL,
country: evidence[0].facility.address.addressCountry
};
const htmlData = await certificateTemplate.getCertificateTemplate(TEMPLATES.TEST_CERTIFICATE);;
const htmlData = await configurationService.getCertificateTemplate(TEMPLATES.TEST_CERTIFICATE);;
let pdfBuffer;
try {
pdfBuffer = await createPDF(htmlData, certificateData);
Expand Down Expand Up @@ -571,7 +571,21 @@ async function certificateAsEUPayload(req, res) {
if (certificateResp.length > 0) {
let certificateRaw = certificateService.getLatestCertificate(certificateResp);
// convert certificate to EU Json
const dccPayload = certificateService.convertCertificateToDCCPayload(certificateRaw);
let dccPayload;
try {
dccPayload = await certificateService.convertCertificateToDCCPayload(certificateRaw);
} catch(err) {
res.statusCode = 500;
res.setHeader("Content-Type", "application/json");
let error = {
date: new Date(),
source: "EUCertificateConverter",
type: "internal-failed",
extra: err.message
};
sendEvents(error);
return JSON.stringify(error);
}
const qrUri = await dcc.signAndPack(await dcc.makeCWT(dccPayload, config.EU_CERTIFICATE_EXPIRY, dccPayload.v[0].co), euPublicKeyP8, euPrivateKeyPem);
let buffer = null;
if (type && type.toLowerCase() === QR_TYPE) {
Expand All @@ -581,7 +595,7 @@ async function certificateAsEUPayload(req, res) {
const dataURL = await QRCode.toDataURL(qrUri, {scale: 2});
let doseToVaccinationDetailsMap = getVaccineDetailsOfPreviousDoses(certificateResp);
const certificateData = prepareDataForVaccineCertificateTemplate(certificateRaw, dataURL, doseToVaccinationDetailsMap);
const htmlData = await certificateTemplate.getCertificateTemplate(TEMPLATES.VACCINATION_CERTIFICATE);;
const htmlData = await configurationService.getCertificateTemplate(TEMPLATES.VACCINATION_CERTIFICATE);;
try {
buffer = await createPDF(htmlData, certificateData);
res.setHeader("Content-Type", "application/pdf");
Expand Down Expand Up @@ -677,7 +691,7 @@ async function certificateAsSHCPayload(req, res) {
const dataURL = await QRCode.toDataURL(qrUri, {scale: 2});
let doseToVaccinationDetailsMap = getVaccineDetailsOfPreviousDoses(certificateResp);
const certificateData = prepareDataForVaccineCertificateTemplate(certificateRaw, dataURL, doseToVaccinationDetailsMap);
const htmlData = await certificateTemplate.getCertificateTemplate(TEMPLATES.VACCINATION_CERTIFICATE);;
const htmlData = await configurationService.getCertificateTemplate(TEMPLATES.VACCINATION_CERTIFICATE);;
try {
buffer = await createPDF(htmlData, certificateData);
res.setHeader("Content-Type", "application/pdf");
Expand Down
23 changes: 15 additions & 8 deletions backend/certificate_api/src/services/certificate_service.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const constants = require('../../configs/constants');
const config = require('../../configs/config');
const countries = require('i18n-iso-countries')
const configService = require('./configuration_service');

const sortCertificatesInDoseAndUpdateTimeAscOrder = (certificates) => {
if (certificates.length > 0) {
Expand All @@ -27,16 +28,22 @@ const getLatestCertificate = (certificates) => {
}
};

const convertCertificateToDCCPayload = (certificateRaw) => {
const convertCertificateToDCCPayload = async(certificateRaw) => {
let certificate = JSON.parse(certificateRaw.certificate);

const configurationService = new configService.ConfigurationService();
const VACCINE_MANUF = await configurationService.getEUVaccineDetails(constants.EU_VACCINE_CONFIG_KEYS.MANUFACTURER);
const EU_VACCINE_PROPH = await configurationService.getEUVaccineDetails(constants.EU_VACCINE_CONFIG_KEYS.PROPHYLAXIS_TYPE);
const EU_VACCINE_CODE = await configurationService.getEUVaccineDetails(constants.EU_VACCINE_CONFIG_KEYS.VACCINE_CODE);
if(VACCINE_MANUF === null || EU_VACCINE_PROPH === null || EU_VACCINE_CODE === null || EU_VACCINE_CODE === undefined || EU_VACCINE_PROPH === undefined || VACCINE_MANUF === undefined) {
throw new Error("EU Vaccine Details are missing from Configuration");
}
const {credentialSubject, evidence} = certificate;
const manufacturerCode = Object.keys(constants.VACCINE_MANUF).filter(a => evidence[0].manufacturer.toLowerCase().includes(a)).length > 0 ?
Object.entries(constants.VACCINE_MANUF).filter(([k, v]) => evidence[0].manufacturer.toLowerCase().includes(k))[0][1] : "";
const prophylaxisCode = Object.keys(constants.EU_VACCINE_PROPH).filter(a => evidence[0].vaccine.toLowerCase().includes(a)).length > 0 ?
Object.entries(constants.EU_VACCINE_PROPH).filter(([k, v]) => evidence[0].vaccine.toLowerCase().includes(k))[0][1] : "";
const vaccineCode = Object.keys(constants.EU_VACCINE_CODE).filter(a => evidence[0].vaccine.toLowerCase().includes(a)).length > 0 ?
Object.entries(constants.EU_VACCINE_CODE).filter(([k, v]) => evidence[0].vaccine.toLowerCase().includes(k))[0][1] : "";
const manufacturerCode = Object.keys(VACCINE_MANUF).filter(a => evidence[0].manufacturer.toLowerCase().includes(a)).length > 0 ?
Object.entries(VACCINE_MANUF).filter(([k, v]) => evidence[0].manufacturer.toLowerCase().includes(k))[0][1] : "";
const prophylaxisCode = Object.keys(EU_VACCINE_PROPH).filter(a => evidence[0].vaccine.toLowerCase().includes(a)).length > 0 ?
Object.entries(EU_VACCINE_PROPH).filter(([k, v]) => evidence[0].vaccine.toLowerCase().includes(k))[0][1] : "";
const vaccineCode = Object.keys(EU_VACCINE_CODE).filter(a => evidence[0].vaccine.toLowerCase().includes(a)).length > 0 ?
Object.entries(EU_VACCINE_CODE).filter(([k, v]) => evidence[0].vaccine.toLowerCase().includes(k))[0][1] : "";
const addressCountry = getAlpha2CodeForCountry(evidence[0].facility.address.addressCountry)
const certificateId = "URN:UVCI:01:" + addressCountry + ":" + evidence[0].certificateId;

Expand Down
140 changes: 140 additions & 0 deletions backend/certificate_api/src/services/configuration_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
const {Etcd3} = require('etcd3');
const sanitizeHtml = require('sanitize-html');

const config = require('../../configs/config');
const {TEMPLATES, EU_VACCINE_CONFIG_KEYS} = require('../../configs/constants');
let etcdClient;
let configuration;
let vaccineCertificateTemplate = null, testCertificateTemplate = null;
let EU_VACCINE_PROPH = null, EU_VACCINE_CODE = null, EU_VACCINE_MANUF = null;

function init() {
etcdClient = new Etcd3({hosts: config.ETCD_URL});
setUpWatcher(TEMPLATES.VACCINATION_CERTIFICATE, );
setUpWatcher(TEMPLATES.TEST_CERTIFICATE);
setUpWatcher(EU_VACCINE_CONFIG_KEYS.VACCINE_CODE);
setUpWatcher(EU_VACCINE_CONFIG_KEYS.MANUFACTURER);
setUpWatcher(EU_VACCINE_CONFIG_KEYS.PROPHYLAXIS_TYPE);
configuration = config.CONFIGURATION_LAYER.toLowerCase() === 'etcd' ? new etcd(): null ;
}

function cleanHTML(html) {
if(html === null) {
return null;
}
const cleanedHtml = sanitizeHtml(html, {
allowedTags: false,
allowedAttributes: false,
allowedClasses: {
"*": ["*"]
},
parser: {
lowerCaseAttributeNames: false
},
allowedScriptDomains: [''],
allowedScriptHostnames: [''],
allowedIframeHostnames: [''],
allowedIframeDomains: ['']
});
return cleanedHtml;
}

function updateConfigValues(key, value) {
switch(key) {
case TEMPLATES.VACCINATION_CERTIFICATE:
vaccineCertificateTemplate = value;
break;
case TEMPLATES.TEST_CERTIFICATE:
testCertificateTemplate = value;
break;
case EU_VACCINE_CONFIG_KEYS.VACCINE_CODE:
EU_VACCINE_CODE = value;
break;
case EU_VACCINE_CONFIG_KEYS.MANUFACTURER:
EU_VACCINE_MANUF = value;
break;
case EU_VACCINE_CONFIG_KEYS.PROPHYLAXIS_TYPE:
EU_VACCINE_PROPH = value;
break;
}
}

function setUpWatcher(templateKey) {
etcdClient.watch()
.key(templateKey)
.create()
.then(watcher => {
watcher
.on('end', (end) => {
console.log('end')
})
.on('connected', (req) => {
console.log('connected');
})
.on('put', res => {
updateConfigValues(templateKey, res.value.toString());
});
})
.catch(err => {
console.log(err);
});
}

async function loadConfigurationValues(key, fetchConfigCallbackFunc) {
let value;
switch(key) {
case TEMPLATES.VACCINATION_CERTIFICATE:
value = vaccineCertificateTemplate;
break;
case TEMPLATES.TEST_CERTIFICATE:
value = testCertificateTemplate;
break;
case EU_VACCINE_CONFIG_KEYS.MANUFACTURER:
value = EU_VACCINE_MANUF;
break;
case EU_VACCINE_CONFIG_KEYS.VACCINE_CODE:
value = EU_VACCINE_CODE;
break;
case EU_VACCINE_CONFIG_KEYS.PROPHYLAXIS_TYPE:
value = EU_VACCINE_PROPH;
break;
}
if(value === null || value === undefined) {
if(configuration === null || configuration === undefined) {
return null;
}
value = fetchConfigCallbackFunc();
}
return value;
}

class ConfigurationService {
async getCertificateTemplate(key) {
let certificateTemplate = await loadConfigurationValues(key, async() => await configuration.getCertificateTemplate(key));
certificateTemplate = cleanHTML(certificateTemplate);
updateConfigValues(key, certificateTemplate);
return certificateTemplate;
}

async getEUVaccineDetails(key) {
let details = await loadConfigurationValues(key, async() => JSON.parse(await configuration.getEUVaccineDetails(key)));
updateConfigValues(key, details);
return details;
}
}

const etcd = function() {
this.getCertificateTemplate = async function(templateKey) {
const template = (await etcdClient.get(templateKey).string());
return template;
}

this.getEUVaccineDetails = async function(key) {
const value = (await etcdClient.get(key).string());
return value;
}
}

module.exports = {
ConfigurationService, init
}
Loading

0 comments on commit 1b2c3bb

Please sign in to comment.