From 95a5f0dcfc79d7649520a306850181ab9f5430af Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 13 Jun 2025 15:37:19 -0400 Subject: [PATCH 1/5] change rems etasu url --- src/lib/etasu.ts | 237 +++++++++--------- src/services/questionnaireresponse.service.ts | 14 ++ 2 files changed, 128 insertions(+), 123 deletions(-) diff --git a/src/lib/etasu.ts b/src/lib/etasu.ts index e66ac424..d74ff01a 100644 --- a/src/lib/etasu.ts +++ b/src/lib/etasu.ts @@ -207,7 +207,6 @@ const createMetRequirementAndNewCase = async ( drug: Medication, requirement: Requirement, questionnaireResponse: QuestionnaireResponse, - res: Response, reqStakeholderReference: string, practitionerReference: string, pharmacistReference: string, @@ -257,11 +256,10 @@ const createMetRequirementAndNewCase = async ( }; if (!(await createAndPushMetRequirements(metReq, remsRequest))) { - res.status(200); message = 'ERROR: failed to create new met requirement for form initial to case'; console.log(message); - res.send(message); - return res; + throw new Error(message); + } // iterate through all other requirements again to create corresponding false metRequirements / assign to existing @@ -316,17 +314,16 @@ const createMetRequirementAndNewCase = async ( remsRequest.status = remsRequestCompletedStatus; const returnedRemsRequestDoc = await remsCaseCollection.create(remsRequest); - res.status(201); - res.send(returnedRemsRequestDoc); - - return res; + + return { + returnedRemsRequestDoc + }; }; const createMetRequirementAndUpdateCase = async ( drug: Medication, requirement: Requirement, questionnaireResponse: QuestionnaireResponse, - res: Response, reqStakeholderReference: string ) => { let returnedMetReqDoc; @@ -403,9 +400,10 @@ const createMetRequirementAndUpdateCase = async ( returnedMetReqDoc = await createMetRequirements(newMetReq); } - res.status(201); - res.send(returnedMetReqDoc); - return res; + return { + returnedMetReqDoc + }; + }; const createMetRequirementAndUpdateCaseNotRequiredToDispense = async ( @@ -413,7 +411,6 @@ const createMetRequirementAndUpdateCaseNotRequiredToDispense = async ( drug: Medication, requirement: Requirement, questionnaireResponse: QuestionnaireResponse, - res: Response, reqStakeholderReference: string ) => { // Find the specific case associated with an individual patient for the patient status form @@ -473,119 +470,17 @@ const createMetRequirementAndUpdateCaseNotRequiredToDispense = async ( console.log(message); } - res.status(201); if (returnRemsRequest) { - res.send(remsRequestToUpdate); + return { + remsRequestToUpdate + }; } else { - res.send(message); - } - return res; -}; - -router.post('/met', async (req: Request, res: Response) => { - try { - const requestBody = req.body as Bundle; - - // extract params and questionnaire response identifier - const params = getResource( - requestBody, - (requestBody.entry?.[0]?.resource as MessageHeader)?.focus?.[0]?.reference || '' - ) as Parameters; - const questionnaireResponse = getQuestionnaireResponse(requestBody) as QuestionnaireResponse; - const questionnaireStringArray = questionnaireResponse?.questionnaire?.split('/'); - const requirementId = questionnaireStringArray?.[questionnaireStringArray.length - 1]; - - // stakeholder and medication references - let prescriptionReference = ''; - let practitionerReference = ''; - let pharmacistReference = ''; - let patientReference = ''; - for (const param of params.parameter || []) { - if (param.name === 'prescription') { - prescriptionReference = param.valueReference?.reference || ''; - } else if (param.name === 'prescriber') { - practitionerReference = param.valueReference?.reference || ''; - } else if (param.name === 'pharmacy') { - pharmacistReference = param.valueReference?.reference || ''; - } else if (param.name === 'source-patient') { - patientReference = param.valueReference?.reference || ''; - } - } - - // obtain drug information from database - const prescription = getResource(requestBody, prescriptionReference) as MedicationRequest; - const medicationCode = getDrugCodeFromMedicationRequest(prescription) as Coding; - const prescriptionSystem = medicationCode?.system; - const prescriptionCode = medicationCode?.code; - const patient = getResource(requestBody, patientReference) as Patient; - - const drug = await medicationCollection - .findOne({ - code: prescriptionCode, - codeSystem: prescriptionSystem - }) - .exec(); - // iterate through each requirement of the drug - if (drug) { - for (const requirement of drug.requirements) { - // figure out which stakeholder the req corresponds to - const stakeholder = requirement.stakeholderType; - const stakeholderReference = - stakeholder === 'prescriber' - ? practitionerReference - : stakeholder === 'pharmacist' - ? pharmacistReference - : patientReference; - - // if the requirement is the one submitted continue - if (requirement.resourceId === requirementId) { - // if the req submitted is a patient enrollment form and requires creating a new case - if (requirement.createNewCase) { - await createMetRequirementAndNewCase( - patient, - drug, - requirement, - questionnaireResponse, - res, - stakeholderReference, - practitionerReference, - pharmacistReference, - patientReference - ); - - return; - } else { - // If it's not the patient status requirement - if (requirement.requiredToDispense) { - await createMetRequirementAndUpdateCase( - drug, - requirement, - questionnaireResponse, - res, - stakeholderReference - ); - return; - } else { - await createMetRequirementAndUpdateCaseNotRequiredToDispense( - patient, - drug, - requirement, - questionnaireResponse, - res, - stakeholderReference - ); - return; - } - } - break; - } - } + return { + message + }; } - } catch (error) { - console.log(error); - throw error; - } -}); + +}; const getResource = (bundle: Bundle, resourceReference: string) => { const temp = resourceReference.split('/'); @@ -618,4 +513,100 @@ const getQuestionnaireResponse = (bundle: Bundle) => { return null; }; + +export const processQuestionnaireResponseSubmission = async (requestBody: Bundle): Promise => { + // extract params and questionnaire response identifier + const params = getResource( + requestBody, + (requestBody.entry?.[0]?.resource as MessageHeader)?.focus?.[0]?.reference || '' + ) as Parameters; + const questionnaireResponse = getQuestionnaireResponse(requestBody) as QuestionnaireResponse; + const questionnaireStringArray = questionnaireResponse?.questionnaire?.split('/'); + const requirementId = questionnaireStringArray?.[questionnaireStringArray.length - 1]; + + // stakeholder and medication references + let prescriptionReference = ''; + let practitionerReference = ''; + let pharmacistReference = ''; + let patientReference = ''; + for (const param of params.parameter || []) { + if (param.name === 'prescription') { + prescriptionReference = param.valueReference?.reference || ''; + } else if (param.name === 'prescriber') { + practitionerReference = param.valueReference?.reference || ''; + } else if (param.name === 'pharmacy') { + pharmacistReference = param.valueReference?.reference || ''; + } else if (param.name === 'source-patient') { + patientReference = param.valueReference?.reference || ''; + } + } + + // obtain drug information from database + const prescription = getResource(requestBody, prescriptionReference) as MedicationRequest; + const medicationCode = getDrugCodeFromMedicationRequest(prescription) as Coding; + const prescriptionSystem = medicationCode?.system; + const prescriptionCode = medicationCode?.code; + const patient = getResource(requestBody, patientReference) as Patient; + + const drug = await medicationCollection + .findOne({ + code: prescriptionCode, + codeSystem: prescriptionSystem + }) + .exec(); + + // iterate through each requirement of the drug + if (drug) { + for (const requirement of drug.requirements) { + // figure out which stakeholder the req corresponds to + const stakeholder = requirement.stakeholderType; + const stakeholderReference = + stakeholder === 'prescriber' + ? practitionerReference + : stakeholder === 'pharmacist' + ? pharmacistReference + : patientReference; + + // if the requirement is the one submitted continue + if (requirement.resourceId === requirementId) { + // if the req submitted is a patient enrollment form and requires creating a new case + if (requirement.createNewCase) { + return await createMetRequirementAndNewCase( + patient, + drug, + requirement, + questionnaireResponse, + stakeholderReference, + practitionerReference, + pharmacistReference, + patientReference + ); + } else { + // If it's not the patient status requirement + if (requirement.requiredToDispense) { + return await createMetRequirementAndUpdateCase( + drug, + requirement, + questionnaireResponse, + stakeholderReference + ); + } else { + return await createMetRequirementAndUpdateCaseNotRequiredToDispense( + patient, + drug, + requirement, + questionnaireResponse, + stakeholderReference + ); + } + } + } + } + } + + throw new Error('No matching requirement found for the submitted questionnaire'); +}; + +export { getResource, getQuestionnaireResponse }; + export default router; diff --git a/src/services/questionnaireresponse.service.ts b/src/services/questionnaireresponse.service.ts index 9d20d461..6890a0dd 100644 --- a/src/services/questionnaireresponse.service.ts +++ b/src/services/questionnaireresponse.service.ts @@ -1,5 +1,7 @@ import { FhirUtilities } from '../fhir/utilities'; import QuestionnaireResponseModel from '../lib/schemas/resources/QuestionnaireResponse'; +import { Bundle } from 'fhir/r4'; +import { processQuestionnaireResponseSubmission } from '../lib/etasu'; module.exports.searchById = async (args: any) => { const { id } = args; @@ -13,3 +15,15 @@ module.exports.create = async (args: any, req: any) => { const { base_version } = args; return await FhirUtilities.store(resource, QuestionnaireResponseModel, base_version); }; + +module.exports.submit = async (args: any, context: any, logger: any) => { + logger.info('Running QuestionnaireResponse $submit operation'); + + try { + const requestBody = args?.resource as Bundle; + return await processQuestionnaireResponseSubmission(requestBody); + } catch (error) { + logger.error('Error in QuestionnaireResponse $submit operation:', error); + throw error; + } +}; From 2283006af26930322c793830faa07aaecdcafd21 Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 13 Jun 2025 15:44:01 -0400 Subject: [PATCH 2/5] run linting --- src/lib/etasu.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/lib/etasu.ts b/src/lib/etasu.ts index d74ff01a..f6c3d776 100644 --- a/src/lib/etasu.ts +++ b/src/lib/etasu.ts @@ -259,7 +259,6 @@ const createMetRequirementAndNewCase = async ( message = 'ERROR: failed to create new met requirement for form initial to case'; console.log(message); throw new Error(message); - } // iterate through all other requirements again to create corresponding false metRequirements / assign to existing @@ -314,7 +313,7 @@ const createMetRequirementAndNewCase = async ( remsRequest.status = remsRequestCompletedStatus; const returnedRemsRequestDoc = await remsCaseCollection.create(remsRequest); - + return { returnedRemsRequestDoc }; @@ -403,7 +402,6 @@ const createMetRequirementAndUpdateCase = async ( return { returnedMetReqDoc }; - }; const createMetRequirementAndUpdateCaseNotRequiredToDispense = async ( @@ -478,8 +476,7 @@ const createMetRequirementAndUpdateCaseNotRequiredToDispense = async ( return { message }; - } - + } }; const getResource = (bundle: Bundle, resourceReference: string) => { @@ -513,7 +510,6 @@ const getQuestionnaireResponse = (bundle: Bundle) => { return null; }; - export const processQuestionnaireResponseSubmission = async (requestBody: Bundle): Promise => { // extract params and questionnaire response identifier const params = getResource( @@ -554,7 +550,7 @@ export const processQuestionnaireResponseSubmission = async (requestBody: Bundle codeSystem: prescriptionSystem }) .exec(); - + // iterate through each requirement of the drug if (drug) { for (const requirement of drug.requirements) { @@ -603,7 +599,7 @@ export const processQuestionnaireResponseSubmission = async (requestBody: Bundle } } } - + throw new Error('No matching requirement found for the submitted questionnaire'); }; From e0e3e4e750ef5e184eb7ac6381c5e2de9587bc5b Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 13 Jun 2025 16:17:12 -0400 Subject: [PATCH 3/5] endpoint path fix --- src/config.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/config.ts b/src/config.ts index 58745c61..de555c5a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -112,7 +112,15 @@ export default { }, questionnaireresponse: { service: './src/services/questionnaireresponse.service.ts', - versions: [fhirConstants.VERSIONS['4_0_0']] + versions: [fhirConstants.VERSIONS['4_0_0']], + operation: [ + { + name: 'submit', + route: '/$submit', + method: 'POST', + reference: 'http://hl7.org/fhir/OperationDefinition/QuestionnaireResponse-submit' + } + ] }, valueset: { service: './src/services/valueset.service.ts', From 998fd90ca7a9b05f00e4a602134025cdec6b4bcb Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Fri, 13 Jun 2025 17:11:12 -0400 Subject: [PATCH 4/5] return 201 status code --- src/services/questionnaireresponse.service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/questionnaireresponse.service.ts b/src/services/questionnaireresponse.service.ts index 6890a0dd..1abb81c9 100644 --- a/src/services/questionnaireresponse.service.ts +++ b/src/services/questionnaireresponse.service.ts @@ -21,7 +21,11 @@ module.exports.submit = async (args: any, context: any, logger: any) => { try { const requestBody = args?.resource as Bundle; - return await processQuestionnaireResponseSubmission(requestBody); + const response = await processQuestionnaireResponseSubmission(requestBody); + return { + statusCode: 201, + resource: response + }; } catch (error) { logger.error('Error in QuestionnaireResponse $submit operation:', error); throw error; From 179afb3aa9b9270d7d9d5d8b671fb8114d324aad Mon Sep 17 00:00:00 2001 From: Sahil Malhotra Date: Mon, 16 Jun 2025 10:50:25 -0400 Subject: [PATCH 5/5] 201 status code --- src/lib/etasu.ts | 4 ++-- src/services/questionnaireresponse.service.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/etasu.ts b/src/lib/etasu.ts index f6c3d776..63ef37be 100644 --- a/src/lib/etasu.ts +++ b/src/lib/etasu.ts @@ -256,7 +256,7 @@ const createMetRequirementAndNewCase = async ( }; if (!(await createAndPushMetRequirements(metReq, remsRequest))) { - message = 'ERROR: failed to create new met requirement for form initial to case'; + message = 'ERROR: failed to create new met requirement and initial case'; console.log(message); throw new Error(message); } @@ -304,7 +304,7 @@ const createMetRequirementAndNewCase = async ( remsRequestCompletedStatus = 'Pending'; if (!(await createAndPushMetRequirements(newMetReq, remsRequest))) { - message = 'ERROR: failed to create new met requirement for form initial to case'; + message = 'ERROR: failed to create new met requirement for form and initial case'; console.log(message); } } diff --git a/src/services/questionnaireresponse.service.ts b/src/services/questionnaireresponse.service.ts index 1abb81c9..a50632e2 100644 --- a/src/services/questionnaireresponse.service.ts +++ b/src/services/questionnaireresponse.service.ts @@ -22,9 +22,9 @@ module.exports.submit = async (args: any, context: any, logger: any) => { try { const requestBody = args?.resource as Bundle; const response = await processQuestionnaireResponseSubmission(requestBody); + context.req.res.status(201); return { - statusCode: 201, - resource: response + response }; } catch (error) { logger.error('Error in QuestionnaireResponse $submit operation:', error);