You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by er...@apache.org on 2020/04/30 00:20:49 UTC

[incubator-superset] branch master updated: feat(errors): add client scaffolding for custom error messages (#9677)

This is an automated email from the ASF dual-hosted git repository.

erikrit pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 5d7b135  feat(errors): add client scaffolding for custom error messages (#9677)
5d7b135 is described below

commit 5d7b13507e207ac06793034208b2d28b4140373b
Author: Erik Ritter <er...@airbnb.com>
AuthorDate: Wed Apr 29 17:20:31 2020 -0700

    feat(errors): add client scaffolding for custom error messages (#9677)
---
 .../getErrorMessageComponentRegistry_spec.tsx      | 64 ++++++++++++++++++++++
 superset-frontend/src/chart/Chart.jsx              |  2 +-
 superset-frontend/src/components/ErrorBoundary.jsx |  2 +-
 .../ErrorMessageWithStackTrace.tsx                 | 22 +++++++-
 .../getErrorMessageComponentRegistry.ts            | 38 +++++++++++++
 .../src/components/ErrorMessage/types.ts           | 47 ++++++++++++++++
 superset-frontend/src/setup/setupApp.ts            |  4 ++
 superset-frontend/src/setup/setupErrorMessages.ts  | 24 ++++++++
 .../src/setup/setupErrorMessagesExtra.ts           | 21 +++++++
 9 files changed, 219 insertions(+), 5 deletions(-)

diff --git a/superset-frontend/spec/javascripts/components/ErrorMessage/getErrorMessageComponentRegistry_spec.tsx b/superset-frontend/spec/javascripts/components/ErrorMessage/getErrorMessageComponentRegistry_spec.tsx
new file mode 100644
index 0000000..b4a88f6
--- /dev/null
+++ b/superset-frontend/spec/javascripts/components/ErrorMessage/getErrorMessageComponentRegistry_spec.tsx
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import getErrorMessageComponentRegistry from 'src/components/ErrorMessage/getErrorMessageComponentRegistry';
+import { ErrorMessageComponentProps } from 'src/components/ErrorMessage/types';
+
+const ERROR_MESSAGE_COMPONENT = (_: ErrorMessageComponentProps) => (
+  <div>Test error</div>
+);
+
+const OVERRIDE_ERROR_MESSAGE_COMPONENT = (_: ErrorMessageComponentProps) => (
+  <div>Custom error</div>
+);
+
+describe('getErrorMessageComponentRegistry', () => {
+  it('returns undefined for a non existent key', () => {
+    expect(getErrorMessageComponentRegistry().get('INVALID_KEY')).toEqual(
+      undefined,
+    );
+  });
+
+  it('returns a component for a set key', () => {
+    getErrorMessageComponentRegistry().registerValue(
+      'VALID_KEY',
+      ERROR_MESSAGE_COMPONENT,
+    );
+
+    expect(getErrorMessageComponentRegistry().get('VALID_KEY')).toEqual(
+      ERROR_MESSAGE_COMPONENT,
+    );
+  });
+
+  it('returns the correct component for an overridden key', () => {
+    getErrorMessageComponentRegistry().registerValue(
+      'OVERRIDE_KEY',
+      ERROR_MESSAGE_COMPONENT,
+    );
+
+    getErrorMessageComponentRegistry().registerValue(
+      'OVERRIDE_KEY',
+      OVERRIDE_ERROR_MESSAGE_COMPONENT,
+    );
+
+    expect(getErrorMessageComponentRegistry().get('OVERRIDE_KEY')).toEqual(
+      OVERRIDE_ERROR_MESSAGE_COMPONENT,
+    );
+  });
+});
diff --git a/superset-frontend/src/chart/Chart.jsx b/superset-frontend/src/chart/Chart.jsx
index 7c19e78..51d392b 100644
--- a/superset-frontend/src/chart/Chart.jsx
+++ b/superset-frontend/src/chart/Chart.jsx
@@ -24,7 +24,7 @@ import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
 import { Logger, LOG_ACTIONS_RENDER_CHART_CONTAINER } from '../logger/LogUtils';
 import Loading from '../components/Loading';
 import RefreshChartOverlay from '../components/RefreshChartOverlay';
-import ErrorMessageWithStackTrace from '../components/ErrorMessageWithStackTrace';
+import ErrorMessageWithStackTrace from '../components/ErrorMessage/ErrorMessageWithStackTrace';
 import ErrorBoundary from '../components/ErrorBoundary';
 import ChartRenderer from './ChartRenderer';
 import './chart.less';
diff --git a/superset-frontend/src/components/ErrorBoundary.jsx b/superset-frontend/src/components/ErrorBoundary.jsx
index bb1d916..f1fa942 100644
--- a/superset-frontend/src/components/ErrorBoundary.jsx
+++ b/superset-frontend/src/components/ErrorBoundary.jsx
@@ -19,7 +19,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { t } from '@superset-ui/translation';
-import ErrorMessageWithStackTrace from './ErrorMessageWithStackTrace';
+import ErrorMessageWithStackTrace from './ErrorMessage/ErrorMessageWithStackTrace';
 
 const propTypes = {
   children: PropTypes.node.isRequired,
diff --git a/superset-frontend/src/components/ErrorMessageWithStackTrace.tsx b/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
similarity index 75%
rename from superset-frontend/src/components/ErrorMessageWithStackTrace.tsx
rename to superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
index 49403bd..6ffe586 100644
--- a/superset-frontend/src/components/ErrorMessageWithStackTrace.tsx
+++ b/superset-frontend/src/components/ErrorMessage/ErrorMessageWithStackTrace.tsx
@@ -19,19 +19,35 @@
 import React, { useState } from 'react';
 // @ts-ignore
 import { Alert, Collapse } from 'react-bootstrap';
+import getErrorMessageComponentRegistry from './getErrorMessageComponentRegistry';
+import { SupersetError } from './types';
 
-interface Props {
-  message: string;
+type Props = {
+  error?: SupersetError;
   link?: string;
+  message: string;
   stackTrace?: string;
-}
+};
 
 export default function ErrorMessageWithStackTrace({
+  error,
   message,
   link,
   stackTrace,
 }: Props) {
   const [showStackTrace, setShowStackTrace] = useState(false);
+
+  // Check if a custom error message component was registered for this message
+  if (error) {
+    const ErrorMessageComponent = getErrorMessageComponentRegistry().get(
+      error.errorType,
+    );
+    if (ErrorMessageComponent) {
+      return <ErrorMessageComponent error={error} />;
+    }
+  }
+
+  // Fallback to the default error message renderer
   return (
     <div className={`stack-trace-container${stackTrace ? ' has-trace' : ''}`}>
       <Alert
diff --git a/superset-frontend/src/components/ErrorMessage/getErrorMessageComponentRegistry.ts b/superset-frontend/src/components/ErrorMessage/getErrorMessageComponentRegistry.ts
new file mode 100644
index 0000000..e21a9d3
--- /dev/null
+++ b/superset-frontend/src/components/ErrorMessage/getErrorMessageComponentRegistry.ts
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Registry, makeSingleton, OverwritePolicy } from '@superset-ui/core';
+import { ErrorMessageComponent } from './types';
+
+class ErrorMessageComponentRegistry extends Registry<
+  ErrorMessageComponent,
+  ErrorMessageComponent
+> {
+  constructor() {
+    super({
+      name: 'ErrorMessageComponent',
+      overwritePolicy: OverwritePolicy.ALLOW,
+    });
+  }
+}
+
+const getErrorMessageComponentRegistry = makeSingleton(
+  ErrorMessageComponentRegistry,
+);
+
+export default getErrorMessageComponentRegistry;
diff --git a/superset-frontend/src/components/ErrorMessage/types.ts b/superset-frontend/src/components/ErrorMessage/types.ts
new file mode 100644
index 0000000..e2cea5b
--- /dev/null
+++ b/superset-frontend/src/components/ErrorMessage/types.ts
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// TODO: Add more error types as we classify more errors
+export const ErrorTypeEnum = {
+  // Generic errors created on the frontend
+  FRONTEND_CSRF_ERROR: 'FRONTEND_CSRF_ERROR',
+  FRONTEND_NETWORK_ERROR: 'FRONTEND_NETWORK_ERROR',
+  FRONTEND_TIMEOUT_ERROR: 'FRONTEND_TIMEOUT_ERROR',
+} as const;
+
+type ValueOf<T> = T[keyof T];
+
+export type ErrorType = ValueOf<typeof ErrorTypeEnum>;
+
+export type ErrorLevel = 'info' | 'warning' | 'error';
+
+export type SupersetError = {
+  errorType: ErrorType;
+  extra: Record<string, any>;
+  level: ErrorLevel;
+  message: string;
+};
+
+export type ErrorMessageComponentProps = {
+  error: SupersetError;
+};
+
+export type ErrorMessageComponent = React.ComponentType<
+  ErrorMessageComponentProps
+>;
diff --git a/superset-frontend/src/setup/setupApp.ts b/superset-frontend/src/setup/setupApp.ts
index 962799f..e81c6d2 100644
--- a/superset-frontend/src/setup/setupApp.ts
+++ b/superset-frontend/src/setup/setupApp.ts
@@ -20,6 +20,7 @@
 import $ from 'jquery';
 import { SupersetClient } from '@superset-ui/connection';
 import getClientErrorObject from '../utils/getClientErrorObject';
+import setupErrorMessages from './setupErrorMessages';
 
 function showApiMessage(resp: { severity?: string; message: string }) {
   const template =
@@ -84,4 +85,7 @@ export default function setupApp() {
   // @ts-ignore
   window.jQuery = $;
   require('bootstrap');
+
+  // setup appwide custom error messages
+  setupErrorMessages();
 }
diff --git a/superset-frontend/src/setup/setupErrorMessages.ts b/superset-frontend/src/setup/setupErrorMessages.ts
new file mode 100644
index 0000000..289b3cb
--- /dev/null
+++ b/superset-frontend/src/setup/setupErrorMessages.ts
@@ -0,0 +1,24 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import setupErrorMessagesExtra from './setupErrorMessagesExtra';
+
+export default function setupErrorMessages() {
+  // TODO: Register error messages to the ErrorMessageComponentRegistry once implemented
+  setupErrorMessagesExtra();
+}
diff --git a/superset-frontend/src/setup/setupErrorMessagesExtra.ts b/superset-frontend/src/setup/setupErrorMessagesExtra.ts
new file mode 100644
index 0000000..fe90e54
--- /dev/null
+++ b/superset-frontend/src/setup/setupErrorMessagesExtra.ts
@@ -0,0 +1,21 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// For individual deployments to add custom error messages
+export default function setupErrorMessagesExtra() {}