using endpoint id and brand id everywhere.
brand id is used for logo specification.
This commit is contained in:
parent
dec1914b91
commit
06aed9c17a
|
@ -215,6 +215,33 @@
|
|||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"offline_sandbox": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.offline_sandbox.ts"
|
||||
}
|
||||
],
|
||||
"optimization": false,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": false,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "30mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -236,6 +263,9 @@
|
|||
"desktop_sandbox": {
|
||||
"browserTarget": "fastenhealth:build:desktop_sandbox"
|
||||
},
|
||||
"offline_sandbox": {
|
||||
"browserTarget": "fastenhealth:build:offline_sandbox"
|
||||
},
|
||||
"desktop_prod": {
|
||||
"browserTarget": "fastenhealth:build:desktop_prod"
|
||||
}
|
||||
|
|
|
@ -35,10 +35,10 @@ const routes: Routes = [
|
|||
{ path: 'explore/:source_id/resource/:resource_type/:resource_id', component: ResourceDetailComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
|
||||
{ path: 'sources', component: MedicalSourcesComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'sources/callback/:source_type', component: MedicalSourcesComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'sources/callback/:state', component: MedicalSourcesComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'resource/create', component: ResourceCreatorComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
|
||||
{ path: 'desktop/callback/:source_id', component: DesktopCallbackComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'desktop/callback/:state', component: DesktopCallbackComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
|
||||
{ path: 'background-jobs', component: BackgroundJobsComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
{ path: 'patient-profile', component: PatientProfileComponent, canActivate: [ IsAuthenticatedAuthGuard] },
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div class="card h-100 d-flex align-items-center justify-content-center mt-3 mt-3 rounded-0 cursor-pointer" [ngClass]="{'card-disable': sourceInfo?.metadata?.hidden}">
|
||||
<div class="card h-100 d-flex align-items-center justify-content-center mt-3 mt-3 rounded-0 cursor-pointer" [ngClass]="{'card-disable': sourceInfo?.brand?.hidden}">
|
||||
<div (click)="onCardClick()" class="card-body" [class.border-left-danger]="status == 'failed'">
|
||||
|
||||
<div class="h-100 d-flex align-items-center">
|
||||
<img imageFallback [src]="'assets/sources/'+(sourceInfo?.metadata.brand_logo ? sourceInfo?.metadata?.brand_logo : sourceInfo?.metadata?.source_type+'.png')" [alt]="sourceInfo?.metadata?.display" class="img-fluid">
|
||||
<img imageFallback [src]="'assets/sources/'+((sourceInfo?.brand?.id || sourceInfo?.source?.brand_id )+'.png')" [alt]="sourceInfo?.brand?.name" class="img-fluid">
|
||||
<div *ngIf="status == 'failed'" class="card-img-overlay">
|
||||
<span class="badge badge-danger">failed</span>
|
||||
</div>
|
||||
|
@ -13,7 +13,7 @@
|
|||
</div>
|
||||
<div class="card-footer text-center p-1" style="width:100%">
|
||||
<small class="tx-gray-700">
|
||||
{{sourceInfo?.metadata?.display || getSourceDisplayName(sourceInfo)}}
|
||||
{{getSourceDisplayName(sourceInfo)}}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -25,14 +25,17 @@ export class MedicalSourcesCardComponent implements OnInit {
|
|||
|
||||
getSourceDisplayName(sourceItem: SourceListItem): string {
|
||||
if(!sourceItem) return "Unknown"
|
||||
if(sourceItem.metadata?.display) {
|
||||
return sourceItem.metadata?.display
|
||||
if(sourceItem.source?.display) {
|
||||
return sourceItem.source?.display
|
||||
}
|
||||
if(sourceItem.source?.source_type == 'manual') {
|
||||
if(sourceItem.source?.platform_type == 'manual') {
|
||||
return 'Uploaded ' + moment(sourceItem.source?.created_at).format('MMM DD, YYYY')
|
||||
} else if(sourceItem.source?.source_type == 'fasten'){
|
||||
} else if(sourceItem.source?.platform_type == 'fasten'){
|
||||
return 'Fasten Health'
|
||||
}
|
||||
if(sourceItem.brand?.name) {
|
||||
return sourceItem.brand?.name
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<app-medical-sources-card class="col-sm-3 mg-b-20 px-3"
|
||||
*ngFor="let sourceData of connectedSourceList"
|
||||
[sourceInfo]="sourceData"
|
||||
[status]="status[sourceData.source?.id] || status[sourceData.metadata?.source_type]"
|
||||
[status]="status[sourceData.source?.id] || status[sourceData.brand?.id]"
|
||||
(onClick)="openModal(contentModalRef, $event)"
|
||||
></app-medical-sources-card>
|
||||
</div>
|
||||
|
@ -12,13 +12,13 @@
|
|||
|
||||
<ng-template #contentModalRef let-modal>
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="modal-basic-title">{{modalSelectedSourceListItem?.metadata["display"]}}</h4>
|
||||
<h4 class="modal-title" id="modal-basic-title">{{modalSelectedSourceListItem?.source?.display || modalSelectedSourceListItem?.brand?.name}}</h4>
|
||||
<button type="button" class="btn close" aria-label="Close" (click)="modal.dismiss('Cross click')">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div *ngIf="modalSelectedSourceListItem.source?.source_type != 'fasten'; else fastenSourceDescription" class="modal-body">
|
||||
<div *ngIf="modalSelectedSourceListItem.source?.platform_type != 'fasten'; else fastenSourceDescription" class="modal-body">
|
||||
<h6>Manage Source</h6>
|
||||
<p>Existing connections can be "Synced", "Reconnected" or "Deleted"</p>
|
||||
<ul>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<a routerLink="/explore/{{modalSelectedSourceListItem?.source?.id}}" (click)="modal.close()" class="btn btn-indigo mr-auto">Explore</a>
|
||||
|
||||
|
||||
<div *ngIf="modalSelectedSourceListItem.source?.source_type != 'fasten'" class="d-inline-block" ngbDropdown>
|
||||
<div *ngIf="modalSelectedSourceListItem.source?.platform_type != 'fasten'" class="d-inline-block" ngbDropdown>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline-indigo"
|
||||
|
@ -52,8 +52,8 @@
|
|||
Actions
|
||||
</button>
|
||||
<div ngbDropdownMenu aria-labelledby="dropdownManual">
|
||||
<button *ngIf="modalSelectedSourceListItem.source?.source_type != 'manual'" ngbDropdownItem (click)="sourceSyncHandler(modalSelectedSourceListItem.source)" type="button" class="btn btn-indigo">Sync</button>
|
||||
<button *ngIf="modalSelectedSourceListItem.source?.source_type != 'manual'" ngbDropdownItem (click)="sourceReconnectHandler(modalSelectedSourceListItem)" type="button" class="btn btn-danger">Reconnect</button>
|
||||
<button *ngIf="modalSelectedSourceListItem.source?.platform_type != 'manual'" ngbDropdownItem (click)="sourceSyncHandler(modalSelectedSourceListItem.source)" type="button" class="btn btn-indigo">Sync</button>
|
||||
<button *ngIf="modalSelectedSourceListItem.source?.platform_type != 'manual'" ngbDropdownItem (click)="sourceReconnectHandler(modalSelectedSourceListItem)" type="button" class="btn btn-danger">Reconnect</button>
|
||||
<button ngbDropdownItem (click)="sourceDeleteHandler()" type="button" class="btn btn-danger">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,6 +12,7 @@ import {ActivatedRoute, Router} from '@angular/router';
|
|||
import {Location} from '@angular/common';
|
||||
import {EventBusService} from '../../services/event-bus.service';
|
||||
import {SourceState} from '../../models/fasten/source-state';
|
||||
import {PatientAccessBrand} from '../../models/patient-access-brands';
|
||||
|
||||
@Component({
|
||||
selector: 'app-medical-sources-connected',
|
||||
|
@ -45,29 +46,41 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
|
||||
//handle connected sources sources
|
||||
const connectedSources = results as Source[]
|
||||
forkJoin(connectedSources.map((source) => this.lighthouseApi.getLighthouseSource(source.source_type))).subscribe((connectedMetadata) => {
|
||||
for(const ndx in connectedSources){
|
||||
this.connectedSourceList.push({source: connectedSources[ndx], metadata: connectedMetadata[ndx]})
|
||||
if(connectedSources[ndx].latest_background_job?.job_status == "STATUS_LOCKED"){
|
||||
this.status[connectedSources[ndx].source_type] = "token"
|
||||
} else if (connectedSources[ndx].latest_background_job?.job_status === "STATUS_FAILED") {
|
||||
this.status[connectedSources[ndx].source_type] = "failed"
|
||||
}
|
||||
forkJoin(connectedSources.map((source) => {
|
||||
if(source.platform_type == 'fasten' || source.platform_type == 'manual') {
|
||||
return this.lighthouseApi.getLighthouseCatalogBrand(source.platform_type)
|
||||
} else {
|
||||
return this.lighthouseApi.getLighthouseCatalogBrand(source.brand_id)
|
||||
}
|
||||
})
|
||||
}))
|
||||
.subscribe((connectedBrand) => {
|
||||
for(const ndx in connectedSources){
|
||||
console.log(connectedSources[ndx])
|
||||
this.connectedSourceList.push({source: connectedSources[ndx], brand: connectedBrand[ndx]})
|
||||
if(connectedSources[ndx].latest_background_job?.job_status == "STATUS_LOCKED"){
|
||||
this.status[connectedSources[ndx].brand_id] = "token"
|
||||
} else if (connectedSources[ndx].latest_background_job?.job_status === "STATUS_FAILED") {
|
||||
this.status[connectedSources[ndx].brand_id] = "failed"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
const callbackSourceType = this.activatedRoute.snapshot.paramMap.get('source_type')
|
||||
if(callbackSourceType) {
|
||||
console.log("handle callback redirect from source")
|
||||
this.status[callbackSourceType] = "token"
|
||||
const callbackState = this.activatedRoute.snapshot.paramMap.get('state')
|
||||
if(callbackState) {
|
||||
|
||||
let sourceInfo = this.lighthouseApi.getSourceState(callbackState)
|
||||
|
||||
console.log("handle callback redirect from source", callbackState, sourceInfo)
|
||||
this.status[sourceInfo.brand_id] = "token"
|
||||
|
||||
//the structure of "availableSourceList" vs "connectedSourceList" sources is slightly different,
|
||||
//connectedSourceList contains a "source" field. The this.fastenApi.createSource() call in the callback function will set it.
|
||||
this.lighthouseApi.getLighthouseSource(callbackSourceType)
|
||||
.then((metadata) => {
|
||||
this.connectedSourceList.push({metadata: metadata})
|
||||
return this.callback(callbackSourceType)
|
||||
this.lighthouseApi.getLighthouseCatalogBrand(sourceInfo.brand_id)
|
||||
.then((brandInfo) => {
|
||||
this.connectedSourceList.push({brand: brandInfo})
|
||||
return this.callback(sourceInfo)
|
||||
})
|
||||
.then(console.log)
|
||||
}
|
||||
|
@ -80,12 +93,12 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
|
||||
/**
|
||||
* if the user is redirected to this page from the lighthouse, we'll need to process the "code" to retrieve the access token & refresh token.
|
||||
* @param sourceType
|
||||
* @param expectedSourceStateInfo
|
||||
*/
|
||||
public async callback(sourceType: string) {
|
||||
public async callback(expectedSourceStateInfo: SourceState) {
|
||||
|
||||
//get the source metadata again
|
||||
await this.lighthouseApi.getLighthouseSource(sourceType)
|
||||
await this.lighthouseApi.getLighthouseSource(expectedSourceStateInfo.endpoint_id)
|
||||
.then(async (sourceMetadata: LighthouseSourceMetadata) => {
|
||||
|
||||
//get required parameters from the URI and local storage
|
||||
|
@ -93,7 +106,6 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
//in desktop mode, we're using fragment routing, and the callback params are in the fragment.
|
||||
const fragmentParams = new URLSearchParams(callbackUrlParts.hash.split('?')?.[1] || '')
|
||||
const callbackCode = callbackUrlParts.searchParams.get("code") || fragmentParams.get("code")
|
||||
const callbackState = callbackUrlParts.searchParams.get("state") || fragmentParams.get("state")
|
||||
const callbackError = callbackUrlParts.searchParams.get("error") || fragmentParams.get("error")
|
||||
const callbackErrorDescription = callbackUrlParts.searchParams.get("error_description") || fragmentParams.get("error_description")
|
||||
|
||||
|
@ -103,8 +115,7 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
});
|
||||
this.location.replaceState(urlTree.toString());
|
||||
|
||||
const expectedSourceStateInfo = JSON.parse(localStorage.getItem(callbackState)) as SourceState
|
||||
localStorage.removeItem(callbackState)
|
||||
localStorage.removeItem(expectedSourceStateInfo.state)
|
||||
|
||||
if(callbackError && !callbackCode){
|
||||
//TOOD: print this message in the UI
|
||||
|
@ -114,10 +125,10 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
}
|
||||
|
||||
console.log("callback code:", callbackCode)
|
||||
this.status[sourceType] = "token"
|
||||
this.status[expectedSourceStateInfo.brand_id] = "token"
|
||||
|
||||
let payload: any
|
||||
payload = await this.lighthouseApi.swapOauthToken(sourceType, sourceMetadata,expectedSourceStateInfo, callbackCode)
|
||||
payload = await this.lighthouseApi.swapOauthToken(sourceMetadata,expectedSourceStateInfo, callbackCode)
|
||||
|
||||
if(!payload.access_token || payload.error){
|
||||
//if the access token is not set, then something is wrong,
|
||||
|
@ -143,38 +154,26 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
|
||||
}
|
||||
|
||||
|
||||
//get the portal information
|
||||
const portalInfo = await this.lighthouseApi.getLighthouseCatalogPortal(expectedSourceStateInfo.portal_id)
|
||||
|
||||
//Create FHIR Client
|
||||
|
||||
const dbSourceCredential = new Source({
|
||||
id: expectedSourceStateInfo.reconnect_source_id,
|
||||
source_type: sourceType,
|
||||
|
||||
authorization_endpoint: sourceMetadata.authorization_endpoint,
|
||||
token_endpoint: sourceMetadata.token_endpoint,
|
||||
introspection_endpoint: sourceMetadata.introspection_endpoint,
|
||||
userinfo_endpoint: sourceMetadata.userinfo_endpoint,
|
||||
registration_endpoint: sourceMetadata.registration_endpoint,
|
||||
api_endpoint_base_url: sourceMetadata.api_endpoint_base_url,
|
||||
display: portalInfo.name,
|
||||
brand_id: expectedSourceStateInfo.brand_id,
|
||||
portal_id: expectedSourceStateInfo.portal_id,
|
||||
endpoint_id: expectedSourceStateInfo.endpoint_id,
|
||||
platform_type: sourceMetadata.platform_type,
|
||||
|
||||
client_id: sourceMetadata.client_id,
|
||||
redirect_uri: sourceMetadata.redirect_uri,
|
||||
scopes_supported: sourceMetadata.scopes_supported,
|
||||
issuer: sourceMetadata.issuer,
|
||||
grant_types_supported: sourceMetadata.grant_types_supported,
|
||||
response_types_supported: sourceMetadata.response_types_supported,
|
||||
aud: sourceMetadata.aud,
|
||||
code_challenge_methods_supported: sourceMetadata.code_challenge_methods_supported || [],
|
||||
confidential: sourceMetadata.confidential,
|
||||
cors_relay_required: sourceMetadata.cors_relay_required,
|
||||
|
||||
patient: payload.patient,
|
||||
access_token: payload.access_token,
|
||||
refresh_token: payload.refresh_token,
|
||||
id_token: payload.id_token,
|
||||
|
||||
dynamic_client_registration_mode: sourceMetadata.dynamic_client_registration_mode,
|
||||
|
||||
// @ts-ignore - in some cases the getAccessTokenExpiration is a string, which cases failures to store Source in db.
|
||||
expires_at: parseInt(this.getAccessTokenExpiration(payload)),
|
||||
})
|
||||
|
@ -182,20 +181,20 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
this.fastenApi.createSource(dbSourceCredential)
|
||||
.subscribe((resp) => {
|
||||
// const sourceSyncMessage = JSON.parse(msg) as SourceSyncMessage
|
||||
delete this.status[sourceType]
|
||||
delete this.status[dbSourceCredential.brand_id]
|
||||
delete this.status[resp.source.id]
|
||||
// window.location.reload();
|
||||
// this.connectedSourceList.
|
||||
|
||||
//find the index of the "inprogress" source in the connected List, and then add this source to its source metadata.
|
||||
let foundSource = this.connectedSourceList.findIndex((item) => item.metadata.source_type == sourceType)
|
||||
let foundSource = this.connectedSourceList.findIndex((item) => item.brand.id == dbSourceCredential.brand_id)
|
||||
this.connectedSourceList[foundSource].source = resp.source
|
||||
|
||||
console.log("source sync-all response:", resp.summary)
|
||||
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Success
|
||||
toastNotification.message = `Successfully connected ${sourceType}`
|
||||
toastNotification.message = `Successfully connected external data source`
|
||||
|
||||
// const upsertSummary = sourceSyncMessage.response as UpsertSummary
|
||||
// if(upsertSummary && upsertSummary.totalResources != upsertSummary.updatedResources.length){
|
||||
|
@ -207,12 +206,12 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
this.toastService.show(toastNotification)
|
||||
},
|
||||
(err) => {
|
||||
delete this.status[sourceType]
|
||||
delete this.status[dbSourceCredential.brand_id]
|
||||
// window.location.reload();
|
||||
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Error
|
||||
toastNotification.message = `An error occurred while accessing ${sourceType}: '${this.extractErrorFromResponse(err)}'`
|
||||
toastNotification.message = `An error occurred while accessing external data source: '${this.extractErrorFromResponse(err)}'`
|
||||
toastNotification.autohide = false
|
||||
toastNotification.link = {
|
||||
text: "View Details",
|
||||
|
@ -223,12 +222,12 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
delete this.status[sourceType]
|
||||
delete this.status[expectedSourceStateInfo.brand_id]
|
||||
// window.location.reload();
|
||||
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Error
|
||||
toastNotification.message = `An error occurred while accessing ${sourceType}: '${JSON.stringify(err)}'`
|
||||
toastNotification.message = `An error occurred while accessing external data source: '${JSON.stringify(err)}'`
|
||||
toastNotification.autohide = false
|
||||
this.toastService.show(toastNotification)
|
||||
console.error(err)
|
||||
|
@ -296,7 +295,7 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
|
||||
public openModal(contentModalRef, sourceListItem: SourceListItem) {
|
||||
if(
|
||||
(this.status[sourceListItem.metadata.source_type] && this.status[sourceListItem.metadata.source_type] != 'failed') //if this source type is currently "loading" dont open the modal window
|
||||
(this.status[sourceListItem.brand.id] && this.status[sourceListItem.brand.id] != 'failed') //if this source type is currently "loading" dont open the modal window
|
||||
|| !sourceListItem.source //if there's no connected source, dont open the modal window
|
||||
|| (this.status[sourceListItem.source.id] && this.status[sourceListItem.source.id] != 'failed') //if this source type is currently "loading" dont open the modal window
|
||||
){
|
||||
|
@ -323,12 +322,12 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
this.fastenApi.syncSource(source.id).subscribe(
|
||||
(respData) => {
|
||||
delete this.status[source.id]
|
||||
delete this.status[source.source_type]
|
||||
delete this.status[source.brand_id]
|
||||
console.log("source sync response:", respData)
|
||||
},
|
||||
(err) => {
|
||||
delete this.status[source.id]
|
||||
delete this.status[source.source_type]
|
||||
delete this.status[source.brand_id]
|
||||
console.log(err)
|
||||
}
|
||||
)
|
||||
|
@ -336,7 +335,7 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
|
||||
public sourceDeleteHandler(){
|
||||
let source = this.modalSelectedSourceListItem.source
|
||||
let sourceDisplayName = this.modalSelectedSourceListItem?.metadata?.display || this.modalSelectedSourceListItem?.source?.source_type || 'unknown'
|
||||
let sourceDisplayName = this.modalSelectedSourceListItem?.source?.display || this.modalSelectedSourceListItem?.brand?.name || 'unknown'
|
||||
|
||||
this.status[source.id] = "authorize"
|
||||
this.modalService.dismissAll()
|
||||
|
@ -344,7 +343,7 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
this.fastenApi.deleteSource(source.id).subscribe(
|
||||
(respData) => {
|
||||
delete this.status[source.id]
|
||||
delete this.status[source.source_type]
|
||||
delete this.status[source.brand_id]
|
||||
|
||||
//delete this source from the connnected list
|
||||
let foundIndex = this.connectedSourceList.findIndex((connectedSource) => {
|
||||
|
@ -365,7 +364,7 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
},
|
||||
(err) => {
|
||||
delete this.status[source.id]
|
||||
delete this.status[source.source_type]
|
||||
delete this.status[source.brand_id]
|
||||
|
||||
const toastNotification = new ToastNotification()
|
||||
toastNotification.type = ToastType.Error
|
||||
|
@ -376,17 +375,18 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
}
|
||||
|
||||
//this is similar to the connectHandler in the MedicalSourcesComponent
|
||||
//TODO: refactor this to use the connectHandler in the MedicalSourcesComponent
|
||||
public sourceReconnectHandler(selectedSourceListItem: SourceListItem){
|
||||
|
||||
let sourceType = selectedSourceListItem.metadata.source_type
|
||||
this.lighthouseApi.getLighthouseSource(sourceType)
|
||||
let endpointId = selectedSourceListItem?.source?.endpoint_id
|
||||
this.lighthouseApi.getLighthouseSource(endpointId)
|
||||
.then(async (sourceMetadata: LighthouseSourceMetadata) => {
|
||||
console.log(sourceMetadata);
|
||||
let authorizationUrl = await this.lighthouseApi.generateSourceAuthorizeUrl(sourceType, sourceMetadata, selectedSourceListItem.source.id)
|
||||
let authorizationUrl = await this.lighthouseApi.generateSourceAuthorizeUrl(sourceMetadata, selectedSourceListItem.source.id)
|
||||
|
||||
console.log('authorize url:', authorizationUrl.toString());
|
||||
// redirect to lighthouse with uri's (or open a new window in desktop mode)
|
||||
this.lighthouseApi.redirectWithOriginAndDestination(authorizationUrl.toString(), sourceType, sourceMetadata.redirect_uri).subscribe((codeData) => {
|
||||
this.lighthouseApi.redirectWithOriginAndDestination(authorizationUrl.toString(), sourceMetadata).subscribe((desktopRedirectData) => {
|
||||
//Note: this code will only run in Desktop mode (with popups)
|
||||
//in non-desktop environments, the user is redirected in the same window, and this code is never executed.
|
||||
|
||||
|
@ -394,7 +394,7 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
|||
this.modalService.dismissAll()
|
||||
|
||||
//redirect the browser back to this page with the code in the query string parameters
|
||||
this.lighthouseApi.redirectWithDesktopCode(sourceType, codeData)
|
||||
this.lighthouseApi.redirectWithDesktopCode(desktopRedirectData.state, desktopRedirectData.codeData)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
export class MetadataSource {
|
||||
aliases?: string[]
|
||||
brand_logo?: string
|
||||
category: string[]
|
||||
display: string
|
||||
hidden: boolean
|
||||
identifiers?: {[name:string]: string}
|
||||
patient_access_description?: string
|
||||
patient_access_url?: string
|
||||
platform_type: string
|
||||
source_type: string
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
export class SourceState {
|
||||
state: string
|
||||
|
||||
source_type: string //used to override the source_type for sources which have a single redirect url (eg. Epic)
|
||||
endpoint_id: string
|
||||
portal_id: string
|
||||
brand_id: string
|
||||
|
||||
reconnect_source_id?: string //used to reconnect a source
|
||||
|
||||
code_verifier?: string
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
import {LighthouseSourceMetadata} from '../lighthouse/lighthouse-source-metadata';
|
||||
import {BackgroundJob} from './background-job';
|
||||
|
||||
export class Source extends LighthouseSourceMetadata{
|
||||
export class Source {
|
||||
id?: string
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
user_id?: number
|
||||
source_type: string
|
||||
|
||||
display?: string
|
||||
brand_id?: string
|
||||
portal_id?: string
|
||||
endpoint_id: string
|
||||
platform_type: string
|
||||
|
||||
latest_background_job?: BackgroundJob
|
||||
|
||||
patient: string
|
||||
client_id: string
|
||||
access_token: string
|
||||
refresh_token?: string
|
||||
id_token?: string
|
||||
|
@ -19,8 +26,7 @@ export class Source extends LighthouseSourceMetadata{
|
|||
dynamic_client_jwks?: any[]
|
||||
dynamic_client_id?: string
|
||||
|
||||
constructor(object: any) {
|
||||
super()
|
||||
constructor(object: Partial<Source>) {
|
||||
return Object.assign(this, object)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
import {MetadataSource} from '../fasten/metadata-source';
|
||||
import {PatientAccessEndpoint} from '../patient-access-brands';
|
||||
|
||||
export class LighthouseSourceMetadata extends MetadataSource {
|
||||
|
||||
authorization_endpoint: string
|
||||
token_endpoint: string
|
||||
introspection_endpoint: string
|
||||
userinfo_endpoint: string
|
||||
registration_endpoint: string
|
||||
export class LighthouseSourceMetadata extends PatientAccessEndpoint {
|
||||
brand_id: string
|
||||
portal_id: string
|
||||
// endpoint_id = embedded PatientAccessEndpoint.id
|
||||
|
||||
scopes_supported: string[]
|
||||
issuer: string
|
||||
grant_types_supported: string[]
|
||||
response_types_supported: string[]
|
||||
response_modes_supported: string[]
|
||||
aud: string
|
||||
code_challenge_methods_supported: string[]
|
||||
|
||||
api_endpoint_base_url: string
|
||||
client_id: string
|
||||
redirect_uri: string
|
||||
|
||||
confidential: boolean
|
||||
dynamic_client_registration_mode: string
|
||||
cors_relay_required: boolean
|
||||
|
||||
issuer: string
|
||||
aud: string
|
||||
|
||||
|
||||
platform_type: string
|
||||
client_id: string
|
||||
redirect_uri: string
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
import {MetadataSource} from '../fasten/metadata-source';
|
||||
import {PatientAccessBrand, PatientAccessPortal} from '../patient-access-brands';
|
||||
|
||||
export interface LighthouseEndpointListDisplayItem {
|
||||
id: string;
|
||||
platform_type: string;
|
||||
}
|
||||
|
||||
export interface LighthousePortalListDisplayItem extends PatientAccessPortal {
|
||||
endpoints?: LighthouseEndpointListDisplayItem[];
|
||||
}
|
||||
|
||||
export interface LighthouseBrandListDisplayItem extends PatientAccessBrand {
|
||||
portals: LighthousePortalListDisplayItem[];
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class LighthouseSourceSearchResult {
|
||||
_index: string;
|
||||
_type: string;
|
||||
_id: string;
|
||||
_score: number;
|
||||
_source: MetadataSource;
|
||||
_source: LighthouseBrandListDisplayItem;
|
||||
sort: string[];
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
// Code generated by tygo. DO NOT EDIT.
|
||||
|
||||
//////////
|
||||
// source: patient_access_brand.go
|
||||
|
||||
/**
|
||||
* TODO: generate via reflection
|
||||
*/
|
||||
export class PatientAccessBrand {
|
||||
/**
|
||||
* Fasten UUID for the brand - id should be a unique identifier for the brand. It is globally unique and should be a UUID
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* List of identifiers for the organization, e.g., external system, etc NPI, etc
|
||||
* Identifiers SHOULD include a platform identifier, so we know where this entry came from, but not required
|
||||
*/
|
||||
identifiers?: any /* datatypes.Identifier */[];
|
||||
/**
|
||||
* RFC3339 Date and time the organization was last updated - Timestamp should be the last updated datetime for the data from this source, not the current date
|
||||
*/
|
||||
last_updated: string;
|
||||
/**
|
||||
* Primary name for the organization to display on a card, e.g., “General Hospital”
|
||||
* Note this is not used within the app, only the Portal name should be used.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* URL for the organization’s primary website. note this is distinct from a patient portal, described under “Patient Access Details” below
|
||||
*/
|
||||
brand_website?: string;
|
||||
/**
|
||||
* URL for the organization’s logo, which will be displayed on a card, Note this is a fallback logo, the primary logo will always be the Portal logo
|
||||
*/
|
||||
logo?: string;
|
||||
/**
|
||||
* List of alternate names for the organization, e.g., “GH”, “General”, “GH Hospital”
|
||||
*/
|
||||
aliases?: string[];
|
||||
/**
|
||||
* List of locations for the organization
|
||||
* These should be the locations where the organization has a physical presence, e.g., a hospital or clinic"
|
||||
*/
|
||||
locations?: any /* datatypes.Address */[];
|
||||
/**
|
||||
* Patient Access Details
|
||||
* These must be references to Patient Access Portal resource Ids
|
||||
*/
|
||||
portal_ids: string[];
|
||||
/**
|
||||
* list of brand ids that were merged together to creat this brand
|
||||
*/
|
||||
brand_ids?: string[];
|
||||
}
|
||||
|
||||
//////////
|
||||
// source: patient_access_endpoint.go
|
||||
|
||||
/**
|
||||
* TODO: generate via reflection
|
||||
*/
|
||||
export class PatientAccessEndpoint {
|
||||
/**
|
||||
* Fasten UUID for the endpoint
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* List of identifiers for the endpoint, e.g., “GH1234”
|
||||
*/
|
||||
identifiers?: any /* datatypes.Identifier */[];
|
||||
/**
|
||||
* RFC3339 Date and time the endpoint was last updated
|
||||
*/
|
||||
last_updated: string;
|
||||
/**
|
||||
* Status of the endpoint, e.g., “active” - http://terminology.hl7.org/CodeSystem/endpoint-status
|
||||
*/
|
||||
status: string;
|
||||
/**
|
||||
* Connection type for the endpoint, e.g., “hl7-fhir-rest” - http://terminology.hl7.org/CodeSystem/endpoint-connection-type
|
||||
*/
|
||||
connection_type: string;
|
||||
/**
|
||||
* Platform type for the endpoint, e.g., “epic”, "cerner"
|
||||
*/
|
||||
platform_type: string;
|
||||
/**
|
||||
* URL for the endpoint, must have trailing slash
|
||||
*/
|
||||
url: string;
|
||||
/**
|
||||
* oauth endpoints
|
||||
*/
|
||||
authorization_endpoint?: string;
|
||||
token_endpoint?: string;
|
||||
introspection_endpoint?: string;
|
||||
userinfo_endpoint?: string;
|
||||
/**
|
||||
* optional - required when Dynamic Client Registration mode is set
|
||||
*/
|
||||
registration_endpoint?: string;
|
||||
/**
|
||||
* Fasten custom configuration
|
||||
*/
|
||||
fhir_version?: string;
|
||||
smart_configuration_url?: string;
|
||||
fhir_capabilities_url?: string;
|
||||
/**
|
||||
* Software info
|
||||
*/
|
||||
software_name?: string;
|
||||
software_version?: string;
|
||||
software_release_date?: string;
|
||||
}
|
||||
|
||||
//////////
|
||||
// source: patient_access_portal.go
|
||||
|
||||
/**
|
||||
* TODO: generate via reflection
|
||||
*/
|
||||
export class PatientAccessPortal {
|
||||
/**
|
||||
* Fasten UUID for the portal
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* List of identifiers for the organization, e.g., “GH1234”
|
||||
*/
|
||||
identifiers?: any /* datatypes.Identifier */[];
|
||||
/**
|
||||
* RFC3339 date & time of the last update to the patient portal’s information
|
||||
*/
|
||||
last_updated: string;
|
||||
/**
|
||||
* Name of the patient portal, e.g., “MyChart”
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* URL for the patient portal’s logo, which will be displayed on a card
|
||||
*/
|
||||
logo?: string;
|
||||
/**
|
||||
* URL for the patient portal, where patients can manage accounts with this provider.
|
||||
*/
|
||||
portal_website?: string;
|
||||
/**
|
||||
* Description of the patient portal, e.g., “Manage your health information with General Hospital”
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
* List of endpoint IDs for the patient portal. This is used to associate the patient portal with the endpoints that are used to access it.
|
||||
*/
|
||||
endpoint_ids: string[];
|
||||
}
|
|
@ -30,7 +30,7 @@
|
|||
<tbody>
|
||||
<tr *ngFor="let backgroundJob of backgroundJobs" [class.border-left-danger]="backgroundJob.job_status == 'STATUS_FAILED'">
|
||||
<td>
|
||||
<img style="max-height:30px" [src]="'assets/sources/'+(backgroundJob?.data?.source_type + '.png')" [alt]="backgroundJob?.data?.source_type" class="img-fluid">
|
||||
<img style="max-height:30px" [src]="'assets/sources/'+(backgroundJob?.data?.brand_id + '.png')" [alt]="backgroundJob?.data?.brand_id" class="img-fluid">
|
||||
</td>
|
||||
<td>{{backgroundJob.job_type}}</td>
|
||||
<td container="body" [ngbTooltip]="backgroundJob.created_at | amDateFormat:'YYYY-MM-DD HH:mm'">{{backgroundJob.created_at | amDateFormat:'LL'}}</td>
|
||||
|
|
|
@ -3,7 +3,6 @@ import {Source} from '../../models/fasten/source';
|
|||
import {Router} from '@angular/router';
|
||||
import {ResourceFhir} from '../../models/fasten/resource_fhir';
|
||||
import {forkJoin} from 'rxjs';
|
||||
import {MetadataSource} from '../../models/fasten/metadata-source';
|
||||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {LighthouseService} from '../../services/lighthouse.service';
|
||||
import { GridStack, GridStackOptions, GridStackWidget } from 'gridstack';
|
||||
|
@ -29,7 +28,6 @@ export class DashboardComponent implements OnInit {
|
|||
recordsCount: number = 0
|
||||
patientForSource: {[name: string]: ResourceFhir} = {}
|
||||
|
||||
metadataSource: { [name: string]: MetadataSource }
|
||||
dashboardConfigs: DashboardConfig[] = []
|
||||
selectedDashboardConfigNdx: number = 0
|
||||
|
||||
|
@ -85,7 +83,7 @@ export class DashboardComponent implements OnInit {
|
|||
}
|
||||
|
||||
isActive(source: Source){
|
||||
if(source.source_type == "manual" || source.source_type == 'fasten'){
|
||||
if(source.platform_type == "manual" || source.platform_type == 'fasten'){
|
||||
return '--'
|
||||
}
|
||||
let expiresDate = new Date(source.expires_at);
|
||||
|
|
|
@ -4,8 +4,14 @@ import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
|||
import {Source} from '../../models/fasten/source';
|
||||
import {forkJoin} from 'rxjs';
|
||||
import {LighthouseService} from '../../services/lighthouse.service';
|
||||
import {SourceListItem} from '../medical-sources/medical-sources.component';
|
||||
import {Router} from '@angular/router';
|
||||
import {LighthouseBrandListDisplayItem} from '../../models/lighthouse/lighthouse-source-search';
|
||||
import {LighthouseSourceMetadata} from '../../models/lighthouse/lighthouse-source-metadata';
|
||||
|
||||
export class SourceListItem {
|
||||
source?: Source
|
||||
metadata: LighthouseSourceMetadata
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-explore',
|
||||
|
@ -27,7 +33,7 @@ export class ExploreComponent implements OnInit {
|
|||
|
||||
//handle connected sources sources
|
||||
const connectedSources = results as Source[]
|
||||
forkJoin(connectedSources.map((source) => this.lighthouseApi.getLighthouseSource(source.source_type))).subscribe((connectedMetadata) => {
|
||||
forkJoin(connectedSources.map((source) => this.lighthouseApi.getLighthouseSource(source.endpoint_id))).subscribe((connectedMetadata) => {
|
||||
for(const ndx in connectedSources){
|
||||
this.connectedSources.push({source: connectedSources[ndx], metadata: connectedMetadata[ndx]})
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
</div>
|
||||
<input [ngModel]="searchTermUpdate | async" (keyup)="searchTermUpdate.next($event.target.value)" type="text" class="form-control" placeholder="Search Term">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">{{this.availableSourceList.length}} of {{this.resultLimits.totalItems}} results</span>
|
||||
<span class="input-group-text">{{this.availableLighthouseBrandList.length}} of {{this.resultLimits.totalItems}} results</span>
|
||||
</div>
|
||||
|
||||
</div><!-- input-group -->
|
||||
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div *ngIf="!loading || availableSourceList.length > 0 else isLoadingTemplate" class="row row-sm"
|
||||
<div *ngIf="!loading || availableLighthouseBrandList.length > 0 else isLoadingTemplate" class="row row-sm"
|
||||
infiniteScroll
|
||||
[infiniteScrollDistance]="2"
|
||||
[infiniteScrollThrottle]="50"
|
||||
|
@ -73,9 +73,9 @@
|
|||
>
|
||||
|
||||
<app-medical-sources-card class="col-sm-3 mg-b-20 px-3"
|
||||
*ngFor="let sourceData of availableSourceList"
|
||||
[sourceInfo]="sourceData"
|
||||
[status]="status[sourceData.metadata.source_type]"
|
||||
*ngFor="let lighthouseBrand of availableLighthouseBrandList"
|
||||
[sourceInfo]="lighthouseBrand"
|
||||
[status]="status[lighthouseBrand.brand.id]"
|
||||
(onClick)="connectModalHandler(contentModalRef, $event)"
|
||||
></app-medical-sources-card>
|
||||
|
||||
|
@ -92,12 +92,12 @@
|
|||
|
||||
<div class="modal-header">
|
||||
<div class="media">
|
||||
<img class="modal-header-media-image mg-sm-r-20 mg-b-20 mg-sm-b-0" [src]="'assets/sources/'+(modalSelectedSourceListItem?.metadata.brand_logo ? modalSelectedSourceListItem?.metadata?.brand_logo : modalSelectedSourceListItem?.metadata?.source_type+'.png')" [alt]="modalSelectedSourceListItem?.metadata?.display">
|
||||
<img class="modal-header-media-image mg-sm-r-20 mg-b-20 mg-sm-b-0" [src]="'assets/sources/'+(modalSelectedBrandListItem?.id +'.png')" [alt]="modalSelectedBrandListItem?.name">
|
||||
<div class="media-body">
|
||||
<h6>{{modalSelectedSourceListItem?.metadata.display}}</h6>
|
||||
<a *ngIf="modalSelectedSourceListItem?.metadata.patient_access_url"
|
||||
[href]="modalSelectedSourceListItem.metadata?.patient_access_url"
|
||||
class="mg-b-0" externalLink>{{modalSelectedSourceListItem?.metadata.patient_access_url | shortDomain}}</a>
|
||||
<h6>{{modalSelectedBrandListItem?.name}}</h6>
|
||||
<a *ngIf="modalSelectedBrandListItem?.brand_website"
|
||||
[href]="modalSelectedBrandListItem?.brand_website"
|
||||
class="mg-b-0" externalLink>{{modalSelectedBrandListItem?.brand_website | shortDomain}}</a>
|
||||
</div><!-- media-body -->
|
||||
</div><!-- media -->
|
||||
<button type="button" class="btn close" aria-label="Close" (click)="modal.dismiss('Cross click')">
|
||||
|
@ -114,40 +114,58 @@
|
|||
If the data about this institution is missing or incorrect, you can <a class="link" href="https://docs.google.com/spreadsheets/d/1ZSgwfd7kwxSnimk4yofIFcR8ZMUls0zi9SZpRiOJBx0/edit?usp=sharing" externalLink>click here</a> to submit a correction.
|
||||
</p>
|
||||
|
||||
<ng-container *ngIf="modalSelectedSourceListItem?.metadata?.category?.length > 0 || modalSelectedSourceListItem?.metadata?.patient_access_description">
|
||||
<hr/>
|
||||
<ng-container *ngIf="modalSelectedSourceListItem?.metadata?.patient_access_description">
|
||||
<h6>About this Source</h6>
|
||||
<p >{{modalSelectedSourceListItem?.metadata?.patient_access_description}}</p>
|
||||
|
||||
<div *ngFor="let portal of modalSelectedBrandListItem.portals" class="list-group">
|
||||
<ng-container *ngFor="let endpoint of portal.endpoints">
|
||||
<div (click)="connectHandler($event, modalSelectedBrandListItem.id, portal.id, endpoint.id)" class="list-group-item list-group-item-action flex-column align-items-start cursor-pointer">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<h5 class="mb-1">
|
||||
<span *ngIf="status[endpoint.id] == 'authorize'" class="spinner-border spinner-border-sm text-primary" role="status" aria-hidden="true"></span>
|
||||
{{portal.name}}
|
||||
</h5>
|
||||
<span class="badge badge-primary badge-pill">{{endpoint.platform_type}}</span>
|
||||
</div>
|
||||
<p class="mb-1">{{portal.description}}</p>
|
||||
<small>{{portal.portal_website | shortDomain}}</small>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="modalSelectedSourceListItem?.metadata.aliases?.length > 0">
|
||||
<h6>Aliases</h6>
|
||||
<ul>
|
||||
<li *ngFor="let alias of modalSelectedSourceListItem?.metadata?.aliases">{{alias}}</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="modalSelectedSourceListItem?.metadata.platform_type">
|
||||
<h6>Platform Type</h6>
|
||||
<p>{{modalSelectedSourceListItem?.metadata.platform_type}}</p>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="modalSelectedSourceListItem?.metadata?.category?.length > 0">
|
||||
<h6>Categories</h6>
|
||||
<ul>
|
||||
<li *ngFor="let cat of modalSelectedSourceListItem?.metadata?.category">{{cat | medicalSourcesCategoryLookup}}</li>
|
||||
</ul>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- <ng-container *ngIf="modalSelectedSourceListItem?.metadata?.category?.length > 0 || modalSelectedSourceListItem?.metadata?.patient_access_description">-->
|
||||
<!-- <hr/>-->
|
||||
<!-- <ng-container *ngIf="modalSelectedSourceListItem?.metadata?.patient_access_description">-->
|
||||
<!-- <h6>About this Source</h6>-->
|
||||
<!-- <p >{{modalSelectedSourceListItem?.metadata?.patient_access_description}}</p>-->
|
||||
<!-- </ng-container>-->
|
||||
<!-- <ng-container *ngIf="modalSelectedSourceListItem?.metadata.aliases?.length > 0">-->
|
||||
<!-- <h6>Aliases</h6>-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li *ngFor="let alias of modalSelectedSourceListItem?.metadata?.aliases">{{alias}}</li>-->
|
||||
<!-- </ul>-->
|
||||
<!-- </ng-container>-->
|
||||
<!-- <ng-container *ngIf="modalSelectedSourceListItem?.metadata.platform_type">-->
|
||||
<!-- <h6>Platform Type</h6>-->
|
||||
<!-- <p>{{modalSelectedSourceListItem?.metadata.platform_type}}</p>-->
|
||||
<!-- </ng-container>-->
|
||||
<!-- <ng-container *ngIf="modalSelectedSourceListItem?.metadata?.category?.length > 0">-->
|
||||
<!-- <h6>Categories</h6>-->
|
||||
<!-- <ul>-->
|
||||
<!-- <li *ngFor="let cat of modalSelectedSourceListItem?.metadata?.category">{{cat | medicalSourcesCategoryLookup}}</li>-->
|
||||
<!-- </ul>-->
|
||||
<!-- </ng-container>-->
|
||||
<!-- </ng-container>-->
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<!-- <button (click)="sourceSyncHandler(modalSelectedSourceListItem.source)" type="button" class="btn btn-indigo">Sync</button>-->
|
||||
<!-- <button (click)="connectHandler($event, modalSelectedSourceListItem.source['source_type'])" type="button" class="btn btn-outline-light">Reconnect</button>-->
|
||||
<button type="button" (click)="connectHandler($event, modalSelectedSourceListItem)" class="btn btn-indigo">
|
||||
<span *ngIf="status[modalSelectedSourceListItem?.metadata?.source_type] == 'authorize'" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
Connect
|
||||
</button>
|
||||
<button (click)="modal.dismiss('Close click')" type="button" class="btn btn-outline-light">Close</button>
|
||||
</div>
|
||||
<!-- <div class="modal-footer">-->
|
||||
<!--<!– <button (click)="sourceSyncHandler(modalSelectedSourceListItem.source)" type="button" class="btn btn-indigo">Sync</button>–>-->
|
||||
<!-- <!– <button (click)="connectHandler($event, modalSelectedSourceListItem.source['source_type'])" type="button" class="btn btn-outline-light">Reconnect</button>–>-->
|
||||
<!-- <button type="button" (click)="connectHandler($event, modalSelectedSourceListItem)" class="btn btn-indigo">-->
|
||||
<!-- <span *ngIf="status[modalSelectedSourceListItem?.metadata?.source_type] == 'authorize'" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>-->
|
||||
<!-- Connect-->
|
||||
<!-- </button>-->
|
||||
<!-- <button (click)="modal.dismiss('Close click')" type="button" class="btn btn-outline-light">Close</button>-->
|
||||
<!-- </div>-->
|
||||
</ng-template>
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import {LighthouseService} from '../../services/lighthouse.service';
|
|||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {LighthouseSourceMetadata} from '../../models/lighthouse/lighthouse-source-metadata';
|
||||
import {Source} from '../../models/fasten/source';
|
||||
import {MetadataSource} from '../../models/fasten/metadata-source';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {environment} from '../../../environments/environment';
|
||||
|
@ -11,18 +10,19 @@ import {BehaviorSubject, forkJoin, Observable, of, Subject} from 'rxjs';
|
|||
import {
|
||||
LighthouseSourceSearch,
|
||||
LighthouseSourceSearchAggregation,
|
||||
LighthouseSourceSearchResult
|
||||
LighthouseBrandListDisplayItem
|
||||
} from '../../models/lighthouse/lighthouse-source-search';
|
||||
import {debounceTime, distinctUntilChanged, pairwise, startWith} from 'rxjs/operators';
|
||||
import {MedicalSourcesFilter, MedicalSourcesFilterService} from '../../services/medical-sources-filter.service';
|
||||
import {FormControl, FormGroup} from '@angular/forms';
|
||||
import * as _ from 'lodash';
|
||||
import {PatientAccessBrand} from '../../models/patient-access-brands';
|
||||
|
||||
export const sourceConnectWindowTimeout = 24*5000 //wait 2 minutes (5 * 24 = 120)
|
||||
|
||||
export class SourceListItem {
|
||||
source?: Source
|
||||
metadata: MetadataSource
|
||||
brand: LighthouseBrandListDisplayItem | PatientAccessBrand
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
@ -38,7 +38,7 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
uploadedFile: File[] = []
|
||||
|
||||
|
||||
availableSourceList: SourceListItem[] = []
|
||||
availableLighthouseBrandList: SourceListItem[] = []
|
||||
searchTermUpdate = new BehaviorSubject<string>("");
|
||||
status: { [name: string]: undefined | "token" | "authorize" } = {}
|
||||
|
||||
|
@ -69,7 +69,7 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
filterForm = this.filterService.filterForm;
|
||||
|
||||
//modal
|
||||
modalSelectedSourceListItem:SourceListItem = null;
|
||||
modalSelectedBrandListItem: LighthouseBrandListDisplayItem | PatientAccessBrand = null;
|
||||
modalCloseResult = '';
|
||||
|
||||
constructor(
|
||||
|
@ -85,7 +85,7 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
console.log("medical-sources - filterChanges", filterInfo)
|
||||
|
||||
//this function should only trigger when there's a change to the filter values -- which requires a new query
|
||||
this.availableSourceList = []
|
||||
this.availableLighthouseBrandList = []
|
||||
this.resultLimits.totalItems = 0
|
||||
this.resultLimits.scrollComplete = false
|
||||
|
||||
|
@ -99,14 +99,18 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
ngOnInit(): void {
|
||||
|
||||
|
||||
//TODO: handle Callbacks from the source connect window
|
||||
const callbackSourceType = this.activatedRoute.snapshot.paramMap.get('source_type')
|
||||
if(callbackSourceType){
|
||||
// TODO: handle Callbacks from the source connect window
|
||||
const callbackState = this.activatedRoute.snapshot.paramMap.get('state')
|
||||
if(callbackState){
|
||||
|
||||
//get the source state information from localstorage
|
||||
let sourceStateInfo = this.lighthouseApi.getSourceState(callbackState)
|
||||
|
||||
//move this source from available to connected (with a progress bar)
|
||||
//remove item from available sources list, add to connected sources.
|
||||
let inProgressAvailableIndex = this.availableSourceList.findIndex((item) => item.metadata.source_type == callbackSourceType)
|
||||
let inProgressAvailableIndex = this.availableLighthouseBrandList.findIndex((item) => item.brand.id == sourceStateInfo.brand_id)
|
||||
if(inProgressAvailableIndex > -1){
|
||||
let sourcesInProgress = this.availableSourceList.splice(inProgressAvailableIndex, 1);
|
||||
let sourcesInProgress = this.availableLighthouseBrandList.splice(inProgressAvailableIndex, 1);
|
||||
}
|
||||
}
|
||||
//we're not in a callback redirect, lets load the sources
|
||||
|
@ -154,8 +158,8 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
// this.searchResults = wrapper.hits.hits;
|
||||
this.resultLimits.totalItems = wrapper.hits.total.value;
|
||||
|
||||
this.availableSourceList = this.availableSourceList.concat(wrapper.hits.hits.map((result) => {
|
||||
return {metadata: result._source}
|
||||
this.availableLighthouseBrandList = this.availableLighthouseBrandList.concat(wrapper.hits.hits.map((result) => {
|
||||
return {brand: result._source}
|
||||
}))
|
||||
|
||||
//check if scroll is complete.
|
||||
|
@ -258,34 +262,37 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
console.log("TODO: connect Handler")
|
||||
|
||||
|
||||
this.modalSelectedSourceListItem = sourceListItem
|
||||
this.modalSelectedBrandListItem = sourceListItem.brand
|
||||
this.modalService.open(contentModalRef, {ariaLabelledBy: 'modal-basic-title'}).result.then((result) => {
|
||||
this.modalSelectedSourceListItem = null
|
||||
this.modalSelectedBrandListItem = null
|
||||
this.modalCloseResult = `Closed with: ${result}`;
|
||||
}, (reason) => {
|
||||
this.modalSelectedSourceListItem = null
|
||||
this.modalSelectedBrandListItem = null
|
||||
});
|
||||
}
|
||||
|
||||
// /**
|
||||
// * after pressing the connect button in the Modal, this function will generate an authorize url for this source, and redirec the user.
|
||||
// * after pressing the connect button in the Modal, this function will generate an authorize url for this source, and redirect the user.
|
||||
// * @param $event
|
||||
// * @param sourceType
|
||||
// */
|
||||
public connectHandler($event, sourceListItem: SourceListItem): void {
|
||||
public connectHandler($event, brandId: string, portalId: string, endpointId: string): void {
|
||||
|
||||
($event.currentTarget as HTMLButtonElement).disabled = true;
|
||||
this.status[sourceListItem.metadata.source_type] = "authorize"
|
||||
this.status[brandId] = "authorize"
|
||||
this.status[endpointId] = "authorize"
|
||||
|
||||
let sourceType = sourceListItem.metadata.source_type
|
||||
this.lighthouseApi.getLighthouseSource(sourceType)
|
||||
this.lighthouseApi.getLighthouseSource(endpointId)
|
||||
.then(async (sourceMetadata: LighthouseSourceMetadata) => {
|
||||
sourceMetadata.brand_id = brandId
|
||||
sourceMetadata.portal_id = portalId
|
||||
|
||||
console.log(sourceMetadata);
|
||||
let authorizationUrl = await this.lighthouseApi.generateSourceAuthorizeUrl(sourceType, sourceMetadata)
|
||||
let authorizationUrl = await this.lighthouseApi.generateSourceAuthorizeUrl(sourceMetadata)
|
||||
|
||||
console.log('authorize url:', authorizationUrl.toString());
|
||||
// redirect to lighthouse with uri's (or open a new window in desktop mode)
|
||||
this.lighthouseApi.redirectWithOriginAndDestination(authorizationUrl.toString(), sourceType, sourceMetadata.redirect_uri).subscribe((codeData) => {
|
||||
this.lighthouseApi.redirectWithOriginAndDestination(authorizationUrl.toString(), sourceMetadata).subscribe((codeData) => {
|
||||
//Note: this code will only run in Desktop mode (with popups)
|
||||
//in non-desktop environments, the user is redirected in the same window, and this code is never executed.
|
||||
|
||||
|
@ -293,7 +300,7 @@ export class MedicalSourcesComponent implements OnInit {
|
|||
this.modalService.dismissAll()
|
||||
|
||||
//redirect the browser back to this page with the code in the query string parameters
|
||||
this.lighthouseApi.redirectWithDesktopCode(sourceType, codeData)
|
||||
this.lighthouseApi.redirectWithDesktopCode(sourceMetadata.platform_type, codeData)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
<div class="">
|
||||
<div class="patient-image-wrap embed-responsive-item">
|
||||
|
||||
<img [src]="'assets/sources/'+selectedSource?.source_type+'.png'"
|
||||
alt="{{selectedSource?.source_type}}"
|
||||
<img [src]="'assets/sources/'+selectedSource?.brand_id+'.png'"
|
||||
alt="{{selectedSource?.endpoint_id}}"
|
||||
class="img-fluid">
|
||||
</div>
|
||||
</div>
|
||||
|
@ -51,7 +51,7 @@
|
|||
<div class="az-content-left">
|
||||
<div class="az-content-breadcrumb">
|
||||
<span>Sources</span>
|
||||
<span>{{selectedSource?.source_type}}</span>
|
||||
<span>{{selectedSource?.endpoint_id}}</span>
|
||||
<span>Details</span>
|
||||
</div>
|
||||
|
||||
|
@ -75,7 +75,7 @@
|
|||
<ng-template #empty >
|
||||
<div class="container">
|
||||
<div class="az-error-wrapper">
|
||||
<h1>{{selectedSource?.source_type}}</h1>
|
||||
<h1>{{selectedSource?.endpoint_id}}</h1>
|
||||
<h2>No resources found for this Healthcare provider</h2>
|
||||
<h6>You may need to re-authenticate to this source, or wait for the resources to sync.</h6>
|
||||
<a routerLink="/sources" class="btn btn-outline-indigo">Sources</a>
|
||||
|
|
|
@ -28,7 +28,7 @@ export class AuthService {
|
|||
const state = uuidV4()
|
||||
let sourceStateInfo = new SourceState()
|
||||
sourceStateInfo.state = state
|
||||
sourceStateInfo.source_type = idp_type
|
||||
// sourceStateInfo.source_type = idp_type
|
||||
sourceStateInfo.redirect_uri = `${window.location.href}/callback/hello`
|
||||
|
||||
// generate the authorization url
|
||||
|
|
|
@ -9,11 +9,9 @@ import {User} from '../models/fasten/user';
|
|||
import {ResourceFhir} from '../models/fasten/resource_fhir';
|
||||
import {SourceSummary} from '../models/fasten/source-summary';
|
||||
import {Summary} from '../models/fasten/summary';
|
||||
import {MetadataSource} from '../models/fasten/metadata-source';
|
||||
import {AuthService} from './auth.service';
|
||||
import {GetEndpointAbsolutePath} from '../../lib/utils/endpoint_absolute_path';
|
||||
import {environment} from '../../environments/environment';
|
||||
import {ResourceAssociation} from '../models/fasten/resource_association';
|
||||
import {ValueSet} from 'fhir/r4';
|
||||
import {AttachmentModel} from '../../lib/models/datatypes/attachment-model';
|
||||
import {BinaryModel} from '../../lib/models/resources/binary-model';
|
||||
|
|
|
@ -7,7 +7,6 @@ import {ResponseWrapper} from '../models/response-wrapper';
|
|||
import {LighthouseSourceMetadata} from '../models/lighthouse/lighthouse-source-metadata';
|
||||
import * as Oauth from '@panva/oauth4webapi';
|
||||
import {SourceState} from '../models/fasten/source-state';
|
||||
import {MetadataSource} from '../models/fasten/metadata-source';
|
||||
import {uuidV4} from '../../lib/utils/uuid';
|
||||
import {LighthouseSourceSearch} from '../models/lighthouse/lighthouse-source-search';
|
||||
import {HTTP_CLIENT_TOKEN} from "../dependency-injection";
|
||||
|
@ -15,6 +14,7 @@ import {MedicalSourcesFilter} from './medical-sources-filter.service';
|
|||
import {OpenExternalLink} from '../../lib/utils/external_link';
|
||||
import {Router, UrlSerializer} from '@angular/router';
|
||||
import {Location} from '@angular/common';
|
||||
import {PatientAccessBrand, PatientAccessEndpoint, PatientAccessPortal} from '../models/patient-access-brands';
|
||||
|
||||
export const sourceConnectDesktopTimeout = 24*5000 //wait 2 minutes (5 * 24 = 120)
|
||||
|
||||
|
@ -30,6 +30,14 @@ export class LighthouseService {
|
|||
private location: Location,
|
||||
) {}
|
||||
|
||||
public storeSourceState(state: string, sourceStateInfo: SourceState) {
|
||||
localStorage.setItem(state, JSON.stringify(sourceStateInfo))
|
||||
}
|
||||
|
||||
public getSourceState(state: string): SourceState {
|
||||
return JSON.parse(localStorage.getItem(state))
|
||||
}
|
||||
|
||||
public searchLighthouseSources(filter: MedicalSourcesFilter): Observable<LighthouseSourceSearch> {
|
||||
if((typeof filter.searchAfter === 'string' || filter.searchAfter instanceof String) && (filter.searchAfter as string).length > 0){
|
||||
filter.searchAfter = (filter.searchAfter as string).split(',')
|
||||
|
@ -46,23 +54,8 @@ export class LighthouseService {
|
|||
);
|
||||
}
|
||||
|
||||
public getLighthouseSourceMetadataMap(showHidden = false): Observable<{[name: string]: MetadataSource}> {
|
||||
const endpointUrl = new URL(`${environment.lighthouse_api_endpoint_base}/list`);
|
||||
if(showHidden){
|
||||
endpointUrl.searchParams.set('show_hidden', 'true');
|
||||
}
|
||||
|
||||
return this._httpClient.get<ResponseWrapper>(endpointUrl.toString())
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
console.log("Metadata RESPONSE", response)
|
||||
return response.data as {[name: string]: MetadataSource}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async getLighthouseSource(sourceType: string): Promise<LighthouseSourceMetadata> {
|
||||
return this._httpClient.get<any>(`${environment.lighthouse_api_endpoint_base}/connect/${sourceType}`)
|
||||
async getLighthouseSource(endpointId: string): Promise<LighthouseSourceMetadata> {
|
||||
return this._httpClient.get<any>(`${environment.lighthouse_api_endpoint_base}/connect/${endpointId}`)
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
return response.data as LighthouseSourceMetadata
|
||||
|
@ -70,12 +63,65 @@ export class LighthouseService {
|
|||
).toPromise();
|
||||
}
|
||||
|
||||
async getLighthouseCatalogBrand(brandIdOrPlatformType: string): Promise<PatientAccessBrand> {
|
||||
if(brandIdOrPlatformType === 'fasten'){
|
||||
return of({
|
||||
id: 'fasten',
|
||||
last_updated: '',
|
||||
portal_ids: [],
|
||||
name: '',
|
||||
platform_type: 'fasten'
|
||||
}).toPromise()
|
||||
} else if (brandIdOrPlatformType === 'manual'){
|
||||
return of({
|
||||
id: 'manual',
|
||||
last_updated: '',
|
||||
portal_ids: [],
|
||||
name: '',
|
||||
platform_type: 'manual'
|
||||
}).toPromise()
|
||||
}
|
||||
|
||||
async generateSourceAuthorizeUrl(sourceType: string, lighthouseSource: LighthouseSourceMetadata, reconnectSourceId?: string): Promise<URL> {
|
||||
const catalogUrl = new URL(`${environment.lighthouse_api_endpoint_base}/catalog`);
|
||||
catalogUrl.searchParams.set('brand_id', brandIdOrPlatformType);
|
||||
return this._httpClient.get<any>(catalogUrl.toString())
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
return response.data as PatientAccessBrand
|
||||
})
|
||||
).toPromise();
|
||||
}
|
||||
|
||||
async getLighthouseCatalogPortal(portalId: string): Promise<PatientAccessPortal> {
|
||||
const catalogUrl = new URL(`${environment.lighthouse_api_endpoint_base}/catalog`);
|
||||
catalogUrl.searchParams.set('portal_id', portalId);
|
||||
return this._httpClient.get<any>(catalogUrl.toString())
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
return response.data as PatientAccessPortal
|
||||
})
|
||||
).toPromise();
|
||||
}
|
||||
|
||||
async getLighthouseCatalogEndpoint(endpointId: string): Promise<PatientAccessEndpoint> {
|
||||
const catalogUrl = new URL(`${environment.lighthouse_api_endpoint_base}/catalog`);
|
||||
catalogUrl.searchParams.set('endpoint_id', endpointId);
|
||||
return this._httpClient.get<any>(catalogUrl.toString())
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
return response.data as PatientAccessEndpoint
|
||||
})
|
||||
).toPromise();
|
||||
}
|
||||
|
||||
|
||||
async generateSourceAuthorizeUrl(lighthouseSource: LighthouseSourceMetadata, reconnectSourceId?: string): Promise<URL> {
|
||||
const state = uuidV4()
|
||||
let sourceStateInfo = new SourceState()
|
||||
sourceStateInfo.state = state
|
||||
sourceStateInfo.source_type = sourceType
|
||||
sourceStateInfo.endpoint_id = lighthouseSource.id
|
||||
sourceStateInfo.portal_id = lighthouseSource.portal_id
|
||||
sourceStateInfo.brand_id = lighthouseSource.brand_id
|
||||
if(reconnectSourceId){
|
||||
//if the source already exists, and we want to re-connect it (because of an expiration), we need to pass the existing source id
|
||||
sourceStateInfo.reconnect_source_id = reconnectSourceId
|
||||
|
@ -112,7 +158,8 @@ export class LighthouseService {
|
|||
authorizationUrl.searchParams.set('code_challenge_method', codeChallengeMethod);
|
||||
}
|
||||
|
||||
localStorage.setItem(state, JSON.stringify(sourceStateInfo))
|
||||
//store the source state info
|
||||
this.storeSourceState(state, sourceStateInfo)
|
||||
|
||||
return authorizationUrl
|
||||
}
|
||||
|
@ -136,23 +183,36 @@ export class LighthouseService {
|
|||
* dest_url - https://patient360la.anthem.com/.../connect/authorize?redirect_uri=https://lighthouse.fastenhealth.com/callback/anthem
|
||||
* redirect_url - lighthouse.fastenhealth.com/sandbox/redirect/anthem?origin_url=...&dest_url=...
|
||||
*/
|
||||
redirectWithOriginAndDestination(destUrl: string, sourceType: string, callbackUri: string): Observable<any> {
|
||||
redirectWithOriginAndDestination(destUrl: string, redirectOpts: {platform_type: string, redirect_uri: string, brand_id: string, portal_id: string, id: string}): Observable<{ codeData:any, state:string }> {
|
||||
const originUrlParts = new URL(window.location.href)
|
||||
|
||||
//retrieve the state info from destUrl
|
||||
const destUrlParts = new URL(destUrl)
|
||||
const state = destUrlParts.searchParams.get("state")
|
||||
|
||||
if(!state){
|
||||
throw new Error("No state found in destination url")
|
||||
}
|
||||
|
||||
if(environment.environment_desktop){
|
||||
//hash based routing
|
||||
originUrlParts.hash = `desktop/callback/${sourceType}`
|
||||
originUrlParts.hash = `desktop/callback/${state}`
|
||||
} else {
|
||||
//path based routing
|
||||
originUrlParts.hash = "" //reset hash in-case its present.
|
||||
originUrlParts.pathname = this.pathJoin([originUrlParts.pathname, `callback/${sourceType}`])
|
||||
originUrlParts.pathname = this.pathJoin([originUrlParts.pathname, `callback/${state}`])
|
||||
}
|
||||
|
||||
const redirectUrlParts = new URL(callbackUri.replace("/callback/", "/redirect/"));
|
||||
let redirectUrl = this.pathJoin([environment.lighthouse_api_endpoint_base, `redirect/${state}`])
|
||||
|
||||
const redirectUrlParts = new URL(redirectUrl);
|
||||
const redirectParams = new URLSearchParams()
|
||||
redirectParams.set("origin_url", originUrlParts.toString())
|
||||
redirectParams.set("dest_url", destUrl)
|
||||
redirectParams.set("desktop_mode", environment.environment_desktop ? "true" : "false")
|
||||
redirectParams.set("brand_id", redirectOpts.brand_id)
|
||||
redirectParams.set("portal_id", redirectOpts.portal_id)
|
||||
redirectParams.set("endpoint_id", redirectOpts.id)
|
||||
redirectUrlParts.search = redirectParams.toString()
|
||||
console.log(redirectUrlParts.toString());
|
||||
|
||||
|
@ -161,9 +221,9 @@ export class LighthouseService {
|
|||
if(environment.environment_desktop && environment.popup_source_auth){
|
||||
//@ts-ignore
|
||||
|
||||
OpenExternalLink(redirectUrlParts.toString(), environment.environment_desktop, sourceType)
|
||||
OpenExternalLink(redirectUrlParts.toString(), environment.environment_desktop, state)
|
||||
|
||||
return this.waitForDesktopCodeOrTimeout(sourceType)
|
||||
return this.waitForDesktopCodeOrTimeout(state)
|
||||
|
||||
//now wait for response from the opened window
|
||||
} else {
|
||||
|
@ -174,7 +234,7 @@ export class LighthouseService {
|
|||
|
||||
}
|
||||
|
||||
async swapOauthToken(sourceType: string, sourceMetadata: LighthouseSourceMetadata, expectedSourceStateInfo: SourceState, code: string): Promise<any>{
|
||||
async swapOauthToken(sourceMetadata: LighthouseSourceMetadata, expectedSourceStateInfo: SourceState, code: string): Promise<any>{
|
||||
// @ts-expect-error
|
||||
const client: oauth.Client = {
|
||||
client_id: sourceMetadata.client_id
|
||||
|
@ -188,7 +248,8 @@ export class LighthouseService {
|
|||
} else {
|
||||
console.log("This is a confidential client, using lighthouse token endpoint.")
|
||||
//if this is a confidential client, we need to "override" token endpoint, and use the Fasten Lighthouse to complete the swap
|
||||
sourceMetadata.token_endpoint = sourceMetadata.redirect_uri.replace("/callback/", "/token/")
|
||||
sourceMetadata.token_endpoint = this.pathJoin([environment.lighthouse_api_endpoint_base, `token/${expectedSourceStateInfo.endpoint_id}`])
|
||||
|
||||
//use a placeholder client_secret (the actual secret is stored in Lighthouse)
|
||||
client.client_secret = "placeholder"
|
||||
client.token_endpoint_auth_method = "client_secret_basic"
|
||||
|
@ -242,7 +303,7 @@ export class LighthouseService {
|
|||
return parts.join(separator);
|
||||
}
|
||||
|
||||
private waitForDesktopCodeOrTimeout(sourceType: string): Observable<any> {
|
||||
private waitForDesktopCodeOrTimeout(state: string): Observable<{ codeData:any, state:string }> {
|
||||
console.log(`waiting for wails Event notification from window`)
|
||||
|
||||
if(typeof wails == "undefined"){
|
||||
|
@ -257,17 +318,20 @@ export class LighthouseService {
|
|||
//throw an error if we wait more than 2 minutes (this will close the window)
|
||||
timeout(sourceConnectDesktopTimeout),
|
||||
//make sure we're only listening to events from the "opened" window.
|
||||
filter((eventPayload: any ) => eventPayload.sender == sourceType),
|
||||
filter((eventPayload: any ) => eventPayload.sender == state),
|
||||
//after filtering, we should only have one event to handle.
|
||||
first(),
|
||||
map((event) => {
|
||||
console.log(`received wails event notification from ${sourceType} window & sending acknowledgment`, event)
|
||||
console.log(`received wails event notification from ${state} window & sending acknowledgment`, event)
|
||||
// @ts-ignore
|
||||
return event.data
|
||||
return {
|
||||
state: state,
|
||||
codeData: event.data
|
||||
}
|
||||
}),
|
||||
catchError((err) => {
|
||||
console.warn(`timed out waiting for notification from ${sourceType} (${sourceConnectDesktopTimeout/1000}s), closing window`)
|
||||
wails.Application.GetWindowByName(sourceType).Window.Close()
|
||||
console.warn(`timed out waiting for notification from ${state} (${sourceConnectDesktopTimeout/1000}s), closing window`)
|
||||
wails.Application.GetWindowByName(state).Window.Close()
|
||||
return throwError(err)
|
||||
})
|
||||
)
|
||||
|
@ -275,7 +339,7 @@ export class LighthouseService {
|
|||
|
||||
//after waiting for the desktop code, we need to redirect to the callback page with the code in the query params
|
||||
// (which is what would have happened if we were in a browser and we were redirected as usual)
|
||||
redirectWithDesktopCode(sourceType: string, codeData: any){
|
||||
redirectWithDesktopCode(state: string, codeData: any){
|
||||
|
||||
if(!codeData){
|
||||
//if we redirected completely, no callback data will be present.
|
||||
|
@ -286,7 +350,7 @@ export class LighthouseService {
|
|||
//redirect to callback page with code
|
||||
|
||||
let urlTree = this.router.createUrlTree(
|
||||
['/sources/callback/' + sourceType],
|
||||
['/sources/callback/' + state],
|
||||
{ queryParams: codeData, }
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
export const environment = {
|
||||
production: true,
|
||||
environment_cloud: false,
|
||||
environment_desktop: true,
|
||||
environment_name: "sandbox",
|
||||
popup_source_auth: false,
|
||||
|
||||
lighthouse_api_endpoint_base: 'http://localhost:4000',
|
||||
// lighthouse_api_endpoint_base: 'https://lighthouse.fastenhealth.com/sandboxbeta',
|
||||
|
||||
//used to specify the api server that we're going to use (can be relative or absolute). Must not have trailing slash
|
||||
fasten_api_endpoint_base: '/api',
|
||||
};
|
|
@ -0,0 +1,799 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@angular/animations@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/animations/-/animations-14.2.12.tgz#9ff16bad809d3ff7526f8359ef214e63e8236f9f"
|
||||
integrity sha512-gwdnFZkvVUr+enUNfhfCGRGGqNHn1+vTA81apLfHYhJxgjiLUtETc4KTOrQevtDm022pEd+LSrvr8r+7ag+jkw==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/cdk@^14.1.0":
|
||||
version "14.2.7"
|
||||
resolved "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz#65eb6fbbeed6120fad4e3913aa66f8b74c853ac3"
|
||||
integrity sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
optionalDependencies:
|
||||
parse5 "^5.0.0"
|
||||
|
||||
"@angular/common@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/common/-/common-14.2.12.tgz#b608ff629a635f323b297000f53f976f71ae3c80"
|
||||
integrity sha512-oZunh9wfInFWhNO1P8uoEs/o4u8kerKMhw8GruywKm1TV7gHDP2Fi5WHGjFqq3XYptgBTPCTSEfyLX6Cwq1PUw==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/compiler@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.12.tgz#2b17667acfb2cda1521de102246b178845838812"
|
||||
integrity sha512-u2MH9+NRwbbFDRNiPWPexed9CnCq9+pGHLuyACSP2uR6Ik68cE6cayeZbIeoEV5vWpda/XsLmJgPJysw7dAZLQ==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/core@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/core/-/core-14.2.12.tgz#118467ec4e8ea082931a84e8cefe722a0e110dc9"
|
||||
integrity sha512-sGQxU5u4uawwvJa6jOTmGoisJiQ5HIN/RoBw99CmoqZIVyUSg9IRJJC1KVdH8gbpWBNLkElZv21lwJTL/msWyg==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/elements@^14.2.12":
|
||||
version "14.3.0"
|
||||
resolved "https://registry.npmjs.org/@angular/elements/-/elements-14.3.0.tgz#f4ce07241d5b05c8c843465cf564f3519ed8d5d5"
|
||||
integrity sha512-fIg8IOD2R36v3SZ8yQEwTC8T71Hk0lbJFJXaOUZDZ6MfwdT8mMkFCujPRXOF0+p/ZnOiq2EhBwuPdjmKTf7XHA==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/forms@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/forms/-/forms-14.2.12.tgz#2174e4e3b87390b0f1ebde0b3fc6c4d2ae793a1a"
|
||||
integrity sha512-7abYlGIT2JnAtutQUlH3fQS6QEpbfftgvsVcZJCyvX0rXL3u2w2vUQkDHJH4YJJp3AHFVCH4/l7R4VcaPnrwvA==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/platform-browser-dynamic@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.12.tgz#d4dac4a488c804ea07213a98450efa124a15d70a"
|
||||
integrity sha512-oZhNJeaBmgw8+KBSYpKz2RYqEDyETC+HJXH8dwIFcP6BqqwL2NE70FdSR7EnOa5c41MEtTmMCGhrJSFR60x5/w==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/platform-browser@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.12.tgz#24fe6bc87d056fc8b91924091da712fef7c66457"
|
||||
integrity sha512-vOarWym8ucl1gjYWCzdwyBha+MTvL381mvTTUu8aUx6nVhHFjv4bvpjlZnZgojecqUPyxOwmPLLHvCZPJVHZYg==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@angular/router@~14.2.12":
|
||||
version "14.2.12"
|
||||
resolved "https://registry.npmjs.org/@angular/router/-/router-14.2.12.tgz#f5b2c9ced3337e4a7af1905b861b114ac80a8482"
|
||||
integrity sha512-r5tVus5RJDNc4U2v0jMtjPiAS1xDsVsJ70lS313DgZmBDHIVZP1cWIehdxwgNlGwQQtAA36eG7toBwqUU3gb/A==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
||||
|
||||
"@ant-design/colors@^5.0.0":
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmjs.org/@ant-design/colors/-/colors-5.1.1.tgz#800b2186b1e27e66432e67d03ed96af3e21d8940"
|
||||
integrity sha512-Txy4KpHrp3q4XZdfgOBqLl+lkQIc3tEvHXOimRN1giX1AEC7mGtyrO9p8iRGJ3FLuVMGa2gNEzQyghVymLttKQ==
|
||||
dependencies:
|
||||
"@ctrl/tinycolor" "^3.3.1"
|
||||
|
||||
"@ant-design/icons-angular@^14.1.0":
|
||||
version "14.1.0"
|
||||
resolved "https://registry.npmjs.org/@ant-design/icons-angular/-/icons-angular-14.1.0.tgz#7612507aae28872ce3ccd139857452426cc30be3"
|
||||
integrity sha512-SvWi8p4L+cCfTGtezeUq1s8kahZEO1UVf7ZR615Fix3c2l9OnAJi+niDkq2fZHjpiarFrt8HoZ2EhMAMN7jfbg==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^5.0.0"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@babel/runtime@^7.21.0":
|
||||
version "7.23.5"
|
||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz#11edb98f8aeec529b82b211028177679144242db"
|
||||
integrity sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@ctrl/tinycolor@^3.3.1":
|
||||
version "3.6.1"
|
||||
resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31"
|
||||
integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
|
||||
|
||||
"@lhncbc/ucum-lhc@^4.1.3", "@lhncbc/ucum-lhc@^4.1.6":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmjs.org/@lhncbc/ucum-lhc/-/ucum-lhc-4.2.0.tgz#522cc16fe37739d7da16c27c8b9ad039ee39c2d7"
|
||||
integrity sha512-OEiWX7IHFHLTFs7+w5EvGtI5dhXhhL0341LqZ9WEBWErtoY0/9xl/vn+wwT9vnBHnjQ7Ux0o7iEUXvN8uVn4xg==
|
||||
dependencies:
|
||||
coffeescript "^2.7.0"
|
||||
csv-parse "^4.4.6"
|
||||
csv-stringify "^1.0.4"
|
||||
escape-html "^1.0.3"
|
||||
is-integer "^1.0.6"
|
||||
jsonfile "^2.2.3"
|
||||
stream "0.0.2"
|
||||
stream-transform "^0.1.1"
|
||||
string-to-stream "^1.1.0"
|
||||
xmldoc "^0.4.0"
|
||||
|
||||
"@microsoft/fetch-event-source@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz#9ceecc94b49fbaa15666e38ae8587f64acce007d"
|
||||
integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==
|
||||
|
||||
"@types/jquery@^3.5.14":
|
||||
version "3.5.29"
|
||||
resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.29.tgz#3c06a1f519cd5fc3a7a108971436c00685b5dcea"
|
||||
integrity sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==
|
||||
dependencies:
|
||||
"@types/sizzle" "*"
|
||||
|
||||
"@types/sizzle@*":
|
||||
version "2.3.8"
|
||||
resolved "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627"
|
||||
integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==
|
||||
|
||||
antlr4@~4.9.3:
|
||||
version "4.9.3"
|
||||
resolved "https://registry.npmjs.org/antlr4/-/antlr4-4.9.3.tgz#268b844ff8ce97d022399a05d4b37aa6ab4047b2"
|
||||
integrity sha512-qNy2odgsa0skmNMCuxzXhM4M8J1YDaPv3TI+vCdnOAanu0N982wBrSqziDKRDctEZLZy9VffqIZXc0UGjjSP/g==
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
array-buffer-byte-length@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead"
|
||||
integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
is-array-buffer "^3.0.1"
|
||||
|
||||
autocomplete-lhc@^18.6.3:
|
||||
version "18.6.3"
|
||||
resolved "https://registry.npmjs.org/autocomplete-lhc/-/autocomplete-lhc-18.6.3.tgz#73217169e58581dfa3f4cd800d8225fcd79aefc2"
|
||||
integrity sha512-1bkfn4RY4bk67O1PzN8Jr6H2APbrYN4RDofqputmmZ/Qe6cw6Dg1TkoqqP/2Y0lyApd/ctWcPpkbsVEnIDp9vQ==
|
||||
|
||||
available-typed-arrays@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
|
||||
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
|
||||
|
||||
call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"
|
||||
integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
get-intrinsic "^1.2.1"
|
||||
set-function-length "^1.1.1"
|
||||
|
||||
coffeescript@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.npmjs.org/coffeescript/-/coffeescript-2.7.0.tgz#a43ec03be6885d6d1454850ea70b9409c391279c"
|
||||
integrity sha512-hzWp6TUE2d/jCcN67LrW1eh5b/rSDKQK6oD6VMLlggYVUUFexgTH9z3dNYihzX4RMhze5FTUsUmOXViJKFQR/A==
|
||||
|
||||
commander@^2.18.0:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
|
||||
|
||||
csv-parse@^4.4.6:
|
||||
version "4.16.3"
|
||||
resolved "https://registry.npmjs.org/csv-parse/-/csv-parse-4.16.3.tgz#7ca624d517212ebc520a36873c3478fa66efbaf7"
|
||||
integrity sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==
|
||||
|
||||
csv-stringify@^1.0.4:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/csv-stringify/-/csv-stringify-1.1.2.tgz#77a41526581bce3380f12b00d7c5bbac70c82b58"
|
||||
integrity sha512-3NmNhhd+AkYs5YtM1GEh01VR6PKj6qch2ayfQaltx5xpcAdThjnbbI5eT8CzRVpXfGKAxnmrSYLsNl/4f3eWiw==
|
||||
dependencies:
|
||||
lodash.get "~4.4.2"
|
||||
|
||||
date-fns@^1.30.1:
|
||||
version "1.30.1"
|
||||
resolved "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
|
||||
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
|
||||
|
||||
date-fns@^2.16.1:
|
||||
version "2.30.0"
|
||||
resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
|
||||
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.21.0"
|
||||
|
||||
deep-equal@^2.0.5:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1"
|
||||
integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==
|
||||
dependencies:
|
||||
array-buffer-byte-length "^1.0.0"
|
||||
call-bind "^1.0.5"
|
||||
es-get-iterator "^1.1.3"
|
||||
get-intrinsic "^1.2.2"
|
||||
is-arguments "^1.1.1"
|
||||
is-array-buffer "^3.0.2"
|
||||
is-date-object "^1.0.5"
|
||||
is-regex "^1.1.4"
|
||||
is-shared-array-buffer "^1.0.2"
|
||||
isarray "^2.0.5"
|
||||
object-is "^1.1.5"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.4"
|
||||
regexp.prototype.flags "^1.5.1"
|
||||
side-channel "^1.0.4"
|
||||
which-boxed-primitive "^1.0.2"
|
||||
which-collection "^1.0.1"
|
||||
which-typed-array "^1.1.13"
|
||||
|
||||
define-data-property@^1.0.1, define-data-property@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"
|
||||
integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.1"
|
||||
gopd "^1.0.1"
|
||||
has-property-descriptors "^1.0.0"
|
||||
|
||||
define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c"
|
||||
integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==
|
||||
dependencies:
|
||||
define-data-property "^1.0.1"
|
||||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
emitter-component@^1.1.1:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz#d65af5833dc7c682fd0ade35f902d16bc4bad772"
|
||||
integrity sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==
|
||||
|
||||
es-get-iterator@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6"
|
||||
integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.3"
|
||||
has-symbols "^1.0.3"
|
||||
is-arguments "^1.1.1"
|
||||
is-map "^2.0.2"
|
||||
is-set "^2.0.2"
|
||||
is-string "^1.0.7"
|
||||
isarray "^2.0.5"
|
||||
stop-iteration-iterator "^1.0.0"
|
||||
|
||||
escape-html@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
fast-copy@^2.1.7:
|
||||
version "2.1.7"
|
||||
resolved "https://registry.npmjs.org/fast-copy/-/fast-copy-2.1.7.tgz#affc9475cb4b555fb488572b2a44231d0c9fa39e"
|
||||
integrity sha512-ozrGwyuCTAy7YgFCua8rmqmytECYk/JYAMXcswOcm0qvGoE3tPb7ivBeIHTOK2DiapBhDZgacIhzhQIKU5TCfA==
|
||||
|
||||
fhirpath@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/fhirpath/-/fhirpath-3.2.0.tgz#1c70cfdf6f22edf3a6d79b157a10b19bca6039e4"
|
||||
integrity sha512-T6j53O5MVd2DkJ+PXHN9cceNdhOFLZ1f5mKOBhuv1NKZ++yKW+6qyUM+YPGvU3ugEM2dERj6Hz4xhPlWaaQH0A==
|
||||
dependencies:
|
||||
"@lhncbc/ucum-lhc" "^4.1.3"
|
||||
antlr4 "~4.9.3"
|
||||
commander "^2.18.0"
|
||||
date-fns "^1.30.1"
|
||||
js-yaml "^3.13.1"
|
||||
|
||||
for-each@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
|
||||
integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
|
||||
dependencies:
|
||||
is-callable "^1.1.3"
|
||||
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
functions-have-names@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b"
|
||||
integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
has-proto "^1.0.1"
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
|
||||
gopd@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
graceful-fs@^4.1.6:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
|
||||
has-bigints@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
|
||||
|
||||
has-property-descriptors@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340"
|
||||
integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.2"
|
||||
|
||||
has-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
|
||||
integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==
|
||||
|
||||
has-symbols@^1.0.2, has-symbols@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
|
||||
has-tostringtag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
|
||||
integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
hasown@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
|
||||
integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
inherits@^2.0.1, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
internal-slot@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930"
|
||||
integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.2"
|
||||
hasown "^2.0.0"
|
||||
side-channel "^1.0.4"
|
||||
|
||||
is-arguments@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
||||
integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe"
|
||||
integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.2.0"
|
||||
is-typed-array "^1.1.10"
|
||||
|
||||
is-bigint@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
|
||||
integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
|
||||
dependencies:
|
||||
has-bigints "^1.0.1"
|
||||
|
||||
is-boolean-object@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
|
||||
integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-callable@^1.1.3:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
|
||||
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
|
||||
|
||||
is-date-object@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
|
||||
integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-finite@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3"
|
||||
integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==
|
||||
|
||||
is-integer@^1.0.6:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.npmjs.org/is-integer/-/is-integer-1.0.7.tgz#6bde81aacddf78b659b6629d629cadc51a886d5c"
|
||||
integrity sha512-RPQc/s9yBHSvpi+hs9dYiJ2cuFeU6x3TyyIp8O2H6SKEltIvJOzRj9ToyvcStDvPR/pS4rxgr1oBFajQjZ2Szg==
|
||||
dependencies:
|
||||
is-finite "^1.0.0"
|
||||
|
||||
is-map@^2.0.1, is-map@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
|
||||
integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==
|
||||
|
||||
is-number-object@^1.0.4:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
|
||||
integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-regex@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
|
||||
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-set@^2.0.1, is-set@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec"
|
||||
integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==
|
||||
|
||||
is-shared-array-buffer@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
|
||||
integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
|
||||
is-string@^1.0.5, is-string@^1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
|
||||
integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-symbol@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
|
||||
integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
is-typed-array@^1.1.10:
|
||||
version "1.1.12"
|
||||
resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a"
|
||||
integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==
|
||||
dependencies:
|
||||
which-typed-array "^1.1.11"
|
||||
|
||||
is-weakmap@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
|
||||
integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
|
||||
|
||||
is-weakset@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d"
|
||||
integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
get-intrinsic "^1.1.1"
|
||||
|
||||
isarray@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
||||
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
|
||||
|
||||
jquery@^3.6.1:
|
||||
version "3.7.1"
|
||||
resolved "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de"
|
||||
integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==
|
||||
|
||||
js-yaml@^3.13.1:
|
||||
version "3.14.1"
|
||||
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
|
||||
integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
jsonfile@^2.2.3:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
|
||||
integrity sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
lforms@^34.3.1:
|
||||
version "34.3.1"
|
||||
resolved "https://registry.npmjs.org/lforms/-/lforms-34.3.1.tgz#54f4e2bd9e1f5d99d1c7cad71849d1ee653fe8d4"
|
||||
integrity sha512-nAstl+sHd+Fxdz+4plrxhbMz7u/tf3OGQY0uc3zI00fd/NFsEXyBSE74aOPgUKOPJw5PCkpQvUkiQO1pucZYjg==
|
||||
dependencies:
|
||||
"@angular/animations" "~14.2.12"
|
||||
"@angular/common" "~14.2.12"
|
||||
"@angular/compiler" "~14.2.12"
|
||||
"@angular/core" "~14.2.12"
|
||||
"@angular/elements" "^14.2.12"
|
||||
"@angular/forms" "~14.2.12"
|
||||
"@angular/platform-browser" "~14.2.12"
|
||||
"@angular/platform-browser-dynamic" "~14.2.12"
|
||||
"@angular/router" "~14.2.12"
|
||||
"@lhncbc/ucum-lhc" "^4.1.6"
|
||||
"@types/jquery" "^3.5.14"
|
||||
autocomplete-lhc "^18.6.3"
|
||||
deep-equal "^2.0.5"
|
||||
fast-copy "^2.1.7"
|
||||
fhirpath "3.2.0"
|
||||
jquery "^3.6.1"
|
||||
moment "^2.29.4"
|
||||
ng-zorro-antd "^14.1.1"
|
||||
rxjs "~6.6.7"
|
||||
tslib "^2.4.1"
|
||||
zone.js "~0.11.8"
|
||||
|
||||
lodash.get@~4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
|
||||
|
||||
moment@^2.29.4:
|
||||
version "2.29.4"
|
||||
resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
|
||||
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
|
||||
|
||||
ng-zorro-antd@^14.1.1:
|
||||
version "14.3.0"
|
||||
resolved "https://registry.npmjs.org/ng-zorro-antd/-/ng-zorro-antd-14.3.0.tgz#0cc5a406b62f4dcbcda2eea4d8d654c27c323dae"
|
||||
integrity sha512-mGVok0DggrvVYTYWpbJJVekBs6j19kAkxB7PdZp0bvYRedpOVWKSEDX1Cigy7txnGw5UsSuzRSn3h6oZcBUmTA==
|
||||
dependencies:
|
||||
"@angular/cdk" "^14.1.0"
|
||||
"@ant-design/icons-angular" "^14.1.0"
|
||||
date-fns "^2.16.1"
|
||||
tslib "^2.3.0"
|
||||
|
||||
object-inspect@^1.9.0:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
|
||||
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
|
||||
|
||||
object-is@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
|
||||
integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
||||
|
||||
object.assign@^4.1.4:
|
||||
version "4.1.5"
|
||||
resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0"
|
||||
integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==
|
||||
dependencies:
|
||||
call-bind "^1.0.5"
|
||||
define-properties "^1.2.1"
|
||||
has-symbols "^1.0.3"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
parse5@^5.0.0:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
|
||||
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
|
||||
|
||||
process-nextick-args@~2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
|
||||
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
|
||||
|
||||
readable-stream@^2.1.0:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
||||
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
||||
dependencies:
|
||||
core-util-is "~1.0.0"
|
||||
inherits "~2.0.3"
|
||||
isarray "~1.0.0"
|
||||
process-nextick-args "~2.0.0"
|
||||
safe-buffer "~5.1.1"
|
||||
string_decoder "~1.1.1"
|
||||
util-deprecate "~1.0.1"
|
||||
|
||||
regenerator-runtime@^0.14.0:
|
||||
version "0.14.0"
|
||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
||||
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
|
||||
|
||||
regexp.prototype.flags@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e"
|
||||
integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.2.0"
|
||||
set-function-name "^2.0.0"
|
||||
|
||||
rxjs@~6.6.7:
|
||||
version "6.6.7"
|
||||
resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
|
||||
integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
sax@~1.1.1:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.npmjs.org/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240"
|
||||
integrity sha512-8zci48uUQyfqynGDSkUMD7FCJB96hwLnlZOXlgs1l3TX+LW27t3psSWKUxC0fxVgA86i8tL4NwGcY1h/6t3ESg==
|
||||
|
||||
set-function-length@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
|
||||
integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==
|
||||
dependencies:
|
||||
define-data-property "^1.1.1"
|
||||
get-intrinsic "^1.2.1"
|
||||
gopd "^1.0.1"
|
||||
has-property-descriptors "^1.0.0"
|
||||
|
||||
set-function-name@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a"
|
||||
integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==
|
||||
dependencies:
|
||||
define-data-property "^1.0.1"
|
||||
functions-have-names "^1.2.3"
|
||||
has-property-descriptors "^1.0.0"
|
||||
|
||||
side-channel@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
|
||||
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
|
||||
dependencies:
|
||||
call-bind "^1.0.0"
|
||||
get-intrinsic "^1.0.2"
|
||||
object-inspect "^1.9.0"
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
|
||||
|
||||
stop-iteration-iterator@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4"
|
||||
integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==
|
||||
dependencies:
|
||||
internal-slot "^1.0.4"
|
||||
|
||||
stream-transform@^0.1.1:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.npmjs.org/stream-transform/-/stream-transform-0.1.2.tgz#7d8e6b4e03ac4781778f8c79517501bfb0762a9f"
|
||||
integrity sha512-3HXId/0W8sktQnQM6rOZf2LuDDMbakMgAjpViLk758/h0br+iGqZFFfUxxJSqEvGvT742PyFr4v/TBXUtowdCg==
|
||||
|
||||
stream@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef"
|
||||
integrity sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==
|
||||
dependencies:
|
||||
emitter-component "^1.1.1"
|
||||
|
||||
string-to-stream@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/string-to-stream/-/string-to-stream-1.1.1.tgz#aba78f73e70661b130ee3e1c0192be4fef6cb599"
|
||||
integrity sha512-QySF2+3Rwq0SdO3s7BAp4x+c3qsClpPQ6abAmb0DGViiSBAkT5kL6JT2iyzEVP+T1SmzHrQD1TwlP9QAHCc+Sw==
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "^2.1.0"
|
||||
|
||||
string_decoder@~1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
|
||||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
tslib@^1.9.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.0, tslib@^2.3.0, tslib@^2.4.1:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
|
||||
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
|
||||
|
||||
util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
|
||||
which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||
integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
|
||||
dependencies:
|
||||
is-bigint "^1.0.1"
|
||||
is-boolean-object "^1.1.0"
|
||||
is-number-object "^1.0.4"
|
||||
is-string "^1.0.5"
|
||||
is-symbol "^1.0.3"
|
||||
|
||||
which-collection@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
|
||||
integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
|
||||
dependencies:
|
||||
is-map "^2.0.1"
|
||||
is-set "^2.0.1"
|
||||
is-weakmap "^2.0.1"
|
||||
is-weakset "^2.0.1"
|
||||
|
||||
which-typed-array@^1.1.11, which-typed-array@^1.1.13:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36"
|
||||
integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.5"
|
||||
call-bind "^1.0.4"
|
||||
for-each "^0.3.3"
|
||||
gopd "^1.0.1"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
xmldoc@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/xmldoc/-/xmldoc-0.4.0.tgz#d257224be8393eaacbf837ef227fd8ec25b36888"
|
||||
integrity sha512-rJ/+/UzYCSlFNuAzGuRyYgkH2G5agdX1UQn4+5siYw9pkNC3Hu/grYNDx/dqYLreeSjnY5oKg74CMBKxJHSg6Q==
|
||||
dependencies:
|
||||
sax "~1.1.1"
|
||||
|
||||
zone.js@~0.11.8:
|
||||
version "0.11.8"
|
||||
resolved "https://registry.npmjs.org/zone.js/-/zone.js-0.11.8.tgz#40dea9adc1ad007b5effb2bfed17f350f1f46a21"
|
||||
integrity sha512-82bctBg2hKcEJ21humWIkXRlLBBmrc3nN7DFh5LGGhcyycO2S7FN8NmdvlcKaGFDNVL4/9kFLmwmInTavdJERA==
|
||||
dependencies:
|
||||
tslib "^2.3.0"
|
Loading…
Reference in New Issue