working summary api
This commit is contained in:
parent
54016c0168
commit
02dcbcc507
|
@ -9,15 +9,17 @@ import (
|
||||||
//TODO: this should match the ID and username for the user.
|
//TODO: this should match the ID and username for the user.
|
||||||
type JWTClaim struct {
|
type JWTClaim struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateJWT(encryptionKey string, username string) (tokenString string, err error) {
|
func GenerateJWT(encryptionKey string, username string, userId string) (tokenString string, err error) {
|
||||||
expirationTime := time.Now().Add(2 * time.Hour)
|
expirationTime := time.Now().Add(2 * time.Hour)
|
||||||
claims := &JWTClaim{
|
claims := &JWTClaim{
|
||||||
Username: username,
|
Username: username,
|
||||||
Email: username,
|
Email: username,
|
||||||
|
UserId: userId,
|
||||||
StandardClaims: jwt.StandardClaims{
|
StandardClaims: jwt.StandardClaims{
|
||||||
ExpiresAt: expirationTime.Unix(),
|
ExpiresAt: expirationTime.Unix(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,6 +13,8 @@ type DatabaseRepository interface {
|
||||||
GetUserByEmail(context.Context, string) (*models.User, error)
|
GetUserByEmail(context.Context, string) (*models.User, error)
|
||||||
GetCurrentUser(context.Context) models.User
|
GetCurrentUser(context.Context) models.User
|
||||||
|
|
||||||
|
GetSummary(ctx context.Context) (*models.Summary, error)
|
||||||
|
|
||||||
UpsertResource(context.Context, models.ResourceFhir) error
|
UpsertResource(context.Context, models.ResourceFhir) error
|
||||||
GetResource(context.Context, string) (*models.ResourceFhir, error)
|
GetResource(context.Context, string) (*models.ResourceFhir, error)
|
||||||
GetResourceBySourceId(context.Context, string, string) (*models.ResourceFhir, error)
|
GetResourceBySourceId(context.Context, string, string) (*models.ResourceFhir, error)
|
||||||
|
|
|
@ -109,6 +109,43 @@ func (sr *sqliteRepository) GetCurrentUser(ctx context.Context) models.User {
|
||||||
return currentUser
|
return currentUser
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// User
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func (sr *sqliteRepository) GetSummary(ctx context.Context) (*models.Summary, error) {
|
||||||
|
|
||||||
|
// we want a count of all resources for this user by type
|
||||||
|
var resourceCountResults []map[string]interface{}
|
||||||
|
|
||||||
|
//group by resource type and return counts
|
||||||
|
// SELECT source_resource_type as resource_type, COUNT(*) as count FROM resource_fhirs WHERE source_id = "53c1e930-63af-46c9-b760-8e83cbc1abd9" GROUP BY source_resource_type;
|
||||||
|
result := sr.gormClient.WithContext(ctx).
|
||||||
|
Model(models.ResourceFhir{}).
|
||||||
|
Select("source_id, source_resource_type as resource_type, count(*) as count").
|
||||||
|
Group("source_resource_type").
|
||||||
|
Where(models.OriginBase{
|
||||||
|
UserID: sr.GetCurrentUser(ctx).ID,
|
||||||
|
}).
|
||||||
|
Scan(&resourceCountResults)
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want a list of all sources (when they were last updated)
|
||||||
|
sources, err := sr.GetSources(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
summary := &models.Summary{
|
||||||
|
Sources: sources,
|
||||||
|
ResourceTypeCounts: resourceCountResults,
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary, nil
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Resource
|
// Resource
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -248,9 +285,9 @@ func (sr *sqliteRepository) GetSourceSummary(ctx context.Context, sourceId strin
|
||||||
//group by resource type and return counts
|
//group by resource type and return counts
|
||||||
// SELECT source_resource_type as resource_type, COUNT(*) as count FROM resource_fhirs WHERE source_id = "53c1e930-63af-46c9-b760-8e83cbc1abd9" GROUP BY source_resource_type;
|
// SELECT source_resource_type as resource_type, COUNT(*) as count FROM resource_fhirs WHERE source_id = "53c1e930-63af-46c9-b760-8e83cbc1abd9" GROUP BY source_resource_type;
|
||||||
|
|
||||||
var results []map[string]interface{}
|
var resourceTypeCounts []map[string]interface{}
|
||||||
|
|
||||||
sr.gormClient.WithContext(ctx).
|
result := sr.gormClient.WithContext(ctx).
|
||||||
Model(models.ResourceFhir{}).
|
Model(models.ResourceFhir{}).
|
||||||
Select("source_id, source_resource_type as resource_type, count(*) as count").
|
Select("source_id, source_resource_type as resource_type, count(*) as count").
|
||||||
Group("source_resource_type").
|
Group("source_resource_type").
|
||||||
|
@ -258,9 +295,13 @@ func (sr *sqliteRepository) GetSourceSummary(ctx context.Context, sourceId strin
|
||||||
UserID: sr.GetCurrentUser(ctx).ID,
|
UserID: sr.GetCurrentUser(ctx).ID,
|
||||||
SourceID: sourceUUID,
|
SourceID: sourceUUID,
|
||||||
}).
|
}).
|
||||||
Scan(&results)
|
Scan(&resourceTypeCounts)
|
||||||
|
|
||||||
sourceSummary.ResourceTypeCounts = results
|
if result.Error != nil {
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSummary.ResourceTypeCounts = resourceTypeCounts
|
||||||
|
|
||||||
return sourceSummary, nil
|
return sourceSummary, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type Summary struct {
|
||||||
|
Sources []Source `json:"sources,omitempty"`
|
||||||
|
ResourceTypeCounts []map[string]interface{} `json:"resource_type_counts,omitempty"`
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ func AuthSignup(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// return JWT
|
// return JWT
|
||||||
tokenString, err := auth.GenerateJWT(appConfig.GetString("web.jwt.encryptionkey"), user.Username)
|
tokenString, err := auth.GenerateJWT(appConfig.GetString("web.jwt.encryptionkey"), user.Username, user.ID.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
return
|
return
|
||||||
|
@ -58,7 +58,7 @@ func AuthSignin(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// return JWT
|
// return JWT
|
||||||
tokenString, err := auth.GenerateJWT(appConfig.GetString("web.jwt.encryptionkey"), user.Username)
|
tokenString, err := auth.GenerateJWT(appConfig.GetString("web.jwt.encryptionkey"), user.Username, user.ID.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "an error occurred generating JWT token"})
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "an error occurred generating JWT token"})
|
||||||
return
|
return
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetSummary(c *gin.Context) {
|
||||||
|
logger := c.MustGet("LOGGER").(*logrus.Entry)
|
||||||
|
databaseRepo := c.MustGet("REPOSITORY").(database.DatabaseRepository)
|
||||||
|
|
||||||
|
summary, err := databaseRepo.GetSummary(c)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorln("An error occurred while retrieving summary", err)
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{"success": true, "data": summary})
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ func RequireAuth() gin.HandlerFunc {
|
||||||
//todo, is this shared between all sessions??
|
//todo, is this shared between all sessions??
|
||||||
c.Set("AUTH_TOKEN", tokenString)
|
c.Set("AUTH_TOKEN", tokenString)
|
||||||
c.Set("AUTH_USERNAME", claim.Username)
|
c.Set("AUTH_USERNAME", claim.Username)
|
||||||
|
c.Set("AUTH_USERID", claim.UserId)
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,8 @@ func (ae *AppEngine) Setup(logger *logrus.Entry) *gin.Engine {
|
||||||
|
|
||||||
secure := api.Group("/secure").Use(middleware.RequireAuth())
|
secure := api.Group("/secure").Use(middleware.RequireAuth())
|
||||||
{
|
{
|
||||||
|
secure.GET("/summary", handler.GetSummary)
|
||||||
|
|
||||||
secure.POST("/source", handler.CreateSource)
|
secure.POST("/source", handler.CreateSource)
|
||||||
secure.POST("/source/manual", handler.CreateManualSource)
|
secure.POST("/source/manual", handler.CreateManualSource)
|
||||||
secure.GET("/source", handler.ListSource)
|
secure.GET("/source", handler.ListSource)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import {Source} from './source';
|
||||||
|
import {ResourceTypeCounts} from './source-summary';
|
||||||
|
|
||||||
|
export class Summary {
|
||||||
|
sources: Source[]
|
||||||
|
resource_type_counts: ResourceTypeCounts[]
|
||||||
|
}
|
|
@ -55,7 +55,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Encounters</label>
|
<label>Encounters</label>
|
||||||
<h4>41</h4>
|
<h4>{{encounterCount}}</h4>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- col -->
|
</div><!-- col -->
|
||||||
<div class="col-6 d-sm-flex align-items-center">
|
<div class="col-6 d-sm-flex align-items-center">
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>All Records</label>
|
<label>All Records</label>
|
||||||
<h4>9,065</h4>
|
<h4>{{recordsCount}}</h4>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- col -->
|
</div><!-- col -->
|
||||||
</div><!-- card-body -->
|
</div><!-- card-body -->
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Sources</label>
|
<label>Sources</label>
|
||||||
<h4>30</h4>
|
<h4>{{sources.length}}</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- col -->
|
</div><!-- col -->
|
||||||
|
|
|
@ -3,6 +3,8 @@ import {FastenApiService} from '../../services/fasten-api.service';
|
||||||
import {LighthouseSource} from '../../models/lighthouse/lighthouse-source';
|
import {LighthouseSource} from '../../models/lighthouse/lighthouse-source';
|
||||||
import {Source} from '../../models/fasten/source';
|
import {Source} from '../../models/fasten/source';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
|
import {Summary} from '../../models/fasten/summary';
|
||||||
|
import {ResourceTypeCounts} from '../../models/fasten/source-summary';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-dashboard',
|
selector: 'app-dashboard',
|
||||||
|
@ -12,15 +14,25 @@ import {Router} from '@angular/router';
|
||||||
export class DashboardComponent implements OnInit {
|
export class DashboardComponent implements OnInit {
|
||||||
|
|
||||||
sources: Source[] = []
|
sources: Source[] = []
|
||||||
|
encounterCount: number = 0
|
||||||
|
recordsCount: number = 0
|
||||||
|
|
||||||
constructor(private fastenApi: FastenApiService, private router: Router) { }
|
constructor(private fastenApi: FastenApiService, private router: Router) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
|
||||||
this.fastenApi.getSources()
|
this.fastenApi.getSummary()
|
||||||
.subscribe( (sourcesList) => {
|
.subscribe( (summary) => {
|
||||||
console.log(sourcesList);
|
console.log(summary);
|
||||||
this.sources = sourcesList
|
this.sources = summary.sources
|
||||||
|
|
||||||
|
//calculate the number of records
|
||||||
|
summary.resource_type_counts.forEach((resourceTypeInfo) => {
|
||||||
|
this.recordsCount += resourceTypeInfo.count
|
||||||
|
if(resourceTypeInfo.resource_type == "Encounter"){
|
||||||
|
this.encounterCount = resourceTypeInfo.count
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
// this.fastenApi.getResources('Patient')
|
// this.fastenApi.getResources('Patient')
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {Source} from '../models/fasten/source';
|
||||||
import {User} from '../models/fasten/user';
|
import {User} from '../models/fasten/user';
|
||||||
import {ResourceFhir} from '../models/fasten/resource_fhir';
|
import {ResourceFhir} from '../models/fasten/resource_fhir';
|
||||||
import {SourceSummary} from '../models/fasten/source-summary';
|
import {SourceSummary} from '../models/fasten/source-summary';
|
||||||
|
import {Summary} from '../models/fasten/summary';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -66,6 +67,15 @@ export class FastenApiService {
|
||||||
/*
|
/*
|
||||||
SECURE ENDPOINTS
|
SECURE ENDPOINTS
|
||||||
*/
|
*/
|
||||||
|
getSummary(): Observable<Summary> {
|
||||||
|
return this._httpClient.get<any>(`${this.getBasePath()}/api/secure/summary`, )
|
||||||
|
.pipe(
|
||||||
|
map((response: ResponseWrapper) => {
|
||||||
|
console.log("Summary RESPONSE", response)
|
||||||
|
return response.data as Summary
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
createSource(source: Source): Observable<Source> {
|
createSource(source: Source): Observable<Source> {
|
||||||
return this._httpClient.post<any>(`${this.getBasePath()}/api/secure/source`, source)
|
return this._httpClient.post<any>(`${this.getBasePath()}/api/secure/source`, source)
|
||||||
|
|
Loading…
Reference in New Issue