adding sourceDetail component.

make sure we 404 if an api endpoint doesn't exist.
This commit is contained in:
Jason Kulatunga 2022-09-14 19:59:16 -07:00
parent f845ae3716
commit 5ae77a0459
14 changed files with 223 additions and 39 deletions

View File

@ -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)
}

View File

@ -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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -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
}

View File

@ -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
}

View File

@ -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) },

View File

@ -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,

View File

@ -1,4 +1,5 @@
export class Source {
id?: string
user_id?: number
source_type: string
patient_id: string

View File

@ -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>

View File

@ -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
});
}

View File

@ -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 -->

View File

@ -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();
});
});

View File

@ -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
})
}
}
}

View File

@ -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(