67. EPMO Open Source Coordination Office Redaction File Detail Report

Produced by Araxis Merge on 10/4/2017 8:04:35 AM Central Daylight Time. See www.araxis.com for information about Merge. This report uses XHTML and CSS2, and is best viewed with a modern standards-compliant browser. For optimum results when printing this report, use landscape orientation and enable printing of background images and colours in your browser.

67.1 Files compared

# Location File Last Modified
1 rdk.zip\rdk\product\production\rdk\src\resources\patient-search patient-search-util.js Mon Aug 28 19:41:48 2017 UTC
2 rdk.zip\rdk\product\production\rdk\src\resources\patient-search patient-search-util.js Tue Oct 3 18:20:44 2017 UTC

67.2 Comparison summary

Description Between
Files 1 and 2
Text Blocks Lines
Unchanged 2 1278
Changed 1 2
Inserted 0 0
Removed 0 0

67.3 Comparison options

Whitespace
Character case Differences in character case are significant
Line endings Differences in line endings (CR and LF characters) are ignored
CR/LF characters Not shown in the comparison detail

67.4 Active regular expressions

No regular expressions were active.

67.5 Comparison detail

  1   'use stric t';
  2  
  3   var rdk =  require('. ./../core/ rdk');
  4   var httpUt il = rdk.u tils.http;
  5   var _ = re quire('lod ash');
  6   var nullCh ecker = rd k.utils.nu llchecker;
  7   var async  = require( 'async');
  8   var search Jds = requ ire('./sea rch-jds');
  9   var S = re quire('str ing');
  10   var sensit ivityUtils  = rdk.uti ls.sensiti vity;
  11   var search Util = req uire('./re sults-pars er');
  12   var hmpPat ientSelect  = require ('./hmp-pa tient-sele ct');
  13   var NO_HMP _SELECT_RP C_ERR_REGE X = /^Vist A SECURITY  error(.*) $/i;
  14   var COMMA_ REGEX = /, +/gi;
  15   var NAME_S EARCH_TYPE  = 'NAME';
  16   var LAST5_ SEARCH_TYP E = 'LAST5 ';
  17   var PID_SE ARCH_TYPE  = 'PID';
  18   var JDSPat ientAttrib uteWhiteli st = ['bir thDate', ' displayNam e', 'famil yName', 'f ullName',  'genderCod e', 'gende rName', 'g ivenNames' , 'icn', ' last5', 'l ocalId', ' pid', 'roo mBed', 'se nsitive',  'ssn', 'su mmary'];
  19  
  20   /**
  21    *
  22    * Retriev es patient  informati on from Vi stA and/or  JDS.
  23    *
  24    * @param  {Object} r eq - The r equest obj ect.
  25    * @param  {string} l ogMessageP refix - Us ed when lo gging mess ages to sh ow who is  calling th is - usefu l for debu gging.
  26    * @param  {Object} j dsServer -  Connectiv ity inform ation for  JDS. Pulle d from req .app.confi g.jdsServe r
  27    * @param  {Object} s earchOptio ns - The s earch opti ons for th e patient  search que ry.
  28    * @param  {string} s earchOptio ns.site -  The site t o search f or patient s on.
  29    * @param  {string} s earchOptio ns.searchT ype - The  type of se arch to pe rform. Can  be ICN, P ID, LAST5,  or NAME.
  30    * @param  {string} s earchOptio ns.searchS tring - Th e string t o search o n. ICN for  ICN searc h, PID for  PID searc h,
  31    *                  f irst lette r of last  name + las t 4 of SSN  for last5  search, f ull or par tial name  for name s earch.
  32    * @param  {function}  callback  - The func tion to ca ll when al l of the p atient dat a has been  retrieved .
  33    */
  34   module.exp orts.callP atientSear ch = funct ion(req, l ogMessageP refix, jds Server, se archOption s, callbac k) {
  35       var lo gger = req .logger;
  36       var si te = searc hOptions.s ite;
  37       var se archType =  searchOpt ions.searc hType;
  38       var se archString  = searchO ptions.sea rchString;
  39       if (se archType = == 'PID' & & !_.start sWith(sear chString,  site + ';' )) {
  40           re turn callb ack(logMes sagePrefix  + ' site  "' + site  + '" doesn \'t match  site in pi d "' + sea rchString  + '"');
  41       }
  42       var se nsitivePat ientAcknow leged = _. result(req , 'query._ ack') || _ .result(re q, 'params ._ack') ||  _.result( req, 'body ._ack') ||  false;
  43       var ha sDGAccess  = _.result (req, 'ses sion.user. dgSensitiv eAccess',  'false') = == 'true';
  44       var LO G_MESSAGE_ PREFIX = ' patient-se arch-util. callPatien tSearch';
  45       if (!l ogMessageP refix) {
  46           lo gMessagePr efix = LOG _MESSAGE_P REFIX;
  47       } else  {
  48           lo gMessagePr efix += '. ' + LOG_ME SSAGE_PREF IX;
  49       }
  50       var ha sHmpPatien tSelectRpc  = _.resul t(req, 'ap p.config.v istaSites[ ' + site +  '].hasHmp PatientSel ectRpc', n ull);
  51       logger .debug('%s  beginning  with hasH mpPatientS electRpc s et to %s',  logMessag ePrefix, h asHmpPatie ntSelectRp c);
  52  
  53       // If  the site i s configur ed to have  the HMP P ATIENT SEL ECT RPC, c all it to  retrieve t he patient  search re sults.
  54       // Oth erwise, ge t the pati ent search  results f rom JDS.
  55       if (ha sHmpPatien tSelectRpc  !== false ) {
  56           lo gger.debug ('%s perfo rming hmpP atientSele ct.fetch u sing site= %s &search Type=%s &s earchStrin g=%s', log MessagePre fix, site,  searchTyp e, searchS tring);
  57           hm pPatientSe lect.fetch (req, sear chOptions,  site, fun ction(erro r, respons e) {
  58                if (erro r) {
  59                    /**
  60                     * I f we get t he securit y error fr om the HMP  PATIENT S ELECT call  we assume  that the
  61                     * R PC is not  installed  at the sit e and need  to call p t-select f rom RC23 f orward
  62                     */
  63                    if ( error.matc h(NO_HMP_S ELECT_RPC_ ERR_REGEX) ) {
  64                         if (!_.isE mpty(_.res ult(req, ' app.config .vistaSite s[' + site  + ']', {} ))) {
  65                             //set  the config  to not ha ve hasHmpP atientSele ctRpc
  66                             req.ap p.config.v istaSites[ site].hasH mpPatientS electRpc =  false;
  67                             logger .trace(req .app.confi g.vistaSit es[site]);
  68                         }
  69                         logger.deb ug('%s per forming pt -select se arch after  failed hm pPatientSe lect attem pt using s ite=%s &se archType=% s &searchS tring=%s',  logMessag ePrefix, s ite, searc hType, sea rchString) ;
  70                         return sea rchJds.get Patients(r eq, search Options, j dsServer,  function(e rr, respon se) {
  71                             var op tions = {
  72                                 fi nalCB: cal lback,
  73                                 ha sDGAccess:  hasDGAcce ss,
  74                                 se nsitivePat ientAcknow leged: sen sitivePati entAcknowl eged,
  75                                 lo gMessagePr efix: logM essagePref ix
  76                             };
  77                             ptSele ctCB(err,  response,  logger, op tions);
  78                         });
  79                    }
  80                    logg er.error(l ogMessageP refix + 'E rror perfo rming sear ch [%s]',  (error.mes sage || er ror));
  81                    erro r.message  = 'There w as an erro r processi ng your re quest. The  error has  been logg ed: ';
  82                    retu rn callbac k(logMessa gePrefix +  ' ' + err or);
  83                }
  84  
  85                if (resp onse.statu sCode >= 3 00) {
  86                    logg er.error(l ogMessageP refix + 'r esponse.st atusCode [ %s]', resp onse.statu sCode);
  87                    retu rn callbac k({
  88                         status: re sponse.sta tusCode,
  89                         message: r esponse
  90                    });
  91                }
  92                // If we 're doing  a patient  search (i. e. NAME or  LAST5), t here's no  need to ca ll JDS.
  93                // If we 're doing  patient se lection, w e do need  to call JD S to get t he unmaske d SSN.
  94                if (sear chType ===  NAME_SEAR CH_TYPE ||  searchTyp e === LAST 5_SEARCH_T YPE) {
  95                    hmpP atientSele ctSearchCB (response,  {
  96                         req: req,
  97                         hasDGAcces s: hasDGAc cess,
  98                         logMessage Prefix: lo gMessagePr efix,
  99                         finalCB: c allback,
  100                         sensitiveP atientAckn owleged: s ensitivePa tientAckno wleged,
  101                         searchOpti ons: searc hOptions
  102                    });
  103                } else {
  104                    hmpP atientSele ctSelectio nCB(respon se, {
  105                         req: req,
  106                         jdsServer:  jdsServer ,
  107                         hasDGAcces s: hasDGAc cess,
  108                         logMessage Prefix: lo gMessagePr efix,
  109                         finalCB: c allback,
  110                         sensitiveP atientAckn owleged: s ensitivePa tientAckno wleged,
  111                         searchOpti ons: searc hOptions
  112                    });
  113                }
  114           }) ;
  115       } else  {
  116           lo gger.debug ('%s perfo rming pt-s elect sear ch instead  of hmpPat ientSelect  using sit e=%s &sear chType=%s  &searchStr ing=%s', l ogMessageP refix, sit e, searchT ype, searc hString);
  117           se archJds.ge tPatients( req, searc hOptions,  jdsServer,  function( err, respo nse) {
  118                var opti ons = {
  119                    fina lCB: callb ack,
  120                    hasD GAccess: h asDGAccess ,
  121                    sens itivePatie ntAcknowle ged: sensi tivePatien tAcknowleg ed,
  122                    logM essagePref ix: logMes sagePrefix
  123                };
  124                ptSelect CB(err, re sponse, lo gger, opti ons);
  125           }) ;
  126       }
  127   };
  128  
  129   var ptSele ctCB = fun ction(err,  response,  logger, o ptions) {
  130       var ha sDGAccess  = options. hasDGAcces s;
  131       var se nsitivePat ientAcknow leged = op tions.sens itivePatie ntAcknowle ged;
  132       if (er r) {
  133           re turn final PatientSea rchCallbac k(err, res ponse, log ger, optio ns);
  134       }
  135       if (_. result(res ponse, 'da ta.items',  []).lengt h > 0) {
  136           fo r (var i =  0; i < re sponse.dat a.items.le ngth; i +=  1) {
  137                var pati ent = resp onse.data. items[i];
  138                if (!pat ient.sensi tive || se nsitivePat ientAcknow leged || h asDGAccess ) {
  139                    pati ent = clea nJDSPatien tAttribute s(sensitiv ityUtils.r emoveSensi tiveFields (patient)) ;
  140                } else {
  141                    pati ent = sens itivityUti ls.hideSen sitiveFiel ds(patient );
  142                }
  143                patient  = searchUt il.transfo rmPatient( patient, t rue);
  144                response .data.item s[i] = pat ient;
  145           }
  146       }
  147       finalP atientSear chCallback (err, resp onse, logg er, option s);
  148   };
  149  
  150   var hmpPat ientSelect SelectionC B = functi on(respons e, options ) {
  151       var re q = option s.req;
  152       var lo gger = req .logger;
  153       var jd sServer =  options.jd sServer;
  154       var ha sDGAccess  = options. hasDGAcces s;
  155       var se nsitivePat ientAcknow leged = op tions.sens itivePatie ntAcknowle ged;
  156       var se archOption s = option s.searchOp tions;
  157       var si te = searc hOptions.s ite;
  158       var lo gMessagePr efix = opt ions.logMe ssagePrefi x ? option s.logMessa gePrefix +  '.hmpPati entSelectS electionCB ' : 'patie ntSearch.h mpPatientS electSelec tionCB';
  159  
  160       if (!_ .isEmpty(_ .result(re q, 'app.co nfig.vista Sites[' +  site + ']' , {}))) {
  161           // set the co nfig to ha ve hasHmpP atientSele ctRpc
  162           re q.app.conf ig.vistaSi tes[site]. hasHmpPati entSelectR pc = true;
  163           lo gger.trace (req.app.c onfig.vist aSites[sit e]);
  164       }
  165  
  166       async. mapSeries( response,  function(p atient, cb ) {
  167           lo gger.debug ('%s sensi tive flag  was %s for  patient % s;', logMe ssagePrefi x, patient .sensitive , patient. fullName);
  168  
  169           //  If patien t is sensi tive and t he user do esn't have  DG access  and they  haven't ac knowledged  that the  patient is  sensitive ,
  170           //  hide the  sensitive  fields and  return th e patient  data.
  171           if  (patient. sensitive  && !sensit ivePatient Acknowlege d && !hasD GAccess) {
  172                logger.t race(patie nt, logMes sagePrefix  + ' has a n _ack of  ' + sensit ivePatient Acknowlege d);
  173                patient  = sensitiv ityUtils.h ideSensiti veFields(p atient);
  174                patient  = searchUt il.transfo rmPatient( patient, f alse);
  175                return s etImmediat e(cb, null , patient) ;
  176           }
  177  
  178           lo gger.debug ('%s check ing for pa tient in J DS', logMe ssagePrefi x);
  179  
  180           //  Set up ca ll to retr ieve full  SSN from J DS since t he VistA d ata masks  it out
  181           va r options  = _.create (searchOpt ions, {
  182                searchSt ring: pati ent.pid,
  183                searchTy pe: PID_SE ARCH_TYPE
  184           }) ;
  185           se archJds.ge tPatients( req, optio ns, jdsSer ver, funct ion(err, j dsResult)  {
  186                if (err)  {
  187                    retu rn cb(err) ;
  188                }
  189                if (!_.i sEmpty(_.r esult(jdsR esult, 'da ta.items',  []))) {
  190                    pati ent.ssn =  _.result(_ .find(jdsR esult.data .items, fu nction(jds Patient) {
  191                         return !_. isEmpty(_. result(jds Patient, ' ssn', '')) ;
  192                    }),  'ssn', pat ient.ssn);
  193                    pati ent.sensit ive = _.re sult(_.fin d(jdsResul t.data.ite ms, functi on(jdsPati ent) {
  194                         return _.r esult(jdsP atient, 's ensitive',  false) == = true;
  195                    }),  'sensitive ', false);
  196                } else {
  197                    pati ent.sensit ive = fals e;
  198                }
  199  
  200                if (!pat ient.sensi tive || se nsitivePat ientAcknow leged || h asDGAccess ) {
  201                    pati ent = clea nJDSPatien tAttribute s(sensitiv ityUtils.r emoveSensi tiveFields (patient)) ;
  202                } else {
  203                    pati ent = sens itivityUti ls.hideSen sitiveFiel ds(patient );
  204                }
  205                patient  = searchUt il.transfo rmPatient( patient, f alse);
  206                return c b(null, pa tient);
  207           }) ;
  208       }, fun ction(err,  patients)  {
  209           va r retvalue  = {
  210                apiVersi on: '1.0',
  211                data: {
  212                    tota lItems: 0,
  213                    curr entItemCou nt: 0,
  214                    item s: []
  215                }
  216           };
  217           // format the  RPC data  to look li ke the JDS  data that  we used t o send bac k
  218           if  (!_.isEmp ty(patient s)) {
  219                retvalue .data.tota lItems = p atients.le ngth;
  220                retvalue .data.curr entItemCou nt = patie nts.length ;
  221                retvalue .data.item s = patien ts;
  222           }
  223           fi nalPatient SearchCall back(err,  retvalue,  logger, op tions);
  224       });
  225   };
  226  
  227   /**
  228    * Process es respons e from RPC  search in  the conte xt of a fu ll name or  last 5 se arch.
  229    *
  230    * @param  {Object} r esponse -  results fr om HMP PAT IENT SELEC T.
  231    * @param  {Object} o ptions - c ontains re levant inf o for proc essing the  results.
  232    */
  233   var hmpPat ientSelect SearchCB =  function( response,  options) {
  234       var re q = option s.req;
  235       var lo gger = req .logger;
  236       var ha sDGAccess  = options. hasDGAcces s;
  237       var se nsitivePat ientAcknow leged = op tions.sens itivePatie ntAcknowle ged;
  238       var se archOption s = option s.searchOp tions;
  239       var si te = searc hOptions.s ite;
  240       var se archType =  _.get(sea rchOptions , 'searchT ype');
  241       var se archString  = _.get(s earchOptio ns, 'searc hString');
  242       var lo gMessagePr efix = opt ions.logMe ssagePrefi x ? option s.logMessa gePrefix +  '.hmpPati entSelectS electionCB ' : 'patie ntSearch.h mpPatientS electSearc hCB';
  243  
  244       if (!_ .isEmpty(_ .result(re q, 'app.co nfig.vista Sites[' +  site + ']' , {}))) {
  245           // set the co nfig to ha ve hasHmpP atientSele ctRpc
  246           re q.app.conf ig.vistaSi tes[site]. hasHmpPati entSelectR pc = true;
  247           lo gger.trace ({ 'config .vistasite s': req.ap p.config.v istaSites[ site] }, ' hmpPatient SelectRpc  set to tru e');
  248       }
  249  
  250       logger .debug('%s  given a r esponse va lue', logM essagePref ix);
  251       logger .trace({ h mpPatientS electRpcRe sponse: re sponse });
  252  
  253       var fi ndComma =  new RegExp (COMMA_REG EX).test(s earchStrin g);
  254       var re moveImprop erlyMatche dPatients  = (searchT ype === NA ME_SEARCH_ TYPE && fi ndComma);
  255  
  256       // US1 7390 - If  there is a  comma in  our search String red uce the re sults from  the HMP P ATIENT SEL ECT RPC so  that the  last name  is an exac t match
  257       if (re moveImprop erlyMatche dPatients)  {
  258           va r improper lyMatchedP atients =  _.remove(r esponse, f unction la stNameExac tMatch(val ue, index,  array) {
  259                var myNa me = _.get (value, 'f ullName');
  260                var rege x = new Re gExp('^' +  _.escapeR egExp(sear chString),  'gi');
  261                var matc hed = rege x.test(myN ame);
  262                // This  is a rever sal of if  we find a  match. If  we find th e match we  are keepi ng it and  not removi ng
  263                return ! matched;
  264           }) ;
  265           re q.logger.d ebug(impro perlyMatch edPatients , logMessa gePrefix +  ' comma s eparated v alue retur ned these' );
  266       }
  267  
  268       async. mapSeries( response,  function(p atient, cb ) {
  269  
  270           pa tient = se nsitivityU tils.remov eSensitive Fields(pat ient);
  271  
  272           if  (patient. sensitive  && !sensit ivePatient Acknowlege d && !hasD GAccess) {
  273                patient  = sensitiv ityUtils.h ideSensiti veFields(p atient);
  274           }
  275  
  276           pa tient = se archUtil.t ransformPa tient(pati ent, false );
  277           re turn cb(nu ll, patien t);
  278  
  279       }, fun ction(err,  patients)  {
  280           va r retvalue  = {
  281                data: {
  282                    tota lItems: 0,
  283                    curr entItemCou nt: 0,
  284                    item s: []
  285                }
  286           };
  287           // format the  RPC data  to look li ke the JDS  data that  we used t o send bac k
  288           if  (!_.isEmp ty(patient s)) {
  289                retvalue .data.tota lItems = p atients.le ngth;
  290                retvalue .data.curr entItemCou nt = patie nts.length ;
  291                retvalue .data.item s = patien ts;
  292           }
  293           fi nalPatient SearchCall back(err,  retvalue,  logger, op tions);
  294       });
  295   };
  296  
  297   function f inalPatien tSearchCal lback(err,  data, log ger, optio ns) {
  298       var lo gMessagePr efix = opt ions.logMe ssagePrefi x ? option s.logMessa gePrefix +  '.finalPa tientSearc hCallback'  : 'patien tSearch.fi nalPatient SearchCall back';
  299       if (er r) {
  300           re turn optio ns.finalCB (err);
  301       }
  302       data.s tatus = 20 0;
  303       logger .trace(dat a, logMess agePrefix  + ' return ing result ');
  304       option s.finalCB( null, data );
  305   }
  306  
  307   /**
  308    * Retriev es patient  demograph ic informa tion from  JDS based  on a given  PID.
  309    *
  310    * @param  {Object} r eq - The r equest obj ect.
  311    * @param  {string} l ogMessageP refix - Us ed when lo gging mess ages to sh ow who is  calling th is - usefu l for debu gging.
  312    * @param  {string} s ite - The  site to se arch for p atients in .
  313    * @param  {string} s earchType  - The type  of search  to perfor m. Can be  ICN, PID,  LAST5, or  NAME.
  314    * @param  {string} p id - The P ID of the  patient to  search fo r.
  315    * @param  {function}  callback  - The func tion to ca ll when al l of the d ata has be en retriev ed.
  316    */
  317   module.exp orts.callJ DSPatientS earch = fu nction(req , logMessa gePrefix,  site, sear chType, pi d, callbac k) {
  318       var LO G_MESSAGE_ PREFIX = ' patient-se arch-util. callJDSPat ientSearch ';
  319       if (!l ogMessageP refix) {
  320           lo gMessagePr efix = LOG _MESSAGE_P REFIX;
  321       } else  {
  322           lo gMessagePr efix += '. ' + LOG_ME SSAGE_PREF IX;
  323       }
  324       var lo gger = req .logger;
  325       var se nsitivePat ientAcknow leged = _. result(req , 'query._ ack') || _ .result(re q, 'params ._ack') ||  _.result( req, 'body ._ack') ||  false;
  326  
  327       var ha sDGAccess  = _.result (req, 'ses sion.user. dgSensitiv eAccess',  'false') = == 'true';
  328  
  329       var op tions = _. extend({},  req.app.c onfig.jdsS erver, {
  330           ur l: '/vpr/'  + pid,
  331           lo gger: logg er,
  332           js on: true
  333       });
  334       logger .debug(log MessagePre fix + ' pe rforming s earch usin g pid=' +  pid);
  335       httpUt il.get(opt ions, func tion(error , response , result)  {
  336           if  (error) {
  337                logger.e rror(logMe ssagePrefi x + ' Erro r performi ng search  [%s]', (er ror.messag e || error ));
  338                error.me ssage = 'T here was a n error pr ocessing y our reques t. The err or has bee n logged:  ';
  339                return c allback(lo gMessagePr efix + ' '  + error,  null);
  340           }
  341           if  (response .statusCod e >= 300)  {
  342                logger.e rror(logMe ssagePrefi x + ' resp onse.statu sCode [%s] ', respons e.statusCo de);
  343                return c allback({
  344                    stat us: respon se.statusC ode,
  345                    mess age: resul t
  346                }, null) ;
  347           }
  348           if  (_.isEmpt y(_.result (result, ' data.items ', {}))) {
  349                return c allback(nu ll, {
  350                    data : {}
  351                });
  352           }
  353           _. each(resul t.data.ite ms, functi on(patient ) {
  354                if (_.is Undefined( patient.se nsitive))  {
  355                    pati ent.sensit ive = fals e;
  356                }
  357                if (!pat ient.sensi tive || se nsitivePat ientAcknow leged || h asDGAccess ) {
  358                    pati ent = clea nJDSPatien tAttribute s(sensitiv ityUtils.r emoveSensi tiveFields (patient)) ;
  359                } else {
  360                    pati ent = sens itivityUti ls.hideSen sitiveFiel ds(patient );
  361                }
  362                patient  = searchUt il.transfo rmPatient( patient, t rue);
  363           }) ;
  364  
  365           re turn callb ack(null,  result);
  366       });
  367   };
  368  
  369   /**
  370    * Removes  unnecessa ry attribu tes from a  patient.
  371    *
  372    * @param  {object} p atient - T he patient  to remove  attribute s from.
  373    * @return  {object}  cleanedPat ient - The  patient,  with unnec essary att ributes re moved.
  374    */
  375   function c leanJDSPat ientAttrib utes(patie nt) {
  376       var cl eanedPatie nt = _.pic k(patient,  JDSPatien tAttribute Whitelist) ;
  377       return  cleanedPa tient;
  378   }
  379  
  380   function v alidRespon seDataItem s(logger,  logMessage Prefix, re sponse) {
  381       //Chec k response .data.item s
  382       if (!r esponse) {
  383           lo gger.debug (logMessag ePrefix +  '_validRes ponseDataI tems got a  null resp onse objec t');
  384           re turn false ;
  385       } else  if (!resp onse.data)  {
  386           lo gger.debug (logMessag ePrefix +  '_validRes ponseDataI tems got a  response  object wit h no data' );
  387           re turn false ;
  388       } else  if (!resp onse.data. items) {
  389           lo gger.debug (logMessag ePrefix +  '_validRes ponseDataI tems got a  response  object wit h no data. items');
  390           re turn false ;
  391       }
  392       logger .debug(log MessagePre fix + '_va lidRespons eDataItems  got a val id respons e object') ;
  393       return  true;
  394   }
  395  
  396   /**
  397    * Pass in  'req.quer y.order' a nd the res ponse whic h contains  data.item s that nee d to be or dered.
  398    * Example : https:// ehmp.vista core.us/re source/pat ient-searc h/full-nam e?name.ful l=Seven&or der=givenN ames%20DES C
  399    *
  400    * @param  logger - r eq.logger  - The logg er
  401    * @param  logMessage Prefix - U sed when l ogging mes sages to s how who is  calling t his - usef ul for deb ugging.
  402    * @param  order - th e field (i n an insta nce of dat a.items) t hat you wa nt to sear ch on.
  403    * If this  is not pa ssed in, t he data wi ll be retu rned as it  is - no s orting wil l take pla ce.
  404    * After t he field y ou want to  search on , you can  specify on e of two v alues:
  405    * 'ASC' f or ascendi ng or 'DES C' for des cending.   If fieldNa me is supp lied but ' ASC' or 'D ESC' is no t supplied , 'ASC' wi ll be assu med.
  406    * @param  response T he object  containing  data.item s that was  returned  from the c all to cal lPatientSe arch
  407    */
  408   module.exp orts.sort  = function (logger, l ogMessageP refix, ord er, respon se) {
  409       //Chec k order ob ject
  410       if (!o rder) {
  411           lo gger.debug (logMessag ePrefix +  '_sort no  sort speci fied - ret urning dat a as is');
  412           re turn;
  413       }
  414       if (va lidRespons eDataItems (logger, l ogMessageP refix + '_ sort', res ponse) ===  false) {
  415           re turn;
  416       }
  417       var fi eldAndOrde r = order. split(' ') ;
  418       var fi eld = fiel dAndOrder[ 0];
  419       var fi eldOrder =  (fieldAnd Order.leng th === 2 ?  fieldAndO rder[1] :  'ASC');
  420       if (fi eldOrder)  {
  421           fi eldOrder =  fieldOrde r.toLowerC ase();
  422       }
  423       logger .debug(log MessagePre fix + '_so rt sorting  by ' + fi eldOrder);
  424       if (fi eldOrder = == 'desc')  {
  425           re sponse.dat a.items =  _.sortBy(r esponse.da ta.items,  field).rev erse();
  426       } else  {
  427           re sponse.dat a.items =  _.sortBy(r esponse.da ta.items,  field);
  428       }
  429   };
  430  
  431   /**
  432    * Because  lodash tr eats NaN a s a number , we need  a way to e nsure that  the value  passed in  is not on ly a numbe r but
  433    * that it  is not a  floating v alue.
  434    *
  435    * @param  num the va riable to  check to s ee if it's  a whole n umber
  436    * @return s {boolean } True if  a whole nu mber.
  437    */
  438   function i sWholeNumb er(num) {
  439       if (nu llChecker. isNullish( num)) {
  440           re turn false ;
  441       }
  442       if (ty peof num = == 'string ' && _.isE mpty(num))  {
  443           re turn false ;
  444       }
  445       return  num % 1 = == 0;
  446   }
  447  
  448   /**
  449    *
  450    * @param  logger - r eq.logger  - The logg er
  451    * @param  logMessage Prefix - U sed when l ogging mes sages to s how who is  calling t his - usef ul for deb ugging.
  452    * @param  start Wher e do you w ant to sta rt returni ng results .
  453    * @param  limit How  many resul ts do you  want retur ned for ea ch page.
  454    * @param  response T he object  containing  data.item s that was  returned  from the c all to cal lPatientSe arch
  455    */
  456   module.exp orts.limit  = functio n(logger,  logMessage Prefix, st art, limit , response ) {
  457       logger .debug(log MessagePre fix + '_li mit start= ' + start  + ', limit =' + limit );
  458       if (!l imit) {
  459           lo gger.debug (logMessag ePrefix +  '_limit no  limit spe cified - r eturning d ata as is' );
  460           re turn;
  461       }
  462       if (!i sWholeNumb er(limit))  {
  463           lo gger.debug (logMessag ePrefix +  '_limit li mit specif ied was no t a whole  number - r eturning d ata as is' );
  464           re turn;
  465       }
  466       if (!s tart) {
  467           st art = 0;
  468       }
  469       if (!i sWholeNumb er(start))  {
  470           lo gger.debug (logMessag ePrefix +  '_limit st art specif ied was no t a whole  number - s etting to  zero');
  471           st art = 0;
  472       }
  473       limit  = Number(l imit);
  474       start  = Number(s tart);
  475       if (va lidRespons eDataItems (logger, l ogMessageP refix + '  limit', re sponse) == = false) {
  476           lo gger.debug (logMessag ePrefix +  '_limit va lidRespons eDataItems  returned  false - re turning da ta as is') ;
  477           re turn;
  478       }
  479       var to talItems =  response. data.items .length;
  480       limit  = limit <  totalItems  ? limit :  totalItem s; //If li mit is big ger than t otalItems,  then just  use total Items
  481  
  482       respon se.data.it emsPerPage  = limit;
  483       respon se.data.st artIndex =  start;
  484       respon se.data.pa geIndex =  (start / l imit | 0);  // jshint  ignore:li ne
  485       respon se.data.to talPages =  (totalIte ms / limit  | 0) + (t otalItems  % limit >  0 ? 1 : 0) ; // jshin t ignore:l ine
  486       logger .debug({
  487           li mit: limit ,
  488           it emsPerPage : _.result (response,  'data.ite msPerPage' , 'No item s per page  found'),
  489           st art: _.res ult(respon se, 'data. startIndex ', 'No sta rt found') ,
  490           pa geIndex: _ .result(re sponse, 'd ata.pageIn dex', 'No  page index  found'),
  491           to talPages:  _.result(r esponse, ' data.total Pages', 'N o total pa ges found' ),
  492           to talItems:  _.result(r esponse, ' data.total Items', 'N o total it ems count  found')
  493       }, log MessagePre fix + 'res ponse data ');
  494  
  495       if (li mit > 0) {
  496           lo gger.debug (logMessag ePrefix +  '_limit st art(' + ty peof start  + ')=' +  start + ',  limit(' +  typeof li mit + ')='  + limit);
  497           va r end = st art + limi t;
  498           lo gger.debug ({
  499                items: r esponse.da ta.items
  500           },  logMessag ePrefix +  '_limit re sponse.dat a.items -  before sli ce(' + sta rt + ', '  + end + ') ');
  501           re sponse.dat a.items =  response.d ata.items. slice(star t, end);
  502           lo gger.debug ({
  503                items: r esponse.da ta.items
  504           },  logMessag ePrefix +  '_limit re sponse.dat a.items -  after slic e(' + star t + ', ' +  end + ')' );
  505       } else  {
  506           lo gger.debug ({
  507                items: r esponse.da ta.items
  508           },  logMessag ePrefix +  '_limit re sponse.dat a.items -  before sli ce(' + sta rt + ')');
  509           re sponse.dat a.items =  response.d ata.items. slice(star t);
  510           lo gger.debug ({
  511                items: r esponse.da ta.items
  512           },  logMessag ePrefix +  '_limit re sponse.dat a.items -  after slic e(' + star t + ')');
  513       }
  514       logger .debug(log MessagePre fix + '_li mit respon se.data.it ems.length : ' + resp onse.data. items.leng th);
  515       respon se.data.cu rrentItemC ount = res ponse.data .items.len gth;
  516       logger .debug(log MessagePre fix + '_li mit respon se.data.cu rrentItemC ount: ' +  response.d ata.curren tItemCount );
  517   };
  518  
  519   function v alidateFil ter(logger , logMessa gePrefix,  filter, re sponse) {
  520       if (!f ilter) {
  521           lo gger.debug (logMessag ePrefix +  '_validate Filter no  filter spe cified - r eturning d ata as is' );
  522           re turn false ;
  523       }
  524       if (va lidRespons eDataItems (logger, l ogMessageP refix + '  filter', r esponse) = == false)  {
  525           re turn false ;
  526       }
  527       if (!_ .startsWit h(filter,  'eq(')) {
  528           lo gger.warn( logMessage Prefix + ' _validateF ilter filt er was not  eq - retu rning data  as is');
  529           re turn false ;
  530       }
  531       if (!_ .endsWith( filter, ') ')) {
  532           lo gger.warn( logMessage Prefix + ' _validateF ilter filt er was not  of the fo rm eq(fiel dName,"fie ldValue")  - no closi ng paren -  returning  data as i s');
  533           re turn false ;
  534       }
  535       if (_. indexOf(fi lter, ',')  === -1) {
  536           lo gger.warn( logMessage Prefix + ' _validateF ilter filt er was not  of the fo rm eq(fiel dName,"fie ldValue")  - no comma  - returni ng data as  is');
  537           re turn false ;
  538       }
  539       return  true;
  540   }
  541  
  542   function p arseFilter (logger, l ogMessageP refix, fil ter) {
  543       var st r = filter .slice(3,  filter.len gth - 1);
  544       var fi eldName =  _.trim(str .slice(0,  _.indexOf( str, ',')) , '\'"');
  545       var fi eldValue =  _.trim(st r.slice(_. indexOf(st r, ',') +  1, str.len gth), '\'" ');
  546       return  {
  547           fi eldName: f ieldName,
  548           fi eldValue:  fieldValue
  549       };
  550   }
  551   /**
  552    * Pass in  'req.quer y.filter'  and the re sponse whi ch contain s data.ite ms that ne ed to be o rdered.
  553    * Example : http:// IP                /resource/ locations/ clinics/pa tients?uid =urn:va:lo cation: R E D A CTED :23&filter =eq(family Name,%22EI GHT%22)
  554    *
  555    * @param  logger - r eq.logger  - The logg er
  556    * @param  logMessage Prefix - U sed when l ogging mes sages to s how who is  calling t his - usef ul for deb ugging.
  557    * @param  filter - t he filter  that follo ws the pat tern &quot ;eq(fieldN ame,"field Value")&qu ot;.  Only  eq is sup ported
  558    * and if  this patte rn is not  found, we  just retur n the data  as it is.
  559    * @param  response T he object  containing  data.item s that was  returned  from the c all to cal lPatientSe arch
  560    */
  561   module.exp orts.filte r = functi on(logger,  logMessag ePrefix, f ilter, res ponse) {
  562       logger .debug(log MessagePre fix + '_fi lter filte ring by '  + filter);
  563       if (!v alidateFil ter(logger , logMessa gePrefix +  '_filter' , filter,  response))  {
  564           re turn;
  565       }
  566       var fi eldNameAnd Value = pa rseFilter( logger, lo gMessagePr efix + '_f ilter', fi lter);
  567       var fi eldName =  fieldNameA ndValue.fi eldName;
  568       var fi eldValue =  fieldName AndValue.f ieldValue;
  569       logger .debug({
  570           fi eldName: f ieldName,
  571           fi eldValue:  fieldValue
  572       }, log MessagePre fix + '_fi lter field Name and f ieldValue' );
  573  
  574       fieldV alue = fie ldValue.to LowerCase( );
  575       var ne wItems = _ .filter(re sponse.dat a.items, f unction(it em) {
  576           lo gger.trace ({
  577                item: it em
  578           },  logMessag ePrefix +  '_filter p erforming  filter');
  579  
  580           va r actualVa lue = item [fieldName ];
  581           lo gger.debug (logMessag ePrefix +  '_filter a ctualValue  is ' + ac tualValue) ;
  582  
  583           if  (nullChec ker.isNotN ullish(act ualValue))  {
  584                actualVa lue = actu alValue.to LowerCase( );
  585           }
  586           lo gger.debug (logMessag ePrefix +  '_filter p erforming  actualValu e is now '  + actualV alue);
  587  
  588           if  (actualVa lue === fi eldValue)  {
  589                logger.d ebug(logMe ssagePrefi x + '_filt er actualV alue (' +  actualValu e + ') mat ches field Value (' +  fieldValu e + ')');
  590                return t rue;
  591           }  else {
  592                logger.d ebug(logMe ssagePrefi x + '_filt er actualV alue (' +  actualValu e + ') doe s NOT matc h fieldVal ue (' + fi eldValue +  ')');
  593                return f alse;
  594           }
  595       });
  596       logger .debug(log MessagePre fix + '_fi lter filte ring finis hed');
  597       respon se.totalIt ems = newI tems.lengt h;
  598       respon se.current ItemCount  = newItems .length;
  599       respon se.data.it ems = newI tems;
  600   };
  601  
  602   function g etSiteFrom Pid(pid) {
  603       if (nu llChecker. isNotNulli sh(pid) &&  S(pid).co ntains(';' )) {
  604           re turn pid.s plit(';')[ 0];
  605       }
  606       return  undefined ;
  607   }
  608  
  609   /**
  610    * Retriev es the sit e from the  session,  pid, or re quest - if  not found , null is  returned.
  611    *
  612    * @param  logger - r eq.logger  - The logg er
  613    * @param  logMessage Prefix - U sed when l ogging mes sages to s how who is  calling t his - usef ul for deb ugging.
  614    * @param  pid - the  pid that c ould conta in the sit e.
  615    * @param  req The re quest that  could con tain the s ite
  616    */
  617   module.exp orts.getSi te = funct ion(logger , logMessa gePrefix,  pid, req)  {
  618       logger .debug(log MessagePre fix + '_ge tSite retr ieving sit e');
  619       var si te;
  620       req.lo gger.debug (logMessag ePrefix +  '_getSite  pid=' + pi d);
  621       site =  getSiteFr omPid(pid) ;
  622       if (nu llChecker. isNotNulli sh(site))  {
  623           re q.logger.d ebug(logMe ssagePrefi x + '_getS ite obtain ed site ('  + site +  ') from pi d');
  624           re turn site;
  625       }
  626       if (nu llChecker. isNotNulli sh(req)) {
  627           si te = req.p aram('site ');
  628           if  (nullChec ker.isNotN ullish(sit e)) {
  629                req.logg er.debug(l ogMessageP refix + '_ getSite ob tained sit e (' + sit e + ') fro m request' );
  630                return s ite;
  631           }
  632       }
  633       if (_. has(req, ' session.us er.site'))  {
  634           si te = req.s ession.use r.site;
  635           re q.logger.d ebug(logMe ssagePrefi x + '_getS ite obtain ed site ('  + site +  ') from re q.session. user.site' );
  636           re turn site;
  637       }
  638       req.lo gger.error (logMessag ePrefix +  '_getSite  unable to  obtain sit e from req uest');
  639       return  null;
  640   };