浏览代码

Prometheus: Fixed Prometheus query editor error (plus new ErrorBoundaryAlert component) (#18838)

* ErrorHandling: Fixed Prometheus query editor error and added error boundary

* Update public/app/core/components/ErrorBoundary/ErrorBoundary.tsx

Co-Authored-By: kay delaney <45561153+kaydelaney@users.noreply.github.com>

* Fixed ts error
Torkel Ödegaard 6 年之前
父节点
当前提交
fe658d7ac2

+ 6 - 4
packages/grafana-ui/src/components/Alert/Alert.tsx

@@ -1,15 +1,16 @@
-import React, { FC } from 'react';
+import React, { FC, ReactNode } from 'react';
 
 interface Props {
-  message: any;
+  title: string;
   button?: {
     text: string;
     onClick: (event: React.MouseEvent) => void;
   };
+  children?: ReactNode;
 }
 
 export const Alert: FC<Props> = props => {
-  const { message, button } = props;
+  const { title, button, children } = props;
   return (
     <div className="alert-container">
       <div className="alert-error alert">
@@ -17,7 +18,8 @@ export const Alert: FC<Props> = props => {
           <i className="fa fa-exclamation-triangle" />
         </div>
         <div className="alert-body">
-          <div className="alert-title">{message}</div>
+          <div className="alert-title">{title}</div>
+          {children && <div className="alert-text">{children}</div>}
         </div>
         {button && (
           <div className="alert-button">

+ 1 - 1
packages/grafana-ui/src/components/Logs/LogRowContext.tsx

@@ -179,7 +179,7 @@ const LogRowContextGroup: React.FunctionComponent<LogRowContextGroupProps> = ({
                 }}
               />
             )}
-            {error && <Alert message={error} />}
+            {error && <Alert title={error} />}
           </div>
         </CustomScrollbar>
       </div>

+ 37 - 4
public/app/core/components/ErrorBoundary/ErrorBoundary.tsx

@@ -1,4 +1,5 @@
-import { Component } from 'react';
+import React, { PureComponent, ReactNode } from 'react';
+import { Alert } from '@grafana/ui';
 
 interface ErrorInfo {
   componentStack: string;
@@ -10,7 +11,7 @@ interface RenderProps {
 }
 
 interface Props {
-  children: (r: RenderProps) => JSX.Element;
+  children: (r: RenderProps) => ReactNode;
 }
 
 interface State {
@@ -18,7 +19,7 @@ interface State {
   errorInfo: ErrorInfo;
 }
 
-class ErrorBoundary extends Component<Props, State> {
+export class ErrorBoundary extends PureComponent<Props, State> {
   readonly state: State = {
     error: null,
     errorInfo: null,
@@ -41,4 +42,36 @@ class ErrorBoundary extends Component<Props, State> {
   }
 }
 
-export default ErrorBoundary;
+interface WithAlertBoxProps {
+  title?: string;
+  children: ReactNode;
+}
+
+export class ErrorBoundaryAlert extends PureComponent<WithAlertBoxProps> {
+  static defaultProps: Partial<WithAlertBoxProps> = {
+    title: 'An unexpected error happened',
+  };
+
+  render() {
+    const { title, children } = this.props;
+    return (
+      <ErrorBoundary>
+        {({ error, errorInfo }) => {
+          if (!errorInfo) {
+            return children;
+          }
+
+          return (
+            <Alert title={title}>
+              <details style={{ whiteSpace: 'pre-wrap' }}>
+                {error && error.toString()}
+                <br />
+                {errorInfo.componentStack}
+              </details>
+            </Alert>
+          );
+        }}
+      </ErrorBoundary>
+    );
+  }
+}

+ 1 - 1
public/app/features/dashboard/dashgrid/PanelChrome.tsx

@@ -5,7 +5,7 @@ import { Unsubscribable } from 'rxjs';
 
 // Components
 import { PanelHeader } from './PanelHeader/PanelHeader';
-import ErrorBoundary from 'app/core/components/ErrorBoundary/ErrorBoundary';
+import { ErrorBoundary } from 'app/core/components/ErrorBoundary/ErrorBoundary';
 
 // Utils & Services
 import { getTimeSrv, TimeSrv } from '../services/TimeSrv';

+ 4 - 1
public/app/features/dashboard/panel_editor/QueryEditorRow.tsx

@@ -8,6 +8,7 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
 import { AngularComponent, getAngularLoader } from '@grafana/runtime';
 import { Emitter } from 'app/core/utils/emitter';
 import { getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
+import { ErrorBoundaryAlert } from 'app/core/components/ErrorBoundary/ErrorBoundary';
 
 // Types
 import { PanelModel } from '../state/PanelModel';
@@ -257,7 +258,9 @@ export class QueryEditorRow extends PureComponent<Props, State> {
             </button>
           </div>
         </div>
-        <div className={bodyClasses}>{this.renderPluginEditor()}</div>
+        <div className={bodyClasses}>
+          <ErrorBoundaryAlert title="Data source query editor failed">{this.renderPluginEditor()}</ErrorBoundaryAlert>
+        </div>
       </div>
     );
   }

+ 1 - 1
public/app/features/explore/ErrorBoundary.tsx

@@ -1,6 +1,6 @@
 import React, { Component } from 'react';
 
-export default class ErrorBoundary extends Component<{}, any> {
+export class ErrorBoundary extends Component<{}, any> {
   constructor(props: {}) {
     super(props);
     this.state = { error: null, errorInfo: null };

+ 2 - 2
public/app/features/explore/Explore.tsx

@@ -12,7 +12,7 @@ import store from 'app/core/store';
 
 // Components
 import { Alert } from '@grafana/ui';
-import ErrorBoundary from './ErrorBoundary';
+import { ErrorBoundary } from './ErrorBoundary';
 import LogsContainer from './LogsContainer';
 import QueryRows from './QueryRows';
 import TableContainer from './TableContainer';
@@ -263,7 +263,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
         <FadeIn duration={datasourceError ? 150 : 5} in={datasourceError ? true : false}>
           <div className="explore-container">
             <Alert
-              message={`Error connecting to datasource: ${datasourceError}`}
+              title={`Error connecting to datasource: ${datasourceError}`}
               button={{ text: 'Reconnect', onClick: this.onReconnect }}
             />
           </div>

+ 1 - 1
public/app/features/explore/Wrapper.tsx

@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
 import { StoreState } from 'app/types';
 import { ExploreId } from 'app/types/explore';
 
-import ErrorBoundary from './ErrorBoundary';
+import { ErrorBoundary } from './ErrorBoundary';
 import Explore from './Explore';
 import { CustomScrollbar } from '@grafana/ui';
 import { resetExploreAction } from './state/actionTypes';

+ 3 - 2
public/app/plugins/datasource/prometheus/components/PromQueryField.tsx

@@ -154,7 +154,8 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
 
   componentDidUpdate(prevProps: PromQueryFieldProps) {
     const { queryResponse } = this.props;
-    if (prevProps.queryResponse && prevProps.queryResponse.series !== queryResponse.series) {
+
+    if (queryResponse && prevProps.queryResponse && prevProps.queryResponse.series !== queryResponse.series) {
       this.refreshHint();
     }
 
@@ -177,7 +178,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
   refreshHint = () => {
     const { datasource, query, queryResponse } = this.props;
 
-    if (!queryResponse.series || queryResponse.series.length === 0) {
+    if (!queryResponse || queryResponse.series.length === 0) {
       this.setState({ hint: null });
       return;
     }