From 0507fc6d43fd6dd568b5a7dc1ec59026dc0ab68d Mon Sep 17 00:00:00 2001
From: xgui3783 <xgui3783@gmail.com>
Date: Tue, 25 Aug 2020 12:56:51 +0200
Subject: [PATCH] bugfix: gracefully handle prv -> 404 #637 (#641)

---
 src/glue.spec.ts                               | 18 ++++++++++++++++++
 src/glue.ts                                    | 14 ++++++++++++--
 .../databrowserModule/preview/preview.base.ts  |  1 +
 3 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/src/glue.spec.ts b/src/glue.spec.ts
index 1ddec9282..884bd388c 100644
--- a/src/glue.spec.ts
+++ b/src/glue.spec.ts
@@ -316,6 +316,24 @@ describe('> glue.ts', () => {
         expect(colorMapMapSpy).toHaveBeenCalledWith(name, EnumColorMapName.VIRIDIS)
         expect(bgFlagSpy).toHaveBeenCalledWith(name, true)
       }))
+
+      it('> if returns 404, should be handled gracefully', fakeAsync(() => {
+
+        const ctrl = TestBed.inject(HttpTestingController)
+        const glue = TestBed.inject(DatasetPreviewGlue)
+
+        const { datasetId, filename } = file3
+
+        const obs$ = glue.getDatasetPreviewFromId({ datasetId, filename })
+        let expectedVal = 'defined'
+        obs$.subscribe(val => expectedVal = val)
+        tick(200)
+
+        const req = ctrl.expectOne(`${DS_PREVIEW_URL}/${encodeURIComponent('minds/core/dataset/v1.0.0')}/${encodeURIComponent(datasetId)}/${encodeURIComponent(filename)}`)
+        req.flush(null, { status: 404, statusText: 'Not found' })
+
+        expect(expectedVal).toBeNull()
+      }))
     })
 
     describe('> #actionOnWidget', () => {
diff --git a/src/glue.ts b/src/glue.ts
index baacec2a5..baeec8147 100644
--- a/src/glue.ts
+++ b/src/glue.ts
@@ -3,7 +3,7 @@ import { OnDestroy, Injectable, Optional, Inject, InjectionToken } from "@angula
 import { PreviewComponentWrapper, DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId } from "./ui/databrowserModule/pure"
 import { Subscription, Observable, forkJoin, of, merge } from "rxjs"
 import { select, Store, ActionReducer, createAction, props, createSelector, Action } from "@ngrx/store"
-import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom, mapTo, switchMapTo, filter, skip } from "rxjs/operators"
+import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom, mapTo, switchMapTo, filter, skip, catchError } from "rxjs/operators"
 import { TypeActionToWidget, EnumActionToWidget, ACTION_TO_WIDGET_TOKEN } from "./widget"
 import { getIdObj } from 'common/util'
 import { MatDialogRef } from "@angular/material/dialog"
@@ -24,6 +24,11 @@ const PREVIEW_FILE_TYPES_NO_UI = [
 
 const DATASET_PREVIEW_ANNOTATION = `DATASET_PREVIEW_ANNOTATION`
 
+const prvFilterNull = ({ prvToDismiss, prvToShow }) => ({
+  prvToDismiss: prvToDismiss.filter(v => !!v),
+  prvToShow: prvToShow.filter(v => !!v),
+})
+
 export const glueActionToggleDatasetPreview = createAction(
   '[glue] toggleDatasetPreview',
   props<{ datasetPreviewFile: IDatasetPreviewData }>()
@@ -182,6 +187,7 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
           : of([])
       })
     }),
+    map(prvFilterNull),
     shareReplay(1)
   )
 
@@ -211,6 +217,7 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
       ? forkJoin(...arr.map(({ kgId, kgSchema, filename }) => this.getDatasetPreviewFromId({ datasetId: kgId, datasetSchema: kgSchema, filename })))
       : of([])
     ),
+    map(arr => arr.filter(item => !!item)),
     shareReplay(1),
   )
 
@@ -251,7 +258,8 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
     const cachedPrv$ = this.fetchedDatasetPreviewCache.get(dsPrvId)
     const filteredDsId = /[a-f0-9-]+$/.exec(datasetId)
     if (cachedPrv$) return cachedPrv$
-    const filedetail$ = this.http.get(`${DS_PREVIEW_URL}/${encodeURIComponent(datasetSchema)}/${filteredDsId}/${encodeURIComponent(filename)}`, { responseType: 'json' }).pipe(
+    const url = `${DS_PREVIEW_URL}/${encodeURIComponent(datasetSchema)}/${filteredDsId}/${encodeURIComponent(filename)}`
+    const filedetail$ = this.http.get(url, { responseType: 'json' }).pipe(
       map(json => {
         return {
           ...json,
@@ -259,6 +267,7 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
           datasetId
         }
       }),
+      catchError((_err, _obs) => of(null))
     )
     this.fetchedDatasetPreviewCache.set(dsPrvId, filedetail$)
     return filedetail$.pipe(
@@ -340,6 +349,7 @@ export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{
           map(prvToShow => ({ prvToDismiss: [], prvToShow }))
         )
       ).pipe(
+        map(prvFilterNull),
         withLatestFrom(this.store$.pipe(
           select(state => state?.viewerState?.templateSelected || null),
           distinctUntilChanged(),
diff --git a/src/ui/databrowserModule/preview/preview.base.ts b/src/ui/databrowserModule/preview/preview.base.ts
index 1a9e4f7b1..7cf93b79b 100644
--- a/src/ui/databrowserModule/preview/preview.base.ts
+++ b/src/ui/databrowserModule/preview/preview.base.ts
@@ -37,6 +37,7 @@ export class PreviewBase implements OnChanges{
     this.getDatasetPreviewFromId({ datasetSchema, datasetId, filename })
       .subscribe(
         file => {
+          if (!file) return
           this.fetchingFlag = false
           this.file = file
           this.previewtype = determinePreviewFileType(file)
-- 
GitLab