From 4f3ee39a94e301697edfd99e912d06c8acc87d99 Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Mon, 14 Sep 2020 15:13:35 -0700 Subject: [PATCH 01/32] Item 7767: basic setup of details section --- core/src/client/ErrorHandler/ErrorHandler.tsx | 10 ++++++-- .../src/client/ErrorHandler/errorHandler.scss | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/core/src/client/ErrorHandler/ErrorHandler.tsx b/core/src/client/ErrorHandler/ErrorHandler.tsx index 05bf3452e9b..357fbac2767 100644 --- a/core/src/client/ErrorHandler/ErrorHandler.tsx +++ b/core/src/client/ErrorHandler/ErrorHandler.tsx @@ -33,8 +33,9 @@ export class ErrorHandler extends React.PureComponent { + const { showDetails } = this.state; this.setState(() => ({ - showDetails: true, + showDetails: !showDetails, })); }; @@ -50,7 +51,12 @@ export class ErrorHandler extends React.PureComponent {/* TODO : ErrorPage, following section in next story*/} - {showDetails &&

{message}

} + {showDetails && +
+
+

{message}

+
+
} ); } diff --git a/core/src/client/ErrorHandler/errorHandler.scss b/core/src/client/ErrorHandler/errorHandler.scss index 3b9f22f9b93..c1634917d1b 100644 --- a/core/src/client/ErrorHandler/errorHandler.scss +++ b/core/src/client/ErrorHandler/errorHandler.scss @@ -32,4 +32,27 @@ .error-details-btn { +} + +.error-details-container { + position: relative; + background: #F3F3F4; + padding: 15px; + border: 1px solid transparent; + margin-top: 5px; +} + +.error-details-container:before, .error-details-container:after { + content: ''; + position: absolute; + bottom: 100%; + left: 199px; + border: 31px solid transparent; + border-bottom-color: #F3F3F4; +} + +.error-details-container:after { + left: 200px; + border: 30px solid transparent; + border-bottom-color: #F3F3F4; } \ No newline at end of file From 66565b6775e09c8a5e2d1be4dd97cf8a7a92b832 Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Tue, 15 Sep 2020 16:26:46 -0700 Subject: [PATCH 02/32] Per error type specific components / pages with details content --- .../org/labkey/api/util/ErrorRenderer.java | 15 +- .../org/labkey/api/util/ExceptionUtil.java | 7 +- .../labkey/api/util/WebPartErrorRenderer.java | 4 +- .../labkey/api/view/template/errorView.jsp | 20 ++- core/src/client/ErrorHandler/ErrorHandler.tsx | 64 +++++--- core/src/client/ErrorHandler/ErrorType.tsx | 145 ++++++++++++++++-- .../src/client/ErrorHandler/errorHandler.scss | 34 +++- core/src/client/ErrorHandler/model.ts | 19 +++ .../src/client/components/ErrorTopSection.tsx | 39 ----- 9 files changed, 264 insertions(+), 83 deletions(-) create mode 100644 core/src/client/ErrorHandler/model.ts delete mode 100644 core/src/client/components/ErrorTopSection.tsx diff --git a/api/src/org/labkey/api/util/ErrorRenderer.java b/api/src/org/labkey/api/util/ErrorRenderer.java index 171ac4c4344..de07ec23368 100644 --- a/api/src/org/labkey/api/util/ErrorRenderer.java +++ b/api/src/org/labkey/api/util/ErrorRenderer.java @@ -17,6 +17,7 @@ package org.labkey.api.util; import org.apache.commons.collections4.IteratorUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.Nullable; import org.labkey.api.data.CoreSchema; import org.labkey.api.data.DbSchema; @@ -40,6 +41,7 @@ public class ErrorRenderer private final boolean _isStartupFailure; private final ErrorRendererProperties _errorRendererProps; private final String _title; + private final String _errorCode; // TODO: ErrorPage use these in React app private boolean _includeHomeButton = true; @@ -49,6 +51,11 @@ public class ErrorRenderer private ErrorType _errorType; + public String getStackTrace() + { + return ExceptionUtils.getStackTrace(getException()); + } + enum ErrorType { notFound, @@ -97,12 +104,13 @@ public void setIncludeStopImpersonatingButton(boolean includeStopImpersonatingBu _includeStopImpersonatingButton = includeStopImpersonatingButton; } - ErrorRenderer(int status, String heading, Throwable x, boolean isStartupFailure) + ErrorRenderer(int status, String errorCode, String heading, Throwable x, boolean isStartupFailure) { _status = status; _exception = x; _isStartupFailure = isStartupFailure; _errorRendererProps = (x instanceof ErrorRendererProperties ? (ErrorRendererProperties)x : null); + _errorCode = errorCode; if (null == _errorRendererProps) { @@ -301,4 +309,9 @@ public void setErrorType(ErrorType errorType) { _errorType = errorType; } + + public String getErrorCode() + { + return _errorCode; + } } diff --git a/api/src/org/labkey/api/util/ExceptionUtil.java b/api/src/org/labkey/api/util/ExceptionUtil.java index 49b4ac5d612..7d5c43b1d0a 100644 --- a/api/src/org/labkey/api/util/ExceptionUtil.java +++ b/api/src/org/labkey/api/util/ExceptionUtil.java @@ -201,9 +201,10 @@ public static WebPartView getErrorWebPartView(int responseStatus, String message public static ErrorRenderer getErrorRenderer(int responseStatus, String message, Throwable ex, @Nullable HttpServletRequest request, boolean isPart, boolean isStartupFailure) { + String errorCode = null; if (!isStartupFailure && responseStatus == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) { - String errorCode = logExceptionToMothership(request, ex); + errorCode = logExceptionToMothership(request, ex); if (null != errorCode) { message = StringUtils.trimToEmpty(message); @@ -219,9 +220,9 @@ public static ErrorRenderer getErrorRenderer(int responseStatus, String message, } if (isPart) - return new WebPartErrorRenderer(responseStatus, message, ex, isStartupFailure); + return new WebPartErrorRenderer(responseStatus, errorCode, message, ex, isStartupFailure); else - return new ErrorRenderer(responseStatus, message, ex, isStartupFailure); + return new ErrorRenderer(responseStatus, errorCode, message, ex, isStartupFailure); } private static ExceptionReportingLevel getExceptionReportingLevel() diff --git a/api/src/org/labkey/api/util/WebPartErrorRenderer.java b/api/src/org/labkey/api/util/WebPartErrorRenderer.java index 3764ff71e58..6d6f35a69f6 100644 --- a/api/src/org/labkey/api/util/WebPartErrorRenderer.java +++ b/api/src/org/labkey/api/util/WebPartErrorRenderer.java @@ -22,9 +22,9 @@ class WebPartErrorRenderer extends ErrorRenderer { private String _id; - WebPartErrorRenderer(int status, String message, Throwable x, boolean isStartupFailure) + WebPartErrorRenderer(int status, String errorCode, String message, Throwable x, boolean isStartupFailure) { - super(status, message, x, isStartupFailure); + super(status, errorCode, message, x, isStartupFailure); } @Override diff --git a/api/src/org/labkey/api/view/template/errorView.jsp b/api/src/org/labkey/api/view/template/errorView.jsp index 77342f105f5..1accfe7cb3e 100644 --- a/api/src/org/labkey/api/view/template/errorView.jsp +++ b/api/src/org/labkey/api/view/template/errorView.jsp @@ -24,9 +24,25 @@
+<% + StringBuilder stackTrace = new StringBuilder(); + stackTrace.append(model.getException().getMessage()); + for (StackTraceElement stackTraceElement : model.getException().getStackTrace()) + { + stackTrace.append("\n"); + stackTrace.append(stackTraceElement.toString()); + } + + +%> + \ No newline at end of file diff --git a/core/src/client/ErrorHandler/ErrorHandler.tsx b/core/src/client/ErrorHandler/ErrorHandler.tsx index 357fbac2767..e31c06ceb70 100644 --- a/core/src/client/ErrorHandler/ErrorHandler.tsx +++ b/core/src/client/ErrorHandler/ErrorHandler.tsx @@ -1,14 +1,14 @@ -import React from 'react'; +import React, { ReactNode } from 'react'; +import { Button, Col, Row } from 'react-bootstrap'; import '@labkey/components/dist/components.css'; import './errorHandler.scss'; -import { ErrorTopSection } from '../components/ErrorTopSection'; -import { ErrorType } from './ErrorType'; +import { getErrorHeading, getImage, getInstruction, getSubHeading, getViewDetails } from './ErrorType'; +import { IErrorDetailsModel } from './model'; export interface AppContext { - message: string; - errorType: ErrorType; + errorDetails: IErrorDetailsModel; } interface ErrorHandlerProps { @@ -39,24 +39,52 @@ export class ErrorHandler extends React.PureComponent { + const { errorDetails } = this.props.context; + return ( + <> +
+ + +
+ {getErrorHeading()} + {getSubHeading(errorDetails)} + {getInstruction(errorDetails)} + + +
+ + {getImage(errorDetails)} +
+
+ + ); + }; + + renderErrorDetailsSection = (): ReactNode => { + const { errorDetails } = this.props.context; + return ( + <> +
{getViewDetails(errorDetails)}
+ + ); + }; + render() { - const { errorType, message } = this.props.context; const { showDetails } = this.state; return ( <> - - {/* TODO : ErrorPage, following section in next story*/} - {showDetails && -
-
-

{message}

-
-
} + {this.renderErrorTopSection()} + {showDetails && this.renderErrorDetailsSection()} ); } diff --git a/core/src/client/ErrorHandler/ErrorType.tsx b/core/src/client/ErrorHandler/ErrorType.tsx index 9e200e360c4..d7bc6ca9a85 100644 --- a/core/src/client/ErrorHandler/ErrorType.tsx +++ b/core/src/client/ErrorHandler/ErrorType.tsx @@ -1,8 +1,26 @@ import React, { ReactNode } from 'react'; + import { imageURL } from '@labkey/components'; +import { IErrorDetailsModel } from './model'; + const ERROR_HEADING = 'Oops! An error has occurred.'; +const DETAILS_SUB_INSTRUCTION = ( + <> +
What else can I do?
+
+ If you are subscribed to LabKey, then reach out to your account manager for immediate help. If you are using + the community edition, your support ticket will be resolved within the next 2 to 3 weeks. +
+
+
+ LabKey support documentation is another immediate resource that is available to all users. + A search through the documentation or previous forum questions may also help troubleshoot your issue. +
+ +); + const NOTFOUND_SUBHEADING = <>It seems like something went wrong. The requested page cannot be found.; const NOTFOUND_INSTRUCTION = ( <> @@ -16,23 +34,103 @@ const NOTFOUND_INSTRUCTION = ( ); +const NOTFOUND_DETAILS = ( + <> +
What went wrong?
+ +
+ Unfortunately, we are unable to specifically identify what went wrong. However, here are the most common + errors: +
+
+
+
+
+
  • + Incorrect URL: the wrong web address has been typed. +
  • +
    +
    Double check and make sure that your URL has been correctly input.
    + +
    +
    + +
    +
  • + Permissions: your account does not have the permissions to view this page. +
  • +
    +
    Contact your administrator to request for access.
    +
    +
    +
    + + {DETAILS_SUB_INSTRUCTION} + +); const PERMISSION_SUBHEADING = <>You do not have the permissions required to access this page.; const PERMISSION_INSTRUCTION = <>Please contact this server's admin to gain access.; +const PERMISSION_DETAILS = ( + <> +
    What is a permission error?
    + +
    + A permission error occurs when the account you've logged into does not have the set permissions to access + this page. + Read More +
    +
    + Try contacting your administrator to request access to this page. +
    +
    +
    +
    +
  • You are currently logged in as:
  • +
  • This server's admin:
  • +
    + +); const CONFIGURATION_SUBHEADING = <>It seems like something went wrong. The requested page cannot be found.; const CONFIGURATION_INSTRUCTION = <>Please check your server configurations.; +const CONFIGURATION_DETAILS = ( + <> +
    What went wrong?
    + +
    + Unfortunately, we are unable to specifically identify what went wrong. It seems that there might be some + issues with your server configuration. +
    +
    +
    +
    +
    +
  • + Server Configuration Errors (Tomcat Errors): issues related to your machine, software + version, or running dependencies.{' '} +
  • +
    +
    Try restarting your current instance of LabKey.
    +
    +
    +
    + + {DETAILS_SUB_INSTRUCTION} + +); const EXECUTION_SUB_HEADING = <>It seems like there is an issue with this installation of LabKey server.; -const EXECUTION_INSTRUCTION = ( +const EXECUTION_INSTRUCTION = (errorCode?: string) => ( <>
    Please report this bug to LabKey Support by copying and pasting both unique reference code and code under 'view details'.
    -
    Your unique reference code is:
    +
    Your unique reference code is: {errorCode}
    ); +const EXECUTION_DETAILS = (stackTrace?: string) =>
    {stackTrace}
    ; export enum ErrorType { notFound = 'general', @@ -46,21 +144,25 @@ const ERROR_TYPE_INFO = { heading: NOTFOUND_SUBHEADING, instruction: NOTFOUND_INSTRUCTION, imagePath: 'notFound_error.svg', + details: NOTFOUND_DETAILS, }, permission: { heading: PERMISSION_SUBHEADING, instruction: PERMISSION_INSTRUCTION, imagePath: 'permission_error.svg', + details: PERMISSION_DETAILS, }, configuration: { heading: CONFIGURATION_SUBHEADING, instruction: CONFIGURATION_INSTRUCTION, imagePath: 'configuration_error.svg', + details: CONFIGURATION_DETAILS, }, execution: { heading: EXECUTION_SUB_HEADING, - instruction: EXECUTION_INSTRUCTION, + instruction: (errorCode?: string) => EXECUTION_INSTRUCTION(errorCode), imagePath: 'code_error.svg', + details: (stackTrace?: string) => EXECUTION_DETAILS(stackTrace), }, }; @@ -68,23 +170,42 @@ export const getErrorHeading = (): ReactNode => { return
    {ERROR_HEADING}
    ; }; -export const getImage = (errorType: ErrorType): ReactNode => { - if (ERROR_TYPE_INFO[errorType]) { - const path = ERROR_TYPE_INFO[errorType].imagePath; +export const getImage = (errorDetails: IErrorDetailsModel): ReactNode => { + if (ERROR_TYPE_INFO[errorDetails.errorType]) { + const path = ERROR_TYPE_INFO[errorDetails.errorType].imagePath; return LabKey Error; } }; -export const getSubHeading = (errorType: ErrorType): ReactNode => { - if (ERROR_TYPE_INFO[errorType]) { - const subHeading = ERROR_TYPE_INFO[errorType].heading; +export const getSubHeading = (errorDetails: IErrorDetailsModel): ReactNode => { + if (ERROR_TYPE_INFO[errorDetails.errorType]) { + const subHeading = ERROR_TYPE_INFO[errorDetails.errorType].heading; return
    {subHeading}
    ; } }; -export const getInstruction = (errorType: ErrorType): ReactNode => { - if (ERROR_TYPE_INFO[errorType]) { - const instruction = ERROR_TYPE_INFO[errorType].instruction; +export const getInstruction = (errorDetails: IErrorDetailsModel): ReactNode => { + if (ERROR_TYPE_INFO[errorDetails.errorType]) { + const errorType = errorDetails.errorType; + let instruction; + if (errorDetails.errorCode) { + instruction = ERROR_TYPE_INFO[errorType].instruction(errorDetails.errorCode); + } else { + instruction = ERROR_TYPE_INFO[errorType].instruction; + } return
    {instruction}
    ; } }; + +export const getViewDetails = (errorDetails: IErrorDetailsModel): ReactNode => { + if (ERROR_TYPE_INFO[errorDetails.errorType]) { + const errorType = errorDetails.errorType; + let details; + if (errorDetails.stackTrace && errorType == ErrorType.execution) { + details = ERROR_TYPE_INFO[errorType].details(errorDetails.stackTrace); + } else { + details = ERROR_TYPE_INFO[errorType].details; + } + return
    {details}
    ; + } +}; diff --git a/core/src/client/ErrorHandler/errorHandler.scss b/core/src/client/ErrorHandler/errorHandler.scss index c1634917d1b..ae324d13171 100644 --- a/core/src/client/ErrorHandler/errorHandler.scss +++ b/core/src/client/ErrorHandler/errorHandler.scss @@ -21,6 +21,27 @@ width: 560px; } +.labkey-error-details { + font-size: 18px; + letter-spacing: 0; + line-height: 21px; + padding-bottom: 2px; +} + +.labkey-error-subdetails { + padding-left: 5rem; +} + +.labkey-error-details-question { + font-weight: bold; +} + +.labkey-error-stacktrace { + font-size: 14px; + padding-bottom: 15px; + width: 560px; +} + .error-backButton { margin: 0.5rem 5rem 0.5rem 0.5rem; background-color: #538AC5; @@ -37,22 +58,23 @@ .error-details-container { position: relative; background: #F3F3F4; - padding: 15px; border: 1px solid transparent; - margin-top: 5px; + margin-top: 5rem; + padding-top: 5rem; + padding-bottom: 5rem; } .error-details-container:before, .error-details-container:after { content: ''; position: absolute; bottom: 100%; - left: 199px; - border: 31px solid transparent; + left: 17rem; + border: 4rem solid transparent; border-bottom-color: #F3F3F4; } .error-details-container:after { - left: 200px; - border: 30px solid transparent; + left: 18rem; + border: 3rem solid transparent; border-bottom-color: #F3F3F4; } \ No newline at end of file diff --git a/core/src/client/ErrorHandler/model.ts b/core/src/client/ErrorHandler/model.ts new file mode 100644 index 00000000000..5b8631465fd --- /dev/null +++ b/core/src/client/ErrorHandler/model.ts @@ -0,0 +1,19 @@ +import { ErrorType } from './ErrorType'; + +export interface IErrorDetailsModel { + message?: string; + errorCode?: string; + stackTrace?: string; + errorType: ErrorType; +} + +export class ErrorDetailsModel implements IErrorDetailsModel { + message?: string; + errorCode?: string; + stackTrace?: string; + errorType: ErrorType; + + constructor(errorDetailsModel: IErrorDetailsModel) { + Object.assign(this, errorDetailsModel); + } +} diff --git a/core/src/client/components/ErrorTopSection.tsx b/core/src/client/components/ErrorTopSection.tsx deleted file mode 100644 index 60badd18141..00000000000 --- a/core/src/client/components/ErrorTopSection.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React, { ReactNode } from 'react'; -import { Button, Col, Row } from 'react-bootstrap'; - -import '../ErrorHandler/errorHandler.scss'; -import { ErrorType, getErrorHeading, getImage, getInstruction, getSubHeading } from '../ErrorHandler/ErrorType'; - -interface ErrorTopSectionProps { - errorType: ErrorType; - onBackClick: () => void; - onViewDetailsClick: () => void; -} - -export class ErrorTopSection extends React.PureComponent { - render(): ReactNode { - const { errorType, onBackClick, onViewDetailsClick } = this.props; - return ( - <> -
    - - -
    - {getErrorHeading()} - {getSubHeading(errorType)} - {getInstruction(errorType)} - - -
    - - {getImage(errorType)} -
    -
    - - ); - } -} From 67debf92184b36848b0f5ac05e205b5bc8d61a7f Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Thu, 17 Sep 2020 09:13:31 -0700 Subject: [PATCH 03/32] help docs links and back button functionality --- core/src/client/ErrorHandler/ErrorHandler.tsx | 16 +++++- core/src/client/ErrorHandler/ErrorType.tsx | 52 ++++++++++++------- .../src/client/ErrorHandler/errorHandler.scss | 1 + 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/core/src/client/ErrorHandler/ErrorHandler.tsx b/core/src/client/ErrorHandler/ErrorHandler.tsx index e31c06ceb70..6731d8f082e 100644 --- a/core/src/client/ErrorHandler/ErrorHandler.tsx +++ b/core/src/client/ErrorHandler/ErrorHandler.tsx @@ -4,6 +4,8 @@ import { Button, Col, Row } from 'react-bootstrap'; import '@labkey/components/dist/components.css'; import './errorHandler.scss'; +import { ActionURL, LabKey } from '@labkey/api'; + import { getErrorHeading, getImage, getInstruction, getSubHeading, getViewDetails } from './ErrorType'; import { IErrorDetailsModel } from './model'; @@ -29,7 +31,19 @@ export class ErrorHandler extends React.PureComponent { - window.history.back(); + // Back button - takes you back to the previous page if available + // and to the ‘home’ folder if not possible to go back to the previous page + const currentLocation = window.location.href; + if (window.history.length !== 1) { + // browsers like chrome stores their homepage as first item + window.history.back(); + } + + const newLocation = window.location.href; + + if (currentLocation === newLocation) { + window.location.href = ActionURL.getBaseURL(false); + } }; onViewDetailsClick = (): void => { diff --git a/core/src/client/ErrorHandler/ErrorType.tsx b/core/src/client/ErrorHandler/ErrorType.tsx index d7bc6ca9a85..296d3ffc527 100644 --- a/core/src/client/ErrorHandler/ErrorType.tsx +++ b/core/src/client/ErrorHandler/ErrorType.tsx @@ -1,6 +1,9 @@ import React, { ReactNode } from 'react'; -import { imageURL } from '@labkey/components'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCheckCircle } from '@fortawesome/free-solid-svg-icons'; + +import { helpLinkNode, imageURL } from '@labkey/components'; import { IErrorDetailsModel } from './model'; @@ -15,8 +18,9 @@ const DETAILS_SUB_INSTRUCTION = (
    - LabKey support documentation is another immediate resource that is available to all users. - A search through the documentation or previous forum questions may also help troubleshoot your issue. + {helpLinkNode('default', 'LabKey support documentation')} is another immediate resource that is available to + all users. A search through the documentation or previous forum questions may also help troubleshoot your + issue.
    ); @@ -25,12 +29,22 @@ const NOTFOUND_SUBHEADING = <>It seems like something went wrong. The requested const NOTFOUND_INSTRUCTION = ( <>
    - {/* TODO: ErrorPage - add href link*/} - Please contact your admin or reference the LabKey support forum. + Please contact your admin or reference the{' '} + + LabKey support forum. +
    - {/* TODO: ErrorPage - add href link*/} - If you would like to file a LabKey support ticket, your unique reference code is: + If you would like to file a{' '} + + {' '} + LabKey support ticket + + , your unique reference code is:
    ); @@ -47,20 +61,21 @@ const NOTFOUND_DETAILS = (
  • - Incorrect URL: the wrong web address has been typed. + Incorrect URL: the wrong web address has been typed. {helpLinkNode('url', 'Read More >')}
  • -
    Double check and make sure that your URL has been correctly input.
    - + Double check and make + sure that your URL has been correctly input.

    -
  • - Permissions: your account does not have the permissions to view this page. + Permissions: your account does not have the permissions to view this page.{' '} + {helpLinkNode('permissionLevels', 'Read More >')}
  • -
    Contact your administrator to request for access.
    + Contact your + administrator to request for access.


    @@ -77,11 +92,11 @@ const PERMISSION_DETAILS = (
    A permission error occurs when the account you've logged into does not have the set permissions to access - this page. - Read More + this page. {helpLinkNode('permissionLevels', 'Read More >')}
    - Try contacting your administrator to request access to this page. + Try contacting your + administrator to request access to this page.


    @@ -108,10 +123,11 @@ const CONFIGURATION_DETAILS = (
  • Server Configuration Errors (Tomcat Errors): issues related to your machine, software - version, or running dependencies.{' '} + version, or running dependencies. {helpLinkNode('troubleshootingAdmin', 'Read More >')}
  • -
    Try restarting your current instance of LabKey.
    + Try restarting your + current instance of LabKey.

    diff --git a/core/src/client/ErrorHandler/errorHandler.scss b/core/src/client/ErrorHandler/errorHandler.scss index ae324d13171..12fbe31dd43 100644 --- a/core/src/client/ErrorHandler/errorHandler.scss +++ b/core/src/client/ErrorHandler/errorHandler.scss @@ -26,6 +26,7 @@ letter-spacing: 0; line-height: 21px; padding-bottom: 2px; + padding-left: 2rem; } .labkey-error-subdetails { From 6956771187a5a1e9b79a4e73dda8117577f532c8 Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Sun, 20 Sep 2020 07:22:11 -0700 Subject: [PATCH 04/32] Permissions page handling of impersonated user --- .../labkey/api/view/template/errorView.jsp | 30 +++++++++++-------- core/package-lock.json | 18 +++++------ core/package.json | 2 +- core/src/client/ErrorHandler/ErrorType.tsx | 21 ++++++++++--- .../src/client/ErrorHandler/errorHandler.scss | 4 +++ 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/api/src/org/labkey/api/view/template/errorView.jsp b/api/src/org/labkey/api/view/template/errorView.jsp index 1accfe7cb3e..fcecc3db99e 100644 --- a/api/src/org/labkey/api/view/template/errorView.jsp +++ b/api/src/org/labkey/api/view/template/errorView.jsp @@ -3,6 +3,7 @@ <%@ page import="org.labkey.api.util.ErrorTemplate" %> <%@ page import="org.labkey.api.util.ErrorRenderer" %> <%@ page import="org.labkey.api.util.UniqueID" %> +<%@ page import="org.labkey.api.util.PageFlowUtil" %> <%@ taglib prefix="labkey" uri="http://www.labkey.org/taglib" %> <%@ page extends="org.labkey.api.jsp.JspBase" %> @@ -10,9 +11,8 @@ @Override public void addClientDependencies(ClientDependencies dependencies) { - dependencies.add("clientapi"); // added this for App Template -// dependencies.add("http://localhost:3001/errorHandler.js"); - dependencies.add("core/gen/errorHandler"); + dependencies.add("core/css/core.css"); + dependencies.add("clientapi"); } %> <% @@ -32,17 +32,23 @@ stackTrace.append("\n"); stackTrace.append(stackTraceElement.toString()); } - - %> \ No newline at end of file diff --git a/core/package-lock.json b/core/package-lock.json index 41b55dc456e..658c2ecb60d 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1887,9 +1887,9 @@ } }, "@labkey/api": { - "version": "1.0.2", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.0.2.tgz", - "integrity": "sha1-Yc8EtcFZ9cyvtNwXKDHOr5VgY8g=" + "version": "1.1.1", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.1.tgz", + "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { "version": "0.91.1", @@ -16134,9 +16134,9 @@ "dev": true }, "ua-parser-js": { - "version": "0.7.21", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", - "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" + "version": "0.7.22", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.22.tgz", + "integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==" }, "uglify-js": { "version": "3.8.0", @@ -17024,9 +17024,9 @@ } }, "whatwg-fetch": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.0.tgz", - "integrity": "sha512-rsum2ulz2iuZH08mJkT0Yi6JnKhwdw4oeyMjokgxd+mmqYSd9cPpOQf01TIWgjxG/U4+QR+AwKq6lSbXVxkyoQ==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz", + "integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==" }, "whatwg-mimetype": { "version": "2.3.0", diff --git a/core/package.json b/core/package.json index 3079a25d74e..03c367cb91f 100644 --- a/core/package.json +++ b/core/package.json @@ -99,7 +99,7 @@ } }, "dependencies": { - "@labkey/api": "1.0.2", + "@labkey/api": "1.1.1", "@labkey/components": "0.91.1", "@labkey/eslint-config-react": "0.0.8", "react-toggle-button": "2.2.0" diff --git a/core/src/client/ErrorHandler/ErrorType.tsx b/core/src/client/ErrorHandler/ErrorType.tsx index 296d3ffc527..a6036a54baf 100644 --- a/core/src/client/ErrorHandler/ErrorType.tsx +++ b/core/src/client/ErrorHandler/ErrorType.tsx @@ -1,10 +1,12 @@ import React, { ReactNode } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCheckCircle } from '@fortawesome/free-solid-svg-icons'; +import { faCheckCircle, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; import { helpLinkNode, imageURL } from '@labkey/components'; +import { getServerContext } from '@labkey/api'; + import { IErrorDetailsModel } from './model'; const ERROR_HEADING = 'Oops! An error has occurred.'; @@ -95,15 +97,26 @@ const PERMISSION_DETAILS = ( this page. {helpLinkNode('permissionLevels', 'Read More >')}
    - Try contacting your + Try contacting your server administrator to request access to this page.


    -
  • You are currently logged in as:
  • -
  • This server's admin:
  • +
  • + {' '} + {getServerContext().user.isSignedIn + ? 'You are currently logged in as: ' + getServerContext().user.displayName + : 'You are not logged in.'}{' '} +
  • +
    + {getServerContext().impersonatingUser !== undefined && ( +
    + You are currently + impersonating as: {getServerContext().impersonatingUser.displayName} +
    + )} ); diff --git a/core/src/client/ErrorHandler/errorHandler.scss b/core/src/client/ErrorHandler/errorHandler.scss index 12fbe31dd43..496889ab98a 100644 --- a/core/src/client/ErrorHandler/errorHandler.scss +++ b/core/src/client/ErrorHandler/errorHandler.scss @@ -78,4 +78,8 @@ left: 18rem; border: 3rem solid transparent; border-bottom-color: #F3F3F4; +} + +.permission-warning-icon { + color: #B82025; } \ No newline at end of file From e8edecf61b70508ac4558b42e630b81dc29673dc Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Mon, 21 Sep 2020 09:55:34 -0700 Subject: [PATCH 05/32] jest tests --- .../labkey/api/view/template/errorView.jsp | 3 +- .../client/ErrorHandler/ErrorHandler.spec.tsx | 83 +++++++++++++++++++ core/src/client/ErrorHandler/ErrorType.tsx | 18 ++-- .../view/template/bootstrap/pageTemplate.jsp | 10 +-- 4 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 core/src/client/ErrorHandler/ErrorHandler.spec.tsx diff --git a/api/src/org/labkey/api/view/template/errorView.jsp b/api/src/org/labkey/api/view/template/errorView.jsp index fcecc3db99e..4718bbb8d51 100644 --- a/api/src/org/labkey/api/view/template/errorView.jsp +++ b/api/src/org/labkey/api/view/template/errorView.jsp @@ -38,7 +38,8 @@ // pulling in theme based css for the not found pages to still respect the site's theme LABKEY.requiresCss("<%=PageFlowUtil.filter("/core/css/" + PageFlowUtil.resolveThemeName(getContainer()) + ".css")%>"); - LABKEY.requiresScript('http://localhost:3001/errorHandler.js', function() { + LABKEY.requiresScript('core/gen/errorHandler.js', function() { + // LABKEY.requiresScript('http://localhost:3001/errorHandler.js', function() { LABKEY.App.__app__.isDOMContentLoaded = true; diff --git a/core/src/client/ErrorHandler/ErrorHandler.spec.tsx b/core/src/client/ErrorHandler/ErrorHandler.spec.tsx new file mode 100644 index 00000000000..e72fbd61d21 --- /dev/null +++ b/core/src/client/ErrorHandler/ErrorHandler.spec.tsx @@ -0,0 +1,83 @@ +import React from 'react'; + +import { mount, shallow } from 'enzyme'; + +import { ErrorHandler } from './ErrorHandler'; +import { ErrorDetailsModel, IErrorDetailsModel } from './model'; +import { ErrorType } from './ErrorType'; + +describe('ErrorHandler', () => { + test('Not found exception', () => { + const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + errorType: ErrorType.notFound, + errorCode: '123XYZ', + stackTrace: undefined, + message: 'This is a not found exception', + }); + const errorContext = { errorDetails: errDetails }; + const wrapper = mount(); + expect(wrapper.find('.error-details-container')).toHaveLength(0); + + wrapper.setState({ showDetails: true }); + const question = wrapper.find('.error-details-container'); + expect(question.text().startsWith('What went wrong?')).toBeTruthy(); + + wrapper.unmount(); + }); + + test('Configuration exception', () => { + const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + errorType: ErrorType.configuration, + errorCode: undefined, + stackTrace: undefined, + message: 'This is a configuration exception', + }); + const errorContext = { errorDetails: errDetails }; + const wrapper = mount(); + expect(wrapper.find('.error-details-container')).toHaveLength(0); + + wrapper.setState({ showDetails: true }); + const question = wrapper.find('.error-details-container'); + expect(question.text().startsWith('What went wrong?')).toBeTruthy(); + + wrapper.unmount(); + }); + + test('Permission exception', () => { + const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + errorType: ErrorType.permission, + errorCode: undefined, + stackTrace: undefined, + message: 'This is a permission exception', + }); + const errorContext = { errorDetails: errDetails }; + const wrapper = shallow(); + expect(wrapper.find('.error-details-container')).toHaveLength(0); + + wrapper.setState({ showDetails: true }); + const question = wrapper.find('.error-details-container'); + expect(question.text().startsWith('What is a permission error?')).toBeTruthy(); + + wrapper.unmount(); + }); + + test('Execution exception', () => { + const sampleTrace = 'java.lang.NullPointerException: null'; + + const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + errorType: ErrorType.execution, + errorCode: '456AAA', + stackTrace: sampleTrace, + message: 'This is a execution exception', + }); + const errorContext = { errorDetails: errDetails }; + const wrapper = shallow(); + expect(wrapper.find('.error-details-container')).toHaveLength(0); + + wrapper.setState({ showDetails: true }); + const question = wrapper.find('.error-details-container'); + expect(question.text().startsWith('java.lang.NullPointerException: null')).toBeTruthy(); + + wrapper.unmount(); + }); +}); diff --git a/core/src/client/ErrorHandler/ErrorType.tsx b/core/src/client/ErrorHandler/ErrorType.tsx index a6036a54baf..71d0402cf59 100644 --- a/core/src/client/ErrorHandler/ErrorType.tsx +++ b/core/src/client/ErrorHandler/ErrorType.tsx @@ -28,7 +28,7 @@ const DETAILS_SUB_INSTRUCTION = ( ); const NOTFOUND_SUBHEADING = <>It seems like something went wrong. The requested page cannot be found.; -const NOTFOUND_INSTRUCTION = ( +const NOTFOUND_INSTRUCTION = (errorCode?: string) => ( <>
    Please contact your admin or reference the{' '} @@ -46,7 +46,7 @@ const NOTFOUND_INSTRUCTION = ( {' '} LabKey support ticket - , your unique reference code is: + , your unique reference code is: {errorCode}
    ); @@ -97,8 +97,8 @@ const PERMISSION_DETAILS = ( this page. {helpLinkNode('permissionLevels', 'Read More >')}
    - Try contacting your server - administrator to request access to this page. + Try contacting your + server administrator to request access to this page.


    @@ -162,7 +162,7 @@ const EXECUTION_INSTRUCTION = (errorCode?: string) => ( const EXECUTION_DETAILS = (stackTrace?: string) =>
    {stackTrace}
    ; export enum ErrorType { - notFound = 'general', + notFound = 'notFound', permission = 'permission', configuration = 'configuration', execution = 'execution', @@ -171,19 +171,19 @@ export enum ErrorType { const ERROR_TYPE_INFO = { notFound: { heading: NOTFOUND_SUBHEADING, - instruction: NOTFOUND_INSTRUCTION, + instruction: (errorCode?: string) => NOTFOUND_INSTRUCTION(errorCode), imagePath: 'notFound_error.svg', details: NOTFOUND_DETAILS, }, permission: { heading: PERMISSION_SUBHEADING, - instruction: PERMISSION_INSTRUCTION, + instruction: () => PERMISSION_INSTRUCTION, imagePath: 'permission_error.svg', details: PERMISSION_DETAILS, }, configuration: { heading: CONFIGURATION_SUBHEADING, - instruction: CONFIGURATION_INSTRUCTION, + instruction: () => CONFIGURATION_INSTRUCTION, imagePath: 'configuration_error.svg', details: CONFIGURATION_DETAILS, }, @@ -220,7 +220,7 @@ export const getInstruction = (errorDetails: IErrorDetailsModel): ReactNode => { if (errorDetails.errorCode) { instruction = ERROR_TYPE_INFO[errorType].instruction(errorDetails.errorCode); } else { - instruction = ERROR_TYPE_INFO[errorType].instruction; + instruction = ERROR_TYPE_INFO[errorType].instruction(); } return
    {instruction}
    ; } diff --git a/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp b/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp index 9962b532d58..815bf46264d 100644 --- a/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp +++ b/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp @@ -108,18 +108,14 @@ %> -<%-- TODO : ErrorPage, look into this--%> <% - try + // container is null for notfound pages + if (null != me.getViewContext().getContainer()) { %> - + <% } - catch (Exception e) - { - LogManager.getLogger().info("This will be resolved in next error page story", e); - } %> \ No newline at end of file From 18949d1669b4f366c81a4632ab3f7c89c8488d8a Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Mon, 21 Sep 2020 11:28:39 -0700 Subject: [PATCH 06/32] upgrade ui-components alpha version --- assay/package-lock.json | 14 +++++++------- assay/package.json | 2 +- core/package-lock.json | 15 ++++----------- core/package.json | 2 +- core/src/client/ErrorHandler/ErrorHandler.tsx | 6 ++++-- experiment/package-lock.json | 14 +++++++------- experiment/package.json | 2 +- issues/package-lock.json | 14 +++++++------- issues/package.json | 2 +- list/package-lock.json | 14 +++++++------- list/package.json | 2 +- query/package-lock.json | 14 +++++++------- query/package.json | 2 +- study/package-lock.json | 14 +++++++------- study/package.json | 2 +- 15 files changed, 57 insertions(+), 62 deletions(-) diff --git a/assay/package-lock.json b/assay/package-lock.json index 514afbdced8..8eea9eaa549 100644 --- a/assay/package-lock.json +++ b/assay/package-lock.json @@ -243,21 +243,21 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" }, "@labkey/api": { - "version": "1.1.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.0.tgz", - "integrity": "sha1-tw++dJ18OzCud/XY6eEqLN5c7b4=" + "version": "1.1.1", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.1.tgz", + "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { - "version": "0.93.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.93.0.tgz", - "integrity": "sha1-lFhz59UkK8g1Y7oK+P53DL7IOOk=", + "version": "0.94.1-fb-ErrorPage-7767.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.94.1-fb-ErrorPage-7767.0.tgz", + "integrity": "sha1-UX4Yj3lZ52YuLrXBbCFuIwIefCo=", "requires": { "@fortawesome/fontawesome-free": "5.14.0", "@fortawesome/fontawesome-svg-core": "1.2.30", "@fortawesome/free-regular-svg-icons": "5.14.0", "@fortawesome/free-solid-svg-icons": "5.14.0", "@fortawesome/react-fontawesome": "0.1.11", - "@labkey/api": "1.1.0", + "@labkey/api": "1.1.1", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", diff --git a/assay/package.json b/assay/package.json index 108d9bf62d4..e9e8c9f682b 100644 --- a/assay/package.json +++ b/assay/package.json @@ -37,7 +37,7 @@ } }, "dependencies": { - "@labkey/components": "0.93.0" + "@labkey/components": "0.94.1-fb-ErrorPage-7767.0" }, "devDependencies": { "@hot-loader/react-dom": "16.13.0", diff --git a/core/package-lock.json b/core/package-lock.json index c52b49af553..798a7d4f11f 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1892,16 +1892,16 @@ "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { - "version": "0.93.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.93.0.tgz", - "integrity": "sha1-lFhz59UkK8g1Y7oK+P53DL7IOOk=", + "version": "0.94.1-fb-ErrorPage-7767.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.94.1-fb-ErrorPage-7767.0.tgz", + "integrity": "sha1-UX4Yj3lZ52YuLrXBbCFuIwIefCo=", "requires": { "@fortawesome/fontawesome-free": "5.14.0", "@fortawesome/fontawesome-svg-core": "1.2.30", "@fortawesome/free-regular-svg-icons": "5.14.0", "@fortawesome/free-solid-svg-icons": "5.14.0", "@fortawesome/react-fontawesome": "0.1.11", - "@labkey/api": "1.1.0", + "@labkey/api": "1.1.1", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", @@ -1931,13 +1931,6 @@ "reactn": "2.2.7", "redux-actions": "2.3.2", "vis-network": "6.5.2" - }, - "dependencies": { - "@labkey/api": { - "version": "1.1.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.0.tgz", - "integrity": "sha1-tw++dJ18OzCud/XY6eEqLN5c7b4=" - } } }, "@labkey/eslint-config-base": { diff --git a/core/package.json b/core/package.json index 6fb665e96be..8a1bfb3e910 100644 --- a/core/package.json +++ b/core/package.json @@ -100,7 +100,7 @@ }, "dependencies": { "@labkey/api": "1.1.1", - "@labkey/components": "0.93.0", + "@labkey/components": "0.94.1-fb-ErrorPage-7767.0", "@labkey/eslint-config-react": "0.0.8", "react-toggle-button": "2.2.0" }, diff --git a/core/src/client/ErrorHandler/ErrorHandler.tsx b/core/src/client/ErrorHandler/ErrorHandler.tsx index ae3d5ac497a..dfb4b63d4ff 100644 --- a/core/src/client/ErrorHandler/ErrorHandler.tsx +++ b/core/src/client/ErrorHandler/ErrorHandler.tsx @@ -1,10 +1,12 @@ import React, { ReactNode } from 'react'; import { Button, Col, Row } from 'react-bootstrap'; -import { ErrorTopSection } from '../components/ErrorTopSection'; -import { ErrorType } from './ErrorType'; +import { getErrorHeading, getImage, getInstruction, getSubHeading, getViewDetails } from './ErrorType'; import './errorHandler.scss'; +import { IErrorDetailsModel } from './model'; + +import { ActionURL } from '@labkey/api'; export interface AppContext { errorDetails: IErrorDetailsModel; diff --git a/experiment/package-lock.json b/experiment/package-lock.json index c96a446b9b6..92439c501fc 100644 --- a/experiment/package-lock.json +++ b/experiment/package-lock.json @@ -243,21 +243,21 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" }, "@labkey/api": { - "version": "1.1.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.0.tgz", - "integrity": "sha1-tw++dJ18OzCud/XY6eEqLN5c7b4=" + "version": "1.1.1", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.1.tgz", + "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { - "version": "0.93.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.93.0.tgz", - "integrity": "sha1-lFhz59UkK8g1Y7oK+P53DL7IOOk=", + "version": "0.94.1-fb-ErrorPage-7767.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.94.1-fb-ErrorPage-7767.0.tgz", + "integrity": "sha1-UX4Yj3lZ52YuLrXBbCFuIwIefCo=", "requires": { "@fortawesome/fontawesome-free": "5.14.0", "@fortawesome/fontawesome-svg-core": "1.2.30", "@fortawesome/free-regular-svg-icons": "5.14.0", "@fortawesome/free-solid-svg-icons": "5.14.0", "@fortawesome/react-fontawesome": "0.1.11", - "@labkey/api": "1.1.0", + "@labkey/api": "1.1.1", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", diff --git a/experiment/package.json b/experiment/package.json index 8eecd0879a5..ee81de9d350 100644 --- a/experiment/package.json +++ b/experiment/package.json @@ -37,7 +37,7 @@ } }, "dependencies": { - "@labkey/components": "0.93.0" + "@labkey/components": "0.94.1-fb-ErrorPage-7767.0" }, "devDependencies": { "@hot-loader/react-dom": "16.13.0", diff --git a/issues/package-lock.json b/issues/package-lock.json index c3762da92b1..811a3f5a655 100644 --- a/issues/package-lock.json +++ b/issues/package-lock.json @@ -243,21 +243,21 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" }, "@labkey/api": { - "version": "1.1.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.0.tgz", - "integrity": "sha1-tw++dJ18OzCud/XY6eEqLN5c7b4=" + "version": "1.1.1", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.1.tgz", + "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { - "version": "0.93.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.93.0.tgz", - "integrity": "sha1-lFhz59UkK8g1Y7oK+P53DL7IOOk=", + "version": "0.94.1-fb-ErrorPage-7767.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.94.1-fb-ErrorPage-7767.0.tgz", + "integrity": "sha1-UX4Yj3lZ52YuLrXBbCFuIwIefCo=", "requires": { "@fortawesome/fontawesome-free": "5.14.0", "@fortawesome/fontawesome-svg-core": "1.2.30", "@fortawesome/free-regular-svg-icons": "5.14.0", "@fortawesome/free-solid-svg-icons": "5.14.0", "@fortawesome/react-fontawesome": "0.1.11", - "@labkey/api": "1.1.0", + "@labkey/api": "1.1.1", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", diff --git a/issues/package.json b/issues/package.json index 4bd57ebe842..fade20d8d3f 100644 --- a/issues/package.json +++ b/issues/package.json @@ -37,7 +37,7 @@ } }, "dependencies": { - "@labkey/components": "0.93.0" + "@labkey/components": "0.94.1-fb-ErrorPage-7767.0" }, "devDependencies": { "@hot-loader/react-dom": "16.13.0", diff --git a/list/package-lock.json b/list/package-lock.json index 9ef979990da..9723bc596fc 100644 --- a/list/package-lock.json +++ b/list/package-lock.json @@ -243,21 +243,21 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" }, "@labkey/api": { - "version": "1.1.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.0.tgz", - "integrity": "sha1-tw++dJ18OzCud/XY6eEqLN5c7b4=" + "version": "1.1.1", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.1.tgz", + "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { - "version": "0.93.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.93.0.tgz", - "integrity": "sha1-lFhz59UkK8g1Y7oK+P53DL7IOOk=", + "version": "0.94.1-fb-ErrorPage-7767.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.94.1-fb-ErrorPage-7767.0.tgz", + "integrity": "sha1-UX4Yj3lZ52YuLrXBbCFuIwIefCo=", "requires": { "@fortawesome/fontawesome-free": "5.14.0", "@fortawesome/fontawesome-svg-core": "1.2.30", "@fortawesome/free-regular-svg-icons": "5.14.0", "@fortawesome/free-solid-svg-icons": "5.14.0", "@fortawesome/react-fontawesome": "0.1.11", - "@labkey/api": "1.1.0", + "@labkey/api": "1.1.1", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", diff --git a/list/package.json b/list/package.json index 20da609deb3..9dc68dcba01 100644 --- a/list/package.json +++ b/list/package.json @@ -37,7 +37,7 @@ } }, "dependencies": { - "@labkey/components": "0.93.0" + "@labkey/components": "0.94.1-fb-ErrorPage-7767.0" }, "devDependencies": { "@hot-loader/react-dom": "16.13.0", diff --git a/query/package-lock.json b/query/package-lock.json index d3f53551e5a..f52cc96dcc5 100644 --- a/query/package-lock.json +++ b/query/package-lock.json @@ -243,21 +243,21 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" }, "@labkey/api": { - "version": "1.1.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.0.tgz", - "integrity": "sha1-tw++dJ18OzCud/XY6eEqLN5c7b4=" + "version": "1.1.1", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.1.tgz", + "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { - "version": "0.93.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.93.0.tgz", - "integrity": "sha1-lFhz59UkK8g1Y7oK+P53DL7IOOk=", + "version": "0.94.1-fb-ErrorPage-7767.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.94.1-fb-ErrorPage-7767.0.tgz", + "integrity": "sha1-UX4Yj3lZ52YuLrXBbCFuIwIefCo=", "requires": { "@fortawesome/fontawesome-free": "5.14.0", "@fortawesome/fontawesome-svg-core": "1.2.30", "@fortawesome/free-regular-svg-icons": "5.14.0", "@fortawesome/free-solid-svg-icons": "5.14.0", "@fortawesome/react-fontawesome": "0.1.11", - "@labkey/api": "1.1.0", + "@labkey/api": "1.1.1", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", diff --git a/query/package.json b/query/package.json index 9243a4cab6e..3cda28e65f1 100644 --- a/query/package.json +++ b/query/package.json @@ -37,7 +37,7 @@ } }, "dependencies": { - "@labkey/components": "0.93.0" + "@labkey/components": "0.94.1-fb-ErrorPage-7767.0" }, "devDependencies": { "@hot-loader/react-dom": "16.13.0", diff --git a/study/package-lock.json b/study/package-lock.json index 7875e638716..0e392598821 100644 --- a/study/package-lock.json +++ b/study/package-lock.json @@ -243,21 +243,21 @@ "integrity": "sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==" }, "@labkey/api": { - "version": "1.1.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.0.tgz", - "integrity": "sha1-tw++dJ18OzCud/XY6eEqLN5c7b4=" + "version": "1.1.1", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/api/-/@labkey/api-1.1.1.tgz", + "integrity": "sha1-q4diKlMLLdE6QjjCAhCKfz9qhNY=" }, "@labkey/components": { - "version": "0.93.0", - "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.93.0.tgz", - "integrity": "sha1-lFhz59UkK8g1Y7oK+P53DL7IOOk=", + "version": "0.94.1-fb-ErrorPage-7767.0", + "resolved": "https://artifactory.labkey.com/artifactory/api/npm/libs-client/@labkey/components/-/@labkey/components-0.94.1-fb-ErrorPage-7767.0.tgz", + "integrity": "sha1-UX4Yj3lZ52YuLrXBbCFuIwIefCo=", "requires": { "@fortawesome/fontawesome-free": "5.14.0", "@fortawesome/fontawesome-svg-core": "1.2.30", "@fortawesome/free-regular-svg-icons": "5.14.0", "@fortawesome/free-solid-svg-icons": "5.14.0", "@fortawesome/react-fontawesome": "0.1.11", - "@labkey/api": "1.1.0", + "@labkey/api": "1.1.1", "bootstrap": "3.4.1", "classnames": "2.2.6", "font-awesome": "4.7.0", diff --git a/study/package.json b/study/package.json index d38a6bd4b15..91bc49ad6fc 100644 --- a/study/package.json +++ b/study/package.json @@ -37,7 +37,7 @@ } }, "dependencies": { - "@labkey/components": "0.93.0" + "@labkey/components": "0.94.1-fb-ErrorPage-7767.0" }, "devDependencies": { "@hot-loader/react-dom": "16.13.0", From 9f2adbcf05397a68a9e0e04a460707b08642f191 Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Tue, 22 Sep 2020 15:02:46 -0700 Subject: [PATCH 07/32] stop impersonating button, address todos, remove old code --- .../org/labkey/api/util/ErrorRenderer.java | 48 +--- .../org/labkey/api/util/ErrorTemplate.java | 12 - api/src/org/labkey/api/util/ErrorView.java | 237 +----------------- .../org/labkey/api/util/ExceptionUtil.java | 98 ++------ .../labkey/api/view/template/errorView.jsp | 4 +- core/src/client/ErrorHandler/ErrorType.tsx | 140 ++++++++--- .../src/client/ErrorHandler/errorHandler.scss | 10 +- core/src/org/labkey/core/CoreModule.java | 2 - .../view/template/bootstrap/pageTemplate.jsp | 6 +- .../org/labkey/core/webdav/DavController.java | 47 ++-- .../controllers/exp/ExperimentController.java | 17 +- .../mothership/MothershipController.java | 6 +- 12 files changed, 184 insertions(+), 443 deletions(-) delete mode 100644 api/src/org/labkey/api/util/ErrorTemplate.java diff --git a/api/src/org/labkey/api/util/ErrorRenderer.java b/api/src/org/labkey/api/util/ErrorRenderer.java index de07ec23368..077a4adef6c 100644 --- a/api/src/org/labkey/api/util/ErrorRenderer.java +++ b/api/src/org/labkey/api/util/ErrorRenderer.java @@ -43,12 +43,6 @@ public class ErrorRenderer private final String _title; private final String _errorCode; - // TODO: ErrorPage use these in React app - private boolean _includeHomeButton = true; - private boolean _includeBackButton = true; - private boolean _includeFolderButton = true; - private boolean _includeStopImpersonatingButton = false; - private ErrorType _errorType; public String getStackTrace() @@ -56,7 +50,7 @@ public String getStackTrace() return ExceptionUtils.getStackTrace(getException()); } - enum ErrorType + public enum ErrorType { notFound, permission, @@ -64,46 +58,6 @@ enum ErrorType execution } - public boolean isIncludeHomeButton() - { - return _includeHomeButton; - } - - public void setIncludeHomeButton(boolean includeHomeButton) - { - _includeHomeButton = includeHomeButton; - } - - public boolean isIncludeBackButton() - { - return _includeBackButton; - } - - public void setIncludeBackButton(boolean includeBackButton) - { - _includeBackButton = includeBackButton; - } - - public boolean isIncludeFolderButton() - { - return _includeFolderButton; - } - - public void setIncludeFolderButton(boolean includeFolderButton) - { - _includeFolderButton = includeFolderButton; - } - - public boolean isIncludeStopImpersonatingButton() - { - return _includeStopImpersonatingButton; - } - - public void setIncludeStopImpersonatingButton(boolean includeStopImpersonatingButton) - { - _includeStopImpersonatingButton = includeStopImpersonatingButton; - } - ErrorRenderer(int status, String errorCode, String heading, Throwable x, boolean isStartupFailure) { _status = status; diff --git a/api/src/org/labkey/api/util/ErrorTemplate.java b/api/src/org/labkey/api/util/ErrorTemplate.java deleted file mode 100644 index e6cd3ebd705..00000000000 --- a/api/src/org/labkey/api/util/ErrorTemplate.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.labkey.api.util; - -import org.labkey.api.view.JspView; - -// TODO: ErrorPage - rename this to ErrorView when that object is deleted -public class ErrorTemplate extends JspView -{ - public ErrorTemplate(ErrorRenderer renderer) - { - super("/org/labkey/api/view/template/errorView.jsp", renderer); - } -} diff --git a/api/src/org/labkey/api/util/ErrorView.java b/api/src/org/labkey/api/util/ErrorView.java index 568cad14643..88a9711ea77 100644 --- a/api/src/org/labkey/api/util/ErrorView.java +++ b/api/src/org/labkey/api/util/ErrorView.java @@ -1,226 +1,11 @@ -/* - * Copyright (c) 2009-2019 LabKey Corporation - * - * Licensed 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. - */ - -package org.labkey.api.util; - -import org.apache.commons.lang3.StringUtils; -import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerManager; -import org.labkey.api.portal.ProjectUrls; -import org.labkey.api.security.LoginUrls; -import org.labkey.api.settings.AppProps; -import org.labkey.api.settings.LookAndFeelProperties; -import org.labkey.api.view.ActionURL; -import org.labkey.api.view.HttpView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.PrintWriter; - -class ErrorView extends HttpView -{ - private final ErrorRenderer _renderer; - private final boolean _startupFailure; - private final boolean _popup; - - private ButtonBarRenderer _bbr = null; - - private boolean _includeHomeButton = true; - private boolean _includeBackButton = true; - private boolean _includeFolderButton = true; - private boolean _includeStopImpersonatingButton = false; - - ErrorView(ErrorRenderer renderer, boolean startupFailure) - { - this(renderer, startupFailure, false); - } - - ErrorView(ErrorRenderer renderer, boolean startupFailure, boolean popup) - { - _renderer = renderer; - _startupFailure = startupFailure; - _popup = popup; - } - - - @Override - public void renderInternal(Object model, HttpServletRequest request, HttpServletResponse response) throws Exception - { - PrintWriter out = response.getWriter(); - - doStartTag(out); - _renderer.renderContent(out, request, _bbr); - doEndTag(out); - } - - - public void doStartTag(PrintWriter out) - { - Container c = null; - - out.println(""); - out.println(""); - - // If it's a startup failure we likely don't have a database, so don't try to handle containers. Instead, hard-code - // some reasonable styles. - if (!_startupFailure) - { - out.println(PageFlowUtil.getStandardIncludes(getViewContext(), null, false)); - - c = getViewContext().getContainer(); - } - else - { - out.println(""); - } - - if (null == c) - { - try - { - c = ContainerManager.getRoot(); - } - catch (Throwable t) - { - // Exception at initial bootstrap (e.g., database not supported) might result in no root - } - } - - //NOTE: Selenium tests expect errors to start with error number and include word "Error" in title - out.println("" + PageFlowUtil.filter(_renderer.getTitle()) + ""); - out.println(""); - _renderer.renderStart(out); - - if (null != _renderer.getHeading()) - { - out.println("

    " + PageFlowUtil.filter(_renderer.getHeading()) + "

    "); - } - - // Why don't we just use c below? - Container contextContainer = getContextContainer(); - - if (null == contextContainer) - contextContainer = c; - - // These buttons are useless if the server fails to start up. Also, they try to hit a database that probably doesn't exist. - if (!_startupFailure) - { - if (_renderer.getStatus() == HttpServletResponse.SC_UNAUTHORIZED) - { - if (getViewContext().getUser().isGuest()) - { - ActionURL url = PageFlowUtil.urlProvider(LoginUrls.class).getLoginURL(getViewContext().getContainer(), null); - out.println("

    You are not currently logged in. You may need to log in to gain the necessary permissions.

    "); - } - LookAndFeelProperties props = LookAndFeelProperties.getInstance(contextContainer); - if (!StringUtils.isBlank(props.getSupportEmail())) - { - out.println("

    If you believe you should have permission to perform this action, please email your system administrator.

    "); - } - } - - if (_popup) - { - out.print(PageFlowUtil.button("Close").href("#").onClick("window.close(); return false;")); - out.println("
    "); - } - else - { - _bbr = new ErrorButtonBarRenderer(c); - } - } - } - - private class ErrorButtonBarRenderer implements ButtonBarRenderer - { - private Container _c; - - ErrorButtonBarRenderer(Container c) - { - _c = c; - } - - @Override - public void render(PrintWriter out) - { - doButtonBar(out, _c); - } - } - - private void doButtonBar(PrintWriter out, Container c) - { - if (_includeHomeButton) - { - out.print(PageFlowUtil.button("Home").href(AppProps.getInstance().getHomePageActionURL())); - out.print(" "); - } - if (_includeBackButton) - { - out.print(PageFlowUtil.generateBackButton()); - out.print(" "); - } - if (_includeFolderButton && !c.isRoot()) - { - ActionURL folderURL = PageFlowUtil.urlProvider(ProjectUrls.class).getStartURL(c); - out.print(PageFlowUtil.button("Folder").href(folderURL)); - out.print(" "); - } - if (_includeStopImpersonatingButton) - { - ActionURL stopUrl = PageFlowUtil.urlProvider(LoginUrls.class).getStopImpersonatingURL(c, getViewContext().getActionURL()); - out.println(PageFlowUtil.button("Stop Impersonating").href(stopUrl).usePost()); - } - - out.println("
    "); - } - - public void doEndTag(PrintWriter out) - { - _renderer.renderEnd(out); - out.println(""); - } - - public void setIncludeHomeButton(boolean includeHomeButton) - { - _includeHomeButton = includeHomeButton; - } - - public void setIncludeBackButton(boolean includeBackButton) - { - _includeBackButton = includeBackButton; - } - - public void setIncludeFolderButton(boolean includeFolderButton) - { - _includeFolderButton = includeFolderButton; - } - - public void setIncludeStopImpersonatingButton(boolean includeStopImpersonatingButton) - { - _includeStopImpersonatingButton = includeStopImpersonatingButton; - } -} +package org.labkey.api.util; + +import org.labkey.api.view.JspView; + +public class ErrorView extends JspView +{ + public ErrorView(ErrorRenderer renderer) + { + super("/org/labkey/api/view/template/errorView.jsp", renderer); + } +} diff --git a/api/src/org/labkey/api/util/ExceptionUtil.java b/api/src/org/labkey/api/util/ExceptionUtil.java index 7d5c43b1d0a..c17fdf938d2 100644 --- a/api/src/org/labkey/api/util/ExceptionUtil.java +++ b/api/src/org/labkey/api/util/ExceptionUtil.java @@ -44,7 +44,6 @@ import org.labkey.api.settings.LookAndFeelProperties; import org.labkey.api.view.ActionURL; import org.labkey.api.view.BadRequestException; -import org.labkey.api.view.ForbiddenProjectException; import org.labkey.api.view.HttpView; import org.labkey.api.view.NotFoundException; import org.labkey.api.view.RedirectException; @@ -173,23 +172,6 @@ public static HtmlString getUnauthorizedMessage(ViewContext context) ""); } - - public static ErrorView getErrorView(int responseStatus, String message, Throwable ex, - HttpServletRequest request, boolean startupFailure) - { - ErrorRenderer renderer = getErrorRenderer(responseStatus, message, ex, request, false, startupFailure); - return new ErrorView(renderer, startupFailure); - } - - - public static ErrorView getErrorView(int responseStatus, String message, Throwable ex, - HttpServletRequest request, boolean startupFailure, boolean popup) - { - ErrorRenderer renderer = getErrorRenderer(responseStatus, message, ex, request, false, startupFailure); - return new ErrorView(renderer, startupFailure, popup); - } - - public static WebPartView getErrorWebPartView(int responseStatus, String message, Throwable ex, HttpServletRequest request) { @@ -205,18 +187,6 @@ public static ErrorRenderer getErrorRenderer(int responseStatus, String message, if (!isStartupFailure && responseStatus == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) { errorCode = logExceptionToMothership(request, ex); - if (null != errorCode) - { - message = StringUtils.trimToEmpty(message); - if (message.length() > 0) - { - if (message.endsWith(".")) - message += " "; - else - message += ". "; - } - message += "If contacting support regarding this error, please refer to error code: " + errorCode; - } } if (isPart) @@ -792,7 +762,7 @@ else if (ex instanceof ConfigurationException) if (null == message && null != unhandledException) { responseStatus = HttpServletResponse.SC_INTERNAL_SERVER_ERROR; - message = responseStatus + ": Unexpected server error"; + message = ex.getMessage(); } //don't log unauthorized (basic-auth challenge), forbiddens, or simple not found (404s) @@ -861,47 +831,9 @@ else if (isJSON || isXML) log.error("Global.handleException", x); } } - else if (AppProps.getInstance().isExperimentalFeatureEnabled(AppProps.EXPERIMENTAL_ERROR_PAGE)) - { - renderErrorPage(ex, responseStatus, message, request, response, pageConfig, errorType, user, log, startupFailure); - } else { - // 7629: Error page redesign -- development in-progress. This path shows the old error view. - ErrorView errorView = ExceptionUtil.getErrorView(responseStatus, message, unhandledException, request, startupFailure); - - if (ex instanceof UnauthorizedException) - { - if (ex instanceof ForbiddenProjectException) - { - // Not allowed in the project... don't offer Home or Folder buttons - errorView.setIncludeHomeButton(false); - errorView.setIncludeFolderButton(false); - } - - // Provide "Stop Impersonating" button if unauthorized while impersonating - if (user.isImpersonated()) - errorView.setIncludeStopImpersonatingButton(true); - } - - try - { - response.setContentType("text/html"); - if (null == responseStatusMessage) - response.setStatus(responseStatus); - else - response.setStatus(responseStatus, responseStatusMessage); - for (Map.Entry entry : headers.entrySet()) - response.addHeader(entry.getKey(), entry.getValue()); - errorView.render(request, response); - } - catch (IllegalStateException ignored) - { - } - catch (Exception x) - { - log.error("Global.handleException", x); - } + renderErrorPage(ex, responseStatus, message, request, response, pageConfig, errorType, user, log, startupFailure); } return null; @@ -914,16 +846,7 @@ private static void renderErrorPage(Throwable ex, int responseStatus, String mes if (ex instanceof UnauthorizedException) { - if (ex instanceof ForbiddenProjectException) - { - // Not allowed in the project... don't offer Home or Folder buttons - renderer.setIncludeHomeButton(false); - renderer.setIncludeFolderButton(false); - } - - // Provide "Stop Impersonating" button if unauthorized while impersonating - if (user.isImpersonated()) - renderer.setIncludeStopImpersonatingButton(true); + errorType = ErrorRenderer.ErrorType.permission; } if (ex instanceof DavException) @@ -944,13 +867,20 @@ private static void renderErrorPage(Throwable ex, int responseStatus, String mes { context = HttpView.currentContext(); } + else + { + // context is null in cases of garbage urls, this helps in rendering error page in App template + context = new ViewContext(); + context.setRequest(request); + context.setResponse(response); + } try { + renderer.setErrorType(errorType); if (HttpView.hasCurrentView()) { - renderer.setErrorType(errorType); - errorView = pageConfig.getTemplate().getTemplate(context, new ErrorTemplate(renderer), pageConfig); + errorView = pageConfig.getTemplate().getTemplate(context, new ErrorView(renderer), pageConfig); if (null == errorView) { @@ -961,7 +891,7 @@ private static void renderErrorPage(Throwable ex, int responseStatus, String mes else { // context can be null for configuration exceptions depending on how far server got through initialization - errorView = PageConfig.Template.Body.getTemplate(new ViewContext(request, response, new ActionURL(ActionURL.getBaseServerURL())), new ErrorTemplate(renderer), pageConfig); + errorView = PageConfig.Template.Body.getTemplate(new ViewContext(request, response, new ActionURL(ActionURL.getBaseServerURL())), new ErrorView(renderer), pageConfig); } pageConfig.addClientDependencies(errorView.getClientDependencies()); @@ -973,7 +903,7 @@ private static void renderErrorPage(Throwable ex, int responseStatus, String mes try { // TODO : ErrorPage, this app template doesn't work - errorView = PageConfig.Template.App.getTemplate(context, new ErrorTemplate(renderer), pageConfig); + errorView = PageConfig.Template.App.getTemplate(context, new ErrorView(renderer), pageConfig); pageConfig.addClientDependencies(errorView.getClientDependencies()); errorView.getView().render(errorView.getModel(), request, response); diff --git a/api/src/org/labkey/api/view/template/errorView.jsp b/api/src/org/labkey/api/view/template/errorView.jsp index 4718bbb8d51..b6345e6943a 100644 --- a/api/src/org/labkey/api/view/template/errorView.jsp +++ b/api/src/org/labkey/api/view/template/errorView.jsp @@ -1,6 +1,6 @@ <%@ page import="org.labkey.api.view.template.ClientDependencies" %> <%@ page import="org.labkey.api.view.HttpView" %> -<%@ page import="org.labkey.api.util.ErrorTemplate" %> +<%@ page import="org.labkey.api.util.ErrorView" %> <%@ page import="org.labkey.api.util.ErrorRenderer" %> <%@ page import="org.labkey.api.util.UniqueID" %> <%@ page import="org.labkey.api.util.PageFlowUtil" %> @@ -16,7 +16,7 @@ } %> <% - ErrorTemplate me = (ErrorTemplate) HttpView.currentView(); + ErrorView me = (ErrorView) HttpView.currentView(); ErrorRenderer model = me.getModelBean(); String appId = "error-handler-app-" + UniqueID.getServerSessionScopedUID(); diff --git a/core/src/client/ErrorHandler/ErrorType.tsx b/core/src/client/ErrorHandler/ErrorType.tsx index 71d0402cf59..61079ae6e61 100644 --- a/core/src/client/ErrorHandler/ErrorType.tsx +++ b/core/src/client/ErrorHandler/ErrorType.tsx @@ -1,11 +1,13 @@ import React, { ReactNode } from 'react'; +import { Button } from 'react-bootstrap'; + import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCheckCircle, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; import { helpLinkNode, imageURL } from '@labkey/components'; -import { getServerContext } from '@labkey/api'; +import { ActionURL, Ajax, getServerContext } from '@labkey/api'; import { IErrorDetailsModel } from './model'; @@ -27,7 +29,17 @@ const DETAILS_SUB_INSTRUCTION = ( ); -const NOTFOUND_SUBHEADING = <>It seems like something went wrong. The requested page cannot be found.; +const NOTFOUND_SUBHEADING = (errorMessage?: string) => ( + <> + {' '} + {errorMessage !== undefined + ? errorMessage.endsWith('.') + ? errorMessage + : errorMessage + '.' + : 'It seems like something went wrong.'}{' '} + The requested page cannot be found. + +); const NOTFOUND_INSTRUCTION = (errorCode?: string) => ( <>
    @@ -36,27 +48,31 @@ const NOTFOUND_INSTRUCTION = (errorCode?: string) => ( LabKey support forum.
    -
    - If you would like to file a{' '} - - {' '} - LabKey support ticket - - , your unique reference code is: {errorCode} -
    + {errorCode !== undefined && ( +
    + If you would like to file a{' '} + + {' '} + LabKey support ticket + + , your unique reference code is: {errorCode} +
    + )} ); -const NOTFOUND_DETAILS = ( +const NOTFOUND_DETAILS = (errorMessage?: string) => ( <>
    What went wrong?
    - Unfortunately, we are unable to specifically identify what went wrong. However, here are the most common - errors: + {errorMessage !== undefined + ? 'Here are the most common errors:' + : 'Unfortunately, we are unable to specifically identify what went wrong. However, here are the most common\n' + + ' errors:'}


    @@ -86,9 +102,9 @@ const NOTFOUND_DETAILS = ( ); -const PERMISSION_SUBHEADING = <>You do not have the permissions required to access this page.; +const PERMISSION_SUBHEADING = () => <>You do not have the permissions required to access this page.; const PERMISSION_INSTRUCTION = <>Please contact this server's admin to gain access.; -const PERMISSION_DETAILS = ( +const PERMISSION_DETAILS = (errorMessage?: string) => ( <>
    What is a permission error?
    @@ -112,23 +128,65 @@ const PERMISSION_DETAILS = (
    {getServerContext().impersonatingUser !== undefined && ( -
    - You are currently - impersonating as: {getServerContext().impersonatingUser.displayName} -
    + <> +
    + You are currently + impersonating as: {getServerContext().impersonatingUser.displayName} +
    +
    + +
    + )} ); -const CONFIGURATION_SUBHEADING = <>It seems like something went wrong. The requested page cannot be found.; +const CONFIGURATION_SUBHEADING = (errorMessage?: string) => ( + <> + {' '} + {errorMessage !== undefined + ? errorMessage.endsWith('.') + ? errorMessage + : errorMessage + '.' + : 'It seems like something went wrong.'}{' '} + The requested page cannot be found. + +); const CONFIGURATION_INSTRUCTION = <>Please check your server configurations.; -const CONFIGURATION_DETAILS = ( +const CONFIGURATION_DETAILS = (errorMessage?: string) => ( <>
    What went wrong?
    - Unfortunately, we are unable to specifically identify what went wrong. It seems that there might be some - issues with your server configuration. + {errorMessage !== undefined + ? errorMessage + : 'Unfortunately, we are unable to specifically identify what went wrong.'}{' '} + It seems that there might be some issues with your server configuration.


    @@ -149,7 +207,14 @@ const CONFIGURATION_DETAILS = ( ); -const EXECUTION_SUB_HEADING = <>It seems like there is an issue with this installation of LabKey server.; +const EXECUTION_SUB_HEADING = (errorMessage?: string) => ( + <> + {' '} + {errorMessage !== undefined + ? errorMessage + : 'It seems like there is an issue with this installation of LabKey server.'} + +); const EXECUTION_INSTRUCTION = (errorCode?: string) => ( <>
    @@ -170,25 +235,25 @@ export enum ErrorType { const ERROR_TYPE_INFO = { notFound: { - heading: NOTFOUND_SUBHEADING, + heading: (errorMessage?: string) => NOTFOUND_SUBHEADING(errorMessage), instruction: (errorCode?: string) => NOTFOUND_INSTRUCTION(errorCode), imagePath: 'notFound_error.svg', details: NOTFOUND_DETAILS, }, permission: { - heading: PERMISSION_SUBHEADING, + heading: () => PERMISSION_SUBHEADING(), instruction: () => PERMISSION_INSTRUCTION, imagePath: 'permission_error.svg', details: PERMISSION_DETAILS, }, configuration: { - heading: CONFIGURATION_SUBHEADING, + heading: (errorMessage?: string) => NOTFOUND_SUBHEADING(errorMessage), instruction: () => CONFIGURATION_INSTRUCTION, imagePath: 'configuration_error.svg', - details: CONFIGURATION_DETAILS, + details: (errorMessage?: string) => CONFIGURATION_DETAILS(errorMessage), }, execution: { - heading: EXECUTION_SUB_HEADING, + heading: (errorMessage?: string) => NOTFOUND_SUBHEADING(errorMessage), instruction: (errorCode?: string) => EXECUTION_INSTRUCTION(errorCode), imagePath: 'code_error.svg', details: (stackTrace?: string) => EXECUTION_DETAILS(stackTrace), @@ -208,7 +273,12 @@ export const getImage = (errorDetails: IErrorDetailsModel): ReactNode => { export const getSubHeading = (errorDetails: IErrorDetailsModel): ReactNode => { if (ERROR_TYPE_INFO[errorDetails.errorType]) { - const subHeading = ERROR_TYPE_INFO[errorDetails.errorType].heading; + let subHeading; + if (errorDetails.message) { + subHeading = ERROR_TYPE_INFO[errorDetails.errorType].heading(errorDetails.message); + } else { + subHeading = ERROR_TYPE_INFO[errorDetails.errorType].heading(); + } return
    {subHeading}
    ; } }; @@ -233,7 +303,7 @@ export const getViewDetails = (errorDetails: IErrorDetailsModel): ReactNode => { if (errorDetails.stackTrace && errorType == ErrorType.execution) { details = ERROR_TYPE_INFO[errorType].details(errorDetails.stackTrace); } else { - details = ERROR_TYPE_INFO[errorType].details; + details = ERROR_TYPE_INFO[errorType].details(errorDetails.message); } return
    {details}
    ; } diff --git a/core/src/client/ErrorHandler/errorHandler.scss b/core/src/client/ErrorHandler/errorHandler.scss index 61462b9dd90..95826defb90 100644 --- a/core/src/client/ErrorHandler/errorHandler.scss +++ b/core/src/client/ErrorHandler/errorHandler.scss @@ -46,16 +46,18 @@ } .error-backButton { + display: inline-block; + text-align: center; margin: 0.5rem 5rem 0.5rem 0.5rem; background-color: #538AC5; font-size: 14px; - width: 10%; padding-left: 15px; padding-right: 15px; } .error-details-btn { - + display: inline-block; + text-align: center; } .error-details-container { @@ -71,13 +73,13 @@ content: ''; position: absolute; bottom: 100%; - left: 17rem; + left: 14rem; border: 4rem solid transparent; border-bottom-color: #F3F3F4; } .error-details-container:after { - left: 18rem; + left: 15rem; border: 3rem solid transparent; border-bottom-color: #F3F3F4; } diff --git a/core/src/org/labkey/core/CoreModule.java b/core/src/org/labkey/core/CoreModule.java index a78ef2037f3..deccc2c95a1 100644 --- a/core/src/org/labkey/core/CoreModule.java +++ b/core/src/org/labkey/core/CoreModule.java @@ -389,8 +389,6 @@ public QuerySchema createSchema(DefaultSchema schema, Module module) AdminConsole.addExperimentalFeatureFlag(RemapCache.EXPERIMENTAL_RESOLVE_LOOKUPS_BY_VALUE, "Resolve lookups by Value", "This feature will attempt to resolve lookups by value through the UI insert/update form. This can be useful when the " + "lookup list is long (> 10000) and the UI stops rendering a dropdown.", false); - AdminConsole.addExperimentalFeatureFlag(AppProps.EXPERIMENTAL_ERROR_PAGE, "New Error Page", - "This is the new error page.", false); SiteValidationService svc = SiteValidationService.get(); if (null != svc) diff --git a/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp b/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp index 815bf46264d..745a798eaa4 100644 --- a/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp +++ b/core/src/org/labkey/core/view/template/bootstrap/pageTemplate.jsp @@ -98,7 +98,11 @@ <% } %> <% - String anchor = model.getAnchor(url); + String anchor = null; + if (null != url) + { + anchor = model.getAnchor(url); + } if (null != anchor) { %> diff --git a/core/src/org/labkey/core/webdav/DavController.java b/core/src/org/labkey/core/webdav/DavController.java index f6618c5a0ab..56eb034dedb 100644 --- a/core/src/org/labkey/core/webdav/DavController.java +++ b/core/src/org/labkey/core/webdav/DavController.java @@ -72,27 +72,10 @@ import org.labkey.api.settings.AppProps; import org.labkey.api.settings.LookAndFeelProperties; import org.labkey.api.test.TestWhen; -import org.labkey.api.util.CSRFException; -import org.labkey.api.util.CSRFUtil; -import org.labkey.api.util.ConfigurationException; -import org.labkey.api.util.DateUtil; -import org.labkey.api.util.ExceptionUtil; -import org.labkey.api.util.FileStream; -import org.labkey.api.util.FileUtil; -import org.labkey.api.util.GUID; -import org.labkey.api.util.HeartBeat; -import org.labkey.api.util.HttpUtil; -import org.labkey.api.util.MemTracker; -import org.labkey.api.util.PageFlowUtil; -import org.labkey.api.util.Pair; -import org.labkey.api.util.Path; -import org.labkey.api.util.ResponseHelper; -import org.labkey.api.util.ShutdownListener; -import org.labkey.api.util.StringUtilsLabKey; -import org.labkey.api.util.URLHelper; -import org.labkey.api.util.UnexpectedException; +import org.labkey.api.util.*; import org.labkey.api.view.ActionURL; import org.labkey.api.view.DefaultModelAndView; +import org.labkey.api.view.HttpView; import org.labkey.api.view.JspView; import org.labkey.api.view.NavTree; import org.labkey.api.view.RedirectException; @@ -427,6 +410,24 @@ WebdavStatus sendError(WebdavStatus status, Exception x) String message = x.getMessage() != null ? x.getMessage() : status.message; if (x instanceof ConfigurationException) message += "\nThis may be a server configuration problem. Contact the site administrator."; + + ErrorRenderer renderer = ExceptionUtil.getErrorRenderer(HttpServletResponse.SC_BAD_REQUEST, message, x, getViewContext().getRequest(), false, false); + renderer.setErrorType(ErrorRenderer.ErrorType.notFound); + PageConfig pageConfig = new PageConfig(); + HttpView errorView = PageConfig.Template.App.getTemplate(getViewContext(), new ErrorView(renderer), pageConfig); + if (null != errorView) + { + pageConfig.addClientDependencies(errorView.getClientDependencies()); + try + { + errorView.render(getViewContext().getRequest(), getViewContext().getResponse()); + return WebdavStatus.SC_BAD_REQUEST; + } + catch (Exception e) + { + return sendError(status, message); + } + } return sendError(status, message); } @@ -775,16 +776,14 @@ else if ("GET".equals(method) && HttpUtil.isBrowser(getRequest())) SearchService ss = SearchService.get(); if (null != ss) ss.notFound((URLHelper)getRequest().getAttribute(ViewServlet.ORIGINAL_URL_URLHELPER)); + + getResponse().sendError(dex.getStatus(), dex); } - if (AppProps.getInstance().isExperimentalFeatureEnabled(AppProps.EXPERIMENTAL_ERROR_PAGE)) - { - ExceptionUtil.handleException(request, response, dex, dex.getMessage(), false, null); - } - // TODO: ErrorPage, look into inserting the following details generated by sendError in ViewDetails section else { getResponse().sendError(dex.getStatus(), dex.getMessage()); } + if (dex.getStatus() != null && dex.getStatus().code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) { ExceptionUtil.logExceptionToMothership(request, dex); diff --git a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java index e6ac11fbc5d..1af3451337b 100644 --- a/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java +++ b/experiment/src/org/labkey/experiment/controllers/exp/ExperimentController.java @@ -150,6 +150,9 @@ import org.labkey.api.study.StudyUrls; import org.labkey.api.util.DOM; import org.labkey.api.util.DOM.LK; +import org.labkey.api.util.ErrorRenderer; +import org.labkey.api.util.ErrorView; +import org.labkey.api.util.ErrorView; import org.labkey.api.util.ExceptionUtil; import org.labkey.api.util.FileUtil; import org.labkey.api.util.HelpTopic; @@ -2235,7 +2238,9 @@ public void export(ConvertArraysToExcelForm form, HttpServletResponse response, catch (JSONException | ClassCastException e) { // We can get a ClassCastException if we expect an array and get a simple String, for example - HttpView errorView = ExceptionUtil.getErrorView(HttpServletResponse.SC_BAD_REQUEST, "Failed to convert to Excel - invalid input", e, getViewContext().getRequest(), false); + ErrorRenderer renderer = ExceptionUtil.getErrorRenderer(HttpServletResponse.SC_BAD_REQUEST, "Failed to convert to Excel - invalid inpu", e, getViewContext().getRequest(), false, false); + renderer.setErrorType(ErrorRenderer.ErrorType.notFound); + HttpView errorView = getPageConfig().getTemplate().getTemplate(getViewContext(), new ErrorView(renderer), getPageConfig()); errorView.render(getViewContext().getRequest(), getViewContext().getResponse()); } } @@ -2305,8 +2310,14 @@ public void export(ConvertArraysToExcelForm form, HttpServletResponse response, } catch (JSONException e) { - HttpView errorView = ExceptionUtil.getErrorView(HttpServletResponse.SC_BAD_REQUEST, "Failed to convert to table - invalid input", e, getViewContext().getRequest(), false); - errorView.render(getViewContext().getRequest(), getViewContext().getResponse()); + ErrorRenderer renderer = ExceptionUtil.getErrorRenderer(HttpServletResponse.SC_BAD_REQUEST, "Failed to convert to table - invalid input", e, getViewContext().getRequest(), false, false); + renderer.setErrorType(ErrorRenderer.ErrorType.notFound); + HttpView errorView = getPageConfig().getTemplate().getTemplate(getViewContext(), new ErrorView(renderer), getPageConfig()); + if (null != errorView) + { + getPageConfig().addClientDependencies(errorView.getClientDependencies()); + errorView.render(getViewContext().getRequest(), getViewContext().getResponse()); + } } } } diff --git a/mothership/src/org/labkey/mothership/MothershipController.java b/mothership/src/org/labkey/mothership/MothershipController.java index 8aa60bd9a42..5af78b4bc66 100644 --- a/mothership/src/org/labkey/mothership/MothershipController.java +++ b/mothership/src/org/labkey/mothership/MothershipController.java @@ -1369,7 +1369,7 @@ public class ThrowConfigurationExceptionAction extends SimpleViewAction @Override public ModelAndView getView(Object form, BindException errors) { - throw new ConfigurationException("Test Configuration exception"); + throw new ConfigurationException("This is a test message for configuration exception."); } @Override @@ -1385,7 +1385,7 @@ public class ThrowNotFoundExceptionAction extends SimpleViewAction @Override public ModelAndView getView(Object form, BindException errors) { - throw new NotFoundException("Test NotFound exception"); + throw new NotFoundException("This is a test message for not found exception."); } @Override @@ -1417,7 +1417,7 @@ public class ThrowExecutionExceptionAction extends SimpleViewAction @Override public ModelAndView getView(Object form, BindException errors) { - throw new NullPointerException("Test Execution exception"); + throw new NullPointerException("This is a test message for execution exception"); } @Override From 13f9091cd51756ee3a0303ade7eff81e67d8ba2b Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Tue, 22 Sep 2020 15:36:50 -0700 Subject: [PATCH 08/32] remove todo --- api/src/org/labkey/api/util/ExceptionUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/org/labkey/api/util/ExceptionUtil.java b/api/src/org/labkey/api/util/ExceptionUtil.java index c17fdf938d2..be15cd857d3 100644 --- a/api/src/org/labkey/api/util/ExceptionUtil.java +++ b/api/src/org/labkey/api/util/ExceptionUtil.java @@ -902,7 +902,6 @@ private static void renderErrorPage(Throwable ex, int responseStatus, String mes // try to render just the react app try { - // TODO : ErrorPage, this app template doesn't work errorView = PageConfig.Template.App.getTemplate(context, new ErrorView(renderer), pageConfig); pageConfig.addClientDependencies(errorView.getClientDependencies()); errorView.getView().render(errorView.getModel(), request, response); From c95cb737851fae823df9e3bae9654883ca8822d7 Mon Sep 17 00:00:00 2001 From: labkey-nicka Date: Tue, 22 Sep 2020 16:47:21 -0700 Subject: [PATCH 09/32] Code review feedback --- .../client/ErrorHandler/ErrorHandler.spec.tsx | 43 +++--- core/src/client/ErrorHandler/ErrorHandler.tsx | 79 +++-------- core/src/client/ErrorHandler/ErrorType.tsx | 129 ++++++++---------- .../src/client/ErrorHandler/errorHandler.scss | 4 + core/src/client/ErrorHandler/model.ts | 20 +-- 5 files changed, 106 insertions(+), 169 deletions(-) diff --git a/core/src/client/ErrorHandler/ErrorHandler.spec.tsx b/core/src/client/ErrorHandler/ErrorHandler.spec.tsx index e72fbd61d21..8eb32857300 100644 --- a/core/src/client/ErrorHandler/ErrorHandler.spec.tsx +++ b/core/src/client/ErrorHandler/ErrorHandler.spec.tsx @@ -1,21 +1,17 @@ import React from 'react'; - import { mount, shallow } from 'enzyme'; import { ErrorHandler } from './ErrorHandler'; -import { ErrorDetailsModel, IErrorDetailsModel } from './model'; -import { ErrorType } from './ErrorType'; +import { ErrorDetails, ErrorType } from './model'; describe('ErrorHandler', () => { test('Not found exception', () => { - const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + const errorDetails: ErrorDetails = { errorType: ErrorType.notFound, errorCode: '123XYZ', - stackTrace: undefined, message: 'This is a not found exception', - }); - const errorContext = { errorDetails: errDetails }; - const wrapper = mount(); + }; + const wrapper = mount(); expect(wrapper.find('.error-details-container')).toHaveLength(0); wrapper.setState({ showDetails: true }); @@ -26,14 +22,11 @@ describe('ErrorHandler', () => { }); test('Configuration exception', () => { - const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + const errorDetails: ErrorDetails = { errorType: ErrorType.configuration, - errorCode: undefined, - stackTrace: undefined, message: 'This is a configuration exception', - }); - const errorContext = { errorDetails: errDetails }; - const wrapper = mount(); + }; + const wrapper = mount(); expect(wrapper.find('.error-details-container')).toHaveLength(0); wrapper.setState({ showDetails: true }); @@ -44,14 +37,11 @@ describe('ErrorHandler', () => { }); test('Permission exception', () => { - const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + const errorDetails: ErrorDetails = { errorType: ErrorType.permission, - errorCode: undefined, - stackTrace: undefined, message: 'This is a permission exception', - }); - const errorContext = { errorDetails: errDetails }; - const wrapper = shallow(); + }; + const wrapper = shallow(); expect(wrapper.find('.error-details-container')).toHaveLength(0); wrapper.setState({ showDetails: true }); @@ -62,21 +52,20 @@ describe('ErrorHandler', () => { }); test('Execution exception', () => { - const sampleTrace = 'java.lang.NullPointerException: null'; + const expectedStackTrace = 'java.lang.NullPointerException: null'; - const errDetails: IErrorDetailsModel = new ErrorDetailsModel({ + const errorDetails: ErrorDetails = { errorType: ErrorType.execution, errorCode: '456AAA', - stackTrace: sampleTrace, message: 'This is a execution exception', - }); - const errorContext = { errorDetails: errDetails }; - const wrapper = shallow(); + stackTrace: expectedStackTrace, + }; + const wrapper = shallow(); expect(wrapper.find('.error-details-container')).toHaveLength(0); wrapper.setState({ showDetails: true }); const question = wrapper.find('.error-details-container'); - expect(question.text().startsWith('java.lang.NullPointerException: null')).toBeTruthy(); + expect(question.text().startsWith(expectedStackTrace)).toBeTruthy(); wrapper.unmount(); }); diff --git a/core/src/client/ErrorHandler/ErrorHandler.tsx b/core/src/client/ErrorHandler/ErrorHandler.tsx index dfb4b63d4ff..b20b3ded824 100644 --- a/core/src/client/ErrorHandler/ErrorHandler.tsx +++ b/core/src/client/ErrorHandler/ErrorHandler.tsx @@ -1,15 +1,13 @@ -import React, { ReactNode } from 'react'; -import { Button, Col, Row } from 'react-bootstrap'; +import React, { PureComponent } from 'react'; +import { ActionURL } from '@labkey/api'; import { getErrorHeading, getImage, getInstruction, getSubHeading, getViewDetails } from './ErrorType'; +import { ErrorDetails } from './model'; import './errorHandler.scss'; -import { IErrorDetailsModel } from './model'; - -import { ActionURL } from '@labkey/api'; export interface AppContext { - errorDetails: IErrorDetailsModel; + errorDetails: ErrorDetails; } interface ErrorHandlerProps { @@ -20,84 +18,51 @@ interface ErrorHandlerState { showDetails: boolean; } -export class ErrorHandler extends React.PureComponent { - constructor(props) { - super(props); +export class ErrorHandler extends PureComponent { - this.state = { - showDetails: false, - }; - } + state: Readonly = { showDetails: false }; onBackClick = (): void => { // Back button - takes you back to the previous page if available // and to the ‘home’ folder if not possible to go back to the previous page - const currentLocation = window.location.href; if (window.history.length !== 1) { // browsers like chrome stores their homepage as first item window.history.back(); - } - - const newLocation = window.location.href; - - if (currentLocation === newLocation) { + } else { window.location.href = ActionURL.getBaseURL(false); } }; onViewDetailsClick = (): void => { const { showDetails } = this.state; - this.setState(() => ({ - showDetails: !showDetails, - })); + this.setState({ showDetails: !showDetails }); }; - renderErrorTopSection = (): ReactNode => { + render() { const { errorDetails } = this.props.context; + const { showDetails } = this.state; + return ( <> -
    - - +
    +
    +
    {getErrorHeading()} {getSubHeading(errorDetails)} {getInstruction(errorDetails)} - - + +
    - - {getImage(errorDetails)} - +
    +
    {getImage(errorDetails)}
    +
    - - ); - }; - - renderErrorDetailsSection = (): ReactNode => { - const { errorDetails } = this.props.context; - return ( - <> -
    {getViewDetails(errorDetails)}
    - - ); - }; - - render() { - const { showDetails } = this.state; - - return ( - <> - {this.renderErrorTopSection()} - {showDetails && this.renderErrorDetailsSection()} + {showDetails &&
    {getViewDetails(errorDetails)}
    } ); } diff --git a/core/src/client/ErrorHandler/ErrorType.tsx b/core/src/client/ErrorHandler/ErrorType.tsx index 61079ae6e61..ea4246d5002 100644 --- a/core/src/client/ErrorHandler/ErrorType.tsx +++ b/core/src/client/ErrorHandler/ErrorType.tsx @@ -1,15 +1,11 @@ import React, { ReactNode } from 'react'; - import { Button } from 'react-bootstrap'; - import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCheckCircle, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; - import { helpLinkNode, imageURL } from '@labkey/components'; - import { ActionURL, Ajax, getServerContext } from '@labkey/api'; -import { IErrorDetailsModel } from './model'; +import { ErrorDetails, ErrorType } from './model'; const ERROR_HEADING = 'Oops! An error has occurred.'; @@ -102,9 +98,9 @@ const NOTFOUND_DETAILS = (errorMessage?: string) => ( ); -const PERMISSION_SUBHEADING = () => <>You do not have the permissions required to access this page.; -const PERMISSION_INSTRUCTION = <>Please contact this server's admin to gain access.; -const PERMISSION_DETAILS = (errorMessage?: string) => ( +const PERMISSION_SUBHEADING = () => 'You do not have the permissions required to access this page.'; +const PERMISSION_INSTRUCTION = () => 'Please contact this server\'s admin to gain access.'; +const PERMISSION_DETAILS = () => ( <>
    What is a permission error?
    @@ -177,7 +173,7 @@ const CONFIGURATION_SUBHEADING = (errorMessage?: string) => ( The requested page cannot be found. ); -const CONFIGURATION_INSTRUCTION = <>Please check your server configurations.; +const CONFIGURATION_INSTRUCTION = () => 'Please check your server configurations.'; const CONFIGURATION_DETAILS = (errorMessage?: string) => ( <>
    What went wrong?
    @@ -226,85 +222,74 @@ const EXECUTION_INSTRUCTION = (errorCode?: string) => ( ); const EXECUTION_DETAILS = (stackTrace?: string) =>
    {stackTrace}
    ; -export enum ErrorType { - notFound = 'notFound', - permission = 'permission', - configuration = 'configuration', - execution = 'execution', +type ErrorTypeInfo = { + details: (errorMessage?: string) => ReactNode; + heading: (errorMessage?: string) => ReactNode; + imagePath: string; + instruction: (errorCode?: string) => ReactNode; } -const ERROR_TYPE_INFO = { - notFound: { - heading: (errorMessage?: string) => NOTFOUND_SUBHEADING(errorMessage), - instruction: (errorCode?: string) => NOTFOUND_INSTRUCTION(errorCode), - imagePath: 'notFound_error.svg', - details: NOTFOUND_DETAILS, - }, - permission: { - heading: () => PERMISSION_SUBHEADING(), - instruction: () => PERMISSION_INSTRUCTION, - imagePath: 'permission_error.svg', - details: PERMISSION_DETAILS, - }, +const ERROR_TYPE_INFO: { [key in ErrorType]: ErrorTypeInfo } = { configuration: { - heading: (errorMessage?: string) => NOTFOUND_SUBHEADING(errorMessage), - instruction: () => CONFIGURATION_INSTRUCTION, + details: CONFIGURATION_DETAILS, + heading: CONFIGURATION_SUBHEADING, imagePath: 'configuration_error.svg', - details: (errorMessage?: string) => CONFIGURATION_DETAILS(errorMessage), + instruction: CONFIGURATION_INSTRUCTION, }, execution: { - heading: (errorMessage?: string) => NOTFOUND_SUBHEADING(errorMessage), - instruction: (errorCode?: string) => EXECUTION_INSTRUCTION(errorCode), + details: EXECUTION_DETAILS, + heading: EXECUTION_SUB_HEADING, imagePath: 'code_error.svg', - details: (stackTrace?: string) => EXECUTION_DETAILS(stackTrace), + instruction: EXECUTION_INSTRUCTION, + }, + notFound: { + details: NOTFOUND_DETAILS, + heading: NOTFOUND_SUBHEADING, + imagePath: 'notFound_error.svg', + instruction: NOTFOUND_INSTRUCTION, + }, + permission: { + details: PERMISSION_DETAILS, + heading: PERMISSION_SUBHEADING, + imagePath: 'permission_error.svg', + instruction: PERMISSION_INSTRUCTION, }, }; -export const getErrorHeading = (): ReactNode => { - return
    {ERROR_HEADING}
    ; -}; +export const getErrorHeading = () =>
    {ERROR_HEADING}
    ; -export const getImage = (errorDetails: IErrorDetailsModel): ReactNode => { - if (ERROR_TYPE_INFO[errorDetails.errorType]) { - const path = ERROR_TYPE_INFO[errorDetails.errorType].imagePath; - return LabKey Error; - } +export const getImage = (errorDetails: ErrorDetails): ReactNode => { + const info = ERROR_TYPE_INFO[errorDetails.errorType]; + if (!info) return null; + + return LabKey Error; }; -export const getSubHeading = (errorDetails: IErrorDetailsModel): ReactNode => { - if (ERROR_TYPE_INFO[errorDetails.errorType]) { - let subHeading; - if (errorDetails.message) { - subHeading = ERROR_TYPE_INFO[errorDetails.errorType].heading(errorDetails.message); - } else { - subHeading = ERROR_TYPE_INFO[errorDetails.errorType].heading(); - } - return
    {subHeading}
    ; - } +export const getSubHeading = (errorDetails: ErrorDetails): ReactNode => { + const info = ERROR_TYPE_INFO[errorDetails.errorType]; + if (!info) return null; + + return
    {info.heading(errorDetails.message)}
    ; }; -export const getInstruction = (errorDetails: IErrorDetailsModel): ReactNode => { - if (ERROR_TYPE_INFO[errorDetails.errorType]) { - const errorType = errorDetails.errorType; - let instruction; - if (errorDetails.errorCode) { - instruction = ERROR_TYPE_INFO[errorType].instruction(errorDetails.errorCode); - } else { - instruction = ERROR_TYPE_INFO[errorType].instruction(); - } - return
    {instruction}
    ; - } +export const getInstruction = (errorDetails: ErrorDetails): ReactNode => { + const info = ERROR_TYPE_INFO[errorDetails.errorType]; + if (!info) return null; + + return
    {info.instruction(errorDetails.errorCode)}
    ; }; -export const getViewDetails = (errorDetails: IErrorDetailsModel): ReactNode => { - if (ERROR_TYPE_INFO[errorDetails.errorType]) { - const errorType = errorDetails.errorType; - let details; - if (errorDetails.stackTrace && errorType == ErrorType.execution) { - details = ERROR_TYPE_INFO[errorType].details(errorDetails.stackTrace); - } else { - details = ERROR_TYPE_INFO[errorType].details(errorDetails.message); - } - return
    {details}
    ; +export const getViewDetails = (errorDetails: ErrorDetails): ReactNode => { + const { errorType, message, stackTrace } = errorDetails; + const info = ERROR_TYPE_INFO[errorType]; + if (!info) return null; + + let details; + if (stackTrace && errorType === ErrorType.execution) { + details = info.details(stackTrace); + } else { + details = info.details(message); } + + return
    {details}
    ; }; diff --git a/core/src/client/ErrorHandler/errorHandler.scss b/core/src/client/ErrorHandler/errorHandler.scss index 95826defb90..bce1157b3eb 100644 --- a/core/src/client/ErrorHandler/errorHandler.scss +++ b/core/src/client/ErrorHandler/errorHandler.scss @@ -55,6 +55,10 @@ padding-right: 15px; } +.error-details-body { + padding: 15px; +} + .error-details-btn { display: inline-block; text-align: center; diff --git a/core/src/client/ErrorHandler/model.ts b/core/src/client/ErrorHandler/model.ts index 5b8631465fd..0e41da8c5ad 100644 --- a/core/src/client/ErrorHandler/model.ts +++ b/core/src/client/ErrorHandler/model.ts @@ -1,19 +1,13 @@ -import { ErrorType } from './ErrorType'; +export enum ErrorType { + notFound = 'notFound', + permission = 'permission', + configuration = 'configuration', + execution = 'execution', +} -export interface IErrorDetailsModel { - message?: string; +export interface ErrorDetails { errorCode?: string; - stackTrace?: string; errorType: ErrorType; -} - -export class ErrorDetailsModel implements IErrorDetailsModel { message?: string; - errorCode?: string; stackTrace?: string; - errorType: ErrorType; - - constructor(errorDetailsModel: IErrorDetailsModel) { - Object.assign(this, errorDetailsModel); - } } From 9b190601d1a19a696a6adbbcbf440a558c34e4a4 Mon Sep 17 00:00:00 2001 From: labkey-ankurj Date: Tue, 22 Sep 2020 17:05:41 -0700 Subject: [PATCH 10/32] flip impersonating user and logged in user info --- core/src/client/ErrorHandler/ErrorType.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/client/ErrorHandler/ErrorType.tsx b/core/src/client/ErrorHandler/ErrorType.tsx index ea4246d5002..46c44b66801 100644 --- a/core/src/client/ErrorHandler/ErrorType.tsx +++ b/core/src/client/ErrorHandler/ErrorType.tsx @@ -99,7 +99,7 @@ const NOTFOUND_DETAILS = (errorMessage?: string) => ( ); const PERMISSION_SUBHEADING = () => 'You do not have the permissions required to access this page.'; -const PERMISSION_INSTRUCTION = () => 'Please contact this server\'s admin to gain access.'; +const PERMISSION_INSTRUCTION = () => "Please contact this server's admin to gain access."; const PERMISSION_DETAILS = () => ( <>
    What is a permission error?
    @@ -118,8 +118,10 @@ const PERMISSION_DETAILS = () => (
  • {' '} {getServerContext().user.isSignedIn - ? 'You are currently logged in as: ' + getServerContext().user.displayName - : 'You are not logged in.'}{' '} + ? 'You are currently logged in as: ' + getServerContext().impersonatingUser !== undefined + ? getServerContext().impersonatingUser.displayName + : getServerContext().user.displayName + : 'You are not logged in.'}

  • @@ -127,7 +129,7 @@ const PERMISSION_DETAILS = () => ( <>
    You are currently - impersonating as: {getServerContext().impersonatingUser.displayName} + impersonating as: {getServerContext().user.displayName}