55 * 2.0.
66 */
77import { v4 as uuidv4 } from 'uuid' ;
8- import { omit } from 'lodash' ;
8+ import { isEmpty , omit } from 'lodash' ;
99import { IContentClient } from '@kbn/content-management-plugin/server/types' ;
10- import type { Logger , SavedObjectsFindResult } from '@kbn/core/server' ;
11- import { isDashboardSection } from '@kbn/dashboard-plugin/common' ;
10+ import type { Logger , SavedObjectsClientContract , SavedObjectsFindResult } from '@kbn/core/server' ;
11+ import { isDashboardPanel } from '@kbn/dashboard-plugin/common' ;
1212import type { DashboardAttributes , DashboardPanel } from '@kbn/dashboard-plugin/server' ;
1313import type {
1414 FieldBasedIndexPatternColumn ,
@@ -33,7 +33,8 @@ export class RelatedDashboardsClient {
3333 private logger : Logger ,
3434 private dashboardClient : IContentClient < Dashboard > ,
3535 private alertsClient : InvestigateAlertsClient ,
36- private alertId : string
36+ private alertId : string ,
37+ private soClient : SavedObjectsClientContract
3738 ) { }
3839
3940 public async fetchRelatedDashboards ( ) : Promise < {
@@ -110,6 +111,43 @@ export class RelatedDashboardsClient {
110111 return sortedDashboards ;
111112 }
112113
114+ private async fetchReferencedPanel ( {
115+ dashboard,
116+ panel,
117+ } : {
118+ dashboard : Dashboard ;
119+ panel : DashboardPanel ;
120+ } ) : Promise < DashboardPanel | null > {
121+ const panelReference = dashboard . references . find (
122+ ( r ) => panel . panelIndex && r . name . includes ( panel . panelIndex ) && r . type === panel . type
123+ ) ;
124+
125+ // A reference of the panel was not found
126+ if ( ! panelReference ) {
127+ this . logger . error (
128+ `Reference for panel of type ${ panel . type } and panelIndex ${ panel . panelIndex } was not found in dashboard with id ${ dashboard . id } `
129+ ) ;
130+ return null ;
131+ }
132+
133+ try {
134+ const so = await this . soClient . get ( panel . type , panelReference . id ) ;
135+ return {
136+ ...panel ,
137+ panelConfig : {
138+ ...panel . panelConfig ,
139+ attributes : so . attributes ,
140+ } ,
141+ } ;
142+ } catch ( error ) {
143+ // There was an error fetching the referenced saved object
144+ this . logger . error (
145+ `Error fetching panel with type ${ panel . type } and id ${ panelReference . id } : ${ error . message } `
146+ ) ;
147+ return null ;
148+ }
149+ }
150+
113151 private async fetchDashboards ( {
114152 page,
115153 perPage = 20 ,
@@ -123,9 +161,24 @@ export class RelatedDashboardsClient {
123161 const {
124162 result : { hits } ,
125163 } = dashboards ;
126- hits . forEach ( ( dashboard : Dashboard ) => {
127- this . dashboardsById . set ( dashboard . id , dashboard ) ;
128- } ) ;
164+ for ( const dashboard of hits ) {
165+ const panels : DashboardAttributes [ 'panels' ] = await Promise . all (
166+ dashboard . attributes . panels . map ( async ( panel ) => {
167+ // Only fetch the panel if it's a panel (not a section) with an empty panelConfig
168+ if ( ! isDashboardPanel ( panel ) || ! isEmpty ( panel . panelConfig ) ) return panel ;
169+ const referencedPanel = await this . fetchReferencedPanel ( {
170+ dashboard,
171+ panel,
172+ } ) ;
173+ return referencedPanel || panel ;
174+ } )
175+ ) ;
176+ this . dashboardsById . set ( dashboard . id , {
177+ ...dashboard ,
178+ attributes : { ...dashboard . attributes , panels } ,
179+ } ) ;
180+ }
181+
129182 const fetchedUntil = ( page - 1 ) * perPage + dashboards . result . hits . length ;
130183
131184 if ( dashboards . result . pagination . total <= fetchedUntil ) {
@@ -146,7 +199,7 @@ export class RelatedDashboardsClient {
146199 } {
147200 const relevantDashboards : SuggestedDashboard [ ] = [ ] ;
148201 this . dashboardsById . forEach ( ( d ) => {
149- const panels = d . attributes . panels ;
202+ const panels = d . attributes . panels . filter ( isDashboardPanel ) ;
150203 const matchingPanels = this . getPanelsByIndex ( index , panels ) ;
151204 if ( matchingPanels . length > 0 ) {
152205 this . logger . debug (
@@ -190,7 +243,7 @@ export class RelatedDashboardsClient {
190243 } {
191244 const relevantDashboards : SuggestedDashboard [ ] = [ ] ;
192245 this . dashboardsById . forEach ( ( d ) => {
193- const panels = d . attributes . panels ;
246+ const panels = d . attributes . panels . filter ( isDashboardPanel ) ;
194247 const matchingPanels = this . getPanelsByField ( fields , panels ) ;
195248 const allMatchingFields = new Set (
196249 matchingPanels . map ( ( p ) => Array . from ( p . matchingFields ) ) . flat ( )
@@ -224,23 +277,17 @@ export class RelatedDashboardsClient {
224277 return { dashboards : relevantDashboards } ;
225278 }
226279
227- private getPanelsByIndex ( index : string , panels : DashboardAttributes [ 'panels' ] ) : DashboardPanel [ ] {
228- const panelsByIndex = panels . filter ( ( p ) => {
229- if ( isDashboardSection ( p ) ) return false ; // filter out sections
230- const panelIndices = this . getPanelIndices ( p ) ;
231- return panelIndices . has ( index ) ;
232- } ) as DashboardPanel [ ] ; // filtering with type guard doesn't actually limit type, so need to cast
280+ private getPanelsByIndex ( index : string , panels : DashboardPanel [ ] ) : DashboardPanel [ ] {
281+ const panelsByIndex = panels . filter ( ( p ) => this . getPanelIndices ( p ) . has ( index ) ) ;
233282 return panelsByIndex ;
234283 }
235284
236285 private getPanelsByField (
237286 fields : string [ ] ,
238- panels : DashboardAttributes [ 'panels' ]
287+ panels : DashboardPanel [ ]
239288 ) : Array < { matchingFields : Set < string > ; panel : DashboardPanel } > {
240289 const panelsByField = panels . reduce ( ( acc , p ) => {
241- if ( isDashboardSection ( p ) ) return acc ; // filter out sections
242- const panelFields = this . getPanelFields ( p ) ;
243- const matchingFields = fields . filter ( ( f ) => panelFields . has ( f ) ) ;
290+ const matchingFields = fields . filter ( ( f ) => this . getPanelFields ( p ) . has ( f ) ) ;
244291 if ( matchingFields . length ) {
245292 acc . push ( { matchingFields : new Set ( matchingFields ) , panel : p } ) ;
246293 }
0 commit comments