Anvendere betragtes som udviklere af afprøvningsprojekter. Denne guide har til formål at give disse udvikler eksempler på en webapplikation og en facade.
Guiden inkluderer instruktioner til opsætning af hele nap platformen samt selvstændige eksempler på kodeimplementationer.
Nap-reference-web er en web applikation, som implementerer nap-typescript-sdk og nap-angular-sdk. Som konsekvens af dette, kan denne køres indlejret i et værtssytem (eks. nap-java-host) og kommunikerer med værtssystemet over en indlejret javascript bro. Igennem denne bro henter applikationen en SAMLassertion og patient konteksten fra værtssytemet, hvorefter den kalder den tilhørende facade (nap-ref-facade) og forsøger at hente aftaledokumenter den valgte patient i værtssystemet. Kaldet indeholder den overførte SAMLassertion som en authentication header, og en hardcoded SOR værdi som X-OrganizationSor header.
Nap-reference-facade er en javaapplikation, som implementerer SEAL.java til at validerer SAMLassertion, org.openehealth.ipf.commons til at hente aftaledokumenter fra Dokumentdelingsservice (DDS), og dk.s4.hl7.builders til at formatere aftaledokumenter fra XML til objekter, hvorfra den hiver den relevante information ud og sender tilbage til klienten.
Funktionaliteten af Nap-ref-web illustreres bedst, når applikation er indlejret i et værstsystem, f.eks. nap-java-host Platformsservices (NAP) - Leverancebeskrivelse.
I følgende beskrives opsætningstrinene for at komme i gang med NAP platformen.
Krav til software:
Docker skal være installeret, da opsætningen foregår via docker-compose.
Et docker netværk kaldet nap_net skal laves (`docker network create nap_net`)
Komponenterne fra https://svn.nspop.dk/svn/components/nap/ skal hentes ned.
OBS vi har ikke nogen distributionskanal for denne executeable endnu. Så foreløbigt:
Kør `mvn install -P generateexecutable` og start den executable det bliver generate i /target.
Login ved at trykke på localhost og bruge default login credentials. Dette vil trække en SAMLassertion fra STS på test1. Du kan bruge denne i 30 min.
Skriv et cpr nummer på en test person (Eksempelvis "2708599967", som er et hyppigt anvendt test cpr nummer). Du kan nu browse rundt i fanerne.
For at fanen "afprøvningsplatformen" fungere, skal Nap-Compose og Nap-Lobby startes, som er de næste trin i opsætning.
Dette vil hoste lobbyen på localhost:8080/nap/lobby/web/, som er den url hosten kigger efter.
Ideen med nap-compose er, at denne reverse proxy fungerer som en NSP loadbalancer med path rewriting.
For at starte denne reverse proxy køres `docker-compose up`. Dette vil åbne port 8080 på localhost.
Nap-lobby er kataloget for de afprøvningsprojekter, der findes i nappen.
Kør `docker-compose up` fra compose/test. Med Nap-compose kørende, er denne nu tilgængelig for nap-host-java.
For at at Lobby skal vise nogen projekter skal den have forbindelse til Nap-admin.
Kør `docker-compose up` fra compose/test. Med Nap-compose kørende, er denne nu tilgængelig for Lobbyen og projekter bliver vist
Der ligger et Developer projekt, der hurtigt kan sætte gang i anvendelsen af NAP - platformen som udviklingsmiljø.
Hvis dette projekt trykkes på, vil java-host åbne en hvilken som helst applikation der hostes på lokalhost:4600.
Når en ny applikation skal ind på platformen, er der 2 steder platformen skal konfigureres før det fungerer.
Denne reverse proxy konfigureres ved at opdatere nap-httpd.conf.
<VirtualHost *:8080> ServerName nap
ProxyPass /nap/reference/web/services/main http://napreffacade:8080
ProxyPassReverse /nap/reference/web/services/main http://napreffacade:8080
ProxyPass /nap/reference/web http://naprefweb:8080
ProxyPassReverse /nap/reference/web http://naprefweb:8080
</VirtualHost>
Lav en konfiguration magen til ovenfor hvor reference og naprefweb udskiftes efter ønske.
Urlen skal dog passe med, den url der indsættes nedenfor
For at dit projekt skal vises i lobbyen, skal projektet optræde i din lokale napadmindb.
Du kan gøre dette med sql inserts direkte eller opdatere compose/db/migration/V2__insert_data_localhost.sql med en kopi af nedenstående, som bare opdateres med dit nye info
insert into administration.Project (active, description, releaseDate, version, name, id) values (true, 'Dit udviklingsprojekt kan åbnes her.', '2018-11-30', '1.0.0', 'Nap Web Develop', UuidToBin(UUID())); insert into administration.Manifest (releaseDate, url, version, id) values ('2019-11-30', 'http://localhost:8080/nap/developer/web/', '1.0.0', UuidToBin(UUID())); insert into WebApp (active, manifest_id, name, id, project_id) values (true, (select id from administration.Manifest where url LIKE 'http://localhost:8080/nap/developer/web/' ), 'Nap Web Developer version 0.0.1', UuidToBin(UUID()), (select id from administration.Project where name LIKE 'Nap Web Develop')); insert into WebApp_cvr (WebApp_id, cvr) values ((select id from administration.WebApp where name LIKE 'Nap Web Developer version 0.0.1'), '20921897'); insert into Manifest_eventCatalogueVersions (Manifest_id, eventCatalogueVersions) values ((select id from administration.Manifest where url LIKE 'http://localhost:8080/nap/developer/web/' ), 1); |
database og flyway skal således køres igen med `docker-compose up & docker-compose down`
Nap-reference-web er bygget med Angular version 9 og fungerer som illustration på:
Brug af NAP SDK
Applikationens hovedfunktionalitet er at vise aftaledokumenter for den patient, der er i kontekst i det værtssystem, den er indlejret. Der skal tilføjes en NAP Bridge på den givne applikations global scope, for at funktionaliteten fungere.
Diagram over NAP Bridge virkmåde, kan ses her.
Da angular frameworket lægger sig op af model-view-controller pattern er dette også implementeret i nap-reference-web.
Derfor er der simple komponenter til at visning aftaler, hjælpinformation, fejl osv.
Services håndterer forretningslogikken.
Derfor er der simple services til håndtering af authentication, applikationsfejl og konfigurering osv.
Der ligger en .npmrc, som sætter npm registry til https://nexus.nspop.dk/nexus/repository/nsp-npm/
Nap-reference-web benytter sig af en konfigurationsfil (assets/configurtation.json), som loades via configuration-servicen. Denne konfigurationsfil bliver således overskrevet i de forskellige docker-compose setups.
Da NSP web applikationer kan blive deployet på vilkårlige paths, er det vigtigt at applikationen fungerer med relative paths. Detter er opnået ved hashrouting.
@NgModule({ imports: [RouterModule.forRoot(routes, { useHash: true })], exports: [RouterModule] }) export class AppRoutingModule { } |
Nap-reference-web benytter sig af testframeworket Karma, da det kommer default med angular. Karma bruger Istanbul til at genere test-coverage rapporter. Testene køres i en headless chromium browser.
Kodedokumentationen bliver generet med TSDoc.
Nap-reference-web implementerer version 1 af eventkataloget.
Session-open starter en trusted session hvor brugerens SAMLassertion overføres. Session-close lukker sessionen og brugerens SAMLassertion slettes. Denne funktionalitet findes i auth-servicen.
export class AuthService { private sessionMessageSubject = new BehaviorSubject<NAPMessage | undefined>( undefined ); /** * The current session * * @memberof AuthService */ public session$ = this.sessionMessageSubject.asObservable(); constructor(private napSDK: NapAngularService) { this.napSDK.incomming$ .pipe( filterEvents([ NAPEventCatalogue.v1.SessionOpen, NAPEventCatalogue.v1.SessionClose, ]) ) .subscribe((napMessage) => { if (napMessage.event.type === NAPEventCatalogue.v1.SessionOpen) { this.sessionMessageSubject.next(napMessage); } else { this.sessionMessageSubject.next(undefined); } }); const napMsg: NAPMessage = { date: new Date().toISOString(), id: UUID(), event: { type: NAPEventCatalogue.v1.SessionOpen }, }; // Ask for the SAMLassertion in host this.napSDK.sendMessage(napMsg); } /** * Tricker logout event */ logout(): void { this.sessionMessageSubject.next(undefined); const napMsg: NAPMessage = { date: new Date().toISOString(), id: UUID(), event: { type: NAPEventCatalogue.v1.SessionClose }, }; this.napSDK.sendMessage(napMsg); } } |
Patient-open sender den brugervalgte patient journal og patient-close lukker den bruger valgte patient (set fra det indlejrede systems perspektiv). Et eksempel på dette kan ses i appointment.component
constructor( private napSDK: NapAngularService, private appointmentService: AppointmentService, private authService: AuthService ) { const napMsg: NAPMessage = { date: new Date().toISOString(), id: UUID(), event: { type: NAPEventCatalogue.v1.PatientOpen, }, }; this.napSDK.sendMessage(napMsg); } public currentPatient$: Observable<NAPPatientInfo | undefined> = this.napSDK.incomming$.pipe( filterEvent(NAPEventCatalogue.v1.PatientOpen), map(message => FHIRValueGetter.getPatientInfo(message)) ); |
Session-error sendes, hvis der sker en uventet fejl i projektet skal dette sendes til værtssystemet og vice versa. Et eksempel på dette findes i appointment-service:
private appointmentEndPoint$ = this.configurationService.fetch( config => config.appointmentsEndpoint ); private serverUrl = this.configurationService.fetch(config => config.serverUrl); private createNapErrorMessage(error: GenericAppError): NAPMessage { return { date: new Date().toISOString(), id: UUID(), event: { type: NAPEventCatalogue.v1.SessionError, context: [ { resource: { resourceType: FHIRValueSetter.FHIRResourceType.Basic, code: { coding: [ { code: NAPEventCatalogue.v1.SessionError, system: FHIRValueSetter.FHIRSystem.NAP, }, ], }, identifier: [ { system: FHIRValueSetter.FHIRIdentifierSystem.NAPErrorMessage, value: error.innerError?.message ? error.innerError?.message : '', }, { system: FHIRValueSetter.FHIRIdentifierSystem.NAPErrorDescription, value: error.errorMessage, }, ], }, }, ], } } } public getAppointments(patientIdentifier: string | undefined): Observable<any[] | undefined> { return combineLatest( [ this.serverUrl, this.appointmentEndPoint$, this.serviceActivator ] ).pipe( switchMap(([serverUrl, endpointPath, _]) => this.http.get<any[]>(serverUrl + endpointPath + '/' + patientIdentifier)), catchError(error => { this.errorService.postError(error); this.napSDK.sendMessage(this.createNapErrorMessage(error)); // indicate to the host that something went wrong return of(undefined); }), ); } |
Nap-reference-facade er bygget med java 8 og fungerer som illustration på:
Brug af Dokumentdelingsservice (DDS)
Dependencies er hentet fra https://nexus.nspop.dk/nexus/content/groups/public og de dependencies, som stilles til rådighed af wildfly8 platformen er angivet med scope provided.
For at kunne kalde Dokumentdelingsservice (DDS) er følgende dependencies anvendt:
dk.sosi.seal | Java-bibliotek til understøttelse af "Den Gode Webservice" og validering af SAMLassertion, se http://digitaliser.dk/group/374971. |
dk.s4.hl7.builders | Dansk profileret XML converter af hl7 clinical documents |
org.openehealth.ipf.commons | Dansk profileret bibliotek til Cross Enterprise Document sharing. Bruges til at integrere Dokumentdelingsservice (DDS). Du kan læse mere på https://github.com/KvalitetsIT/aftaledeling/tree/master/dgws-eksempel/src/main/java/dk/sds/appointment. |
Alt konfiguration foregår ved at loade filer fra et selv-defineret et wildfly module ind i classpath under deployment.
Dette gør wildfly ved at modulet defineres i jboss-deployment-structure.xml.
Testene bliver eksekveret af maven-surefire-plugin med test frameworket junit. Test coverage bliver målt af Jacoco.
Kodedokumentationen overholder javadoc standarden således dette kan generes med et værktøj fra fx den IDE man bruger.
dk.sosi.seal bliver brugt til at verificere SAMLassertion.
/** * @param headers Map of request headers * @return Saml assertion if its valid or null in case of invalid SAML assertion * @throws AuthenticationException If ant authentication exception occure */ public OIOSAMLAssertion extractAndValidate(MultivaluedMap<String, String> headers) throws AuthenticationException { final List<String> authentication = headers.get(AUTHENTICATION_PROPERTY); try { String xml = new String(Base64.decode(authentication.get(0)), StandardCharsets.UTF_8); Document doc = parseXml(xml); Element encryptedAssertionElm = doc.getDocumentElement(); PrivateKey privateKeyForAudience = certificateVault.getSystemCredentialPair().getPrivateKey(); // decrypt the xml for the assertion and parse it final Element element = EncryptionUtil.decryptAndDetach(encryptedAssertionElm, privateKeyForAudience); OIOSAMLAssertion assertion = new OIOSAMLAssertion(element); log.debug("extracted: \n" + assertion.getUID()); validateAssertion(assertion); return assertion; } catch (IOException | ParserConfigurationException | SAXException e) { throw new AuthenticationException("Could not validate authentication header", e); } } |
Nap-reference-facade har 2 snitflader som bliver beskrevet i følgende.
Den eneste aftage af denne service er nap-reference-web.
Headers | |
---|---|
Key | Value |
Authentication | BASE64 encoded SAMLassertion |
X-OrganizationSor | Sor nummer (bruges af dokumentdelingsservicen) |
Bruges af loadbalanceren for at tjekke at servicen er deployet. Returnerer en html side med deployment info.
Da denne service ikke er afhængige af database eller andre interne services, returnere den altid statuskode 200, hvis aplikationen kører.
http://nap/web/reference/services/main/aftaler/124567890
{
"title": "Ekkokardiografi",
"orgName": "Skejby Sygehus",
"indication": "Har ondt i hjertet somme tider",
"date": 1591142400
}