Skip to content

Commit

Permalink
CORE-1952 Display limit check errors from instant launch actions
Browse files Browse the repository at this point in the history
* Updated the `InstantLaunchButtonWrapper` to check for error codes
related to VICE permission or limit errors, and to display the same
related dialog as displayed from the `AppName` in the apps listing.

* Since `POST /analyses` does not return `limitChecks` info in errors,
refactored the `instantlyLaunch` service facade to parse `limitChecks`
info from the `/quicklaunches/{id}/app-info` call it already makes,
and to reject the call if the `canRun` flag is set to `false`.

* Removed `catch` from `instantlyLaunch` promises so that errors from
those endpoints can be properly displayed in the UI
(rather than causing undefined `ql` or `app` exceptions).

* Refactored `RunErrorDialog` from `AppName` into its own component.

* Removed duplicate `usageSummary*Response` mocks from data stories.

* Removed duplicate template args from data listing stories.

* Fixed a deprecated `rows` warning in `FormMultilineTextField`.

* Added missing and updated vice i18n namespaces to pages.
  • Loading branch information
psarando committed Oct 14, 2023
1 parent ce9d266 commit 3251953
Show file tree
Hide file tree
Showing 18 changed files with 603 additions and 332 deletions.
61 changes: 13 additions & 48 deletions src/components/apps/AppName.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,25 @@
/**
* A component that displays app's name.
*
* @author sriram
* @author sriram, psarando
*/
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import { useTranslation } from "i18n";
import PropTypes from "prop-types";

import Link from "next/link";
import DELink from "components/utils/DELink";
import { ERROR_CODES } from "components/error/errorCode";
import AccessRequestDialog from "components/vice/AccessRequestDialog";
import RunErrorDialog from "components/vice/RunErrorDialog";
import VicePendingRequestDlg from "components/vice/VicePendingRequestDlg";

import { useUserProfile } from "contexts/userProfile";
import DEDialog from "components/utils/DEDialog";
import RunError from "./RunError";
import { useAppLaunchLink } from "./utils";
import ids from "./ids";

import Highlighter from "components/highlighter/Highlighter";
import { Button, Link as MuiLink, Typography } from "@material-ui/core";

function RunErrorDialog(props) {
const { baseId, code, open, viceQuota, runningJobs, onClose } = props;
const { t } = useTranslation("launch");
const { t: i18Common } = useTranslation("common");
let title;
if (code === ERROR_CODES.ERR_LIMIT_REACHED) {
title = t("jobLimitReached");
} else if (code === ERROR_CODES.ERR_FORBIDDEN) {
title = t("accessDenied");
}
return (
<DEDialog
baseId={baseId}
title={title}
open={open}
actions={
<Button color="primary" onClick={onClose}>
{i18Common("ok")}
</Button>
}
onClose={onClose}
>
<Typography>
<RunError
code={code}
viceQuota={viceQuota}
runningJobs={runningJobs}
/>
</Typography>
</DEDialog>
);
}
import { Link as MuiLink } from "@material-ui/core";

