From c590663537faab12d4416edd3f7756b678ee5460 Mon Sep 17 00:00:00 2001 From: Jason Kulatunga Date: Wed, 11 Oct 2023 07:54:18 -0700 Subject: [PATCH] adding ability to delete source + associated records. --- backend/pkg/database/interface.go | 1 + backend/pkg/database/sqlite_repository.go | 56 +++++++++++++++++++ backend/pkg/web/handler/source.go | 13 +++++ backend/pkg/web/server.go | 1 + .../medical-sources-connected.component.html | 2 +- .../medical-sources-connected.component.ts | 41 ++++++++++++++ .../src/app/services/fasten-api.service.ts | 9 +++ 7 files changed, 122 insertions(+), 1 deletion(-) diff --git a/backend/pkg/database/interface.go b/backend/pkg/database/interface.go index a6664b50..d42a4101 100644 --- a/backend/pkg/database/interface.go +++ b/backend/pkg/database/interface.go @@ -38,6 +38,7 @@ type DatabaseRepository interface { GetSourceSummary(context.Context, string) (*models.SourceSummary, error) GetSources(context.Context) ([]models.SourceCredential, error) UpdateSource(ctx context.Context, sourceCreds *models.SourceCredential) error + DeleteSource(ctx context.Context, sourceId string) (int64, error) CreateGlossaryEntry(ctx context.Context, glossaryEntry *models.Glossary) error GetGlossaryEntry(ctx context.Context, code string, codeSystem string) (*models.Glossary, error) diff --git a/backend/pkg/database/sqlite_repository.go b/backend/pkg/database/sqlite_repository.go index e0160af8..e04aafaf 100644 --- a/backend/pkg/database/sqlite_repository.go +++ b/backend/pkg/database/sqlite_repository.go @@ -976,6 +976,62 @@ func (sr *SqliteRepository) GetSources(ctx context.Context) ([]models.SourceCred return sourceCreds, results.Error } +func (sr *SqliteRepository) DeleteSource(ctx context.Context, sourceId string) (int64, error) { + currentUser, currentUserErr := sr.GetCurrentUser(ctx) + if currentUserErr != nil { + return 0, currentUserErr + } + + if strings.TrimSpace(sourceId) == "" { + return 0, fmt.Errorf("sourceId cannot be blank") + } + //delete all resources for this source + sourceUUID, err := uuid.Parse(sourceId) + if err != nil { + return 0, err + } + + rowsEffected := int64(0) + resourceTypes := databaseModel.GetAllowedResourceTypes() + for _, resourceType := range resourceTypes { + tableName, err := databaseModel.GetTableNameByResourceType(resourceType) + if err != nil { + return 0, err + } + results := sr.GormClient.WithContext(ctx). + Where(models.OriginBase{ + UserID: currentUser.ID, + SourceID: sourceUUID, + }). + Table(tableName). + Delete(&models.ResourceBase{}) + rowsEffected += results.RowsAffected + if results.Error != nil { + return rowsEffected, results.Error + } + } + + //delete relatedResources entries + results := sr.GormClient.WithContext(ctx). + Where(models.RelatedResource{ResourceBaseUserID: currentUser.ID, ResourceBaseSourceID: sourceUUID}). + Delete(&models.RelatedResource{}) + if results.Error != nil { + return rowsEffected, results.Error + } + + //soft delete the source credential + results = sr.GormClient.WithContext(ctx). + Where(models.SourceCredential{ + ModelBase: models.ModelBase{ + ID: sourceUUID, + }, + UserID: currentUser.ID, + }). + Delete(&models.SourceCredential{}) + rowsEffected += results.RowsAffected + return rowsEffected, results.Error +} + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Background Job //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/backend/pkg/web/handler/source.go b/backend/pkg/web/handler/source.go index a5b201bd..a67071a1 100644 --- a/backend/pkg/web/handler/source.go +++ b/backend/pkg/web/handler/source.go @@ -312,3 +312,16 @@ func ListSource(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{"success": true, "data": sourceCreds}) } + +func DeleteSource(c *gin.Context) { + logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry) + databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository) + + rowsEffected, err := databaseRepo.DeleteSource(c, c.Param("sourceId")) + if err != nil { + logger.Errorln("An error occurred while deleting source credential", err) + c.JSON(http.StatusInternalServerError, gin.H{"success": false}) + return + } + c.JSON(http.StatusOK, gin.H{"success": true, "data": rowsEffected}) +} diff --git a/backend/pkg/web/server.go b/backend/pkg/web/server.go index 3b8a9f1a..cfd0031a 100644 --- a/backend/pkg/web/server.go +++ b/backend/pkg/web/server.go @@ -68,6 +68,7 @@ func (ae *AppEngine) Setup() (*gin.RouterGroup, *gin.Engine) { secure.POST("/source/manual", handler.CreateManualSource) secure.GET("/source", handler.ListSource) secure.GET("/source/:sourceId", handler.GetSource) + secure.DELETE("/source/:sourceId", handler.DeleteSource) secure.POST("/source/:sourceId/sync", handler.SourceSync) secure.GET("/source/:sourceId/summary", handler.GetSourceSummary) secure.GET("/resource/fhir", handler.ListResourceFhir) diff --git a/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html b/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html index 4c694a09..338157af 100644 --- a/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html +++ b/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.html @@ -45,7 +45,7 @@ - + diff --git a/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.ts b/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.ts index e1556319..39c89eaa 100644 --- a/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.ts +++ b/frontend/src/app/components/medical-sources-connected/medical-sources-connected.component.ts @@ -323,6 +323,47 @@ export class MedicalSourcesConnectedComponent implements OnInit { ) } + public sourceDeleteHandler(){ + let source = this.modalSelectedSourceListItem.source + let sourceDisplayName = this.modalSelectedSourceListItem?.metadata?.display || this.modalSelectedSourceListItem?.source?.source_type || 'unknown' + + this.status[source.id] = "authorize" + this.modalService.dismissAll() + + this.fastenApi.deleteSource(source.id).subscribe( + (respData) => { + delete this.status[source.id] + delete this.status[source.source_type] + + //delete this source from the connnected list + let foundIndex = this.connectedSourceList.findIndex((connectedSource) => { + return connectedSource?.source?.id == source.id + }, this) + if(foundIndex > -1){ + this.connectedSourceList.splice(foundIndex, 1) + } + + console.log("source delete response:", respData) + + + const toastNotification = new ToastNotification() + toastNotification.type = ToastType.Success + toastNotification.message = `Successfully deleted source: ${sourceDisplayName}, ${respData} row(s) effected` + this.toastService.show(toastNotification) + + }, + (err) => { + delete this.status[source.id] + delete this.status[source.source_type] + + const toastNotification = new ToastNotification() + toastNotification.type = ToastType.Error + toastNotification.message = `An error occurred while deleting source: ${sourceDisplayName}` + this.toastService.show(toastNotification) + console.log(err) + }) + } + private getDismissReason(reason: any): string { if (reason === ModalDismissReasons.ESC) { diff --git a/frontend/src/app/services/fasten-api.service.ts b/frontend/src/app/services/fasten-api.service.ts index 2a372266..09e73212 100644 --- a/frontend/src/app/services/fasten-api.service.ts +++ b/frontend/src/app/services/fasten-api.service.ts @@ -137,6 +137,15 @@ export class FastenApiService { ); } + deleteSource(sourceId: string): Observable { + return this._httpClient.delete(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/source/${sourceId}`) + .pipe( + map((response: ResponseWrapper) => { + return response.data as number + }) + ); + } + syncSource(sourceId: string): Observable { return this._httpClient.post(`${GetEndpointAbsolutePath(globalThis.location, environment.fasten_api_endpoint_base)}/secure/source/${sourceId}/sync`, {}) .pipe(