37. EPMO Open Source Coordination Office Redaction File Detail Report

Produced by Araxis Merge on 10/4/2017 8:04:33 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.

37.1 Files compared

# Location File Last Modified
1 rdk.zip\rdk\product\production\rdk\src\interceptors synchronize.js Mon Aug 28 19:41:48 2017 UTC
2 rdk.zip\rdk\product\production\rdk\src\interceptors synchronize.js Tue Oct 3 18:08:48 2017 UTC

37.2 Comparison summary

Description Between
Files 1 and 2
Text Blocks Lines
Unchanged 6 1206
Changed 5 10
Inserted 0 0
Removed 0 0

37.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

37.4 Active regular expressions

No regular expressions were active.

37.5 Comparison detail

  1   'use stric t';
  2  
  3   let _ = re quire('lod ash');
  4   let moment  = require ('moment') ;
  5  
  6   let isPars edToPositi veInteger  = require( '../utils/ integer-ch ecker').is ParsedToPo sitiveInte ger;
  7  
  8  
  9   // Perhaps  a future  version sh ould use t he jds-syn c-subsyste m.js
  10   // impleme ntation ra ther than  including  this here.
  11   const stan dardErrorR esponse =  {
  12       status : 500,
  13       data:  {
  14           er ror: {
  15                code: 50 0,
  16                message:  'There wa s an error  processin g your req uest. The  error has  been logge d.'
  17           }
  18       }
  19   };
  20  
  21   // Perhaps  a future  version sh ould use t he jds-syn c-subsyste m.js
  22   // impleme ntation ra ther than  including  this here.
  23   const noSi teResponse  = {
  24       status : 404,
  25       data:  {
  26           er ror: {
  27                code: 40 4,
  28                message:  'This pat ient\'s re cord is no t yet acce ssible. Pl ease try a gain in a  few minute s. If it i s still no t accessib le, please  contact y our HIMS r epresentat ive and ha ve the pat ient loade d into you r local Vi stA.'
  29           }
  30       }
  31   };
  32  
  33   /*
  34   function i ntercept(r eq, res, n ext)
  35  
  36   For testin g purposes , you can  inject a j dsSync ins tance in t he req par ameter:
  37   req.jdsSyn c = { // t est implem entation o bject }.
  38  
  39   To write a  test harn ess, you m ust provid e the foll owing:
  40  
  41   req = {
  42       audit:  {},
  43       sessio n: {
  44           us er: {
  45                  site: ' R E D A CTED ' // the ' mySite' va lue
  46           }
  47       },
  48       interc eptors: {
  49           sy nchronize:  {
  50                disabled : false
  51           }
  52       },
  53       param:  function( key) {
  54           if (key === ' pid') {
  55                  return ' R E D A CTED ;3'; // th e pid of t he test pa tient
  56           }
  57       },
  58       params : {
  59             pid: ' R E D A CTED ;3' // the  pid of th e test pat ient
  60       },
  61       logger : {
  62           tr ace: funct ion() {},
  63           de bug: funct ion() {},
  64           in fo: functi on() {},
  65           wa rn: functi on() {},
  66           er ror: funct ion() {},
  67           fa tal: funct ion() {},
  68       },
  69       app: {
  70           co nfig: {
  71                resync:  {
  72                    inac tivityTime outMillis:  1000 * 60  * 60 * 24 ,
  73                    last SyncMaxInt ervalMilli s: 1000 *  60 * 10,
  74                    erro rCooldownM inInterval Millis: 10 00 * 60
  75                },
  76                jdsSync:  {
  77                    sett ings: {
  78                         waitMillis : 1000,
  79                         timeoutMil lis: 1000  * 60 * 7,
  80                         syncExists WaitDelayM illis: 100 0 * 30
  81                    }
  82                },
  83                vxSyncSe rver: {
  84                      baseUrl: ' http:// IP             '
  85                },
  86                jdsServe r: {
  87                      baseUrl: ' http:// IP                ',
  88                    urlL engthLimit : 120
  89                }
  90           }
  91       },
  92       jdsSyn c: { // Th ese are al l implemen ted in jds -sync-subs ystem.js
  93           ge tPatientSt atusSimple : function (pid, req,  callback)  {},
  94           wa itForPatie ntLoad: fu nction(pid , mySite,  req, callb ack) {},
  95           cl earPatient : function (pid, req,  callback)  {},
  96           lo adPatientP rioritized : function (pid, mySi te, req, c allback) { }
  97       }
  98   }
  99  
  100   res = {
  101       status : function (statusCod e) {
  102           re turn {
  103                rdkSend:  function( response)  {};
  104           };
  105       }
  106   }
  107  
  108   */
  109   function i ntercept(r eq, res, n ext) {
  110       req.lo gger.debug ('synchron ize.interc ept()');
  111  
  112       let co nfig = req .app.confi g;
  113       let lo gger = req .logger;
  114  
  115       let jd sSync = re q.jdsSync  || _.get(r eq, ['app' , 'subsyst ems', 'jds Sync']);
  116  
  117       // Thi s is the " mySite" (i f any)
  118       let my Site = _.g et(req, [' session',  'user', 's ite'], nul l);
  119  
  120       // Thi s is the m aximum amo unt of tim e that is  allowed to  pass with out an upd ate
  121       // to  a job stat us or meta stamp befo re the job  is consid ered to ha ve stalled .
  122       let in activityTi meoutMilli s = config .resync.in activityTi meoutMilli s || 1000  * 60 * 60  * 24;
  123  
  124       // Thi s is the a mount of t ime betwee n calls to  the sync  endpoint.  This is be cause
  125       // we  don't want  to call s ync every  time, but  we need to  call it o ften enoug h to
  126       // ens ure that s econdary d ata can't  get too st ale. VxSyn c doesn't  duplicate  sync
  127       // req uests (i.e . multiple  sync requ ests on th e same pat ient at th e same tim e),
  128       // but  too many  calls clos e together  can degra de perform ance.
  129       let la stSyncMaxI ntervalMil lis = conf ig.resync. lastSyncMa xIntervalM illis || 1 000 * 60 *  10;
  130  
  131       // Thi s is the m inimum amo unt of tim e that mus t pass bef ore the ex istence of  a "hasErr or"
  132       // att ribute in  the sync s tatus for  "MySite" c auses a ne w sync. Er rors withi n less tha n than
  133       // tha t interval  of time w ill cause  the interc eptor to r eturn with  the stand ard error  response.
  134       let er rorCooldow nMinInterv alMillis =  config.re sync.error CooldownMi nIntervalM illis || 1 000 * 60;
  135       if (is Intercepto rDisabled( config)) {
  136           lo gger.warn( 'synchroni ze.interce pt() inter ceptor dis abled');
  137           re turn next( );
  138       }
  139  
  140       // Not e: Strictl y speaking , this is  not a 'pid ' the way  VxSync def ines that  term.
  141       // It  is just a  general 'p atient-ide ntifier'.
  142       let pi d = _.get( req, 'quer y.pid') ||  _.get(req , 'body.pi d') || _.g et(req, 'p arams.pid' );
  143  
  144       if (!p id) {
  145           lo gger.debug ('synchron ize.interc ept() no " pid" query  parameter , skip syn c');
  146           re turn next( );
  147       }
  148  
  149       // Syn chronizing  a patient  on an EDI PI or ICN  type ident ifier mean s that the
  150       // syn c intercep tor will w ait for th e first si te to comp lete befor e returnin g.
  151       if (is Edipi(pid)  || isIcn( pid)) {
  152           lo gger.debug ('synchron ize.interc ept() "pid " value wa s an EDIPI  or ICN. W aiting for  pid: [%s]  sync on a t least on e site', p id);
  153           re turn waitF orFirstSit ePatientSy nc(logger,  config, j dsSync, pi d, req, re s, next);
  154       }
  155  
  156       jdsSyn c.getPatie ntStatusSi mple(pid,  req, funct ion(error,  syncStatu s) {
  157           lo gger.trace ({
  158                status:  syncStatus
  159           },  `synchron ize.interc ept() call ed jdsSync .getPatien tStatusSim ple() for  sync statu s check fo r pid: [${ pid}], myS ite: [${my Site}]`);
  160           lo gger.debug (`synchron ize.interc ept() call ed jdsSync .getPatien tStatusSim ple() for  sync statu s check fo r pid: [${ pid}], myS ite: [${my Site}]`);
  161  
  162           //  1. on err or, next()
  163           lo gger.debug ('synchron ize.interc ept() 1. C heck for e rror retri eving simp le sync st atus for p id: [%s],  mySite: [% s]', pid,  mySite);
  164           if  (error ||  _.isEmpty (syncStatu s) || _.is Empty(sync Status.dat a) || (syn cStatus.da ta.error & & syncStat us.data.er ror.code ! == 404)) {
  165                logger.w arn({
  166                    erro r: error
  167                }, `sync hronize.in tercept()  1a. Error  retrieving  simple sy nc status  for pid: [ ${pid}], m ySite: [${ mySite}];  skipping`) ;
  168                return n ext();
  169           }
  170  
  171           //  2. patien t not foun d, therefo re, it sho uld be syn chronized
  172           lo gger.info( 'synchroni ze.interce pt() 2. Ch eck for pa tient not  found, syn chronizing  pid: [%s] , mySite:  [%s]', pid , mySite);
  173           if  (syncStat us.data.er ror && syn cStatus.da ta.error.c ode === 40 4) {
  174                logger.i nfo('synch ronize.int ercept() 2 a. Patient  not found , synchron izing pid:  [%s], myS ite: [%s]' , pid, myS ite);
  175                return s yncPatient (config, l ogger, jds Sync, pid,  mySite, r eq, res, n ext);
  176           }
  177  
  178           //  3. if syn c for mySi te is comp lete, chec k to be su re that sy nc() doesn 't need to  be called
  179           lo gger.debug ('synchron ize.interc ept() 3. c heck if pa tient pid:  [%s], myS ite: [%s]  is synced,  but excee ds timeout ', pid, my Site);
  180           le t mySiteSy nchComplet e = _.get( syncStatus , ['data',  'sites',  mySite, 's yncComplet ed']);
  181           if  (mySiteSy nchComplet e) {
  182                if (isSy ncLastUpda teTimeoutE xceeded(sy ncStatus.d ata, lastS yncMaxInte rvalMillis )) {
  183                    logg er.trace(s yncStatus,  'synchron ize.interc ept() 3a.  Patient pi d: [%s], m ySite: [%s ] synced,  but exceed ed timeout , resyncin g patient' , pid, myS ite);
  184                    logg er.info('s ynchronize .intercept () 3a. Pat ient pid:  [%s], mySi te: [%s] s ynced, but  exceeded  timeout, r esyncing p atient', p id, mySite );
  185                    retu rn syncPat ient(confi g, logger,  jdsSync,  pid, mySit e, req, re s, next);
  186                }
  187  
  188                logger.i nfo('synch ronize.int ercept() 3 b. Patient  pid: [%s] , mySite:  [%s] is sy nced and s yncPatient () does no t need to  be called' , pid, myS ite);
  189                return n ext();
  190           }
  191  
  192           //  4. if lat estEnterpr iseSyncReq uestTimest amp is emp ty, clear  and resync
  193           //  Failing t o clear th e patient  first can  result in  the VistA  data not b eing synce d.
  194           //  This cond ition can  happen whe n all of a  patient's  job histo ry is dele ted. Even
  195           //  though th e patient  sync was c omplete, r emoving th e job hist ory will c ause the
  196           //  latestEnt erpriseSyn cRequestTi mestamp fi eld to con tain an em pty string  (and the
  197           //  sync stat us to be i ncomplete) .
  198           lo gger.debug ('jds-sync -subsystem .waitForPa tientLoad( ) 4. check SimpleStat us() check  pid: [%s] , priority Site: [%s] , latestEn terpriseSy ncRequestT imestamp:  [%s] is an  integer',  pid, mySi te, syncSt atus.data. latestEnte rpriseSync RequestTim estamp);
  199           if  (!isParse dToPositiv eInteger(s yncStatus. data.lates tEnterpris eSyncReque stTimestam p)) {
  200                logger.i nfo('synch ronize.int ercept() 4 a. latestE nterpriseS yncRequest Timestamp: [%s] is no t an integ er, cleari ng and res yncing pid : [%s], my Site: [%s] ', syncSta tus.data.l atestEnter priseSyncR equestTime stamp, pid , mySite);
  201                return c learThenSy ncPatient( config, lo gger, jdsS ync, pid,  mySite, re q, res, ne xt);
  202           }
  203  
  204           //  5. if the re is an e rror in th e sync sta tus for my Site, chec k to sync  again or j ust return  an error
  205           lo gger.info( 'synchroni ze.interce pt() 5. Ch eck for er ror found  in site [% s] sync st atus for p id: [%s]',  mySite, p id);
  206           le t errorInS ite = _.ge t(syncStat us, ['data ', 'sites' , mySite,  'hasError' ], false);
  207           if  (errorInS ite) {
  208                if (isEr rorCooldow nTimeoutEx ceeded(syn cStatus.da ta, errorC ooldownMin IntervalMi llis)) {
  209                    logg er.info('s ynchronize .intercept () 5a. An  error foun d in site  [%s] for p id: [%s],  cooldown e xceeded, r esyncing p atient', m ySite, pid );
  210                    retu rn syncPat ient(confi g, logger,  jdsSync,  pid, mySit e, req, re s, next);
  211                }
  212  
  213                logger.i nfo('synch ronize.int ercept() 5 b. An erro r found in  site [%s]  for pid:  [%s], cool down excee ded, retur ning error  message',  mySite, p id);
  214                return r es.status( 500).rdkSe nd('There  was an err or process ing your r equest. Th e error ha s been log ged.');
  215           }
  216  
  217           //  6. if the  sync has  been inact ive too lo ng, clearA ndSync()
  218           lo gger.info( 'synchroni ze.interce pt() 6. Ch eck if the  activity  timeout wa s exceeded  and patie nt should  be cleared  and resyn ced pid: [ %s], mySit e: [%s]',  pid, mySit e);
  219           if  (!syncSta tus.data.s yncComplet ed && isSy ncLastUpda teTimeoutE xceeded(sy ncStatus.d ata, inact ivityTimeo utMillis))  {
  220                logger.i nfo('synch ronize.int ercept() 6 a. The act ivity time out was ex ceeded, cl earing and  resyncing  pid: [%s] , mySite:  [%s]', pid , mySite);
  221                return c learThenSy ncPatient( config, lo gger, jdsS ync, pid,  mySite, re q, res, ne xt);
  222           }
  223  
  224           //  7. if the  sync for  mySite is  not comple te and syn c is not " stuck", wa it for syn c on mySit e to compl ete
  225           lo gger.info( 'synchroni ze.interce pt() 7a. C heck if pa tient pid:  [%s], myS ite: [%s]  sync in pr ogress nor mally and  sync inter ceptor sho uld contin ue to wait ', pid, my Site);
  226           if  (!mySiteS ynchComple te && !isS yncLastUpd ateTimeout Exceeded(s yncStatus. data, inac tivityTime outMillis) ) {
  227                logger.i nfo('synch ronize.int ercept() 7 a. Patient  pid: [%s] , mySite:  [%s] sync  already in  progress- -awaiting  completion ', pid, my Site);
  228                return w aitForPati entSync(co nfig, logg er, jdsSyn c, pid, my Site, req,  res, next );
  229           }
  230  
  231           //  8. In all  other cas es, sync t he patient  (note: th is will us e 'mySite'  to determ ine comple tion)
  232           lo gger.trace (syncStatu s, 'synchr onize.inte rcept() 8.  Calling s ync for pi d: [%s], m ySite: [%s ]', pid, m ySite);
  233           lo gger.info( 'synchroni ze.interce pt() 8. Ca lling sync  for pid:  [%s], mySi te: [%s]',  pid, mySi te);
  234           sy ncPatient( config, lo gger, jdsS ync, pid,  mySite, re q, res, ne xt);
  235       });
  236   }
  237  
  238  
  239   function w aitForPati entSync(co nfig, logg er, jdsSyn c, pid, my Site, req,  res, next ) {
  240       logger .debug('sy nchronize. waitForPat ientSync()  waiting f or pid: [% s], mySite : [%s]', p id, mySite );
  241  
  242       jdsSyn c.waitForP atientLoad (pid, mySi te, req, f unction(er ror, resul t) {
  243           lo gger.debug ('synchron ize.waitFo rPatientSy nc() start  sync wait  callback  for pid: [ %s], mySit e: [%s]',  pid, mySit e);
  244           if  (error) {
  245                let stat us = _.isN umber(erro r) ? error  : 500;
  246                return r es.status( status).rd kSend(resu lt || erro r);
  247           }
  248  
  249           ne xt();
  250       });
  251   }
  252  
  253   function c learThenSy ncPatient( config, lo gger, jdsS ync, pid,  mySite, re q, res, ne xt) {
  254       logger .debug('sy nchronize. clearThenS yncPatient () Clearin g, then sy nchronizin g pid: [%s ], mySite:  [%s]', pi d, mySite) ;
  255  
  256       jdsSyn c.clearPat ient(pid,  req, funct ion(error,  result) {
  257           lo gger.debug ('synchron ize.clearT henSyncPat ient() sta rt syncCle ar callbac k for pid:  [%s], myS ite: [%s]' , pid, myS ite);
  258           if  (error) {
  259                let stat us = _.isN umber(erro r) ? error  : 500;
  260                result =  result ||  error;
  261                return r es.status( status).rd kSend(resu lt);
  262           }
  263  
  264           sy ncPatient( config, lo gger, jdsS ync, pid,  mySite, re q, res, ne xt);
  265       });
  266   }
  267  
  268   function s yncPatient (config, l ogger, jds Sync, pid,  mySite, r eq, res, n ext) {
  269       logger .debug('sy nchronize. syncPatien t() Synchr onizing pi d: [%s], m ySite: [%s ]', pid, m ySite);
  270  
  271       jdsSyn c.loadPati entPriorit ized(pid,  mySite, re q, functio n(error, r esult) {
  272           lo gger.debug ('synchron ize.syncPa tient() ca lled jdsSy nc.loadPat ientPriori tized() fo r pid: [%s ], mySite:  [%s]', pi d, mySite) ;
  273           if  (error) {
  274                if (erro r === 404)  {
  275                    logg er.debug(' synchroniz e.syncPati ent() 404  syncLoad f or pid: [% s], mySite : [%s]', p id, mySite );
  276                    retu rn res.sta tus(404).r dkSend(noS iteRespons e);
  277                }
  278  
  279                logger.e rror('sync hronize.sy ncPatient( ) Error sy nchronizin g pid: [%s ], mySite:  [%s]', pi d, mySite) ;
  280                logger.e rror('sync hronize.sy ncPatient( ) %j', err or);
  281                return r es.status( 500).rdkSe nd('There  was an err or process ing your r equest. Th e error ha s been log ged.');
  282           }
  283  
  284           if  (!result)  {
  285                logger.e rror('sync hronize.sy ncPatient( ) Empty re sponse fro m sync sub system for  pid: [%s] , mySite:  [%s]', pid , mySite);
  286                return r es.status( 500).rdkSe nd('There  was an err or process ing your r equest. Th e error ha s been log ged.');
  287           }
  288  
  289           if  (!_.has(r esult, 'da ta') || re sult.statu s === 500)  {
  290                logger.e rror('sync hronize.sy ncPatient( ) Error re sponse fro m sync sub system for  `pid: [%s ], mySite:  [%s]`', p id, mySite );
  291                logger.e rror('sync hronize.sy ncPatient( ) %j', res ult);
  292  
  293                return n ext();
  294           }
  295  
  296           if  (result.s tatus ===  404) {
  297                logger.d ebug('sync hronize.sy ncPatient( ) 404 sync Load for p id: [%s],  mySite: [% s]', pid,  mySite);
  298                return r es.status( result.sta tus).rdkSe nd(result) ;
  299           }
  300  
  301           lo gger.trace (result);
  302  
  303           lo gger.debug ('synchron ize.syncPa tient() sy ncLoad for  pid: [%s] , mySite:  [%s] - con tinue', pi d, mySite) ;
  304           ne xt();
  305       });
  306   }
  307  
  308  
  309   /*
  310   Variadic F unction
  311   function w aitForFirs tSitePatie ntSync(log ger, confi g, pid, re q, res, ne xt, startT ime)
  312   function w aitForFirs tSitePatie ntSync(log ger, confi g, pid, re q, res, ne xt)
  313  
  314   startTime:  not inclu ded in the  initial r ecursive c all
  315   */
  316   function w aitForFirs tSitePatie ntSync(log ger, confi g, jdsSync , pid, req , res, nex t, startTi me) {
  317       logger .debug('sy nchronize. waitForFir stSitePati entSync()  pid: [%s]' , pid);
  318  
  319       jdsSyn c.getPatie ntStatusSi mple(pid,  req, funct ion(error,  syncStatu s) {
  320           lo gger.trace ({
  321                status:  syncStatus
  322           },  `synchron ize.waitFo rFirstSite PatientSyn c() called  jdsSync.g etPatientS tatusSimpl e() for pi d: [${pid} ] for firs t site`);
  323  
  324           //  1. error  returned i n callback
  325           lo gger.debug ('synchron ize.waitFo rFirstSite PatientSyn c() 1. Che ck for err or retriev ing simple  sync stat us for pid : [%s]', p id);
  326           if  (error ||  _.isEmpty (syncStatu s) || _.is Empty(sync Status.dat a) || (syn cStatus.da ta.error & & syncStat us.data.er ror.code ! == 404)) {
  327                logger.w arn({
  328                    erro r: error
  329                }, `sync hronize.wa itForFirst SitePatien tSync() 1a . Error re trieving s imple sync  status fo r pid: [${ pid}], so  skipping`) ;
  330                return n ext();
  331           }
  332  
  333           le t now = Da te.now();
  334           le t loopDela yMillis =  config.jds Sync.setti ngs.waitMi llis;
  335           le t syncTime outMillis  = config.j dsSync.set tings.time outMillis;
  336           le t syncExis tsWaitDela yMillis =  config.jds Sync.setti ngs.syncEx istsWaitDe layMillis;
  337  
  338           //  if sync h as already  started,  try to use  the earli est timest amp/stampT ime for th e
  339           //  sync time out calcul ation. Oth erwise use  the curre nt time. N ote that t his should
  340           //  only be r elevant to  the first  invocatio n of waitF orFirstSit ePatientSy nc() in th e
  341           //  recursive  call stac k.
  342           st artTime =  startTime  || minMome nt([new Da te(),
  343                syncStat us.data.la testEnterp riseSyncRe questTimes tamp,
  344                syncStat us.data.la testJobTim estamp,
  345                moment(s yncStatus. data.lates tSourceSta mpTime, 'Y YYYMMDDHHm mss')
  346           ]) .valueOf() ;
  347  
  348           lo gger.debug ('synchron ize.waitFo rFirstSite PatientSyn c() pid: [ %s] loopDe layMillis= %s syncTim eoutMillis =%s syncEx istsWaitDe layMillis= %s', pid,  loopDelayM illis, syn cTimeoutMi llis, sync ExistsWait DelayMilli s);
  349  
  350           //  2. 404 an d 404-time out exceed ed: res.se nd(result. status, re sult.data)
  351           lo gger.debug ('synchron ize.waitFo rFirstSite PatientSyn c() 2. Che ck for pat ient not f ound, pid:  [%s]', pi d);
  352           if  (syncStat us.data.er ror && syn cStatus.da ta.error.c ode === 40 4) {
  353                if (isSy ncExistsDe layAtTimeo ut(startTi me, syncEx istsWaitDe layMillis) ) {
  354                    logg er.warn('s ynchronize .waitForFi rstSitePat ientSync()  2a. Patie nt Sync fo r pid: [%s ], timeout  waiting t o get past  404 statu s', pid);
  355                    retu rn res.sta tus(404).r dkSend(noS iteRespons e);
  356                }
  357  
  358                logger.i nfo('synch ronize.wai tForFirstS itePatient Sync() 2b.  Patient s ync for [% s] returne d 404, con tinuing to  wait', pi d);
  359                return s etTimeout( waitForFir stSitePati entSync, l oopDelayMi llis, logg er, config , jdsSync,  pid, req,  res, next , startTim e);
  360           }
  361  
  362           //  3. Patien t Sync com plete: nex t()
  363           lo gger.info( 'synchroni ze.waitFor FirstSiteP atientSync () 3. Chec k if sync  complete f or at leas t one site  for pid:  [%s]', pid );
  364           if  (isOneSit eCompleted (syncStatu s)) {
  365                logger.i nfo('synch ronize.wai tForFirstS itePatient Sync() 3a.  Sync comp lete for a t least on e site for  pid: [%s] ', pid);
  366                return n ext();
  367           }
  368  
  369           //  4. if lat estEnterpr iseSyncReq uestTimest amp is emp ty, this w ill not re solve, so  return an  error
  370           //  This cond ition can  happen whe n all of a  patient's  job histo ry is dele ted. Even
  371           //  though th e patient  sync was c omplete, r emoving th e job hist ory will c ause the
  372           //  latestEnt erpriseSyn cRequestTi mestamp fi eld to con tain an em pty string  (and the
  373           //  sync stat us to be i ncomplete) .
  374           lo gger.debug ('synchron ize.waitFo rFirstSite PatientSyn c() 4. pid : [%s], la testEnterp riseSyncRe questTimes tamp: [%s] ', pid, sy ncStatus.d ata.latest Enterprise SyncReques tTimestamp );
  375           if  (!isParse dToPositiv eInteger(s yncStatus. data.lates tEnterpris eSyncReque stTimestam p)) {
  376                logger.e rror('sync hronize.wa itForFirst SitePatien tSync() 4a . checkSim pleStatus( ) pid: [%s ], latestE nterpriseS yncRequest Timestamp:  [%s] is n ot an Inte ger', pid,  syncStatu s.data.lat estEnterpr iseSyncReq uestTimest amp);
  377                return r es.status( 500).rdkSe nd(standar dErrorResp onse);
  378           }
  379  
  380           //  5. sync t imeout exc eeded befo re at leas t one site  completed : return s tandardErr orResponse
  381           lo gger.info( 'synchroni ze.waitFor FirstSiteP atientSync () 5. Chec k for time out while  waiting fo r at least  one site  to complet e for sync  on pid: [ %s]', pid) ;
  382           if  (now - st artTime >  syncTimeou tMillis) {
  383                logger.w arn('synch ronize.wai tForFirstS itePatient Sync() 5a.  Timeout w aiting for  at least  one site t o complete  for sync  on pid: [% s]', pid);
  384                return r es.status( 500).rdkSe nd(standar dErrorResp onse);
  385           }
  386  
  387           //  6. All si tes are in  Sync/Job  error
  388           lo gger.info( 'synchroni ze.waitFor FirstSiteP atientSync () 6. Chec k if sync  had an err or every s ite status  for pid:  [%s]', pid );
  389           if  (isEveryS iteInError (syncStatu s)) {
  390                logger.w arn('synch ronize.wai tForFirstS itePatient Sync() 6a.  Sync had  an error e very site  status for  pid: [%s] ', pid);
  391                return r es.status( 500).rdkSe nd(standar dErrorResp onse);
  392           }
  393  
  394           //  7. everyt hing else:  wait and  test again
  395           lo gger.info( 'synchroni ze.waitFor FirstSiteP atientSync () 7. Pati ent sync f or [%s] no t yet comp lete for a t least on e site, co ntinuing t o wait', p id);
  396           re turn setTi meout(wait ForFirstSi tePatientS ync, loopD elayMillis , logger,  config, jd sSync, pid , req, res , next, st artTime);
  397       });
  398   }
  399  
  400  
  401   function i sOneSiteCo mpleted(si mpleSyncSt atus) {
  402       let da ta = _.get (simpleSyn cStatus, ' data', {}) ;
  403  
  404       if (da ta.syncCom pleted) {
  405           re turn true;
  406       }
  407  
  408       return  _.some(da ta.sites,  function(s ite) {
  409           re turn site. syncComple ted;
  410       });
  411   }
  412  
  413   function i sEverySite InError(si mpleSyncSt atus) {
  414       let da ta = _.get (simpleSyn cStatus, ' data', {}) ;
  415  
  416       if (_. isEmpty(da ta.sites))  {
  417           re turn false ;
  418       }
  419  
  420       return  _.every(d ata.sites,  function( site) {
  421           re turn site. hasError;
  422       });
  423   }
  424  
  425  
  426   function i sIntercept orDisabled (config) {
  427       return  _.has(con fig, 'inte rceptors')  && _.has( config.int erceptors,  'synchron ize') && c onfig.inte rceptors.s ynchronize .disabled  === true;
  428   }
  429  
  430   function i sPid(id) {
  431       return  !_.isEmpt y(id) && / ^[0-9a-fA- F]{4};[0-9 ]+$/.test( id);
  432   }
  433  
  434   function i sIcn(id) {
  435       return  !_.isEmpt y(id) && / \w+V\w+/.t est(id);
  436   }
  437  
  438   function i sEdipi(id)  {
  439       return  !_.isEmpt y(id) && / ^DOD;\d+/. test(id);
  440   }
  441  
  442  
  443   /*
  444   Returns th e earliest  moment or  undefined  if the it ems in the  moments a rray
  445   are not va lid Date/t imestamp/m oment obje cts. Note  the value  returned i s a
  446   moment obj ect.
  447   */
  448   function m inMoment(m oments) {
  449       return  fetchMome ntByCriter ium(moment .prototype .isBefore,  moments);
  450   }
  451  
  452  
  453   /*
  454   Returns th e earliest  moment or  undefined  if the it ems in the  moments a rray
  455   are not va lid Date/t imestamp/m oment obje cts. Note  the value  returned i s a
  456   moment obj ect.
  457   */
  458   function m axMoment(m oments) {
  459       return  fetchMome ntByCriter ium(moment .prototype .isAfter,  moments);
  460   }
  461  
  462  
  463   /*
  464   Returns th e last mom ent that p asses the  func test  or undefin ed if the
  465   items in t he moments  array are  not valid  Date/time stamp/mome nt objects .
  466   Note the v alue retur ned is a m oment obje ct.
  467   */
  468   function f etchMoment ByCriteriu m(func, mo ments) {
  469       if (_. isUndefine d(moments)  || _.isNu ll(moments )) {
  470           re turn;
  471       }
  472  
  473       moment s = !_.isA rray(momen ts) ? [mom ents] : mo ments;
  474       let va lue;
  475  
  476       _.each (moments,  function(m omentObj)  {
  477           if  (moment.i sDate(mome ntObj) ||  _.isNumber (momentObj )) {
  478                momentOb j = moment (momentObj );
  479           }
  480  
  481           if  (moment.i sMoment(mo mentObj) & & momentOb j.isValid( ) && (_.is Undefined( value) ||  func.call( momentObj,  value)))  {
  482                value =  momentObj;
  483           }
  484       });
  485  
  486       return  value;
  487   }
  488  
  489   /*
  490   Variadic F unction:
  491   function i sSyncExist sDelayAtTi meout(star tTime, syn cExistsWai tDelayMill is, now) {
  492   function i sSyncExist sDelayAtTi meout(star tTime, syn cExistsWai tDelayMill is) {
  493  
  494   startTime:  a millise cond times tamp value  similar t o what is  returned b y Date.now ()
  495  
  496   syncExists WaitDelayM illis: a m illisecond  value.
  497  
  498   now: a mil lisecond t imestamp v alue simil ar to what  is return ed by Date .now()
  499   If now is  not includ ed or is n ot a numbe r, this fu nction wil l generate  a value
  500   using Date .now()
  501   */
  502   function i sSyncExist sDelayAtTi meout(star tTime, syn cExistsWai tDelayMill is, now) {
  503       if (!_ .isNumber( now)) {
  504           no w = Date.n ow();
  505       }
  506  
  507       return  now - sta rtTime > s yncExistsW aitDelayMi llis;
  508   }
  509  
  510   /*
  511   Variadic F unction:
  512   function i sSyncLastU pdateTimeo utExceeded (status, i nactivityT imeoutMill is, now)
  513   function i sSyncLastU pdateTimeo utExceeded (status, i nactivityT imeoutMill is)
  514  
  515   This funct ion checks  an all-si tes simple  patient s ync status  to determ ine
  516   if the syn c has "sta lled". The  sync is c onsidered  to have st alled if t he
  517   interval b etween now  and the l ast time u pdate (i.e . the late st value o f
  518   all of the  non-site  time value s: job tim estamp and  metaStamp Time) exce eds
  519   the value  of 'inacti vityTimeou tMills'.
  520  
  521   status: Th is should  be a gener al simple  patient sy nc status  (i.e. not  site-speci fic).
  522  
  523   now: This  should be  either a n umber valu e (e.g. Da te().getTi me()) or a  Date obje ct.
  524   In any oth er instanc e, the val ue of now  will be th e current  date/time.
  525   */
  526   function i sSyncLastU pdateTimeo utExceeded (status, i nactivityT imeoutMill is, now) {
  527       if (_. isEmpty(st atus)) {
  528           re turn false ;
  529       }
  530  
  531       let la testEnterp riseSyncRe questTimes tamp = mom ent(status .latestEnt erpriseSyn cRequestTi mestamp);
  532       let la testJobTim estamp = m oment(stat us.latestJ obTimestam p);
  533       let la testSource StampTime  = moment(s tatus.late stSourceSt ampTime, ' YYYYMMDDHH mmss');
  534  
  535       let la testUpdate Time = max Moment([la testEnterp riseSyncRe questTimes tamp, late stJobTimes tamp, late stSourceSt ampTime]);
  536  
  537       if (!l atestUpdat eTime) {
  538           re turn false ;
  539       }
  540  
  541       if (mo ment.isDat e(now) ||  _.isNumber (now)) {
  542           no w = moment (now);
  543       } else  {
  544           no w = moment ();
  545       }
  546  
  547       // dea dline is t he latest  time for t he last up date witho ut the
  548       // int erceptor c onsidering  the patie nt sync to  have stal led.
  549       let de adline = n ow.subtrac t(inactivi tyTimeoutM illis, 'mi lliseconds ');
  550  
  551       return  latestUpd ateTime.is Before(dea dline);
  552   }
  553  
  554   /*
  555   Variadic F unction:
  556   function i sErrorCool downTimeou tExceeded( status, er rorCooldow nMinInterv alMillis,  now)
  557   function i sErrorCool downTimeou tExceeded( status, er rorCooldow nMinInterv alMillis)
  558  
  559   This funct ion checks  a simple  patient sy nc status  to determi ne if the  cooldown p eriod
  560   since the  sync was s tarted has  passed.
  561  
  562   status: Th is should  be a simpl e patient  sync statu s.
  563  
  564   now: This  should be  either a n umber valu e (e.g. Da te().getTi me()) or a  Date obje ct.
  565   In any oth er instanc e, the val ue of now  will be th e current  date/time.
  566   */
  567   function i sErrorCool downTimeou tExceeded( status, er rorCooldow nMinInterv alMillis,  now) {
  568       if (_. isEmpty(st atus)) {
  569           re turn false ;
  570       }
  571  
  572       let la testEnterp riseSyncRe questTimes tamp = mom ent(status .latestEnt erpriseSyn cRequestTi mestamp);
  573  
  574       if (la testEnterp riseSyncRe questTimes tamp < 1)  {
  575           re turn false ;
  576       }
  577  
  578       if (mo ment.isDat e(now) ||  _.isNumber (now)) {
  579           no w = moment (now);
  580       } else  {
  581           no w = moment ();
  582       }
  583  
  584       // dea dline is t he latest  time for t he last up date witho ut the
  585       // int erceptor c onsidering  the patie nt sync to  have stal led.
  586       let de adline = n ow.subtrac t(errorCoo ldownMinIn tervalMill is, 'milli seconds');
  587  
  588       return  latestEnt erpriseSyn cRequestTi mestamp.is Before(dea dline);
  589   }
  590  
  591  
  592   module.exp orts = int ercept;
  593   intercept. _isSyncLas tUpdateTim eoutExceed ed = isSyn cLastUpdat eTimeoutEx ceeded;
  594   intercept. _isErrorCo oldownTime outExceede d = isErro rCooldownT imeoutExce eded;
  595   intercept. _isInterce ptorDisabl ed = isInt erceptorDi sabled;
  596   intercept. _maxMoment  = maxMome nt;
  597   intercept. _minMoment  = minMome nt;
  598   intercept. _isPid = i sPid;
  599   intercept. _isIcn = i sIcn;
  600   intercept. _isEdipi =  isEdipi;
  601   intercept. _clearThen SyncPatien t = clearT henSyncPat ient;
  602   intercept. _syncPatie nt = syncP atient;
  603   intercept. _waitForFi rstSitePat ientSync =  waitForFi rstSitePat ientSync;
  604   intercept. _isSyncExi stsDelayAt Timeout =  isSyncExis tsDelayAtT imeout;
  605   intercept. _isOneSite Completed  = isOneSit eCompleted ;
  606   intercept. _isEverySi teInError  = isEveryS iteInError ;
  607   intercept. _standardE rrorRespon se = stand ardErrorRe sponse;
  608   intercept. _noSiteRes ponse = no SiteRespon se;