adding sourceDetail component.
make sure we 404 if an api endpoint doesn't exist.
This commit is contained in:
parent
f845ae3716
commit
5ae77a0459
|
@ -19,5 +19,6 @@ type DatabaseRepository interface {
|
|||
//UpsertOrganziation(context.Context, *models.Organization) error
|
||||
|
||||
CreateSource(context.Context, *models.Source) error
|
||||
GetSource(context.Context, string) (*models.Source, error)
|
||||
GetSources(context.Context) ([]models.Source, error)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gorm.io/gorm"
|
||||
"net/url"
|
||||
|
@ -164,6 +165,20 @@ func (sr *sqliteRepository) CreateSource(ctx context.Context, sourceCreds *model
|
|||
return nil
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetSource(ctx context.Context, sourceId string) (*models.Source, error) {
|
||||
sourceUUID, err := uuid.Parse(sourceId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sourceCred models.Source
|
||||
results := sr.gormClient.WithContext(ctx).
|
||||
Where(models.Source{UserID: sr.GetCurrentUser(ctx).ID, ModelBase: models.ModelBase{ID: sourceUUID}}).
|
||||
First(&sourceCred)
|
||||
|
||||
return &sourceCred, results.Error
|
||||
}
|
||||
|
||||
func (sr *sqliteRepository) GetSources(ctx context.Context) ([]models.Source, error) {
|
||||
|
||||
var sourceCreds []models.Source
|
||||
|
@ -174,16 +189,6 @@ func (sr *sqliteRepository) GetSources(ctx context.Context) ([]models.Source, er
|
|||
return sourceCreds, results.Error
|
||||
}
|
||||
|
||||
//func (sr *sqliteRepository) GetSource(ctx context.Context, source_type string) (models.Source, error) {
|
||||
//
|
||||
// var providerCredentials models.Source
|
||||
// results := sr.gormClient.WithContext(ctx).
|
||||
// Where(models.Source{UserID: sr.GetCurrentUser().ID, SourceType: source_type}).
|
||||
// Find(&providerCredentials)
|
||||
//
|
||||
// return providerCredential, results.Error
|
||||
//}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utilities
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -57,13 +57,26 @@ func CreateSource(c *gin.Context) {
|
|||
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCred})
|
||||
}
|
||||
|
||||
func GetSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
sourceCred, err := databaseRepo.GetSource(c, c.Param("sourceId"))
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while retrieving source credential", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCred})
|
||||
}
|
||||
|
||||
func ListSource(c *gin.Context) {
|
||||
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||
|
||||
sourceCreds, err := databaseRepo.GetSources(c)
|
||||
if err != nil {
|
||||
logger.Errorln("An error occurred while storing source credential", err)
|
||||
logger.Errorln("An error occurred while listing source credentials", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||
return
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
|||
{
|
||||
secure.POST("/source", handler.CreateSource)
|
||||
secure.GET("/source", handler.ListSource)
|
||||
secure.GET("/source/:sourceId", handler.GetSource)
|
||||
|
||||
secure.GET("/source/raw/:sourceType/*path", handler.RawRequestSource)
|
||||
|
||||
secure.GET("/fhir/:sourceResourceType/*sourceResourceId", handler.RequestResourceFhir)
|
||||
|
@ -64,7 +66,12 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
|||
|
||||
//catch-all, serve index page.
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
c.File(fmt.Sprintf("%s/index.html", ae.Config.GetString("web.src.frontend.path")))
|
||||
path := c.Request.URL.Path
|
||||
if strings.HasPrefix(path, "/api") {
|
||||
c.JSON(http.StatusNotFound, gin.H{"success": false, "error": "404 endpoint not found"})
|
||||
} else {
|
||||
c.File(fmt.Sprintf("%s/index.html", ae.Config.GetString("web.src.frontend.path")))
|
||||
}
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {ResourceDetailComponent} from './pages/resource-detail/resource-detail.c
|
|||
import {AuthSigninComponent} from './pages/auth-signin/auth-signin.component';
|
||||
import {AuthSignupComponent} from './pages/auth-signup/auth-signup.component';
|
||||
import {CanActivateAuthGuard} from './services/can-activate.auth-guard';
|
||||
import {SourceDetailComponent} from './pages/source-detail/source-detail.component';
|
||||
|
||||
const routes: Routes = [
|
||||
|
||||
|
@ -16,7 +17,8 @@ const routes: Routes = [
|
|||
|
||||
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
|
||||
{ path: 'dashboard', component: DashboardComponent, canActivate: [ CanActivateAuthGuard] },
|
||||
{ path: 'detail', component: ResourceDetailComponent, canActivate: [ CanActivateAuthGuard] },
|
||||
{ path: 'source/:source_id', component: SourceDetailComponent, canActivate: [ CanActivateAuthGuard] },
|
||||
{ path: 'source/:source_id:/resource/:resource_id', component: ResourceDetailComponent, canActivate: [ CanActivateAuthGuard] },
|
||||
{ path: 'sources', component: MedicalSourcesComponent, canActivate: [ CanActivateAuthGuard] },
|
||||
// { path: 'general-pages', loadChildren: () => import('./general-pages/general-pages.module').then(m => m.GeneralPagesModule) },
|
||||
// { path: 'ui-elements', loadChildren: () => import('./ui-elements/ui-elements.module').then(m => m.UiElementsModule) },
|
||||
|
|
|
@ -23,6 +23,7 @@ import { AuthInterceptorService } from './services/auth-interceptor.service';
|
|||
import { CanActivateAuthGuard } from './services/can-activate.auth-guard';
|
||||
import {FastenApiService} from './services/fasten-api.service';
|
||||
import {Router} from '@angular/router';
|
||||
import { SourceDetailComponent } from './pages/source-detail/source-detail.component';
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
|
@ -33,6 +34,7 @@ import {Router} from '@angular/router';
|
|||
ResourceDetailComponent,
|
||||
AuthSignupComponent,
|
||||
AuthSigninComponent,
|
||||
SourceDetailComponent,
|
||||
],
|
||||
imports: [
|
||||
FormsModule,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export class Source {
|
||||
id?: string
|
||||
user_id?: number
|
||||
source_type: string
|
||||
patient_id: string
|
||||
|
|
|
@ -141,27 +141,38 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="az-content-text mg-b-20">Part of this date range occurs before the new users metric had been calculated, so the old users metric is displayed.</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="wd-45p">Name</th>
|
||||
<th>Source</th>
|
||||
<th>Identifier</th>
|
||||
<th>Last Updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<table class="table mg-b-0">
|
||||
<tbody>
|
||||
<tr *ngFor="let patient of patients">
|
||||
<td><strong>{{patient.user_id}}</strong></td>
|
||||
<td><strong>{{patient.source_id}}</strong></td>
|
||||
<td>{{patient.source_resource_id}}</td>
|
||||
<td>{{patient.UpdatedAt | date}}</td>
|
||||
<tr *ngFor="let source of sources" (click)="selectSource(source)" class="alert" role="alert">
|
||||
<td class="align-middle">
|
||||
<div class="media">
|
||||
<img [src]="'assets/sources/'+source.source_type+'.png'"
|
||||
alt="Geeks"
|
||||
class="mr-3"
|
||||
style="width:100px;">
|
||||
<div class="media-body">
|
||||
<h5>{{source.source_type}}</h5>
|
||||
<p>
|
||||
GeeksforGeeks is a computer science portal.
|
||||
It is a best programming platform.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle"><p><small class="tx-gray-600">status:</small><br/> active</p></td>
|
||||
<td class="align-middle"><p><small class="tx-gray-600">last updated:</small><br/> {{source.updated_at | date}}</p></td>
|
||||
<td class="align-middle"><p><small class="tx-gray-600">expires:</small><br/> {{source.expires_at}}</p></td>
|
||||
<td class="align-middle"><p><i class="fas fa-chevron-right"></i></td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
</div><!-- table-responsive -->
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core';
|
|||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
import {LighthouseSource} from '../../models/lighthouse/lighthouse-source';
|
||||
import {Patient} from '../../models/display/patient';
|
||||
import {Source} from '../../models/fasten/source';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dashboard',
|
||||
|
@ -10,21 +12,25 @@ import {Patient} from '../../models/display/patient';
|
|||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
|
||||
patients = []
|
||||
sources: Source[] = []
|
||||
|
||||
constructor(private fastenApi: FastenApiService) { }
|
||||
constructor(private fastenApi: FastenApiService, private router: Router) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.fastenApi.getResources('Patient')
|
||||
.subscribe( (patientsList) => {
|
||||
console.log(patientsList);
|
||||
let display_patients = this.patients
|
||||
patientsList.forEach(function(fhirPatientResource){
|
||||
display_patients.push(new Patient(fhirPatientResource.payload))
|
||||
})
|
||||
console.log(display_patients)
|
||||
this.patients = display_patients
|
||||
|
||||
this.fastenApi.getSources()
|
||||
.subscribe( (sourcesList) => {
|
||||
console.log(sourcesList);
|
||||
this.sources = sourcesList
|
||||
})
|
||||
// this.fastenApi.getResources('Patient')
|
||||
|
||||
}
|
||||
|
||||
selectSource(selectedSource: Source){
|
||||
this.router.navigateByUrl(`/source/${selectedSource.id}`, {
|
||||
state: selectedSource
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<div class="az-content">
|
||||
<div class="container">
|
||||
<div class="az-content-left">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">Cras justo odio</li>
|
||||
<li class="list-group-item active">Dapibus ac facilisis in</li>
|
||||
<li class="list-group-item">Morbi leo risus</li>
|
||||
<li class="list-group-item">Porta ac consectetur ac</li>
|
||||
<li class="list-group-item">Vestibulum at eros</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="az-content-body pd-lg-l-40 d-flex flex-column">
|
||||
<div class="az-content-breadcrumb">
|
||||
<span>Sources</span>
|
||||
<span>Aetna</span>
|
||||
<span>Details</span>
|
||||
</div>
|
||||
|
||||
<h2 class="az-content-title mg-t-40">Medical Sources</h2>
|
||||
|
||||
<div class="az-content-label mg-b-5">Insurance Companies</div>
|
||||
<p class="mg-b-20">The following medical insurance companies have API's which Fasten can use to retrieve your medical history.
|
||||
Please click the logos below to initiate the connection.</p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered mg-b-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Position</th>
|
||||
<th>Salary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">1</th>
|
||||
<td>Tiger Nixon</td>
|
||||
<td>System Architect</td>
|
||||
<td>$320,800</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">2</th>
|
||||
<td>Garrett Winters</td>
|
||||
<td>Accountant</td>
|
||||
<td>$170,750</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">3</th>
|
||||
<td>Ashton Cox</td>
|
||||
<td>Junior Technical Author</td>
|
||||
<td>$86,000</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">4</th>
|
||||
<td>Cedric Kelly</td>
|
||||
<td>Senior Javascript Developer</td>
|
||||
<td>$433,060</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">5</th>
|
||||
<td>Airi Satou</td>
|
||||
<td>Accountant</td>
|
||||
<td>$162,700</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div><!-- az-content-body -->
|
||||
</div><!-- container -->
|
||||
</div><!-- az-content -->
|
|
@ -0,0 +1,23 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SourceDetailComponent } from './source-detail.component';
|
||||
|
||||
describe('SourceDetailComponent', () => {
|
||||
let component: SourceDetailComponent;
|
||||
let fixture: ComponentFixture<SourceDetailComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SourceDetailComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SourceDetailComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {Source} from '../../models/fasten/source';
|
||||
import {FastenApiService} from '../../services/fasten-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-source-detail',
|
||||
templateUrl: './source-detail.component.html',
|
||||
styleUrls: ['./source-detail.component.scss']
|
||||
})
|
||||
export class SourceDetailComponent implements OnInit {
|
||||
|
||||
selectedSource: Source = null
|
||||
|
||||
constructor(private fastenApi: FastenApiService, private router: Router, private route: ActivatedRoute) {
|
||||
//check if the current Source was sent over using the router state storage:
|
||||
if(this.router.getCurrentNavigation().extras.state){
|
||||
this.selectedSource = this.router.getCurrentNavigation().extras.state as Source
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if(this.selectedSource == null) {
|
||||
//lookup the source by ID.
|
||||
this.fastenApi.getSource(this.route.snapshot.paramMap.get('source_id')).subscribe((source) => {
|
||||
this.selectedSource = source
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -86,6 +86,16 @@ export class FastenApiService {
|
|||
);
|
||||
}
|
||||
|
||||
getSource(sourceId: string): Observable<Source> {
|
||||
return this._httpClient.get<any>(`${this.getBasePath()}/api/secure/source/${sourceId}`)
|
||||
.pipe(
|
||||
map((response: ResponseWrapper) => {
|
||||
console.log("SOURCE RESPONSE", response)
|
||||
return response.data as Source
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getResources(resourceType: string, resourceId?: string ): Observable<ResourceFhir[]> {
|
||||
return this._httpClient.get<any>(`${this.getBasePath()}/api/secure/fhir/${resourceType}/${resourceId ? resourceId : ''}`)
|
||||
.pipe(
|
||||
|
|
Loading…
Reference in New Issue