From b4b3be14cfd784686c7e8452a35342f6c3a02304 Mon Sep 17 00:00:00 2001 From: Benj Fredrick Date: Sat, 3 Apr 2021 11:40:16 -0600 Subject: [PATCH 1/6] MVS-738: start of code to create a Client in TimeTap --- broker/endpoints/Patient.js | 4 ++ broker/services/timetapService.js | 88 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 broker/services/timetapService.js diff --git a/broker/endpoints/Patient.js b/broker/endpoints/Patient.js index 1d0ed9a5..334effe6 100644 --- a/broker/endpoints/Patient.js +++ b/broker/endpoints/Patient.js @@ -4,6 +4,7 @@ const RelatedPerson = require("../models/RelatedPerson"); const {body, param, validationResult, sanitizeBody} = require('express-validator'); const Appointment = require("../models/Appointment"); const uuid = require('uuid'); +const tt_service = require('../services/timetapService'); exports.read = [ @@ -275,6 +276,9 @@ async function createPatient(patient, head) { const createdPatient = await axios.post(`/Patient`, resource); patientID.resourceId = createdPatient.data.id; + // Create the new Patient/Client record in TimeTap + tt_service.createTimetapClient(createdPatient.data); + // If RelatedPerson was not created with a link to a patient, // update the resource to link to the new Patient if (head == null) { diff --git a/broker/services/timetapService.js b/broker/services/timetapService.js new file mode 100644 index 00000000..b6abca2d --- /dev/null +++ b/broker/services/timetapService.js @@ -0,0 +1,88 @@ +require("dotenv").config({ path: `${__dirname}/../.env` }); +const { body, param, validationResult, sanitizeBody } = require("express-validator"); +const crypto = require("crypto"); + +let axios = require("axios").default; +axios.defaults.baseURL = process.env.TIMETAP_BASEURL; + +let tt_sessiontoken = ""; +if (tt_sessiontoken == "") { + fetchTimetapSessionToken(); +} + +async function fetchTimetapSessionToken() { + // Build our input values + let timestamp = Date.now(); + let signature = crypto + .createHash("md5") + .update(process.env.TIMETAP_APIKEY + process.env.TIMETAP_SECRETKEY) + .digest("hex"); + + // Fetch the new Token + const res = await axios.get("/sessionToken", { + params: { + apiKey: process.env.TIMETAP_APIKEY, + timestamp: timestamp, + signature: signature, + }, + }); + + tt_sessiontoken = res.data.sessionToken; +} + +exports.createTimetapClient = [ + param("patient", "Validation message here.").isLength({ min: 1, max: 1 }), + + async (req, res) => { + console.log("Starting createTimetapClient"); + console.log(patient); + const data = { + firstName: patient.name.given[0], + lastName: patient.name.family, + fullName: patient.name.given[0] + " " + patient.name.family, + // emailAddress: patient.telecom.wtfbbq, + dateOfBirth: patient.birthDate, + sex: patient.gender, + address1: patient.address[0].line[0], + address2: patient.address[0].line[1], + city: patient.address[0].city, + // county: "foo", + state: patient.address[0].state, + zip: patient.address[0].postalCode, + country: patient.address[0].country, + timeZone: {}, + allowWaitListText: true, + homePhone: "555-444-6666", + cellPhone: "777-333-2222", + clientIdsClientCanView: [], + fields: [], + locale: "en-US", + tags: [], + }; + + // axios + // .post("/clients", data, { + // headers: { + // Authorization: `Basic ${tt_sessiontoken}`, + // }, + // }) + // .then((response) => { + // console.log("Patient created in TimeTap!"); + // }) + // .catch((err) => { + // console.log("API request failed, attempting to refresh the token."); + // console.log("the magical 'this': " + this); + // refreshTimetapSessionToken(this, [patient]); + // }); + }, +]; + +async function refreshTimetapSessionToken(callback, ...args) { + try { + fetchTimetapSessionToken(); + console.log("Session Token refreshed! " + tt_sessiontoken); + callback.call(args); + } catch { + console.log("Timetap is broken."); + } +} From 8c05114d168fe444af7ac9499567dedf5ca9a9ae Mon Sep 17 00:00:00 2001 From: Nick Collins Date: Tue, 6 Apr 2021 10:10:06 -0600 Subject: [PATCH 2/6] timetap axios fix --- broker/endpoints/Patient.js | 2 +- broker/services/timetapService.js | 108 ++++++++++++++++-------------- 2 files changed, 57 insertions(+), 53 deletions(-) diff --git a/broker/endpoints/Patient.js b/broker/endpoints/Patient.js index 334effe6..c0476d37 100644 --- a/broker/endpoints/Patient.js +++ b/broker/endpoints/Patient.js @@ -277,7 +277,7 @@ async function createPatient(patient, head) { patientID.resourceId = createdPatient.data.id; // Create the new Patient/Client record in TimeTap - tt_service.createTimetapClient(createdPatient.data); + await tt_service.createTimetapClient(createdPatient.data); // If RelatedPerson was not created with a link to a patient, // update the resource to link to the new Patient diff --git a/broker/services/timetapService.js b/broker/services/timetapService.js index b6abca2d..1b0803f0 100644 --- a/broker/services/timetapService.js +++ b/broker/services/timetapService.js @@ -2,12 +2,19 @@ require("dotenv").config({ path: `${__dirname}/../.env` }); const { body, param, validationResult, sanitizeBody } = require("express-validator"); const crypto = require("crypto"); -let axios = require("axios").default; -axios.defaults.baseURL = process.env.TIMETAP_BASEURL; +const axios = require("axios"); +const timetapAxios = axios.create({ + baseURL: process.env.TIMETAP_BASEURL +}) let tt_sessiontoken = ""; if (tt_sessiontoken == "") { - fetchTimetapSessionToken(); + try { + fetchTimetapSessionToken(); + } catch (error) { + console.log(error); + } + } async function fetchTimetapSessionToken() { @@ -19,69 +26,66 @@ async function fetchTimetapSessionToken() { .digest("hex"); // Fetch the new Token - const res = await axios.get("/sessionToken", { + const res = await timetapAxios.get("/sessionToken", { params: { apiKey: process.env.TIMETAP_APIKEY, timestamp: timestamp, signature: signature, }, }); - + console.log(res.data.sessionToken); tt_sessiontoken = res.data.sessionToken; } -exports.createTimetapClient = [ - param("patient", "Validation message here.").isLength({ min: 1, max: 1 }), - - async (req, res) => { - console.log("Starting createTimetapClient"); - console.log(patient); - const data = { - firstName: patient.name.given[0], - lastName: patient.name.family, - fullName: patient.name.given[0] + " " + patient.name.family, - // emailAddress: patient.telecom.wtfbbq, - dateOfBirth: patient.birthDate, - sex: patient.gender, - address1: patient.address[0].line[0], - address2: patient.address[0].line[1], - city: patient.address[0].city, - // county: "foo", - state: patient.address[0].state, - zip: patient.address[0].postalCode, - country: patient.address[0].country, - timeZone: {}, - allowWaitListText: true, - homePhone: "555-444-6666", - cellPhone: "777-333-2222", - clientIdsClientCanView: [], - fields: [], - locale: "en-US", - tags: [], - }; +exports.createTimetapClient = async (patient) => { + console.log("Starting createTimetapClient"); + console.log(patient); - // axios - // .post("/clients", data, { - // headers: { - // Authorization: `Basic ${tt_sessiontoken}`, - // }, - // }) - // .then((response) => { - // console.log("Patient created in TimeTap!"); - // }) - // .catch((err) => { - // console.log("API request failed, attempting to refresh the token."); - // console.log("the magical 'this': " + this); - // refreshTimetapSessionToken(this, [patient]); - // }); - }, -]; + const data = { + firstName: patient.name[0].given[0], + lastName: patient.name[0].family, + fullName: `${patient.name[0].given[0]} ${patient.name[0].family}`, + // emailAddress: patient.telecom.wtfbbq, + dateOfBirth: patient.birthDate, + sex: patient.gender, + address1: patient.address[0].line[0], + address2: patient.address[0].line[1], + city: patient.address[0].city, + // county: "foo", + state: patient.address[0].state, + zip: patient.address[0].postalCode, + country: patient.address[0].country, + timeZone: {}, + allowWaitListText: true, + homePhone: "555-444-6666", + cellPhone: "777-333-2222", + clientIdsClientCanView: [], + fields: [], + locale: "en-US", + tags: [], + }; + + await timetapAxios + .post("/clients", data, { + headers: { + Authorization: `Basic ${tt_sessiontoken}`, + }, + }) + .then((response) => { + console.log("Patient created in TimeTap!"); + }) + .catch(async (err) => { + console.log("API request failed, attempting to refresh the token."); + console.log("the magical 'this': " + this); + await refreshTimetapSessionToken(this, [patient]); + }); +}; async function refreshTimetapSessionToken(callback, ...args) { try { - fetchTimetapSessionToken(); + await fetchTimetapSessionToken(); console.log("Session Token refreshed! " + tt_sessiontoken); - callback.call(args); + await callback.call(args); } catch { console.log("Timetap is broken."); } From a58f5540e6248a18ec35a76ead00fafab8194720 Mon Sep 17 00:00:00 2001 From: Nick Collins Date: Tue, 6 Apr 2021 17:07:09 -0600 Subject: [PATCH 3/6] bearer not basic --- broker/services/timetapService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/broker/services/timetapService.js b/broker/services/timetapService.js index 1b0803f0..077e3cc0 100644 --- a/broker/services/timetapService.js +++ b/broker/services/timetapService.js @@ -68,7 +68,7 @@ exports.createTimetapClient = async (patient) => { await timetapAxios .post("/clients", data, { headers: { - Authorization: `Basic ${tt_sessiontoken}`, + Authorization: `Bearer ${tt_sessiontoken}`, }, }) .then((response) => { From 2ed881042ee10d748e8ea2e5622ad35993ec3b40 Mon Sep 17 00:00:00 2001 From: Nick Collins Date: Tue, 6 Apr 2021 17:11:28 -0600 Subject: [PATCH 4/6] I think this is how it should work --- broker/services/timetapService.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/broker/services/timetapService.js b/broker/services/timetapService.js index 077e3cc0..fdd4cd3f 100644 --- a/broker/services/timetapService.js +++ b/broker/services/timetapService.js @@ -81,11 +81,11 @@ exports.createTimetapClient = async (patient) => { }); }; -async function refreshTimetapSessionToken(callback, ...args) { +async function refreshTimetapSessionToken(callback, args) { try { await fetchTimetapSessionToken(); console.log("Session Token refreshed! " + tt_sessiontoken); - await callback.call(args); + await callback(...args); } catch { console.log("Timetap is broken."); } From b53ab46d2ab8e6a5fa07097d27448239fd5b9775 Mon Sep 17 00:00:00 2001 From: Benj Fredrick Date: Tue, 13 Apr 2021 23:09:30 -0600 Subject: [PATCH 5/6] MVS-738: cleanup on the details --- broker/services/timetapService.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/broker/services/timetapService.js b/broker/services/timetapService.js index fdd4cd3f..d61723d7 100644 --- a/broker/services/timetapService.js +++ b/broker/services/timetapService.js @@ -33,32 +33,39 @@ async function fetchTimetapSessionToken() { signature: signature, }, }); - console.log(res.data.sessionToken); tt_sessiontoken = res.data.sessionToken; } exports.createTimetapClient = async (patient) => { - console.log("Starting createTimetapClient"); - console.log(patient); + // Create the Patient record in Timetap + let phone = ""; + let email = ""; + for (let i = 0; i < patient.telecom.length; i++) { + if (patient.telecom[i].system === "phone") { + phone = patient.telecom[i].value; + } + if (patient.telecom[i].system === "email") { + email = patient.telecom[i].value; + } + } const data = { firstName: patient.name[0].given[0], lastName: patient.name[0].family, fullName: `${patient.name[0].given[0]} ${patient.name[0].family}`, - // emailAddress: patient.telecom.wtfbbq, + emailAddress: email, dateOfBirth: patient.birthDate, sex: patient.gender, address1: patient.address[0].line[0], address2: patient.address[0].line[1], city: patient.address[0].city, - // county: "foo", state: patient.address[0].state, zip: patient.address[0].postalCode, country: patient.address[0].country, timeZone: {}, allowWaitListText: true, - homePhone: "555-444-6666", - cellPhone: "777-333-2222", + homePhone: phone, + cellPhone: phone, clientIdsClientCanView: [], fields: [], locale: "en-US", @@ -72,11 +79,10 @@ exports.createTimetapClient = async (patient) => { }, }) .then((response) => { - console.log("Patient created in TimeTap!"); + // Success! }) .catch(async (err) => { - console.log("API request failed, attempting to refresh the token."); - console.log("the magical 'this': " + this); + // Attempt to refresh our TT token await refreshTimetapSessionToken(this, [patient]); }); }; @@ -84,9 +90,8 @@ exports.createTimetapClient = async (patient) => { async function refreshTimetapSessionToken(callback, args) { try { await fetchTimetapSessionToken(); - console.log("Session Token refreshed! " + tt_sessiontoken); await callback(...args); } catch { - console.log("Timetap is broken."); + // TimeTap token refresh failed } } From 4debc700a9475b357eead29c78b8f0ee59b560db Mon Sep 17 00:00:00 2001 From: Benj Fredrick Date: Wed, 14 Apr 2021 11:14:26 -0600 Subject: [PATCH 6/6] MVS-738: add an .env.template file for the broker app --- broker/.env.template | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 broker/.env.template diff --git a/broker/.env.template b/broker/.env.template new file mode 100644 index 00000000..a2dbcd45 --- /dev/null +++ b/broker/.env.template @@ -0,0 +1,8 @@ +DEVELOPMENT=1 +FHIR_URL_BASE=http://fhir-dstu2-nprogram.azurewebsites.net/ + +# TimeTap API config +# +TIMETAP_APIKEY=12345678 +TIMETAP_SECRETKEY=12345wertyfghjkyt +TIMETAP_BASEURL=https://api.timetap.com/test \ No newline at end of file