44. EPMO Open Source Coordination Office Redaction File Detail Report

Produced by Araxis Merge on 4/18/2019 5:55:21 PM Eastern 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.

44.1 Files compared

# Location File Last Modified
1 MCCF_EDI_TAS_TASCore v2.0_Build_10.zip\MCCF_EDI_TAS_API_Core README.md Wed Apr 3 02:27:40 2019 UTC
2 MCCF_EDI_TAS_TASCore v2.0_Build_10.zip\MCCF_EDI_TAS_API_Core README.md Mon Apr 15 17:23:23 2019 UTC

44.2 Comparison summary

Description Between
Files 1 and 2
Text Blocks Lines
Unchanged 4 1774
Changed 3 6
Inserted 0 0
Removed 0 0

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

44.4 Active regular expressions

No regular expressions were active.

44.5 Comparison detail

  1   # TAS API
  2  
  3   ## Install ing
  4  
  5   TAS API fu nctionalit y is desig ned to be  run via Do cker in bo th product ion and, f or non-GFE  developme nt (see be low notes  for GFE se tup).
  6  
  7   While this  is not re quired in  developmen t, it grea tly simpli fies the w orkflow, e xpands cap abilities,  adds secu rity emula tion, and  enables en tire infra structures  to be spu n up and d own togeth er.
  8  
  9   Building s upport for  the TAS A PI is just  the build ing of a s ingle bina ry from th e `./src`  folde :
  10  
  11       docker  build . - f Dockerfi le.dev -t  tas/node8
  12  
  13   > tas/node 8 is the b inary for  all Node 8  work
  14  
  15   To setup p rerequisit es for the  TAS API,  run the fo llowing fr om the `./ src` folde r:
  16  
  17       docker  run -v $P WD:/var/ap p tas/node 8 npm inst all
  18  
  19   Most comma nds will b e prefixed  with `doc ker run -i t -v $PWD: /var/app`.
  20  
  21   ## Running
  22  
  23   With the ` tas/node8`  binary bu ilt, run i t from `./ src`, spec ifying the  code loca tion (`$PW D`) and th e port to  use (3000) :
  24  
  25       docker  run -it - v $PWD:/va r/app -p 3 000:3000 t as/node8
  26  
  27   > By defau lt, tas/no de8 will r un PM2, wh ich will r un the API . See belo w for more  examples  that use o ther tools  in the sa me binary.
  28  
  29   You can ad d environm ent variab les (`-e`)  as needed . The foll owing will  enable ve rbose outp ut for man y parts of  the API:
  30  
  31       docker  run -it - e "DEBUG=a pi,elastic ,azure,fs, core,memor y,webclien t,errors"  -v $PWD:/v ar/app -p  3000:3000  tas/node8
  32  
  33   > Common d ebug filte rs: `test,  security,  errors, h ttp, api,  api-intern al, core,  providers,  elastic,  azure`
  34  
  35   Set produc tion mode  by setting  the `NODE _ENV` envi ronment va riable.
  36  
  37   ### Tests
  38  
  39   Run tests  with `moch a`:
  40  
  41       docker  run -it - v $PWD:/va r/app tas/ node8 moch a
  42  
  43   With verbo se output:
  44  
  45       docker  run -it - e "DEBUG=a pi,elastic ,azure,fs, core,memor y,webclien t,errors"  -v $PWD:/v ar/app tas /node8 moc ha
  46  
  47   This will  run tests  in the `te st/` folde r. Running  `mocha ta s/test` wi ll run TAS  API inter nal tests.
  48  
  49   Run tests  on save wi th `-w`. T his is hel pful durin g developm ent.
  50  
  51   A more com plete exam ple:
  52  
  53         docker run  -v `pwd`: /var/app - e "DEBUG=a pi,api-int ernal,azur e,data,fs, core,memor y,webclien t,errors"  -e "TAS_EN ABLE_HTTP= true" -e " TAS_ACTIVA TE=logging ,health,me trics" -e  "TAS_PROVI DERS=LOGGI NG:memory, Metabase:a zure" -e " TAS_AZURE_ STORAGE_NA ME=tasstor age01" -e  "TAS_AZURE _STORAGE_K EY=qv806MR EPLACE_WIT H_ACTUAL_K EYnDWQ=="  -e "TAS_MO NGODB_CONN ECTION_STR ING=mongod b:// IP             7" -e "TAS _MONGODB_D ATABASE=te st tas/nod e8 mocha - w tas/test
  54  
  55   ## GFE Set up
  56  
  57   Developmen t using No de relies  on events  from the f ile system . When you  save a fi le, PM2 wi ll automat ically rel oad your a pplication  or Mocha  will rerun  all your  tests. How ever, beca use the GF E VPN bloc ks traffic  directly  to a local  VM, these  events ar e never ra ised. Ther efore, Doc ker develo pment is n ot feasibl e on a GFE .
  58  
  59   > Merely r unning the  API as a  dependency  (e.g. to  support FH IR or the  UI), the a forementio ned Docker  setup is  perfectly  fine for r unning the  API.
  60  
  61   To run the  API for d evelopment , you setu p everythi ng directl y on the G FE:
  62  
  63   1) Ensure  Git for Wi ndows is i nstalled.
  64  
  65   2) Start g it bash.
  66  
  67   3) Run `gi t clone ss h://git@bi tbucket.or g/halfaker /mccf_tas_ api`, then  move to ` src` folde r)
  68  
  69   4) Run `np m install  -g pm2@2.9 .3 mocha@4 .1.0` to s etup PM2 ( runs the A PI) and Mo cha (runs  tests)
  70  
  71   5) Run the  following  inside th e src fold er (most w ork will b e done in  the src fo lder): `np m install`
  72  
  73   <<<<<<< HE AD
  74   =======
  75   6a) To run  the API,  run `pm2 s tart -x ta s/app.js`,  but for a ny practic al work, y ou'll want  to ensure  a few env ironment v ariables a re set in  that insta nce of the  API, ther efore run:  `DEBUG=ap i,memory T AS_IMPERSO NATE_SEC_I D=_0000111 11 TAS_ENA BLE_OVERRI DDEN_IAM_T RAITS=true  TAS_ENVIR ONMENT=dev  pm2 start  -x tas/ap p.js --nam e=api`. Yo u will add  and remov e modules  from DEBUG  throughou t your day -to-day de velopment.
  76  
  77   6b) To run  tests, th e environm ent variab les are th e same, bu t instead  of pm2, yo u wil run  mocha: `DE BUG=api,me mory TAS_I MPERSONATE _SEC_ID=_0 00011111 T AS_ENABLE_ OVERRIDDEN _IAM_TRAIT S=true TAS _ENVIRONME NT=dev moc ha`
  78  
  79   You can vi ew pm2 log s with `pm 2 logs api `.
  80  
  81   ### Enviro nment Vari ables
  82  
  83   >>>>>>> c8 d08cf6cfb3 01005470c8 d9540b2d29 2091613a
  84   * `DEBUG`  - Node.js  debugging  module. Se e examples  in this d ocument fo r usage.
  85  
  86   * `TAS_ENA BLE_HTTP`  - set to ` true` to a llow HTTP  calls. Whe n not set  to `true`,  HTTP call s will not  occur.
  87  
  88   * `TAS_PRO VIDERS` -  used to st ate which  APIs use w hich dynam ic data pr oviders. D efault is  `memory`.  Example: ` TAS_PROVID ERS=LOGGIN G:memory,M etabase:az ure`
  89  
  90   * `TAS_APP LICATION_L OGIN_URL`  - IAM logi n page; va ries per e nvironment
  91  
  92   * `TAS_ACT IVATE` - u sed to act ive only s pecific AP Is. For ex ample, one  deploymen t may only  have logg ing, but a nother may  only have  config. D efault is  to activat e all endp oints. Exa mple: `TAS _ACTIVATE= logging,he alth,metri cs`
  93  
  94   The follow ing are en vironment  variables  used to sp ecify conn ection inf ormation.  These exis t for each  data prov ider.
  95  
  96   * `TAS_FIL ESYSTEM_DA TA_ROOT` -  should no rmally be  set to  `/ var/log/ta s`; use Do cker volum e mapping  to specify  actual lo cation; ** NOT FOR PR ODUCTION U SE**.
  97  
  98   * `TAS_MON GODB_CONNE CTION_STRI NG`
  99  
  100   * `TAS_MON GODB_DATAB ASE`
  101  
  102   * `TAS_AZU RE_STORAGE _NAME`
  103  
  104   * `TAS_AZU RE_STORAGE _KEY`
  105    
  106   * `TAS_QUE UE_ADDRESS ` - Queue  service ad dress; doe s not spec ify a spec ific queue . Example:  `TAS_QUEU E_ADDRESS= amqp://tas -queue:567 2`
  107  
  108   ## Archite cture
  109  
  110   To ensure  proper sep aration of  concerns,  layer bou ndaries ar e defined  and decoup led.
  111  
  112   These laye rs include : activati on (analog ous to pre sentation) , core, an d data.
  113  
  114   The activa tion later  is how fu nctionalit y is calle d (e.g. HT TP/AMQP),  the core i s the busi ness funct ionality,  and the da ta layer p rovides a  dynamic pr ovider mod el to allo w simplifi ed data-ac cess.
  115  
  116   Future sec tions of t his docume nt will ex plain each  in furthe r details.
  117  
  118   ![Architec ture](./re adme/archi tecture.pn g)
  119  
  120   ## Usage
  121  
  122   ### API Co mmands
  123  
  124   The primar y way to w ork with t he TAS API  is via co mmands. Th ese abstra ct much of  the under lying plum bing for b oth HTTP e ndpoints a nd AMQP. T hey're you r primary  way of cre ating func tionality  with the T AS API.
  125  
  126   Raw HTTP v erbs (e.g.  GET, PATC H, POST) t ie your ap plication  closely to  a specifi c protocol  and don't  relate di rectly to  business r equirement s. Command s are prot ocol agnos tic and ex pand your  capabiliti es to stre tch to you r requirem ents. POST  and PATCH  do not ob vious rela te to busi ness requi rements, b ut "conver t", "trans late", "in crement" m ight.
  127  
  128   Focusing o n the desi gn of HTTP  verb usag e distract s from the  implement ation of b usiness re quirements  and is un necessaril y limiting . Therefor e, it's be st to focu s on comma nds in the  TAS API.
  129  
  130   Refer to v 3 of the e xample end point for  a full in- line comma nd example  (a better , more mod ular appro ach is dis cussed lat er).
  131  
  132   Consider t he followi ng snippet :
  133  
  134       const  commands =  new $tas. command.Re gistry()
  135  
  136       //+ pr e-defined,  general u se command s
  137       const  { Incremen tCommand,  MultiplyCo mmand } =  require('. ./commands ')
  138  
  139       comman ds
  140           .s tart('incr ement')
  141           .n ext(new In crementCom mand())
  142           .e nd()
  143  
  144   This loads  a series  of predefi ned comman ds and add s their fu nctionalit y to an AP I endpoint . If the e ndpoint na me is `exa mple` usin g `data` ( see later  section on  namespace s and vers ioning), t hese could  be access ed via the  following  paths:
  145  
  146       /api/c ore/exampl e/data/inc rement
  147  
  148   This comma nd model h elps to hi de abstrac t notions  of HTTP ve rbs and al lows direc t implemen tation bas ed on requ irements.  You aren't  calling a n abstract  "POST", y ou're call ing a conc rete "incr ement".
  149  
  150   Commands c an also be  built inl ine withou t being de fined for  reuse:
  151  
  152       //+ in line-creat ed command s
  153       comman ds.create( 'create',  async (sc,  values, a dapter) =>  adapter.c reateRespo nse($tas.c onstants.s tatus.SUCC ESS, await  example.p ersist(sc,  values)))
  154       comman ds.create( 'findById' , async (s c, values,  adapter)  => {
  155           tr y {
  156                const re sult = awa it example .getOne(sc .secid, va lues.id)
  157                return a dapter.cre ateRespons e($tas.con stants.sta tus.SUCCES S, result)
  158           }
  159           ca tch (ex) {
  160                if (ex.m essage.ind exOf('not  found') >  -1) {
  161                    retu rn adapter .createRes ponse($tas .constants .status.NO T_FOUND)
  162                }
  163                return a dapter.cre ateRespons e($tas.con stants.sta tus.ERROR,  'Unexpect ed error.' )
  164           }
  165       })
  166       comman ds.create( 'delete',  async (sc,  values, a dapter) =>  {
  167           aw ait exampl e.delete(s c.secid, v alues.id)
  168           re turn adapt er.createR esponse($t as.constan ts.status. NO_CONTENT )
  169       })
  170  
  171   These woul d be acces sible via  the follow ing paths:
  172  
  173       /api/c ore/exampl e/data/cre ate
  174       /api/c ore/exampl e/data/fin dById
  175       /api/c ore/exampl e/data/del ete
  176  
  177   > Commands  are not m eant to be  tied to H TTP, thus  the use of  the adapt er.
  178  
  179   You would  call this  API as fol lows:
  180  
  181       POST / api/core/e xample/dat a/create
  182  
  183       BODY:  { "a": 1,  "b": 2, "c ": 3 }
  184  
  185   ### Comman d Manageme nt
  186  
  187   These comm ands can b e organize d into the ir own fil es, allowi ng for ind ependent G it version  control a nd managem ent. These  are the c ommands us ed in a pr evious exa mple.
  188  
  189       ./comm ands
  190           ./ fhir
  191                fhir-gro up-by.js
  192                fhir-que ry.js
  193           co ncat.js
  194           in crement.js
  195           in dex.js
  196           mu ltiply.js
  197  
  198   Each comma nd is in i ts own fil e, but is  combined a nd exporte d in index .js (this  is an arti fact of ho w Node.js  works):
  199  
  200       "use s trict"
  201  
  202       const  $tas = req uire('../. ./../tas') ()
  203  
  204       //+ pr e-defined,  general u se command s
  205       const  fhir = req uire('./fh ir')
  206       const  ConcatComm and = requ ire('./con cat')
  207       const  IncrementC ommand = r equire('./ increment' )
  208       const  MultiplyCo mmand = re quire('./m ultiply')
  209  
  210       module .exports =  { QueryCo mmand, fhi r, ConcatC ommand, In crementCom mand, Mult iplyComman d }
  211  
  212   ### Comman d Examples
  213  
  214   Consider t he followi ng command  in its ow n file:
  215  
  216       class  extends $t as.command .Command {
  217           as ync run(se curityCont ext, value s, adapter ) {
  218                return a dapter.cre ateRespons e($tas.con stants.sta tus.SUCCES S, values. number + 1 )
  219           }
  220       }
  221  
  222   Called wit h:
  223       
  224       POST / api/core/e xample/mod ular/incre ment
  225  
  226       BODY:  {
  227           "n umber": 9
  228       }
  229  
  230   This would  call Incr ementComma nd and ret urn `{ "nu mber": 10  }`.
  231  
  232   Consider t he followi ng more pr actical ex ample:
  233  
  234       const  $tas = req uire('../. ./../tas') ()
  235  
  236       module .exports =  class ext ends $tas. command.Co mmand {
  237           as ync run(se curityCont ext, value s, adapter ) {
  238                return a dapter.cre ateRespons e($tas.con stants.sta tus.SUCCES S, await $ tas.fhir.c all(values .resource,  values.pa rams, secu rityContex t))
  239           }
  240       }
  241  
  242   This comma nd would b e accessed  with the  following  (the comma nd name "f hir" is se t in the ` super('fhi r')` line  in the pre vious exam ple):
  243  
  244       POST / api/core/e xample/v2/ fhir
  245  
  246       BODY:  {
  247           "r esource":  "Bundle",
  248           "p arams": {
  249                "locatio n": "001",
  250                "transac tion": "27 0",
  251                "_id": " 1"
  252           }
  253       }
  254  
  255   ### Comman d Pipeline s
  256  
  257   Commands c an also be  run in se ries. Cons ider the f ollowing e xample:
  258  
  259       comman ds
  260           .s tart('ampl ify')
  261           .n ext(new In crementCom mand())
  262           .n ext(new Mu ltiplyComm and())
  263           .e nd()
  264  
  265   This examp le uses th e same Inc rementComm and shown  previously , but also  the follo wing: Mult iplyComman d:
  266  
  267       class  extends $t as.command .Command {
  268           as ync run(_,  values, a dapter) {
  269                return a dapter.cre ateRespons e($tas.con stants.sta tus.SUCCES S, { numbe r: parseIn t(values.n umber) * 1 0 })
  270           }
  271       }
  272  
  273   This will  declare a  publicly c ommand nam ed "amplif y".
  274  
  275   This would  be called  with:
  276   <<<<<<< HE AD
  277       
  278   =======
  279  
  280   >>>>>>> c8 d08cf6cfb3 01005470c8 d9540b2d29 2091613a
  281       POST / api/core/e xample/v2/ amplify
  282  
  283       BODY:  { "number" : 4 }
  284  
  285   The result  will be ` { "number" : 50 }`. T his is bec ause Incre mentComman d will inc rement 4 t o 5, then  Multiply w ill scale  5 to 50.
  286  
  287   Here's an  example wi th data ac cess:
  288  
  289       comman ds
  290           .s tart('fhir -query')
  291           .n ext(new fh ir.FhirQue ryCommand( ))
  292           .n ext(new fh ir.FhirGro upByComman d())
  293           .e nd()
  294  
  295   This pipel ine uses t he followi ng two com mands:
  296  
  297   `FhirQuery Command`:
  298  
  299       class  extends $t as.command .Command {
  300           as ync run(se curityCont ext, value s, adapter ) {
  301                const tt l = $tas.c onstants.O NE_MINUTE  * 5
  302                const ke y = 'fhir: :' + JSON. stringify( values.res ource) + J SON.string ify(values .params)
  303                return $ tas.cache. build(key,  async ()  => {
  304                    retu rn adapter .createRes ponse($tas .constants .status.SU CCESS, awa it $tas.fh ir.call(va lues.resou rce, value s.params,  securityCo ntext))
  305                }, ttl)
  306           }
  307       }
  308  
  309   `FhirGroup ByCommand` :
  310  
  311       class  extends $t as.command .Command {
  312           as ync run(_,  values, a dapter) {
  313                return a dapter.cre ateRespons e($tas.con stants.sta tus.SUCCES S, $tas.fh ir.groupBy ResourceTy pe(values) )
  314           }
  315       }
  316  
  317  
  318   This would  be called  with:
  319       
  320       POST / api/core/e xample/mod ular/fhir- query
  321  
  322       BODY:
  323           {
  324                "resourc e": "Bundl e",
  325                "params" : {
  326                    "loc ation": "0 01",
  327                    "tra nsaction":  "270",
  328                    "_id ": "1"
  329                }
  330           }
  331  
  332   The result  will be a n group ag gregation  of FHIR da ta.
  333  
  334   Notice the  FhirQuery Command co mmand has  caching, t hus the re sults will  return fa ster on su bsequent c alls.
  335  
  336   ### Queuei ng
  337  
  338   To enable  queueing,  a queue ad dress must  be set vi a the appr opriate en vironment  variable.  Example:
  339  
  340       TAS_QU EUE_ADDRES S=amqp://1 27.0.0.1:5 672
  341  
  342   Queueing r elates mor e to the p rotocol us ed than th e specific  product s elected. P ut differe ntly, queu eing relat es to AMQP  (`Advance d Message  Queueing P rotocol`),  not Rabbi tMQ.
  343  
  344   AMQP 0.9 a bstraction  is built- in.
  345  
  346   To send to  a queue,  use the fo llowing:
  347  
  348       await  $tas.provi ders.amqp. send(queue _name, obj )
  349  
  350   This will  send the u nmodified  object to  the queue.
  351  
  352   To attach  the securi ty context  (e.g. use r informat ion) to th e object,  use the fo llowing:
  353  
  354       await  $tas.provi ders.amqp. send(queue _name, sec urityConte xt, obj)
  355  
  356   This will  add `$secu rityContex t` propert y to the o bject.
  357  
  358   To listen  for messag es, use th e followin g:
  359  
  360       const  callback =  content = > { }
  361       await  $tas.provi ders.amqp. listen(que ue_name, c allback)
  362  
  363   To configu re transpo rt securit y, set one  or more o f the foll owing envi ronment va riable:
  364  
  365   * TAS_QUEU E_CERTIFIC ATE_PATH
  366   * TAS_QUEU E_KEY_PATH
  367   * TAS_QUEU E_CA_PATH
  368   * TAS_QUEU E_PASSPHRA SE
  369  
  370   If you hav e problems  connectin g to a que ue, you ca n use Moch a to run ` tas/test/q ueue-troub leshooting .js`. This  also serv es as a se ries of ex amples.
  371  
  372   #### Comma nd Queuein g
  373  
  374   Commands a lso provid e an abstr action lay er for que ues.
  375  
  376   Command qu eueing can  be added  to any end point with  the follo wing segme nt taken f rom the ex ample endp oint:
  377  
  378       $tas.p roviders.a mqp.enable AmqpComman dListener( 'example-c ommands',  require('. /commands/ amqp-dispa tcher'))
  379  
  380   This sets  up the AMQ P command  dispatcher  on the 'e xample-com mands' que ue.
  381  
  382   Consider t he followi ng snippet  from a AM QP command  dispatche r:
  383  
  384       comman ds
  385           .s tart('scal e')
  386           .n ext(new In crementCom mand())
  387           .n ext(new Mu ltiplyComm and())
  388           .n ext(new Se ndCommand( ))
  389           .e nd()
  390  
  391   These use  the previo us command s Incremen tCommand a nd Multipl yCommand,  but also t he followi ng SendCom mand:
  392  
  393       class  extends $t as.command .Command {
  394           as ync run(se curityCont ext, value s, adapter ) {
  395                const nu mber = par seInt(valu es.number)  || 0
  396                if (numb er < 10000 ) {
  397                    awai t $tas.pro viders.amq p.send("ex ample-comm ands", sec urityConte xt, { "nam e": "scale ", "values ": { "numb er": numbe r } })
  398                }
  399                return a dapter.cre ateRespons e($tas.con stants.sta tus.SUCCES S, { numbe r: parseIn t(values.n umber) * 1 0 })
  400           }
  401       }
  402  
  403   In order t o more cle arly expla in the exa mple, cons ider the f ollowing c ommand lis tening on  HTTP:
  404  
  405       if ($t as.config. QUEUE_ADDR ESS) {
  406           co mmands.cre ate('scale ', async ( sc, values , adapter)  => adapte r.createRe sponse($ta s.constant s.status.S UCCESS, aw ait $tas.p roviders.a mqp.send(" example-co mmands", s c, { name:  'scale',  values })  || 0))
  407       }
  408  
  409   > This com mand will  only be re gistered i f the TAS_ QUEUE_ADDR ESS enviro nment vari able is se t.
  410  
  411   In this mo re advance d example,  we're usi ng the fol lowing to  call the A PI:
  412  
  413       POST / api/core/e xample/mod ular/scale
  414  
  415       BODY {  "number":  4 }
  416  
  417   This will  cause the  command re gistered t o HTTP to  send an in crement co mmand to t he `exampl e` queue.  The AMQP d ispatcher  will pick  up the mes sage, incr ement it,  multiply i t, then Se ndCommand  will creat e another  message to  put in th e queue if  the resul t is less  than 10000 .
  418  
  419   This recur sive queue  command e xample sho ws the fle xibility o f commands .
  420  
  421   Notice als o that the  same comm ands are u sed betwee n HTTP and  AMQP. Com mands are  not tied t o protocol s.
  422  
  423   Command qu eues can b e helpful  for things  like logg ing, where  they may  be a very  large set  of data.
  424  
  425   As shown i n passing,  you can c heck to se e if queue ing is con figured by  checking  the the `$ tas.config .QUEUE_ADD RESS` vari able:
  426  
  427       if ($t as.config. QUEUE_ADDR ESS) { }
  428  
  429   The format  of the qu eue messag e will be  in the fol lowing for mat:
  430  
  431       { $nam e, $conten t: { value s }, $secu rityContex t }
  432  
  433   ### Namesp aces
  434  
  435   API comman ds live in  namespace s. Namespa ces provid e differen t surface  areas with in an endp oint. A na mespace is  the URI s egment tha t follows  the endpoi nt name:
  436  
  437       /api/c ore/exampl e/NAMESPAC E/convert
  438  
  439   You can ha ve the sam e commands  do differ ent things  in differ ent namesp aces. "con vert" may  have one m eaning in  one namesp ace, but s omething e ntirely di fferent in  another.  For exampl e, a loggi ng endpoin t may have  different  logging t echniques  for differ ent system s. /loggin g/fhir/cre ate may ac t differen tly than / logging/az ure/create .
  440  
  441   For public -facing AP Is, namesp aces can b e used to  provide ve rsioning.  However, v ersioning  is differe nt than tr aditional  software.  This relat es to the  fact that  it can be  very diffi cult for a  consumer  to switch  APIs. Ther efore, eac h API vers ion should  be treate d as a dif ferent pro duct. Ther e should b e very few  API versi ons.
  442  
  443   > Rule of  thumb: **a  version i s required  when the  return obj ect change s**. It's  generally  more diffi cult to mo dify an ex isting API  to return  a differe nt data ty pe than to  accept on e. The poi nt is that  you want  to maintai n backward  compatibi lity until  everyone  has moves  to the new  version.  Extending  functional ity does n ot immedia tely call  for a vers ion update ; do not c onfused Gi t versioni ng and app lication v ersioning  with API v ersioning.
  444  
  445   ### Endpoi nts
  446  
  447   The endpoi nt is the  fundamenta l unit of  the API. E ach endpoi nt is isol ated from  every othe r. If one  endpoint b reaks, the  others wi ll not fai l. Endpoin ts have na mespaces,  which is w here comma nds live.  An endpoin t is decla red by the  `endpoint .js` file  at the bas e of your  endpoint f older.
  448  
  449   Here's a f ull exampl e of an en dpoint:
  450  
  451       const  $tas = req uire('../. ./tas')()
  452  
  453       module .exports.i nit = (app , router)  => {
  454           $t as.setupBi nding(rout er, 'examp le', async  ns => {
  455                //+ mult iple names paces impl ies namesp ace routin g
  456                ns.regis ter("v1")
  457                ns.regis ter("v2")
  458                ns.regis ter("v3")
  459           })
  460       }
  461  
  462   Inside the  callback  of the `$t as.setupBi nding` fun ction, you  can setup  additiona l listener s for you  endpoint,  such as an  AMQP or W ebSocket l istener.
  463  
  464   # $tas obj ect
  465  
  466   All intera ctions wit hin the TA S API are  done throu gh the `$t as` object . Once you  load the  `$tas` obj ect, no mo re file im ports are  required t o access f oundationa l API func tionality.
  467  
  468   You access  the objec t with the  following :
  469  
  470       const  $tas = req uire('../. ./tas')()
  471  
  472   > Your pat h will be  relative t o where yo ur file is .
  473  
  474   One very c ommon use  case is lo ading a da ta provide r: 
  475  
  476       const  example =  $tas.provi ders.load( 'example')
  477  
  478   You can al so access  a provider  directly:
  479  
  480       const  azure = $t as.provide rs.azure
  481  
  482   Another co mmon use c ase is loa ding a cor e
  483  
  484       const  logging =  $tas.core. load('logg ing')
  485  
  486   You also a ccess help ers via th e `$tas` o bject:
  487  
  488       const  utility =  $tas.helpe rs.utility
  489       const  response =  $tas.help ers.respon se
  490       const  idgen = $t as.helpers .idgen
  491  
  492   Many of th ese helper s provide  very commo n function ality:
  493  
  494       const  camelCase  = $tas.hel pers.text. toCamelCas e("ThisSta rtsAsPasca lCase")
  495  
  496       const  cleanArray  = $tas.he lpers.arra y.distinct ([1, 2, 3,  3, 3, 3,  3, 4, 5, 5 , 5])
  497  
  498       const  url = $tas .helpers.u ri.join(.. .['a', 'b' , 'c', 'd' ])
  499  
  500   You can al so access  caching vi a $tas:
  501  
  502       await  $tas.cache .set('Meta base::' +  name, valu e)
  503  
  504       let va lue = awai t $tas.cac he.get('Me tabase::'  + name)
  505  
  506   > Cache is  global, s o it's a g ood idea t o scope yo ur variabl es. For ex ample, not  "timeout" , but "Met abase::tim eout".
  507  
  508   You can al so use cal l FHIR and  parse obj ects with  the $tas.f hir object . This has  a number  of methods , includin g the foll owing: `ca ll`, `summ arize` (ge ts names o f resource s returned ), `groupB yResourceT ype` (grou p by resou rce), `cre ateExtensi onObject`,  and `crea teIdentifi erObject`.
  509  
  510   Additional  FHIR pars ing can be  done with  the $tas. helpers.jm espath obj ect. See [ http://jme spath.org/ ](http://j mespath.or g/) for de tails on J MESPath qu ery langua ge.
  511  
  512   > You can  find more  $tas examp les in `./ tas/test`.
  513  
  514   ## Authori zation
  515  
  516   IAM handle s authenti cation, bu t authoriz ation is c ustom. Req uests cont ain the HT TP_ACCESSR OLES heade r with the  format "R OLE1^ROLE2 ^ROLE3". T he TAS API  handles t he interna l mechanic s of parsi ng these r oles.
  517  
  518   Operations  can be lo cked down  to group b y using th e Authoriz eCommand c ommand
  519  
  520   Example:
  521  
  522       comman ds
  523           .s tart('incr ement')
  524           .n ext(new Au thorizeCom mand('SBTE ST_ADMIN') )
  525           .n ext(new In crementCom mand())
  526           .e nd()
  527  
  528   To allow m ore than o ne group,  use the ar ray form:
  529  
  530       comman ds
  531           .s tart('conc at')
  532           .n ext(new Au thorizeCom mand(['SBT EST_WRITER ', 'SBTEST _ADMIN']))
  533           .n ext(new Co ncatComman d())
  534           .e nd()
  535  
  536   ## Validat ion
  537  
  538   Validated  is provide d with the  validate. js library  and expos ed via Val idateComma nd.
  539  
  540   See the fo llowing co mplete exa mple:
  541  
  542       //+ pr actical pi peline 
  543       const  validator  = {
  544           re source: {  presence:  true, form at: { patt ern: "[a-z 0-9]+", fl ags: "i" }  },
  545           pa rams: { pr esence: tr ue }
  546       }
  547       comman ds
  548           .s tart('fhir -query', {  descripti on: () =>  { return {  summary:  `Loads dat a from FHI R, extract s identifi ers.`, val idator } }  })
  549           .n ext(new Au thorizeCom mand(['SBT EST_WRITER ', 'SBTEST _ADMIN']))
  550           .n ext(new Va lidateComm and(valida tor))
  551           .n ext(new fh ir.FhirQue ryCommand( ))
  552           .n ext(new fh ir.FhirGro upByComman d())
  553           .e nd()
  554  
  555   This examp le will ch eck author ization, v alidate, q uery FHIR,  then grou p the data . If the i nput data  is invalid , the Vali dateComman d will imm ediately s top proces sing and r eturn the  result.
  556  
  557   ## Descrip tions
  558  
  559   You can vi ew what pu blicly acc essible co mmands are  avilable  on an endp oint names pace by ca lling the  `_describe ` command.
  560  
  561   Example:
  562  
  563         http:// IP        /api/sb/ex ample/modu lar/_descr ibe
  564  
  565   Snippet of  output:
  566  
  567       [
  568           {
  569                "name":  "query",
  570                "descrip tion": {
  571                    "sum mary": "Qu eries the  example da ta.",
  572                    "inp ut": {
  573                         "start": " number",
  574                         "size": "n umber"
  575                    }
  576                }
  577           },
  578           {
  579                "name":  "amplify",
  580                "descrip tion": {
  581                    "sum mary": "In crements n umber, the n multipli es by 10." ,
  582                    "inp ut": {
  583                         "number":  "number"
  584                    }
  585                }
  586           },
  587           .. .
  588       ]
  589  
  590   You set th is in the  descriptio n function  in a comm and:
  591  
  592       class  extends $t as.command .Command {
  593           de scription( ) {
  594                return {
  595                    summ ary: `Adds  1.`,
  596                    inpu t: {
  597                         number: "n umber",
  598                    }
  599                }
  600           }
  601           as ync run(se curityCont ext, value s, adapter ) {
  602                return a dapter.cre ateRespons e($tas.con stants.sta tus.SUCCES S, { numbe r: parseIn t(values.n umber) + 1  })
  603           }
  604       }
  605  
  606   Pipelines  create a n ew command . Therefor e when usi ng the Aut horizeComm and comman d, you los e your des cription.  You can co py a comma nd's descr iption to  the pipeli ne with `$ tas.comman d.Registry ::copyDesc ription(CO MMAND_NAME )`.
  607  
  608   Example:
  609  
  610       comman ds
  611           .s tart('incr ement', {  descriptio n: command s.copyDesc ription(In crementCom mand) })
  612           .n ext(new Au thorizeCom mand('SBTE ST_ADMIN') )
  613           .n ext(new In crementCom mand())
  614           .e nd()
  615  
  616   ## Data
  617  
  618   ### Data P roviders
  619  
  620   Drivers fo r database s are gene rally writ ten by the  organizat ion that b uild the d atabase. T hese entir ely abstra ct the det ails of th e the data base. You  never work  directly  with the T abular Dat a Stream f ormat for  SQL Server , the wire  protocol  for MongoD B, or ODat a with Azu re Table S torage. Th ese mechan ics are en tirely abs tracted. T here's no  point in d iscussing  them. You  program ag ainst the  driver, no t the prot ocol.
  621  
  622   In the TAS  API, data  providers  abstract  the driver s. These p roviders h elp to abs tract data base conne ctivity in cluding al l connecti on managem ent. conne ction stri ngs, user  names, and  passwords .
  623  
  624   Here are s ome exampl e data pro viders:
  625  
  626   * AMQP (sp ecial prov ider, see  details la ter)
  627  
  628   * Azure
  629  
  630   * Elastics earch (v6)
  631  
  632   * Memory
  633  
  634   #### Data  Binders
  635  
  636   When worki ng with bu siness log ic and com mands, you  never pro gram direc tly agains t a provid er. The di fferences  between da tabases ar e often so  great, th at it's im practical  to do so. 
  637  
  638   Consider f or a momen t the diff erences be tween Azur e Table St orage, Mon goDB, and  Elasticsea rch IDs wo rk differe ntly in ea ch of thes e database s. differe nces: Azur e Table St orage uses  a Partiti onKey/RowK ey combina tion, Mong oDB uses a  rich Obje ctID objec t, Elastic search use s a long s tring. The  binder is  where you  smooth ou t the diff erences, s o you neve r have to  deal with  database I Ds in your  business  logic. You r binder w ould add t he special  IDs on wr ite and re move them  on read.
  639  
  640   Therefore,  data bind ers provid e you with  a decoupl ed way of  interactin g with dat a provider s.
  641  
  642   > To summa rize: Driv ers abstra ct wire me chanics. P roviders a bstract dr ivers. Bin ders abstr act provid ers.
  643  
  644   The follow ing line w ill gain d ata access  for `exam ple`:
  645  
  646       const  example =  $tas.provi ders.load( 'example')
  647  
  648   Here's an  example of  using `ex ample` dat a:
  649  
  650       await  example.ge t(user_id,  start, si ze, query)
  651  
  652   Notice tha t no data  provider i s specifie d. This is  set via e nvironment  variables  and contr olled by C M.
  653  
  654   There may  be, howeve r, times w hen you ab solutely n eed a spec ific provi der. In th ese cases,  you can s pecify tha t provider . This sho uld be ext remely rar e:
  655  
  656       const  example =  $tas.provi ders.load( 'example') ('azure)
  657  
  658   The defaul t provider  is `memor y`. This i s a safe d efault for  the sake  of testing  and easy  of develop ment.
  659  
  660   > Do **NOT ** access  a database  directly  in your co re-layer.  This will  break the  ability to  test with  the in-me mory datab ase and ti e an API t ightly to  one single  database  implementa tion. Alwa ys use a d ata binder .
  661  
  662  
  663   You must c reate a da ta binder  for each d ata provid er you wan t to suppo rt. Your b inder will  call the  provider.  The bindin g will be  aware of t he specifi cs of the  data provi der and wi ll be some what aware  of your b usiness do main. It's  a hybrid  position.
  664  
  665   > Because  your binde r is aware  of the pr ovider, it  can use a nything in  the provi der, inclu ding the u nderlying  provider d river. Thi s means th at if you  need more  advanced f unctionali ty, you ca n use prov ider funct ionality d irectly in  your bind er. For ex ample, if  you wanted  to use th e MongoDB  aggregate  pipeline,  you would  do this in  the binde r.
  666  
  667   An API's d ata-provid er is conf igured via  the TAS_P ROVIDERS e nvironment  variable  with a for mat simila r to this  example:
  668  
  669       TAS_PR OVIDERS=LO GGING:fs,M etabase:az ure,EXAMPL E:azure
  670  
  671   > The `mem ory` provi der is the  default.
  672  
  673   Most provi ders will  not activa te without  the requi red enviro nment vari able. For  example, ` TAS_AZURE_ STORAGE_NA ME` and `T AS_AZURE_S TORAGE_KEY ` are requ ired to ac tivate Azu re storage  and `TAS_ MONGODB_CO NNECTION_S TRING` and  `TAS_MONG ODB_DATABA SE` are re quired to  activate M ongoDB sto rage.
  674  
  675   Once you h ave proven  the mecha nics of yo ur memory  binder, yo u can use  your memor y binder f or all tes ts. It's s trongly re commended  that, wher e possible  (e.g. not  using agg regates),  that you w rite suppo rt for a m emory data  provider  first. Thi s will ena ble testin g without  requiring  database s etup. Give n working  tests for  a specific  provider,  there's n o need to  retest a p rovider wh en testing  higher-le vel functi onality. I n short: u se the mem ory provid er for tes ts.
  676  
  677   ## curl Ex amples
  678  
  679   Here you c reate two  documents,  then: que ry by size  1 (showin g only doc ument 4444 44444444),  query by  size 2 (sh owing both ), query b y size 1 a nd start 2  (showing  only docum ent 333333 333333).
  680  
  681         HOST_BASE= http:// IP        /api/core
  682       curl - sXPOST -H  "Content-T ype: appli cation/jso n" -H 'Coo kie: IAMSE SSION=ABCD EF' $HOST_ BASE/examp le/data/cr eate -d '{  "type": 1 ,"data": " the quick  brown fox"  }'
  683       curl - sXPOST -H  "Content-T ype: appli cation/jso n" -H 'Coo kie: IAMSE SSION=ABCD EF' $HOST_ BASE/examp le/data/cr eate -d '{  "type": 1 ,"data": " jumped ove r the lazy  dog" }'
  684       curl - sXPOST -H  "Content-T ype: appli cation/jso n" -H 'Coo kie: IAMSE SSION=ABCD EF' $HOST_ BASE/examp le/data/qu ery -d '{  "start": 1 , "size":  1 }' | doc ker run -i  python py thon -m js on.tool
  685       curl - sXPOST -H  "Content-T ype: appli cation/jso n" -H 'Coo kie: IAMSE SSION=ABCD EF' $HOST_ BASE/examp le/data/qu ery -d '{  "start": 1 , "size":  2 }' | doc ker run -i  python py thon -m js on.tool
  686       curl - sXPOST -H  "Content-T ype: appli cation/jso n" -H 'Coo kie: IAMSE SSION=ABCD EF' $HOST_ BASE/examp le/data/qu ery -d '{  "start": 2 , "size":  1 }' | doc ker run -i  python py thon -m js on.tool
  687       curl - sXPOST -H  "Content-T ype: appli cation/jso n" -H 'Coo kie: IAMSE SSION=ABCD EF' $HOST_ BASE/examp le/data/qu ery -d '{  "start": 1 , "size":  1, "query" : { "textS earch": "d og" } }' |  docker ru n -i pytho n python - m json.too l
  688  
  689   > `python  -m json.to ol` is use d to forma t. `docker  run -i py thon pytho n -m json. tool` is u sed to avo id install ing Python .
  690  
  691   ## Structu ral Overvi ew of APIs  with TAS  API
  692  
  693   The intern al mechani cs of this  project a re contain ed in the  `tas/` pro ject. **Do  not edit  files in t his folder .**
  694  
  695   Product de velopment  is done in  the `app/ ` and `tes t/` folder s
  696  
  697   Review the  following  structure :
  698  
  699       app/
  700           ex ample/bind ings/
  701                azure.js
  702                memory.j s
  703                mongodb. js
  704           ex ample/name spaces/
  705                data.js
  706                modular. js
  707                v1.js
  708           ex ample/core .js
  709           ex ample/endp oint.js
  710           co nfig.js
  711           en dpoints.js
  712       test/
  713  
  714   `example/`  represent s a comple te endpoin t. An endp oint is ef fectively  its own ap plication.  With this  understan ding, the  TAS API ca n be thoug ht of a as  platform  for a as a  suite of  products.
  715  
  716   #### `app/ `
  717  
  718   This folde r will con tain all p roduct API  developme nt. Each f older unde r `app/` i ndicates a  separate  endpoint.
  719  
  720   #### `app/ endpoints. js`
  721  
  722   This is wh ere each A PI is conn ected with  the overa ll system.  Example:
  723  
  724       const  $tas = req uire('../t as')()
  725       const  setup = $t as.setupEn dpoint
  726  
  727       module .exports.i nit = app  => {
  728           se tup(app, " config")
  729           // + ...more  endpoints
  730       }
  731  
  732   #### `api/ example/en dpoint.js`
  733  
  734   This file  contains t he setup a nd exposur e of an en dpoint. An  example w as shown e arlier.
  735  
  736   #### `api/ example/co re.js`
  737  
  738   This file  contains d evelopment  specific  to product  business  logic. It  is not HTT P or AMQP  aware, nor  is it tie d directly  to any sp ecific dat abase.
  739  
  740   #### `api/ example/bi ndings/`
  741  
  742   This folde r contains  bindings  for variou s database  providers . Not all  data provi ders will  be used. H owever, it 's encoura ged to use  `memory`  for unit t esting. Mo re details  in a late r section.
  743  
  744   #### `api/ endpoints/ versions/`
  745  
  746   This folde r contains  the actua l, version ed product  service A PI HTTP lo gic.
  747  
  748   #### `test /`
  749  
  750   This folde r contains  various t ests for p roduct ser vice APIs.
  751  
  752   > Some con figuration  is requir ed to be e xternal to  the appli cation, se t by envir onment var iables. Th ese can be  access fr om `proces s.env` and  set in `p roductConf ig.js`.
  753  
  754   ## Advance d Details
  755  
  756   ### Isolat ion
  757  
  758   Each API i s entirely  isolated.  Coding er rors (e.g.  syntax er rors) in o ne API wil l not caus e another  API to bre ak.
  759  
  760   ### Partic ular Activ ation
  761  
  762   By default  all APIs  are active . Set TAS_ ACTIVATE t o activate  only part icular API s.
  763  
  764   This is us eful for t ransitioni ng an API  to a a sin gle API ho sting mode l (e.g. mi croservice ).
  765  
  766   For exampl e, one dep loyment ma y only hav e logging,  but anoth er may onl y have con fig.
  767  
  768   Example: ` TAS_ACTIVA TE=logging ,health,me trics`
  769  
  770   ## Unit Te sting
  771  
  772   Testing sh ould be do ne at each  layer of  your appli cation. Yo u should w rite tests  for the s ake of pro ving code  for your d ata provid ers, for y our core l ayer, and  for endpoi nts.
  773  
  774   Review the  `tas/test ` folder f or testing  examples.
  775  
  776   Endpoint t ests use t he followi ng pattern :
  777  
  778       "use s trict"
  779  
  780       const  $tas = req uire('../' )()
  781       const  { app, cha i } = $tas .chaiTest( )
  782       const  expect = c hai.expect
  783       const  assert = c hai.assert
  784       
  785       const  nconfig =  $tas.confi g
  786       const  idgen = $t as.helpers .idgen
  787  
  788       descri be("PUT /a pi/sbtest" , () => {
  789           be fore(() =>  {
  790                app.star t()
  791           })
  792           af ter(() =>  {
  793                app.stop ()
  794           })
  795  
  796           it ("puts and  get entit y entity",  async fun ction () {
  797                const us er_id = id gen.genera te('user_i d' + this. test.title )
  798                const id  = idgen.g enerate(th is.test.ti tle)
  799  
  800                let res  = await ch ai.request (app.baseA ddress())
  801                    .put ('/api/v2/ sbtest/' +  id)
  802                    .set ('Cookie',  'IAMSESSI ON=' + thi s.test.tit le)
  803                    .sen d({
  804                         type: 'war ning',
  805                         level: 'in fo',
  806                         data: 'thi s is an er ror messag e'
  807                    })
  808                expect(r es).to.hav e.status(2 04)
  809  
  810                res = aw ait chai.r equest(app .baseAddre ss())
  811                    .get ('/api/v2/ sbtest/' +  id)
  812                    .set ('Authoriz ation', 'B earer ' +  user_id)
  813                const re sults = re s.body
  814                expect(r esults.ite m.type).to .eq('warni ng')
  815           })
  816       })
  817  
  818   Before tes ts, the ap p is start ed:
  819  
  820       app.st art()
  821  
  822   After test s, the app  is stoppe d:
  823  
  824       app.st op()
  825  
  826   To ensure  your tests  call the  correct ad dress, be  sure to us e `app.bas eAddress() `:
  827  
  828       chai.r equest(app .baseAddre ss())
  829  
  830   To link yo ur tests t ogether, g enerate an  ID and us e it as yo ur authori zation tok en:
  831  
  832       const  user_id =  idgen.gene rate('0')
  833  
  834       ...     
  835       
  836       .set(' Authorizat ion', 'Bea rer ' + us er_id)
  837  
  838   Review `ta s/test/end point-*-.j s` tests f or more co mplex exam ples.
  839  
  840   ## Adding  an Endpoin t
  841  
  842   To create  a new endp oint:
  843  
  844   1) Copy th e `./tas/a pp/templat e` folder  to the `./ app` folde r
  845   2) Rename
  846   3) Registe r to `./ap p/endpoint s.js` via  the `setup ` function  (e.g. `se tup(app, ' logging')` ).
  847  
  848   ## Program ming Langu ages and F ormats
  849  
  850   The API fo undation i s built on  Node.js 8  using the  programmi ng languag e ES6 (aka  ES2015).
  851  
  852   ES6, like  ES5 JavaSc ript, and  TypeScript  separates  lined wit h new line s (and cod e on the s ame line w ith semico lon). Beca use lines  are alread y separate d by new l ines, semi colons are  not used  to separat e lines. T his is an  artifact o f C/C++/C#  and clutt ers ES6/Ty peScript c ode. Avoid  semicolon s where po ssible.
  853  
  854   ES6 has ri ch functio nal progra mming feat ures that  ES5 lacks,  like map/ reduce/fil ter/find,  therefore  3rd party  functional  libraries  like unde rscore and  lodash ar e largely  unneeded.  See [http: //es6-feat ures.org]( http://es6 -features. org) and [ https://no de.green]( https://no de.green)  for more d etails.
  855  
  856   Files use  LF, not CR LF. Keep a n empty li ne at the  end of eac h file.
  857  
  858   ## Databas e Setup
  859  
  860   Elasticsea rch, memor y, file-sy stem, and  Azure Tabl e Storage  do not req uire any a dditional  setup.
  861           
  862   ## Databas e Connecti vity
  863  
  864   To example  database  connectivi ty, you mu st send th e applicat ion a conn ection str ing via en vironment  variable.
  865  
  866   This is se t in your  `docker-co mpose.yml`  file or w ith the Do cker `-e`  commandlin e argument .
  867  
  868   See the be ginning of  this docu ment for d etails.
  869  
  870   ## Authent ication fo r Developm ent
  871  
  872   To emulate  authentic ation, use  the stand ard `Cooki e` header  and specif y a value  for IAMSES SION:
  873  
  874       curl - H 'Cookie:  IAMSESSIO N=ABCDEFG'  http://12 7.0.0.1:30 00/api/cor e/logging/ v2
  875  
  876   Authorizat ion is pro vided with  the ACCES SROLES hea der
  877  
  878       curl - H 'Cookie:  IAMSESSIO N=ABCDEFG'  -H "ACCES SROLES: SB TEST_ADMIN " http://1 27.0.0.1:3 000/api/co re/logging /v2
  879  
  880   In the ful l Docker d eveloper e nvironment , fuller a uthenticat ion emulat ion is pro vided auto matically.
  881  
  882   ## Usage E xamples
  883  
  884   **GET**
  885  
  886       curl - H 'Cookie:  IAMSESSIO N=ABCDEFG'  http://12 7.0.0.1:30 00/api/log ging/v2
  887  
  888   **POST**
  889  
  890       curl - XPOST -H " Content-Ty pe: applic ation/json " -H 'Cook ie: IAMSES SION=ABCDE FG' http:/ /127.0.0.1 :3000/api/ logging/v2  -d '{"typ e":1,"data ":2}'