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(