diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html
index 71c7dbbd..0680b43f 100644
--- a/frontend/src/app/app.component.html
+++ b/frontend/src/app/app.component.html
@@ -1,25 +1 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts
index 4abad5fc..957feeb1 100644
--- a/frontend/src/app/app.component.ts
+++ b/frontend/src/app/app.component.ts
@@ -1,17 +1,4 @@
import { Component } from '@angular/core';
-import * as Oauth from '@panva/oauth4webapi';
-import { concatMap, delay, retryWhen } from 'rxjs/operators';
-import { Observable, of, throwError } from 'rxjs';
-import * as FHIR from "fhirclient"
-import {getAccessTokenExpiration} from 'fhirclient/lib/lib';
-import BrowserAdapter from 'fhirclient/lib/adapters/BrowserAdapter';
-import {PassportService} from './services/passport.service';
-import {ProviderConfig} from './models/passport/provider-config';
-import {AuthorizeClaim} from './models/passport/authorize-claim';
-import {FastenApiService} from './services/fasten-api.service';
-import {ProviderCredential} from './models/fasten/provider-credential';
-export const retryCount = 24; //wait 2 minutes (5 * 24 = 120)
-export const retryWaitMilliSeconds = 5000; //wait 5 seconds
@Component({
selector: 'app-root',
@@ -20,148 +7,6 @@ export const retryWaitMilliSeconds = 5000; //wait 5 seconds
})
export class AppComponent {
title = 'fastenhealth';
-
-
- constructor(
- private passportApi: PassportService,
- private fastenApi: FastenApiService,
- ) { }
-
- connect(provider: string) {
- this.passportApi.getProviderConfig(provider)
- .subscribe(async (connectData: ProviderConfig) => {
- console.log(connectData);
-
- // https://github.com/panva/oauth4webapi/blob/8eba19eac408bdec5c1fe8abac2710c50bfadcc3/examples/public.ts
- const codeVerifier = Oauth.generateRandomCodeVerifier();
- const codeChallenge = await Oauth.calculatePKCECodeChallenge(codeVerifier);
- const codeChallengeMethod = 'S256';
- const state = this.uuidV4()
-
- const authorizationUrl = this.passportApi.generatePKCEProviderAuthorizeUrl(codeVerifier, codeChallenge, codeChallengeMethod, state, connectData)
-
- console.log('authorize url:', authorizationUrl.toString());
- // open new browser window
- window.open(authorizationUrl.toString(), "_blank");
-
- //wait for response
- this.waitForClaimOrTimeout(provider, state).subscribe(async (claimData: AuthorizeClaim) => {
- console.log("claim response:", claimData)
-
-
- //swap code for token
- let sub: string
- let access_token: string
-
- // @ts-expect-error
- const client: oauth.Client = {
- client_id: connectData.client_id,
- token_endpoint_auth_method: 'none',
- }
-
- const as = {
- issuer: `${authorizationUrl.protocol}//${authorizationUrl.host}`,
- authorization_endpoint: `${connectData.oauth_endpoint_base_url}/authorize`,
- token_endpoint: `${connectData.oauth_endpoint_base_url}/token`,
- introspect_endpoint: `${connectData.oauth_endpoint_base_url}/introspect`,
- }
-
- console.log("STARTING--- Oauth.validateAuthResponse")
- const params = Oauth.validateAuthResponse(as, client, new URLSearchParams(claimData as any), state)
- if (Oauth.isOAuth2Error(params)) {
- console.log('error', params)
- throw new Error() // Handle OAuth 2.0 redirect error
- }
- console.log("ENDING--- Oauth.validateAuthResponse")
- console.log("STARTING--- Oauth.authorizationCodeGrantRequest")
- const response = await Oauth.authorizationCodeGrantRequest(
- as,
- client,
- params,
- connectData.redirect_uri,
- codeVerifier,
- )
- const payload = await response.json()
- console.log("ENDING--- Oauth.authorizationCodeGrantRequest", payload)
-
-
- //Create FHIR Client
- const providerCredential: ProviderCredential = {
- oauth_endpoint_base_url: connectData.oauth_endpoint_base_url,
- api_endpoint_base_url: connectData.api_endpoint_base_url,
- client_id: connectData.client_id,
- redirect_uri: connectData.redirect_uri,
- scopes: connectData.scopes.join(' '),
- patient: payload.patient,
- access_token: payload.access_token,
- refresh_token: payload.refresh_token,
- id_token: payload.id_token,
- expires_at: getAccessTokenExpiration(payload, new BrowserAdapter()),
- code_challenge: codeChallenge,
- code_verifier: codeVerifier,
- }
-
- this.fastenApi.createProviderCredential(providerCredential).subscribe( (respData) => {
- console.log("provider credential create response:", respData)
- })
-
-
-
-
- // console.log("STARTING--- FHIR.client(clientState)", clientState)
- // const fhirClient = FHIR.client(clientState);
- //
- // console.log("STARTING--- client.request(Patient)")
- // const patientResponse = await fhirClient.request("PatientAccess/v1/$userinfo")
- // console.log(patientResponse)
-
-
-
-
-
- // // fetch userinfo response
- //
- // const response = await oauth.userInfoRequest(as, client, access_token)
- //
- // let challenges: oauth.WWWAuthenticateChallenge[] | undefined
- // if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) {
- // for (const challenge of challenges) {
- // console.log('challenge', challenge)
- // }
- // throw new Error() // Handle www-authenticate challenges as needed
- // }
- //
- // const result = await oauth.processUserInfoResponse(as, client, sub, response)
- // console.log('result', result)
-
-
-
- })
-
- });
- }
- waitForClaimOrTimeout(providerId: string, state: string): Observable {
- return this.passportApi.getProviderAuthorizeClaim(providerId, state).pipe(
- retryWhen(error =>
- error.pipe(
- concatMap((error, count) => {
- if (count <= retryCount && error.status == 500) {
- return of(error);
- }
- return throwError(error);
- }),
- delay(retryWaitMilliSeconds)
- )
- )
- )
- }
-
- uuidV4(){
- // @ts-ignore
- return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
- (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
- );
- }
}
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index cb973f05..4c2b55cf 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -4,7 +4,6 @@ import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
-import { DashboardComponent } from './pages/dashboard/dashboard.component';
import { AdminLayoutComponent } from "./layouts/admin-layout/admin-layout.component";
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { ComponentsModule } from "./components/components.module";
diff --git a/frontend/src/app/components/sidebar/sidebar.component.ts b/frontend/src/app/components/sidebar/sidebar.component.ts
index 061c24b5..bd67a3c9 100644
--- a/frontend/src/app/components/sidebar/sidebar.component.ts
+++ b/frontend/src/app/components/sidebar/sidebar.component.ts
@@ -3,7 +3,6 @@ import { Component, OnInit } from "@angular/core";
declare interface RouteInfo {
path: string;
title: string;
- rtlTitle: string;
icon: string;
class: string;
}
@@ -11,58 +10,32 @@ export const ROUTES: RouteInfo[] = [
{
path: "/dashboard",
title: "Dashboard",
- rtlTitle: "لوحة القيادة",
icon: "icon-chart-pie-36",
class: ""
},
{
- path: "/icons",
- title: "Icons",
- rtlTitle: "الرموز",
- icon: "icon-atom",
+ path: "/providers",
+ title: "Medical Providers",
+ icon: "icon-cloud-download-93",
class: ""
},
{
- path: "/maps",
- title: "Maps",
- rtlTitle: "خرائط",
- icon: "icon-pin",
- class: "" },
- {
- path: "/notifications",
- title: "Notifications",
- rtlTitle: "إخطارات",
- icon: "icon-bell-55",
- class: ""
- },
-
- {
- path: "/user",
- title: "User Profile",
- rtlTitle: "ملف تعريفي للمستخدم",
+ path: "/patient",
+ title: "Patient Profile",
icon: "icon-single-02",
class: ""
},
{
path: "/tables",
title: "Table List",
- rtlTitle: "قائمة الجدول",
icon: "icon-puzzle-10",
class: ""
},
{
path: "/typography",
title: "Typography",
- rtlTitle: "طباعة",
icon: "icon-align-center",
class: ""
- },
- {
- path: "/rtl",
- title: "RTL Support",
- rtlTitle: "ار تي ال",
- icon: "icon-world",
- class: ""
}
];
diff --git a/frontend/src/app/layouts/admin-layout/admin-layout.module.ts b/frontend/src/app/layouts/admin-layout/admin-layout.module.ts
index ca3f395d..f4b36ab2 100644
--- a/frontend/src/app/layouts/admin-layout/admin-layout.module.ts
+++ b/frontend/src/app/layouts/admin-layout/admin-layout.module.ts
@@ -6,8 +6,9 @@ import { FormsModule } from "@angular/forms";
import { AdminLayoutRoutes } from "./admin-layout.routing";
import { DashboardComponent } from "../../pages/dashboard/dashboard.component";
-import { UserComponent } from "../../pages/user/user.component";
-// import { RtlComponent } from "../../pages/rtl/rtl.component";
+import { PatientComponent } from "../../pages/patient/patient.component";
+import { MedicalProvidersComponent } from "../../pages/medical-providers/medical-providers.component";
+
import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
@@ -21,7 +22,8 @@ import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
],
declarations: [
DashboardComponent,
- UserComponent,
+ PatientComponent,
+ MedicalProvidersComponent
]
})
export class AdminLayoutModule {}
diff --git a/frontend/src/app/layouts/admin-layout/admin-layout.routing.ts b/frontend/src/app/layouts/admin-layout/admin-layout.routing.ts
index d12b64ed..3de94084 100644
--- a/frontend/src/app/layouts/admin-layout/admin-layout.routing.ts
+++ b/frontend/src/app/layouts/admin-layout/admin-layout.routing.ts
@@ -1,10 +1,11 @@
import { Routes } from "@angular/router";
import { DashboardComponent } from "../../pages/dashboard/dashboard.component";
-import { UserComponent } from "../../pages/user/user.component";
-// import { RtlComponent } from "../../pages/rtl/rtl.component";
+import { PatientComponent } from "../../pages/patient/patient.component";
+import {MedicalProvidersComponent} from '../../pages/medical-providers/medical-providers.component';
export const AdminLayoutRoutes: Routes = [
{ path: "dashboard", component: DashboardComponent },
- { path: "user", component: UserComponent },
+ { path: "patient", component: PatientComponent },
+ { path: "providers", component: MedicalProvidersComponent },
];
diff --git a/frontend/src/app/pages/medical-providers/medical-providers.component.html b/frontend/src/app/pages/medical-providers/medical-providers.component.html
new file mode 100644
index 00000000..5f3a4589
--- /dev/null
+++ b/frontend/src/app/pages/medical-providers/medical-providers.component.html
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
Aetna
+
+
+
+
+
+
+
+
Anthem
+
+
+
+
+
+
+
Cigna
+
+
+
+
+
+
+
Humana
+
+
+
+
+
+
+
Kaiser
+
+
+
+
+
+
+
United Healthcare
+
+
+
+
+
+
+
+
diff --git a/frontend/src/app/pages/medical-providers/medical-providers.component.scss b/frontend/src/app/pages/medical-providers/medical-providers.component.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/frontend/src/app/pages/medical-providers/medical-providers.component.spec.ts b/frontend/src/app/pages/medical-providers/medical-providers.component.spec.ts
new file mode 100644
index 00000000..f9ca6bed
--- /dev/null
+++ b/frontend/src/app/pages/medical-providers/medical-providers.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MedicalProvidersComponent } from './medical-providers.component';
+
+describe('MedicalProvidersComponent', () => {
+ let component: MedicalProvidersComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MedicalProvidersComponent ]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(MedicalProvidersComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/pages/medical-providers/medical-providers.component.ts b/frontend/src/app/pages/medical-providers/medical-providers.component.ts
new file mode 100644
index 00000000..44a7f2be
--- /dev/null
+++ b/frontend/src/app/pages/medical-providers/medical-providers.component.ts
@@ -0,0 +1,167 @@
+import { Component, OnInit } from '@angular/core';
+import {PassportService} from '../../services/passport.service';
+import {FastenApiService} from '../../services/fasten-api.service';
+import {ProviderConfig} from '../../models/passport/provider-config';
+import * as Oauth from '@panva/oauth4webapi';
+import {AuthorizeClaim} from '../../models/passport/authorize-claim';
+import {ProviderCredential} from '../../models/fasten/provider-credential';
+import {getAccessTokenExpiration} from 'fhirclient/lib/lib';
+import BrowserAdapter from 'fhirclient/lib/adapters/BrowserAdapter';
+import {Observable, of, throwError} from 'rxjs';
+import {concatMap, delay, retryWhen} from 'rxjs/operators';
+import * as FHIR from "fhirclient"
+
+export const retryCount = 24; //wait 2 minutes (5 * 24 = 120)
+export const retryWaitMilliSeconds = 5000; //wait 5 seconds
+
+@Component({
+ selector: 'app-medical-providers',
+ templateUrl: './medical-providers.component.html',
+ styleUrls: ['./medical-providers.component.scss']
+})
+export class MedicalProvidersComponent implements OnInit {
+
+ constructor(
+ private passportApi: PassportService,
+ private fastenApi: FastenApiService,
+ ) { }
+
+ ngOnInit(): void {
+ }
+
+ connect(provider: string) {
+ this.passportApi.getProviderConfig(provider)
+ .subscribe(async (connectData: ProviderConfig) => {
+ console.log(connectData);
+
+ // https://github.com/panva/oauth4webapi/blob/8eba19eac408bdec5c1fe8abac2710c50bfadcc3/examples/public.ts
+ const codeVerifier = Oauth.generateRandomCodeVerifier();
+ const codeChallenge = await Oauth.calculatePKCECodeChallenge(codeVerifier);
+ const codeChallengeMethod = 'S256';
+ const state = this.uuidV4()
+
+ const authorizationUrl = this.passportApi.generatePKCEProviderAuthorizeUrl(codeVerifier, codeChallenge, codeChallengeMethod, state, connectData)
+
+ console.log('authorize url:', authorizationUrl.toString());
+ // open new browser window
+ window.open(authorizationUrl.toString(), "_blank");
+
+ //wait for response
+ this.waitForClaimOrTimeout(provider, state).subscribe(async (claimData: AuthorizeClaim) => {
+ console.log("claim response:", claimData)
+
+
+ //swap code for token
+ let sub: string
+ let access_token: string
+
+ // @ts-expect-error
+ const client: oauth.Client = {
+ client_id: connectData.client_id,
+ token_endpoint_auth_method: 'none',
+ }
+
+ const as = {
+ issuer: `${authorizationUrl.protocol}//${authorizationUrl.host}`,
+ authorization_endpoint: `${connectData.oauth_endpoint_base_url}/authorize`,
+ token_endpoint: `${connectData.oauth_endpoint_base_url}/token`,
+ introspect_endpoint: `${connectData.oauth_endpoint_base_url}/introspect`,
+ }
+
+ console.log("STARTING--- Oauth.validateAuthResponse")
+ const params = Oauth.validateAuthResponse(as, client, new URLSearchParams(claimData as any), state)
+ if (Oauth.isOAuth2Error(params)) {
+ console.log('error', params)
+ throw new Error() // Handle OAuth 2.0 redirect error
+ }
+ console.log("ENDING--- Oauth.validateAuthResponse")
+ console.log("STARTING--- Oauth.authorizationCodeGrantRequest")
+ const response = await Oauth.authorizationCodeGrantRequest(
+ as,
+ client,
+ params,
+ connectData.redirect_uri,
+ codeVerifier,
+ )
+ const payload = await response.json()
+ console.log("ENDING--- Oauth.authorizationCodeGrantRequest", payload)
+
+
+ //Create FHIR Client
+ const providerCredential: ProviderCredential = {
+ oauth_endpoint_base_url: connectData.oauth_endpoint_base_url,
+ api_endpoint_base_url: connectData.api_endpoint_base_url,
+ client_id: connectData.client_id,
+ redirect_uri: connectData.redirect_uri,
+ scopes: connectData.scopes.join(' '),
+ patient: payload.patient,
+ access_token: payload.access_token,
+ refresh_token: payload.refresh_token,
+ id_token: payload.id_token,
+ expires_at: getAccessTokenExpiration(payload, new BrowserAdapter()),
+ code_challenge: codeChallenge,
+ code_verifier: codeVerifier,
+ }
+
+ this.fastenApi.createProviderCredential(providerCredential).subscribe( (respData) => {
+ console.log("provider credential create response:", respData)
+ })
+
+
+
+
+ // console.log("STARTING--- FHIR.client(clientState)", clientState)
+ // const fhirClient = FHIR.client(clientState);
+ //
+ // console.log("STARTING--- client.request(Patient)")
+ // const patientResponse = await fhirClient.request("PatientAccess/v1/$userinfo")
+ // console.log(patientResponse)
+
+
+
+
+
+ // // fetch userinfo response
+ //
+ // const response = await oauth.userInfoRequest(as, client, access_token)
+ //
+ // let challenges: oauth.WWWAuthenticateChallenge[] | undefined
+ // if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) {
+ // for (const challenge of challenges) {
+ // console.log('challenge', challenge)
+ // }
+ // throw new Error() // Handle www-authenticate challenges as needed
+ // }
+ //
+ // const result = await oauth.processUserInfoResponse(as, client, sub, response)
+ // console.log('result', result)
+
+
+
+ })
+
+ });
+ }
+ waitForClaimOrTimeout(providerId: string, state: string): Observable {
+ return this.passportApi.getProviderAuthorizeClaim(providerId, state).pipe(
+ retryWhen(error =>
+ error.pipe(
+ concatMap((error, count) => {
+ if (count <= retryCount && error.status == 500) {
+ return of(error);
+ }
+ return throwError(error);
+ }),
+ delay(retryWaitMilliSeconds)
+ )
+ )
+ )
+ }
+
+ uuidV4(){
+ // @ts-ignore
+ return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
+ (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
+ );
+ }
+}
diff --git a/frontend/src/app/pages/user/user.component.html b/frontend/src/app/pages/patient/patient.component.html
similarity index 100%
rename from frontend/src/app/pages/user/user.component.html
rename to frontend/src/app/pages/patient/patient.component.html
diff --git a/frontend/src/app/pages/patient/patient.component.ts b/frontend/src/app/pages/patient/patient.component.ts
new file mode 100644
index 00000000..7a4b5ebe
--- /dev/null
+++ b/frontend/src/app/pages/patient/patient.component.ts
@@ -0,0 +1,11 @@
+import { Component, OnInit } from "@angular/core";
+
+@Component({
+ selector: "app-patient",
+ templateUrl: "patient.component.html"
+})
+export class PatientComponent implements OnInit {
+ constructor() {}
+
+ ngOnInit() {}
+}
diff --git a/frontend/src/app/pages/user/user.component.ts b/frontend/src/app/pages/user/user.component.ts
deleted file mode 100644
index 35a83b5d..00000000
--- a/frontend/src/app/pages/user/user.component.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Component, OnInit } from "@angular/core";
-
-@Component({
- selector: "app-user",
- templateUrl: "user.component.html"
-})
-export class UserComponent implements OnInit {
- constructor() {}
-
- ngOnInit() {}
-}