From a66caeba1780f8811013c364ae506385bdc8c260 Mon Sep 17 00:00:00 2001 From: Andrew Azores Date: Tue, 23 Apr 2024 14:47:47 -0400 Subject: [PATCH] fix(graphql): handle nullable response fields that can occur when there are server errors (#1246) --- .../AllTargetsArchivedRecordingsTable.tsx | 22 ++++++++++--------- .../AutomatedAnalysisCard.tsx | 4 ++-- src/app/RecordingMetadata/BulkEditLabels.tsx | 4 ++-- .../Recordings/ArchivedRecordingsTable.tsx | 4 ++-- src/app/Shared/Services/Api.service.tsx | 20 ++++++++--------- src/app/Topology/Entity/EntityDetails.tsx | 2 +- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/app/Archives/AllTargetsArchivedRecordingsTable.tsx b/src/app/Archives/AllTargetsArchivedRecordingsTable.tsx index 0b2c2e47a..a35c0b6db 100644 --- a/src/app/Archives/AllTargetsArchivedRecordingsTable.tsx +++ b/src/app/Archives/AllTargetsArchivedRecordingsTable.tsx @@ -199,15 +199,17 @@ export const AllTargetsArchivedRecordingsTable: React.FC { - return v.data.targetNodes.map((node) => { - const target: Target = node.target; - return { - target, - targetAsObs: of(target), - archiveCount: node.target.archivedRecordings.aggregate.count, - recordings: node.target.archivedRecordings.data as ArchivedRecording[], - }; - }); + return v.data?.targetNodes + ?.map((node) => { + const target: Target = node?.target; + return { + target, + targetAsObs: of(target), + archiveCount: node?.target?.archivedRecordings?.aggregate?.count ?? 0, + recordings: (node?.target?.archivedRecordings?.data as ArchivedRecording[]) ?? [], + }; + }) + .filter((v) => !!v.target); }), ) .subscribe({ @@ -258,7 +260,7 @@ export const AllTargetsArchivedRecordingsTable: React.FC queryArchivedRecordings(target.id!) .pipe( first(), - map((v) => v.data.targetNodes[0].target.archivedRecordings.data as ArchivedRecording[]), + map((v) => (v.data?.targetNodes[0]?.target?.archivedRecordings?.data as ArchivedRecording[]) ?? []), ) .subscribe({ next: (recordings) => { @@ -368,7 +368,7 @@ export const AutomatedAnalysisCard: DashboardCardFC } } }), - map((v) => v.data.targetNodes[0].target.activeRecordings.data[0] as Recording), + map((v) => v.data?.targetNodes[0]?.target?.activeRecordings?.data[0] as Recording), tap((recording) => { if (recording === null || recording === undefined) { throw new Error(NO_RECORDINGS_MESSAGE); diff --git a/src/app/RecordingMetadata/BulkEditLabels.tsx b/src/app/RecordingMetadata/BulkEditLabels.tsx index 9d5d57873..dd326ebfe 100644 --- a/src/app/RecordingMetadata/BulkEditLabels.tsx +++ b/src/app/RecordingMetadata/BulkEditLabels.tsx @@ -182,7 +182,7 @@ export const BulkEditLabels: React.FC = ({ { filter: { sourceTarget: UPLOADS_SUBDIRECTORY } }, ) .pipe( - map((v) => v.data.archivedRecordings.data as ArchivedRecording[]), + map((v) => (v.data?.archivedRecordings?.data as ArchivedRecording[]) ?? []), first(), ) : context.target.target().pipe( @@ -213,7 +213,7 @@ export const BulkEditLabels: React.FC = ({ { id: target.id! }, ), ), - map((v) => v.data.targetNodes[0].target.archivedRecordings.data as ArchivedRecording[]), + map((v) => (v.data?.targetNodes[0]?.target?.archivedRecordings?.data as ArchivedRecording[]) ?? []), first(), ); } diff --git a/src/app/Recordings/ArchivedRecordingsTable.tsx b/src/app/Recordings/ArchivedRecordingsTable.tsx index cbf240caa..c8fd7d5d5 100644 --- a/src/app/Recordings/ArchivedRecordingsTable.tsx +++ b/src/app/Recordings/ArchivedRecordingsTable.tsx @@ -247,7 +247,7 @@ export const ArchivedRecordingsTable: React.FC = ( } else if (isUploadsTable) { addSubscription( queryUploadedRecordings() - .pipe(map((v) => v.data.archivedRecordings.data as ArchivedRecording[])) + .pipe(map((v) => (v?.data?.archivedRecordings?.data as ArchivedRecording[]) ?? [])) .subscribe({ next: handleRecordings, error: handleError, @@ -260,7 +260,7 @@ export const ArchivedRecordingsTable: React.FC = ( filter((target) => !!target), first(), concatMap((target: Target) => queryTargetRecordings(target.id!)), - map((v) => v.data.targetNodes[0].target.archivedRecordings.data as ArchivedRecording[]), + map((v) => (v.data?.targetNodes[0]?.target?.archivedRecordings?.data as ArchivedRecording[]) ?? []), ) .subscribe({ next: handleRecordings, diff --git a/src/app/Shared/Services/Api.service.tsx b/src/app/Shared/Services/Api.service.tsx index 255036e9b..c6b317ffe 100644 --- a/src/app/Shared/Services/Api.service.tsx +++ b/src/app/Shared/Services/Api.service.tsx @@ -591,7 +591,7 @@ export class ApiService { recordingName, labels: labels.map((label) => ({ key: label.key, value: label.value })), }, - ).pipe(map((v) => v.data.archivedRecordings.data as ArchivedRecording[])); + ).pipe(map((v) => (v.data?.archivedRecordings?.data as ArchivedRecording[]) ?? [])); } isProbeEnabled(): Observable { @@ -972,7 +972,7 @@ export class ApiService { }, ), ), - map((v) => v.data.targetNodes[0].target.archivedRecordings as ArchivedRecording[]), + map((v) => (v.data?.targetNodes[0]?.target?.archivedRecordings as ArchivedRecording[]) ?? []), ); } @@ -1000,7 +1000,7 @@ export class ApiService { recordingName, labels: labels.map((label) => ({ key: label.key, value: label.value })), }, - ).pipe(map((v) => v.data.archivedRecordings.data as ArchivedRecording[])); + ).pipe(map((v) => (v.data?.archivedRecordings?.data as ArchivedRecording[]) ?? [])); } postTargetRecordingMetadata(recordingName: string, labels: KeyValue[]): Observable { @@ -1037,7 +1037,7 @@ export class ApiService { }, ), ), - map((v) => v.data.targetNodes[0].target.activeRecordings as ActiveRecording[]), + map((v) => (v.data?.targetNodes[0]?.target?.activeRecordings as ActiveRecording[]) ?? []), ); } @@ -1180,7 +1180,7 @@ export class ApiService { ).pipe( first(), map((body) => - body.data.environmentNodes[0].descendantTargets.reduce( + (body.data?.environmentNodes[0]?.descendantTargets ?? []).reduce( (acc: number, curr) => acc + curr.target.activeRecordings.aggregate.count, 0, ), @@ -1212,11 +1212,11 @@ export class ApiService { true, ).pipe( map((resp) => { - const nodes = resp.data.targetNodes; + const nodes = resp.data?.targetNodes ?? []; if (nodes.length === 0) { return false; } - const count = nodes[0].target.activeRecordings.aggregate.count; + const count = nodes[0]?.target?.activeRecordings?.aggregate?.count ?? 0; return count > 0; }), catchError((_) => of(false)), @@ -1292,11 +1292,11 @@ export class ApiService { { id: target.id! }, ).pipe( map((resp) => { - const nodes = resp.data.targetNodes; + const nodes = resp.data?.targetNodes ?? []; if (!nodes || nodes.length === 0) { return {}; } - return nodes[0]?.target.mbeanMetrics; + return nodes[0]?.target?.mbeanMetrics ?? {}; }), catchError((_) => of({})), ); @@ -1329,7 +1329,7 @@ export class ApiService { { id: target.id! }, true, true, - ).pipe(map((v) => v.data.targetNodes[0].target.archivedRecordings.data as ArchivedRecording[])); + ).pipe(map((v) => (v.data?.targetNodes[0]?.target?.archivedRecordings?.data as ArchivedRecording[]) ?? [])); } getTargetActiveRecordings(target: TargetStub): Observable { diff --git a/src/app/Topology/Entity/EntityDetails.tsx b/src/app/Topology/Entity/EntityDetails.tsx index 600e7392c..3ec111e6a 100644 --- a/src/app/Topology/Entity/EntityDetails.tsx +++ b/src/app/Topology/Entity/EntityDetails.tsx @@ -300,7 +300,7 @@ const MBeanDetails: React.FC<{ { id: targetId }, ) .pipe( - map((resp) => resp.data.targetNodes[0].target.mbeanMetrics || {}), + map((resp) => resp.data?.targetNodes[0]?.target?.mbeanMetrics ?? {}), catchError((_) => of({})), ) .subscribe(setMbeanMetrics),