function AppName(props) {
const {
Expand All @@ -65,24 +31,23 @@ function AppName(props) {
searchTerm,
limitChecks,
} = props;
const [runErrorCodes, setRunErrorCodes] = useState(null);
const [userProfile] = useUserProfile();
const [viceQuota, setViceQuota] = useState();
const [runningJobs, setRunningJobs] = useState();
const [accessRequestDialogOpen, setAccessRequestDialogOpen] =
useState(false);
const [pendingRequestDlgOpen, setPendingRequestDlgOpen] = useState(false);
const [errorDialogOpen, setErrorDialogOpen] = useState(false);
const [href, as] = useAppLaunchLink(systemId, appId);
const { t } = useTranslation("apps");

useEffect(() => {
if (limitChecks && !limitChecks.canRun) {
setRunErrorCodes(limitChecks.results[0]?.reasonCodes);
setViceQuota(limitChecks.results[0]?.additionalInfo?.maxJobs);
setRunningJobs(limitChecks.results[0]?.additionalInfo?.runningJobs);
}
}, [limitChecks]);
let runErrorCodes;
let viceQuota;
let runningJobs;

if (limitChecks?.results) {
runErrorCodes = limitChecks.results[0]?.reasonCodes;
viceQuota = limitChecks.results[0]?.additionalInfo?.maxJobs;
runningJobs = limitChecks.results[0]?.additionalInfo?.runningJobs;
}

let title = "";
if (isDisabled) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/apps/launch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ import { addSavedLaunch } from "serviceFacades/savedLaunches";

import { trackIntercomEvent, IntercomEvents } from "common/intercom";

import RunError from "components/apps/RunError";
import AppLaunchWizard from "./AppLaunchWizard";
import WrappedErrorHandler from "components/error/WrappedErrorHandler";
import { ERROR_CODES } from "components/error/errorCode";
import AccessRequestDialog from "components/vice/AccessRequestDialog";
import RunError from "components/vice/RunError";
import ids from "components/apps/ids";

import { Button, Typography } from "@material-ui/core";
Expand Down
2 changes: 1 addition & 1 deletion src/components/forms/FormMultilineTextField.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from "react";
import FormTextField from "./FormTextField";

const FormMultilineTextField = (props) => (
<FormTextField multiline rows={3} {...props} />
<FormTextField multiline minRows={3} {...props} />
);

export default FormMultilineTextField;
48 changes: 47 additions & 1 deletion src/components/instantlaunches/InstantLaunchButtonWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ import { useDefaultOutputDir } from "components/data/utils";
import withErrorAnnouncer from "components/error/withErrorAnnouncer";
import { getHost } from "components/utils/getHost";
import SignInDialog from "components/utils/SignInDialog";
import AccessRequestDialog from "components/vice/AccessRequestDialog";
import RunErrorDialog from "components/vice/RunErrorDialog";
import VicePendingRequestDlg from "components/vice/VicePendingRequestDlg";

import constants from "constants.js";
import { useUserProfile } from "contexts/userProfile";
import ids from "./ids";
import { InstantLaunchSubmissionDialog } from "./index";
import { instantlyLaunch } from "serviceFacades/instantlaunches";
import { useTranslation } from "i18n";
import { ERROR_CODES } from "components/error/errorCode";

function InstantLaunchButtonWrapper(props) {
const {
Expand All @@ -35,6 +41,11 @@ function InstantLaunchButtonWrapper(props) {

const [open, setOpen] = React.useState(false);
const [signInDlgOpen, setSignInDlgOpen] = React.useState(false);
const [accessRequestDialogOpen, setAccessRequestDialogOpen] =
React.useState(false);
const [pendingRequestDlgOpen, setPendingRequestDlgOpen] =
React.useState(false);
const [runErrorDetails, setRunErrorDetails] = React.useState(null);
const [ilUrl, setIlUrl] = React.useState();

const { t } = useTranslation("launch");
Expand Down Expand Up @@ -66,7 +77,25 @@ function InstantLaunchButtonWrapper(props) {
},
onError: (err) => {
setOpen(false);
showErrorAnnouncer(err.message, err);

const respData = err.response?.data;
const runErrorCode = respData?.error_code;
const details = respData?.details;

if (runErrorCode === ERROR_CODES.ERR_PERMISSION_NEEDED) {
if (details?.pendingRequest) {
setPendingRequestDlgOpen(true);
} else {
setAccessRequestDialogOpen(true);
}
} else if (
runErrorCode === ERROR_CODES.ERR_LIMIT_REACHED ||
runErrorCode === ERROR_CODES.ERR_FORBIDDEN
) {
setRunErrorDetails({ runErrorCode, ...details });
} else {
showErrorAnnouncer(err.message, err);
}
},
});

Expand All @@ -91,6 +120,23 @@ function InstantLaunchButtonWrapper(props) {
open={signInDlgOpen}
handleClose={() => setSignInDlgOpen(false)}
/>
<AccessRequestDialog
open={accessRequestDialogOpen}
baseId={ids.ACCESS_REQUEST_DLG}
onClose={() => setAccessRequestDialogOpen(false)}
/>
<VicePendingRequestDlg
open={pendingRequestDlgOpen}
onClose={() => setPendingRequestDlgOpen(false)}
/>
<RunErrorDialog
baseId={ids.RUN_ERROR_DLG}
open={!!runErrorDetails}
onClose={() => setRunErrorDetails(null)}
code={runErrorDetails?.runErrorCode}
runningJobs={runErrorDetails?.runningJobs}
viceQuota={runErrorDetails?.maxJobs}
/>
</>
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/instantlaunches/ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ export default {
ADD_LISTING: "addToListing",
RM_LISTING: "removeFromListing",
ADD_NAV_DRAWER: "addToDrawer",
ACCESS_REQUEST_DLG: "accessRequestDlg",
RUN_ERROR_DLG: "runErrorDlg",
};
36 changes: 12 additions & 24 deletions src/components/apps/RunError.js → src/components/vice/RunError.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
/**
* @author sriram
*
* Return a Translation component based on the App launch run error code.
*
* @author sriram, psarando
*/
import React from "react";
import { Trans, useTranslation } from "i18n";
import { intercomShow } from "common/intercom";
import { ERROR_CODES } from "components/error/errorCode";
import { Link } from "@material-ui/core";

const SupportLink = (props) => (
<Link
variant="body1"
component="button"
onClick={intercomShow}
{...props}
/>
);

export default function RunError(props) {
const { code, runningJobs, viceQuota } = props;
const { t } = useTranslation("launch");
Expand All @@ -22,17 +30,7 @@ export default function RunError(props) {
components={{
b: <b />,
br: <br />,
support: (
<Link
href="#"
component="button"
onClick={(event) => {
// prevent form submission
event.preventDefault();
intercomShow();
}}
/>
),
support: <SupportLink />,
}}
/>
);
Expand All @@ -42,17 +40,7 @@ export default function RunError(props) {
t={t}
i18nKey="launchForbiddenPrompt"
components={{
support: (
<Link
href="#"
component="button"
onClick={(event) => {
// prevent form submission
event.preventDefault();
intercomShow();
}}
/>
),
support: <SupportLink />,
}}
/>
);
Expand Down
50 changes: 50 additions & 0 deletions src/components/vice/RunErrorDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* A dialog component to display the App launch run error code.
*
* @author sriram, psarando
*/
import React from "react";

import { useTranslation } from "i18n";
import { ERROR_CODES } from "components/error/errorCode";
import DEDialog from "components/utils/DEDialog";
import RunError from "./RunError";

import { Button, Typography } from "@material-ui/core";

const RunErrorDialog = (props) => {
const { baseId, code, open, viceQuota, runningJobs, onClose } = props;

const { t } = useTranslation(["launch", "common"]);

let title;
if (code === ERROR_CODES.ERR_LIMIT_REACHED) {
title = t("jobLimitReached");
} else if (code === ERROR_CODES.ERR_FORBIDDEN) {
title = t("accessDenied");
}

return (
<DEDialog
baseId={baseId}
title={title}
open={open}
actions={
<Button color="primary" onClick={onClose}>
{t("common:ok")}
</Button>
}
onClose={onClose}
>
<Typography>
<RunError
code={code}
viceQuota={viceQuota}
runningJobs={runningJobs}
/>
</Typography>
</DEDialog>
);
};

export default RunErrorDialog;
5 changes: 4 additions & 1 deletion src/i18n.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,20 @@ import {
} from "next-i18next";

// The always-needed namespaces, mainly from the components in the global
// toolbars, such as bags, global search, and the notifications menu.
// toolbars, such as instant launch drawer items, bags, global search, and the
// notifications menu.
const RequiredNamespaces = [
"analyses",
"apps",
"bags",
"common",
"instantlaunches",
"launch",
"notifications",
"search",
"sharing",
"util",
"vice",
];

// reexport everything
Expand Down
3 changes: 1 addition & 2 deletions src/pages/apps/[systemId]/[appId]/launch.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,9 @@ export async function getServerSideProps({ locale }) {
props: {
...(await serverSideTranslations(locale, [
"data",
"launch",
"upload",
"urlImport",
// "apps" already included by RequiredNamespaces
// "apps" and "launch" already included by RequiredNamespaces
...RequiredNamespaces,
])),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,9 @@ export async function getServerSideProps({ locale }) {
props: {
...(await serverSideTranslations(locale, [
"data",
"launch",
"upload",
"urlImport",
// "apps" already included by RequiredNamespaces
// "apps" and "launch" already included by RequiredNamespaces
...RequiredNamespaces,
])),
},
Expand Down
1 change: 0 additions & 1 deletion src/pages/data/ds/[...pathItems].js
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ export async function getServerSideProps({ locale }) {
props: {
...(await serverSideTranslations(locale, [
"data",
"launch",
"metadata",
"upload",
"urlImport",
Expand Down
Loading

0 comments on commit 3251953

Please sign in to comment.