From ed52c37bc645970cf47d7d6da1762419d80c94a2 Mon Sep 17 00:00:00 2001 From: Adrian Paul Date: Wed, 29 Apr 2020 17:42:15 -0400 Subject: [PATCH 1/8] feat(VariantDb): #2468 variant db page --- src/App.js | 82 ++++++++++++++------------ src/assets/appache-zeppelin.png | Bin 0 -> 12277 bytes src/common/routes.js | 1 + src/components/Header/index.js | 12 +++- src/components/VariantDb/index.css | 12 ++++ src/components/VariantDb/index.tsx | 91 +++++++++++++++++++++++++++++ 6 files changed, 158 insertions(+), 40 deletions(-) create mode 100644 src/assets/appache-zeppelin.png create mode 100644 src/components/VariantDb/index.css create mode 100644 src/components/VariantDb/index.tsx diff --git a/src/App.js b/src/App.js index cb30c7f57..9747f51c4 100644 --- a/src/App.js +++ b/src/App.js @@ -35,33 +35,25 @@ import ErrorBoundary from 'ErrorBoundary'; import ROUTES from 'common/routes'; import isPlainObject from 'lodash/isPlainObject'; import isEmpty from 'lodash/isEmpty'; +import VariantDb from './components/VariantDb'; -const userIsRequiredToLogIn = loggedInUser => { - return ( - (loggedInUser === null || - loggedInUser === undefined || - (isPlainObject(loggedInUser) && isEmpty(loggedInUser))) && - requireLogin - ); -}; +const userIsRequiredToLogIn = (loggedInUser) => + (loggedInUser === null || + loggedInUser === undefined || + (isPlainObject(loggedInUser) && isEmpty(loggedInUser))) && + requireLogin; -const userIsNotLoggedInOrMustCompleteJoinForm = loggedInUser => { - return ( - !loggedInUser || - isEmpty(loggedInUser) || - !loggedInUser.roles || - !loggedInUser.roles[0] || - !loggedInUser.acceptedTerms - ); -}; +const userIsNotLoggedInOrMustCompleteJoinForm = (loggedInUser) => + !loggedInUser || + isEmpty(loggedInUser) || + !loggedInUser.roles || + !loggedInUser.roles[0] || + !loggedInUser.acceptedTerms; -const userIsLoggedInButMustCompleteJoinForm = loggedInUser => { - return ( - isPlainObject(loggedInUser) && - !isEmpty(loggedInUser) && - (!loggedInUser.roles || !loggedInUser.roles[0] || !loggedInUser.acceptedTerms) - ); -}; +const userIsLoggedInButMustCompleteJoinForm = (loggedInUser) => + isPlainObject(loggedInUser) && + !isEmpty(loggedInUser) && + (!loggedInUser.roles || !loggedInUser.roles[0] || !loggedInUser.acceptedTerms); const forceSelectRole = ({ loggedInUser, isLoadingUser, WrapperPage = Page, ...props }) => { if (isLoadingUser) { @@ -99,7 +91,7 @@ const App = compose( + render={(props) => forceSelectRole({ isLoadingUser, Component: CohortBuilder, @@ -116,21 +108,35 @@ const App = compose( { - return forceSelectRole({ + render={(props) => + forceSelectRole({ isLoadingUser, Component: MemberSearchPage, loggedInUser, isAdmin: state.isAdmin, loggedInUserToken: state.loggedInUserToken, ...props, - }); - }} + }) + } + /> + + forceSelectRole({ + isLoadingUser, + Component: VariantDb, + loggedInUser, + isAdmin: state.isAdmin, + loggedInUserToken: state.loggedInUserToken, + ...props, + }) + } /> + render={(props) => forceSelectRole({ api, isLoadingUser, @@ -144,7 +150,7 @@ const App = compose( + render={(props) => forceSelectRole({ isLoadingUser, loggedInUser, @@ -157,7 +163,7 @@ const App = compose( + render={(props) => forceSelectRole({ isLoadingUser, Component: FileRepo, @@ -172,7 +178,7 @@ const App = compose( + render={(props) => forceSelectRole({ api, isLoadingUser, @@ -185,7 +191,7 @@ const App = compose( { + render={(props) => { if (userIsNotLoggedInOrMustCompleteJoinForm(loggedInUser)) { return ( + render={(props) => forceSelectRole({ api, isLoadingUser, @@ -228,7 +234,7 @@ const App = compose( ( + render={(props) => ( + render={(props) => forceSelectRole({ api, isLoadingUser, @@ -260,7 +266,7 @@ const App = compose( { + render={(props) => { const userIdUrlParam = props.match.params.userID; return forceSelectRole({ api, diff --git a/src/assets/appache-zeppelin.png b/src/assets/appache-zeppelin.png new file mode 100644 index 0000000000000000000000000000000000000000..567e72187087b1196023896febf53389118428b5 GIT binary patch literal 12277 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 literal 0 HcmV?d00001 diff --git a/src/common/routes.js b/src/common/routes.js index 0f2d95c80..28f77af38 100644 --- a/src/common/routes.js +++ b/src/common/routes.js @@ -16,6 +16,7 @@ const ROUTES = { search: '/search', searchMember: '/memberPage', user: '/user', + variantDb: '/variantDb', profile: '/profile', }; diff --git a/src/components/Header/index.js b/src/components/Header/index.js index f828a11e9..26c5c9e11 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -72,7 +72,7 @@ const renderAlertIfAny = (loggedInUser, currentError, dismissError) => { return null; }; -const Header = props => { +const Header = (props) => { const { currentError, dismissError, @@ -114,6 +114,14 @@ const Header = props => { Explore Data +
  • + + {' '} + Variant DB + +
  • File Repository @@ -168,7 +176,7 @@ Header.propTypes = { enabledFeatures: PropTypes.object, }; -const mapStateToProps = state => ({ +const mapStateToProps = (state) => ({ loggedInUser: state.user.loggedInUser, currentError: state.errors.currentError, }); diff --git a/src/components/VariantDb/index.css b/src/components/VariantDb/index.css new file mode 100644 index 000000000..ee8388f8a --- /dev/null +++ b/src/components/VariantDb/index.css @@ -0,0 +1,12 @@ + +.ant-list-item { + padding: 5px; +} + +.white-background { + background: #FFFFFF; +} + +.middle-align { + text-align: center; +} \ No newline at end of file diff --git a/src/components/VariantDb/index.tsx b/src/components/VariantDb/index.tsx new file mode 100644 index 000000000..811e4aea7 --- /dev/null +++ b/src/components/VariantDb/index.tsx @@ -0,0 +1,91 @@ +import * as React from 'react'; +import { Button, Col, List, Row } from 'antd'; +import { Typography } from 'antd'; +import { RocketOutlined } from '@ant-design/icons'; +import azicon from 'assets/appache-zeppelin.png'; + +import './index.css'; + +const { Title } = Typography; + +class VariantDb extends React.Component { + data = [ + { + name: 'Studies', + value: 13, + }, + { + name: 'Participants', + value:
    14494
    , + }, + { + name: 'Genes', + value: 21393, + }, + { + name: 'Variants', + value: 29848393, + }, + { + name: 'Exomic variants', + value: 2387298, + }, + ]; + + render() { + return ( +
    + + + Germline Small Variant Database + + The Kids First germline small variant data warehouse contains harmonized variant calls + and clinical data on probands and their parents. + + + + + + +
    + AppacheZeppelin +
    + Kids First is providing members with their own SPARK cluster running a web-based + Zeppelin notrebooks dansbox to explore, query and visualize its germline variant + datasets. Using Zeppelin, bioinformaticians can create interactive data analytics + and collaborative documents with SQL, Scala, Python, and more.. +
    + +
    + + + + Data Release 1 +
    May 13, 2020
    +
    + } + dataSource={this.data} + renderItem={(item) => ( + + + {item.name}: + {item.value} + + + )} + /> + + +
    + ); + } +} + +export default VariantDb; From 3f667afd3336c31ea7daffc5b1e853c79ce637b3 Mon Sep 17 00:00:00 2001 From: Adrian Paul Date: Fri, 1 May 2020 11:17:22 -0400 Subject: [PATCH 2/8] feat(VariantDb): #2468 adding testing and css fix --- src/App.js | 1 + .../VariantDb/Test/VariantDb.test.tsx | 24 +++++++++ src/components/VariantDb/index.css | 5 +- src/components/VariantDb/index.tsx | 49 +++++++++++-------- 4 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 src/components/VariantDb/Test/VariantDb.test.tsx diff --git a/src/App.js b/src/App.js index 9747f51c4..84025c577 100644 --- a/src/App.js +++ b/src/App.js @@ -127,6 +127,7 @@ const App = compose( isLoadingUser, Component: VariantDb, loggedInUser, + WrapperPage: FixedFooterPage, isAdmin: state.isAdmin, loggedInUserToken: state.loggedInUserToken, ...props, diff --git a/src/components/VariantDb/Test/VariantDb.test.tsx b/src/components/VariantDb/Test/VariantDb.test.tsx new file mode 100644 index 000000000..9617c940d --- /dev/null +++ b/src/components/VariantDb/Test/VariantDb.test.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { configure, mount, ReactWrapper } from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +import VariantDb from '../index'; + +configure({ adapter: new Adapter() }); + +describe('VariantDb', () => { + const props = {}; + + let wrapper: ReactWrapper; + + beforeEach(() => { + wrapper = mount(); + }); + + afterAll(() => { + wrapper.unmount(); + }); + + it('should render VariantDb Page', () => { + expect(wrapper.length).toEqual(1); + }); +}); diff --git a/src/components/VariantDb/index.css b/src/components/VariantDb/index.css index ee8388f8a..c8f70c61f 100644 --- a/src/components/VariantDb/index.css +++ b/src/components/VariantDb/index.css @@ -9,4 +9,7 @@ .middle-align { text-align: center; -} \ No newline at end of file +} +.ant-list-header{ + padding: 0; +} diff --git a/src/components/VariantDb/index.tsx b/src/components/VariantDb/index.tsx index 811e4aea7..4444dd909 100644 --- a/src/components/VariantDb/index.tsx +++ b/src/components/VariantDb/index.tsx @@ -35,15 +35,17 @@ class VariantDb extends React.Component { render() { return (
    - + - Germline Small Variant Database - + <Title level={2} style={{ marginBottom: 8 }}> + Germline Small Variant Database + + The Kids First germline small variant data warehouse contains harmonized variant calls and clinical data on probands and their parents. - + @@ -63,24 +65,29 @@ class VariantDb extends React.Component {
    - - - Data Release 1 -
    May 13, 2020
    - - } - dataSource={this.data} - renderItem={(item) => ( - - - {item.name}: - {item.value} + +
    + + Data Release 1 +
    May 13, 2020
    - - )} - /> + } + dataSource={this.data} + renderItem={(item) => ( + + + {item.name}: + {item.value} + + + )} + /> +
    From 11b8668e95c67edd87c1858e0a02787a2342a79c Mon Sep 17 00:00:00 2001 From: Adrian Paul Date: Mon, 4 May 2020 17:17:24 -0400 Subject: [PATCH 3/8] feat(VariantDb): #2468 api variant db --- src/App.js | 1 + src/components/Header/index.js | 31 ++++--- .../VariantDb/Test/VariantDb.test.tsx | 3 + .../VariantDb/fetchVariantCluster.tsx | 36 ++++++++ src/components/VariantDb/index.tsx | 82 ++++++++++++++++++- 5 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 src/components/VariantDb/fetchVariantCluster.tsx diff --git a/src/App.js b/src/App.js index 84025c577..d9772b345 100644 --- a/src/App.js +++ b/src/App.js @@ -124,6 +124,7 @@ const App = compose( exact render={(props) => forceSelectRole({ + api, isLoadingUser, Component: VariantDb, loggedInUser, diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 26c5c9e11..67ef3d387 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -3,11 +3,14 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { Link, withRouter } from 'react-router-dom'; import { compose } from 'recompose'; -import HouseIcon from 'react-icons/lib/fa/home'; -import DatabaseIcon from 'react-icons/lib/fa/database'; -import UserIcon from 'react-icons/lib/fa/user'; -import ExploreDataIcon from 'icons/ExploreDataIcon'; import { Alert, Badge, Layout } from 'antd'; +import { + DatabaseOutlined, + FolderOutlined, + UserOutlined, + TeamOutlined, + HomeOutlined, +} from '@ant-design/icons'; import logoPath from 'assets/logo-kids-first-data-portal.svg'; import Row from 'uikit/Row'; @@ -103,35 +106,29 @@ const Header = (props) => {
  • - Dashboard + Dashboard
  • - {' '} - Explore Data + Explore Data
  • - {' '} - Variant DB + + Variant DB +
  • - File Repository + File Repository
  • - - Members - + Members
  • diff --git a/src/components/VariantDb/Test/VariantDb.test.tsx b/src/components/VariantDb/Test/VariantDb.test.tsx index 9617c940d..5dff792d2 100644 --- a/src/components/VariantDb/Test/VariantDb.test.tsx +++ b/src/components/VariantDb/Test/VariantDb.test.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { configure, mount, ReactWrapper } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import VariantDb from '../index'; +import { jestPatchMatchMedia } from '../../../utils'; configure({ adapter: new Adapter() }); @@ -10,6 +11,8 @@ describe('VariantDb', () => { let wrapper: ReactWrapper; + beforeAll(() => jestPatchMatchMedia()); + beforeEach(() => { wrapper = mount(); }); diff --git a/src/components/VariantDb/fetchVariantCluster.tsx b/src/components/VariantDb/fetchVariantCluster.tsx new file mode 100644 index 000000000..354584fb1 --- /dev/null +++ b/src/components/VariantDb/fetchVariantCluster.tsx @@ -0,0 +1,36 @@ +export const launchCluster = async (api: Function) => { + let response; + const url = 'https://kf-api-variant-cluster-qa.kidsfirstdrc.org/stack'; + + try { + response = await api({ + method: 'POST', + url: url, + headers: { + Authorization: + 'Bearer eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1ODg1OTYzODEsImV4cCI6MTU4ODY4Mjc4MSwic3ViIjoiYWRkNzQxMDItMjRmOS00YTZjLThjYzEtZWRlMTM5NGQ3MGJmIiwiaXNzIjoiZWdvIiwiYXVkIjpbXSwianRpIjoiNmUyYjE4MzEtOTdlMi00MTI0LWE0MGItYTVkMjFhNDg3MDE2IiwiY29udGV4dCI6eyJ1c2VyIjp7Im5hbWUiOiJhZHJpYW5wYXVsY2h1QGdtYWlsLmNvbSIsImVtYWlsIjoiYWRyaWFucGF1bGNodUBnbWFpbC5jb20iLCJzdGF0dXMiOiJBcHByb3ZlZCIsImZpcnN0TmFtZSI6IkFkcmlhbiIsImxhc3ROYW1lIjoiUGF1bCIsImNyZWF0ZWRBdCI6IjIwMTktMTItMDYgMDg6MTg6MTUiLCJsYXN0TG9naW4iOiIyMDIwLTA1LTA0IDEyOjQ2OjIxIiwicHJlZmVycmVkTGFuZ3VhZ2UiOm51bGwsInJvbGVzIjpbIkFETUlOIl0sImdyb3VwcyI6W10sInBlcm1pc3Npb25zIjpbXX19fQ.dWcmQRe2Fw95k7FMxjSOaPO29_wot6f8Olvm53PpXTFTtQ3VyzlbUHgQCX6-ZAmsppPQ1vJQz7ucFcQDFkNofbjyyyouqNZyO5URHes2LnzNj8pq1C8RwusstLSvToSprujQkiHFEFLMHyAAJdGZz11rzWVHQb8ZlDvqPcwArCX_f8oh9-K_TQKnkHsME16Bld4opm3WQR7sTe23-pgOc_dFn4mFEgzx2VFw2q4yDoXXmN89MXo7a3chXLvqsvAnz9wb9iKjhAPTHxOW9NMjqSjurYak16031BKkkELhju5jxGkh-YxsiPBp6VBPYSkdKiOitOxQK1JWqQz-TgBJaQ', + }, + }); + } catch (err) { + throw new Error(err); + } + return response; +}; + +export const getStatus = async (api: Function) => { + let response; + const url = 'https://kf-api-variant-cluster-qa.kidsfirstdrc.org/stack'; + try { + response = await api({ + method: 'GET', + url: url, + headers: { + Authorization: + 'Bearer eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1ODg1OTYzODEsImV4cCI6MTU4ODY4Mjc4MSwic3ViIjoiYWRkNzQxMDItMjRmOS00YTZjLThjYzEtZWRlMTM5NGQ3MGJmIiwiaXNzIjoiZWdvIiwiYXVkIjpbXSwianRpIjoiNmUyYjE4MzEtOTdlMi00MTI0LWE0MGItYTVkMjFhNDg3MDE2IiwiY29udGV4dCI6eyJ1c2VyIjp7Im5hbWUiOiJhZHJpYW5wYXVsY2h1QGdtYWlsLmNvbSIsImVtYWlsIjoiYWRyaWFucGF1bGNodUBnbWFpbC5jb20iLCJzdGF0dXMiOiJBcHByb3ZlZCIsImZpcnN0TmFtZSI6IkFkcmlhbiIsImxhc3ROYW1lIjoiUGF1bCIsImNyZWF0ZWRBdCI6IjIwMTktMTItMDYgMDg6MTg6MTUiLCJsYXN0TG9naW4iOiIyMDIwLTA1LTA0IDEyOjQ2OjIxIiwicHJlZmVycmVkTGFuZ3VhZ2UiOm51bGwsInJvbGVzIjpbIkFETUlOIl0sImdyb3VwcyI6W10sInBlcm1pc3Npb25zIjpbXX19fQ.dWcmQRe2Fw95k7FMxjSOaPO29_wot6f8Olvm53PpXTFTtQ3VyzlbUHgQCX6-ZAmsppPQ1vJQz7ucFcQDFkNofbjyyyouqNZyO5URHes2LnzNj8pq1C8RwusstLSvToSprujQkiHFEFLMHyAAJdGZz11rzWVHQb8ZlDvqPcwArCX_f8oh9-K_TQKnkHsME16Bld4opm3WQR7sTe23-pgOc_dFn4mFEgzx2VFw2q4yDoXXmN89MXo7a3chXLvqsvAnz9wb9iKjhAPTHxOW9NMjqSjurYak16031BKkkELhju5jxGkh-YxsiPBp6VBPYSkdKiOitOxQK1JWqQz-TgBJaQ', + }, + }); + } catch (err) { + throw new Error(err); + } + return response; +}; diff --git a/src/components/VariantDb/index.tsx b/src/components/VariantDb/index.tsx index 4444dd909..986091a35 100644 --- a/src/components/VariantDb/index.tsx +++ b/src/components/VariantDb/index.tsx @@ -1,14 +1,33 @@ import * as React from 'react'; import { Button, Col, List, Row } from 'antd'; import { Typography } from 'antd'; -import { RocketOutlined } from '@ant-design/icons'; +import { RocketOutlined, LoadingOutlined } from '@ant-design/icons'; import azicon from 'assets/appache-zeppelin.png'; +import { launchCluster, getStatus } from './fetchVariantCluster'; import './index.css'; const { Title } = Typography; +const MAX_MINUTES_TRY = 10; //minutes +const INCREMENT = 3000; + +type VariantDbProps = { + api: Function; + isAdmin: boolean; + loggedInUserToken: string; +}; + +type VariantDbState = { + clusterStared: boolean; + isFetching: boolean; +}; + +class VariantDb extends React.Component { + state = { + clusterStared: false, + isFetching: false, + }; -class VariantDb extends React.Component { data = [ { name: 'Studies', @@ -32,7 +51,60 @@ class VariantDb extends React.Component { }, ]; + getCluster = (api: Function) => { + let interval: NodeJS.Timeout; + let counter = 1; + + const verifyStatus = () => { + if (counter * INCREMENT > MAX_MINUTES_TRY * 60 * 1000) { + this.setState({ + clusterStared: false, + isFetching: false, + }); + clearInterval(interval); + } + + getStatus(api).then((res) => { + if (res.status === 'CREATE_COMPLETE') { + this.setState({ + clusterStared: true, + isFetching: false, + }); + console.log(res, counter); + clearInterval(interval); + } else { + console.log(res, counter); + counter++; + } + }); + }; + + interval = setInterval(verifyStatus, INCREMENT); + }; + + handleClick = () => { + const { clusterStared, isFetching } = this.state; + const { api } = this.props; + + if (!isFetching && !clusterStared) { + this.setState({ + isFetching: true, + }); + launchCluster(api).then((res) => { + // if (res.state === 'CREATE_IN_PROGRESS') this.getCluster(api); + console.log(res.status, 'STATUS b4'); + this.getCluster(api); + }); + } else if (clusterStared) { + console.log('Launching cluster'); + //launch cluster + } else { + console.log('Waiting fo cluster the be build'); + } + }; + render() { + const { clusterStared, isFetching } = this.state; return (
    @@ -60,7 +132,11 @@ class VariantDb extends React.Component { datasets. Using Zeppelin, bioinformaticians can create interactive data analytics and collaborative documents with SQL, Scala, Python, and more..
    - From 03f12393edfafce13bcbbed680a981fcbd91a0af Mon Sep 17 00:00:00 2001 From: Adrian Paul Date: Tue, 5 May 2020 17:34:29 -0400 Subject: [PATCH 4/8] feat(VariantDb): #2468 refactoring and error handeling --- src/App.js | 2 - src/common/injectGlobals.ts | 1 + .../VariantDb/LaunchClusterCard.tsx | 91 +++++++++ .../VariantDb/fetchVariantCluster.tsx | 52 ++--- src/components/VariantDb/index.tsx | 179 ++++++++++++------ src/components/VariantDb/store.ts | 18 ++ 6 files changed, 249 insertions(+), 94 deletions(-) create mode 100644 src/components/VariantDb/LaunchClusterCard.tsx create mode 100644 src/components/VariantDb/store.ts diff --git a/src/App.js b/src/App.js index d9772b345..c5a909752 100644 --- a/src/App.js +++ b/src/App.js @@ -129,8 +129,6 @@ const App = compose( Component: VariantDb, loggedInUser, WrapperPage: FixedFooterPage, - isAdmin: state.isAdmin, - loggedInUserToken: state.loggedInUserToken, ...props, }) } diff --git a/src/common/injectGlobals.ts b/src/common/injectGlobals.ts index e8ff2c446..cd352b9fb 100644 --- a/src/common/injectGlobals.ts +++ b/src/common/injectGlobals.ts @@ -92,6 +92,7 @@ export const reactApiDataVersionApi: string = getApplicationEnvVar('DATA_VERSION export const reactApiDataVersionFallback: string = getApplicationEnvVar('DATA_VERSION_FALLBACK') || ''; export const reactApiSearchMembersApi = getApplicationEnvVar('SEARCH_MEMBERS_API') || null; +export const kfVariantCluster = getApplicationEnvVar('VARIANT_CLUSTER_API') || null; // Public Stats export const publicStatsApiRoot = getApplicationEnvVar('PUBLIC_STATS_ROOT') || ''; diff --git a/src/components/VariantDb/LaunchClusterCard.tsx b/src/components/VariantDb/LaunchClusterCard.tsx new file mode 100644 index 000000000..b42273e3c --- /dev/null +++ b/src/components/VariantDb/LaunchClusterCard.tsx @@ -0,0 +1,91 @@ +import azicon from '../../assets/appache-zeppelin.png'; +import { Button, Col, Modal, Row } from 'antd'; +import { DeleteOutlined, LoadingOutlined, RocketOutlined, ToolOutlined } from '@ant-design/icons'; +import * as React from 'react'; +import { clusterStatus, isInterimState, canBeDeleted } from './store'; + +interface Props { + status: string; + modalVisible: boolean; + hideModalOk: (e: React.MouseEvent) => void; + hideModal: (e: React.MouseEvent) => void; + showModal: (e: React.MouseEvent) => void; + handleClick: (e: React.MouseEvent) => void; +} + +const LaunchClusterCard = (props: Props) => { + const { status, modalVisible, hideModalOk, hideModal, showModal, handleClick } = props; + + let buttonText; + let buttonIcon; + switch (status) { + case clusterStatus.createComplete: + buttonText = 'Launch your SPARK cluster'; + buttonIcon = ; + break; + case clusterStatus.createInProgress: + buttonText = 'Building your SPARK cluster'; + buttonIcon = ; + break; + case clusterStatus.deleteInProgress: + buttonText = 'Deleting your SPARK cluster'; + buttonIcon = ; + break; + case clusterStatus.rollback: + buttonText = 'Error, please delete the cluster'; + buttonIcon = ; + break; + default: + buttonText = 'Build a SPARK cluster'; + buttonIcon = ; + } + return ( +
    + AppacheZeppelin +
    + Kids First is providing members with their own SPARK cluster running a web-based Zeppelin + notrebooks dansbox to explore, query and visualize its germline variant datasets. Using + Zeppelin, bioinformaticians can create interactive data analytics and collaborative + documents with SQL, Scala, Python, and more.. +
    + + + + + + {canBeDeleted(status) ? ( +
    + + +

    You want to delete this cluster?

    +
    +
    + ) : ( + '' + )} + +
    +
    + ); +}; + +export default LaunchClusterCard; diff --git a/src/components/VariantDb/fetchVariantCluster.tsx b/src/components/VariantDb/fetchVariantCluster.tsx index 354584fb1..68a615b75 100644 --- a/src/components/VariantDb/fetchVariantCluster.tsx +++ b/src/components/VariantDb/fetchVariantCluster.tsx @@ -1,36 +1,20 @@ -export const launchCluster = async (api: Function) => { - let response; - const url = 'https://kf-api-variant-cluster-qa.kidsfirstdrc.org/stack'; +import { kfVariantCluster } from 'common/injectGlobals'; - try { - response = await api({ - method: 'POST', - url: url, - headers: { - Authorization: - 'Bearer eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1ODg1OTYzODEsImV4cCI6MTU4ODY4Mjc4MSwic3ViIjoiYWRkNzQxMDItMjRmOS00YTZjLThjYzEtZWRlMTM5NGQ3MGJmIiwiaXNzIjoiZWdvIiwiYXVkIjpbXSwianRpIjoiNmUyYjE4MzEtOTdlMi00MTI0LWE0MGItYTVkMjFhNDg3MDE2IiwiY29udGV4dCI6eyJ1c2VyIjp7Im5hbWUiOiJhZHJpYW5wYXVsY2h1QGdtYWlsLmNvbSIsImVtYWlsIjoiYWRyaWFucGF1bGNodUBnbWFpbC5jb20iLCJzdGF0dXMiOiJBcHByb3ZlZCIsImZpcnN0TmFtZSI6IkFkcmlhbiIsImxhc3ROYW1lIjoiUGF1bCIsImNyZWF0ZWRBdCI6IjIwMTktMTItMDYgMDg6MTg6MTUiLCJsYXN0TG9naW4iOiIyMDIwLTA1LTA0IDEyOjQ2OjIxIiwicHJlZmVycmVkTGFuZ3VhZ2UiOm51bGwsInJvbGVzIjpbIkFETUlOIl0sImdyb3VwcyI6W10sInBlcm1pc3Npb25zIjpbXX19fQ.dWcmQRe2Fw95k7FMxjSOaPO29_wot6f8Olvm53PpXTFTtQ3VyzlbUHgQCX6-ZAmsppPQ1vJQz7ucFcQDFkNofbjyyyouqNZyO5URHes2LnzNj8pq1C8RwusstLSvToSprujQkiHFEFLMHyAAJdGZz11rzWVHQb8ZlDvqPcwArCX_f8oh9-K_TQKnkHsME16Bld4opm3WQR7sTe23-pgOc_dFn4mFEgzx2VFw2q4yDoXXmN89MXo7a3chXLvqsvAnz9wb9iKjhAPTHxOW9NMjqSjurYak16031BKkkELhju5jxGkh-YxsiPBp6VBPYSkdKiOitOxQK1JWqQz-TgBJaQ', - }, - }); - } catch (err) { - throw new Error(err); - } - return response; -}; +export const launchCluster = async (api: Function) => + await api({ + method: 'POST', + url: kfVariantCluster, + }); -export const getStatus = async (api: Function) => { - let response; - const url = 'https://kf-api-variant-cluster-qa.kidsfirstdrc.org/stack'; - try { - response = await api({ - method: 'GET', - url: url, - headers: { - Authorization: - 'Bearer eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1ODg1OTYzODEsImV4cCI6MTU4ODY4Mjc4MSwic3ViIjoiYWRkNzQxMDItMjRmOS00YTZjLThjYzEtZWRlMTM5NGQ3MGJmIiwiaXNzIjoiZWdvIiwiYXVkIjpbXSwianRpIjoiNmUyYjE4MzEtOTdlMi00MTI0LWE0MGItYTVkMjFhNDg3MDE2IiwiY29udGV4dCI6eyJ1c2VyIjp7Im5hbWUiOiJhZHJpYW5wYXVsY2h1QGdtYWlsLmNvbSIsImVtYWlsIjoiYWRyaWFucGF1bGNodUBnbWFpbC5jb20iLCJzdGF0dXMiOiJBcHByb3ZlZCIsImZpcnN0TmFtZSI6IkFkcmlhbiIsImxhc3ROYW1lIjoiUGF1bCIsImNyZWF0ZWRBdCI6IjIwMTktMTItMDYgMDg6MTg6MTUiLCJsYXN0TG9naW4iOiIyMDIwLTA1LTA0IDEyOjQ2OjIxIiwicHJlZmVycmVkTGFuZ3VhZ2UiOm51bGwsInJvbGVzIjpbIkFETUlOIl0sImdyb3VwcyI6W10sInBlcm1pc3Npb25zIjpbXX19fQ.dWcmQRe2Fw95k7FMxjSOaPO29_wot6f8Olvm53PpXTFTtQ3VyzlbUHgQCX6-ZAmsppPQ1vJQz7ucFcQDFkNofbjyyyouqNZyO5URHes2LnzNj8pq1C8RwusstLSvToSprujQkiHFEFLMHyAAJdGZz11rzWVHQb8ZlDvqPcwArCX_f8oh9-K_TQKnkHsME16Bld4opm3WQR7sTe23-pgOc_dFn4mFEgzx2VFw2q4yDoXXmN89MXo7a3chXLvqsvAnz9wb9iKjhAPTHxOW9NMjqSjurYak16031BKkkELhju5jxGkh-YxsiPBp6VBPYSkdKiOitOxQK1JWqQz-TgBJaQ', - }, - }); - } catch (err) { - throw new Error(err); - } - return response; -}; +export const getStatus = async (api: Function) => + await api({ + method: 'GET', + // url: kfVariantCluster, + url: 'https://kf-api-variant-cluster-qa.kidsfirstdrc.org/sstack', + }); + +export const deleteCluster = async (api: Function) => + await api({ + method: 'DELETE', + url: kfVariantCluster, + }); diff --git a/src/components/VariantDb/index.tsx b/src/components/VariantDb/index.tsx index 986091a35..9e810dc75 100644 --- a/src/components/VariantDb/index.tsx +++ b/src/components/VariantDb/index.tsx @@ -1,31 +1,27 @@ import * as React from 'react'; -import { Button, Col, List, Row } from 'antd'; -import { Typography } from 'antd'; -import { RocketOutlined, LoadingOutlined } from '@ant-design/icons'; -import azicon from 'assets/appache-zeppelin.png'; -import { launchCluster, getStatus } from './fetchVariantCluster'; +import { Button, Col, List, notification, Row, Typography } from 'antd'; +import { deleteCluster, getStatus, launchCluster } from './fetchVariantCluster'; +import { MAX_MINUTES_TRY, INCREMENT, clusterStatus, isInterimState } from './store'; import './index.css'; +import LaunchClusterCard from './LaunchClusterCard'; const { Title } = Typography; -const MAX_MINUTES_TRY = 10; //minutes -const INCREMENT = 3000; type VariantDbProps = { api: Function; isAdmin: boolean; - loggedInUserToken: string; }; type VariantDbState = { - clusterStared: boolean; - isFetching: boolean; + status: string; + modalVisible: boolean; }; class VariantDb extends React.Component { state = { - clusterStared: false, - isFetching: false, + status: '', + modalVisible: false, }; data = [ @@ -51,60 +47,138 @@ class VariantDb extends React.Component { }, ]; - getCluster = (api: Function) => { + errorNotification = (message: string, decription: string) => { + notification.error({ + message: message, + description: decription, + }); + }; + + async componentDidMount() { + const { api } = this.props; + try { + const res = await getStatus(api); + + if (isInterimState(res.status)) { + this.resolveInterimState(api); + } else if (res.status === clusterStatus.rollback) { + await deleteCluster(api); + this.resolveInterimState(api); + } + + this.setState({ + status: res.status, + }); + } catch (e) { + this.errorNotification(`Error ${e.response.status}`, 'Cannot connect with cluster'); + } + } + + resolveInterimState = (api: Function) => { let interval: NodeJS.Timeout; let counter = 1; - const verifyStatus = () => { + const verifyStatus = async () => { if (counter * INCREMENT > MAX_MINUTES_TRY * 60 * 1000) { this.setState({ - clusterStared: false, - isFetching: false, + status: clusterStatus.stopped, }); - clearInterval(interval); + clearInterval(interval); // throw timeout exception } - getStatus(api).then((res) => { - if (res.status === 'CREATE_COMPLETE') { + try { + const res = await getStatus(api); + + if (!isInterimState(res.status)) { this.setState({ - clusterStared: true, - isFetching: false, + status: res.status, }); - console.log(res, counter); clearInterval(interval); } else { - console.log(res, counter); counter++; } - }); + } catch (e) { + this.errorNotification(`Error ${e.response.status}`, 'Cannot connect with cluster'); + } }; interval = setInterval(verifyStatus, INCREMENT); }; - handleClick = () => { - const { clusterStared, isFetching } = this.state; + handleClick = async () => { + const { status } = this.state; const { api } = this.props; - if (!isFetching && !clusterStared) { + if (status === clusterStatus.stopped) { this.setState({ - isFetching: true, - }); - launchCluster(api).then((res) => { - // if (res.state === 'CREATE_IN_PROGRESS') this.getCluster(api); - console.log(res.status, 'STATUS b4'); - this.getCluster(api); + status: clusterStatus.createInProgress, }); - } else if (clusterStared) { - console.log('Launching cluster'); - //launch cluster - } else { - console.log('Waiting fo cluster the be build'); + + this.openNotification(); + + try { + await launchCluster(api); + this.resolveInterimState(api); + } catch (e) { + this.errorNotification(`Error ${e.response.status}`, 'Cannot launch cluster'); + } } + // else if (status === clusterStatus.createComplete) { + // //launch cluster + // } + }; + + openNotification = () => { + const key = `open${Date.now()}`; + const btn = ( + + ); + notification.open({ + message: 'Building the cluster, please wait', + description: + 'The Spark cluster is being build. This can take up to 10 min to complete. ' + + 'You can continue using the portal and come back to this page once this operation ' + + 'has completed', + duration: 15, + btn, + key, + }); + }; + + showModal = () => { + this.setState({ + modalVisible: true, + }); + }; + + hideModal = () => { + this.setState({ + modalVisible: false, + }); + }; + + hideModalOk = async () => { + const { api } = this.props; + this.setState({ + status: clusterStatus.deleteInProgress, + }); + try { + await deleteCluster(api); + } catch (e) { + this.errorNotification(`Error ${e.response.status}`, 'Error deleting the cluster'); + } + + this.setState({ + modalVisible: false, + }); + + this.resolveInterimState(api); }; render() { - const { clusterStared, isFetching } = this.state; + const { status, modalVisible } = this.state; return (
    @@ -121,25 +195,14 @@ class VariantDb extends React.Component { -
    - AppacheZeppelin -
    - Kids First is providing members with their own SPARK cluster running a web-based - Zeppelin notrebooks dansbox to explore, query and visualize its germline variant - datasets. Using Zeppelin, bioinformaticians can create interactive data analytics - and collaborative documents with SQL, Scala, Python, and more.. -
    - -
    +
    + status === clusterStatus.createInProgress || status === clusterStatus.deleteInProgress; + +export const canBeDeleted = (status: string) => + status === clusterStatus.createInProgress || + status === clusterStatus.createComplete || + status === clusterStatus.rollback; From 3cb2dfa93973ee00d3790b9da9df4b0603bbf4d2 Mon Sep 17 00:00:00 2001 From: Adrian Paul Date: Thu, 7 May 2020 16:52:08 -0400 Subject: [PATCH 5/8] feat(VariantDb): #2468 tests --- .../VariantDb/LaunchClusterCard.tsx | 9 +++- .../VariantDb/Test/VariantDb.test.tsx | 51 ++++++++++++++----- .../VariantDb/fetchVariantCluster.tsx | 3 +- src/components/VariantDb/index.tsx | 21 +++----- 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/src/components/VariantDb/LaunchClusterCard.tsx b/src/components/VariantDb/LaunchClusterCard.tsx index b42273e3c..0b70d5493 100644 --- a/src/components/VariantDb/LaunchClusterCard.tsx +++ b/src/components/VariantDb/LaunchClusterCard.tsx @@ -54,6 +54,7 @@ const LaunchClusterCard = (props: Props) => { { - const props = {}; - - let wrapper: ReactWrapper; + const mockApi = jest.fn(); + // mockApi.mockReturnValueOnce(Promise.resolve({ status: 'STOPPED' })); beforeAll(() => jestPatchMatchMedia()); - beforeEach(() => { - wrapper = mount(); - }); + let wrapper: Enzyme.ShallowWrapper, React.Component<{}, {}, any>>; - afterAll(() => { - wrapper.unmount(); + it('should render Page', () => { + mockApi.mockReturnValueOnce( + new Promise((resolve) => { + setTimeout(function () { + resolve({ status: 'STOPPED' }); + }, 1000); + }), + ); + wrapper = mount(); + expect(wrapper.length).toEqual(1); }); - it('should render VariantDb Page', () => { - expect(wrapper.length).toEqual(1); + it('should render delete button when required', () => { + mockApi + .mockResolvedValue({ status: 'STOPPED' }) + .mockResolvedValueOnce({ status: 'CREATE_IN_PROGRESS' }) + .mockResolvedValueOnce({ status: 'CREATE_COMPLETE' }) + .mockResolvedValueOnce({ status: 'DELETE_IN_PROGRESS' }) + .mockResolvedValueOnce({ status: 'ROLLBACK_COMPLETE' }); + + wrapper = mount(); + wrapper.setState({ status: 'CREATE_IN_PROGRESS' }); + expect(wrapper.find('#deleteClusterButton').length).toBeGreaterThan(0); + + wrapper = mount(); + wrapper.setState({ status: 'CREATE_COMPLETE' }); + expect(wrapper.find('#deleteClusterButton').length).toBeGreaterThan(0); + + wrapper = mount(); + wrapper.setState({ status: 'DELETE_IN_PROGRESS' }); + expect(wrapper.find('#deleteClusterButton').length).toBe(0); + + wrapper = mount(); + wrapper.setState({ status: 'ROLLBACK_COMPLETE' }); + expect(wrapper.find('#deleteClusterButton').length).toBeGreaterThan(0); }); }); diff --git a/src/components/VariantDb/fetchVariantCluster.tsx b/src/components/VariantDb/fetchVariantCluster.tsx index 68a615b75..949795696 100644 --- a/src/components/VariantDb/fetchVariantCluster.tsx +++ b/src/components/VariantDb/fetchVariantCluster.tsx @@ -9,8 +9,7 @@ export const launchCluster = async (api: Function) => export const getStatus = async (api: Function) => await api({ method: 'GET', - // url: kfVariantCluster, - url: 'https://kf-api-variant-cluster-qa.kidsfirstdrc.org/sstack', + url: kfVariantCluster, }); export const deleteCluster = async (api: Function) => diff --git a/src/components/VariantDb/index.tsx b/src/components/VariantDb/index.tsx index 9e810dc75..1772b1fbf 100644 --- a/src/components/VariantDb/index.tsx +++ b/src/components/VariantDb/index.tsx @@ -10,7 +10,6 @@ const { Title } = Typography; type VariantDbProps = { api: Function; - isAdmin: boolean; }; type VariantDbState = { @@ -58,7 +57,6 @@ class VariantDb extends React.Component { const { api } = this.props; try { const res = await getStatus(api); - if (isInterimState(res.status)) { this.resolveInterimState(api); } else if (res.status === clusterStatus.rollback) { @@ -70,7 +68,7 @@ class VariantDb extends React.Component { status: res.status, }); } catch (e) { - this.errorNotification(`Error ${e.response.status}`, 'Cannot connect with cluster'); + this.errorNotification(`Error ${e.response?.status || ''}`, 'Cannot connect with cluster'); } } @@ -98,10 +96,9 @@ class VariantDb extends React.Component { counter++; } } catch (e) { - this.errorNotification(`Error ${e.response.status}`, 'Cannot connect with cluster'); + this.errorNotification(`Error ${e.response?.status || ''}`, 'Cannot connect with cluster'); } }; - interval = setInterval(verifyStatus, INCREMENT); }; @@ -110,17 +107,15 @@ class VariantDb extends React.Component { const { api } = this.props; if (status === clusterStatus.stopped) { - this.setState({ - status: clusterStatus.createInProgress, - }); - - this.openNotification(); - try { await launchCluster(api); + this.setState({ + status: clusterStatus.createInProgress, + }); + this.openNotification(); this.resolveInterimState(api); } catch (e) { - this.errorNotification(`Error ${e.response.status}`, 'Cannot launch cluster'); + this.errorNotification(`Error ${e.response?.status || ''}`, 'Cannot launch cluster'); } } // else if (status === clusterStatus.createComplete) { @@ -167,7 +162,7 @@ class VariantDb extends React.Component { try { await deleteCluster(api); } catch (e) { - this.errorNotification(`Error ${e.response.status}`, 'Error deleting the cluster'); + this.errorNotification(`Error ${e.response?.status || ''}`, 'Error deleting the cluster'); } this.setState({ From c9b8582bb6ce7f1131753a121f673f36df821357 Mon Sep 17 00:00:00 2001 From: Adrian Paul Date: Fri, 8 May 2020 15:54:15 -0400 Subject: [PATCH 6/8] feat(variantDB): #2468 variant db only for allowed users --- src/common/profile.js | 7 +++ .../ParticipantsTable.js | 29 +++++------ .../ParticipantsTableView/index.js | 49 ++++++++++--------- src/components/Header/index.js | 17 ++++--- src/components/Login/utils.js | 17 +++++-- src/components/VariantDb/index.tsx | 21 ++++++-- src/stateProviders/provideLoggedInUser.js | 48 +++++++++--------- 7 files changed, 114 insertions(+), 74 deletions(-) diff --git a/src/common/profile.js b/src/common/profile.js index d6c877f0a..46400bf13 100644 --- a/src/common/profile.js +++ b/src/common/profile.js @@ -17,3 +17,10 @@ export const extractInfoFromRoles = roles => { Icon, }; }; + +export const isPartOfGroup = (group, user) => { + if (!user || !group) { + return false; + } + return user.egoGroups ? user.egoGroups.includes(group) : false; +}; diff --git a/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js b/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js index fda370e3c..29f31c638 100644 --- a/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js +++ b/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js @@ -20,7 +20,7 @@ import FileIcon from 'icons/FileIcon'; import { MONDOLink } from '../../Utils/DiagnosisAndPhenotypeLinks'; import SaveSetModal from './SaveSetModal'; import './ParticipantTableView.css'; -import { isFeatureEnabled } from 'common/featuresToggles'; +import { isPartOfGroup } from 'common/profile'; const SelectionCell = ({ value: checked, onCellSelected, row }) => { return ( @@ -30,7 +30,7 @@ const SelectionCell = ({ value: checked, onCellSelected, row }) => { onChange={ row ? () => onCellSelected(!checked, row) - : evt => { + : (evt) => { onCellSelected(evt.currentTarget.checked); } } @@ -38,7 +38,7 @@ const SelectionCell = ({ value: checked, onCellSelected, row }) => { ); }; -const isMondo = datum => typeof datum === 'string' && datum.includes('MONDO'); +const isMondo = (datum) => typeof datum === 'string' && datum.includes('MONDO'); const enhance = compose(withState('collapsed', 'setCollapsed', true)); @@ -121,7 +121,7 @@ const ParticipantIdLink = compose(({ value: idParticipant }) => { const participantsTableViewColumns = (onRowSelected, onAllRowsSelected, dirtyHack) => [ { - Header: props => { + Header: (props) => { return ( ); }, - Cell: props => { + Cell: (props) => { return ; }, accessor: 'selected', @@ -143,7 +143,7 @@ const participantsTableViewColumns = (onRowSelected, onAllRowsSelected, dirtyHac { Header: 'Participant ID', accessor: 'participantId', - Cell: props => , + Cell: (props) => , }, { Header: 'Study Name', @@ -155,7 +155,7 @@ const participantsTableViewColumns = (onRowSelected, onAllRowsSelected, dirtyHac { Header: 'Diagnosis Category', accessor: 'diagnosisCategories', - Cell: props => ( + Cell: (props) => ( ( + Cell: (props) => ( ( + Cell: (props) => ( ( + Cell: (props) => ( , + Cell: (props) => , field: 'files', sortable: false, }, @@ -231,7 +231,7 @@ class ParticipantsTable extends Component { this.state = { columns: configureCols( participantsTableViewColumns(props.onRowSelected, props.onAllRowsSelected, this.dirtyHack), - ).map(field => + ).map((field) => field.sortable !== false && SORTABLE_FIELDS_MAPPING.has(field.accessor) ? { ...field, sortable: true } : { ...field, sortable: false }, @@ -286,6 +286,7 @@ class ParticipantsTable extends Component { const selectedRowsCount = allRowsSelected ? dataTotalCount : selectedRows.length; const projectId = arrangerProjectId; + console.log(loggedInUser, 'this'); return ( {this.state.showModal && ( @@ -311,7 +312,7 @@ class ParticipantsTable extends Component { {`\u00A0participant${ selectedRowsCount > 1 ? 's are' : ' is' } selected\u00A0`} - @@ -320,7 +321,7 @@ class ParticipantsTable extends Component {
    - {isFeatureEnabled('variantsDb') && ( + {isPartOfGroup('kf-investigator', loggedInUser) && ( From 34c0526d1b8cebeecc0a666faf1b7178642557cd Mon Sep 17 00:00:00 2001 From: Adrian Paul Date: Mon, 11 May 2020 13:27:47 -0400 Subject: [PATCH 8/8] feat(variantDb): #2468 review comment --- src/common/injectGlobals.ts | 2 +- src/common/profile.js | 11 ++--- .../ParticipantsTable.js | 1 - src/components/Login/utils.js | 2 +- .../VariantDb/LaunchClusterCard.tsx | 10 ++-- .../VariantDb/Test/VariantDb.test.tsx | 48 +++++++++---------- .../VariantDb/fetchVariantCluster.tsx | 8 ++-- src/components/VariantDb/index.css | 6 +++ 8 files changed, 40 insertions(+), 48 deletions(-) diff --git a/src/common/injectGlobals.ts b/src/common/injectGlobals.ts index cd352b9fb..ebeffa0ce 100644 --- a/src/common/injectGlobals.ts +++ b/src/common/injectGlobals.ts @@ -92,7 +92,7 @@ export const reactApiDataVersionApi: string = getApplicationEnvVar('DATA_VERSION export const reactApiDataVersionFallback: string = getApplicationEnvVar('DATA_VERSION_FALLBACK') || ''; export const reactApiSearchMembersApi = getApplicationEnvVar('SEARCH_MEMBERS_API') || null; -export const kfVariantCluster = getApplicationEnvVar('VARIANT_CLUSTER_API') || null; +export const kfVariantClusterUrl = getApplicationEnvVar('VARIANT_CLUSTER_API') || null; // Public Stats export const publicStatsApiRoot = getApplicationEnvVar('PUBLIC_STATS_ROOT') || ''; diff --git a/src/common/profile.js b/src/common/profile.js index 46400bf13..40c81a470 100644 --- a/src/common/profile.js +++ b/src/common/profile.js @@ -1,12 +1,12 @@ import { ROLES } from './constants'; -export const extractInfoFromRoles = roles => { +export const extractInfoFromRoles = (roles) => { if (!Array.isArray(roles) || roles.length === 0) { return null; } const role = roles[0]; - const displayInfo = ROLES.find(r => r.type === role); + const displayInfo = ROLES.find((r) => r.type === role); const Icon = displayInfo.icon; const backgroundColor = displayInfo.color; const roleName = displayInfo.displayName; @@ -18,9 +18,4 @@ export const extractInfoFromRoles = roles => { }; }; -export const isPartOfGroup = (group, user) => { - if (!user || !group) { - return false; - } - return user.egoGroups ? user.egoGroups.includes(group) : false; -}; +export const isPartOfGroup = (group, user) => (user?.egoGroups || []).includes(group); diff --git a/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js b/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js index 6e5bcc46d..c87a0d807 100644 --- a/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js +++ b/src/components/CohortBuilder/ParticipantsTableView/ParticipantsTable.js @@ -286,7 +286,6 @@ class ParticipantsTable extends Component { const selectedRowsCount = allRowsSelected ? dataTotalCount : selectedRows.length; const projectId = arrangerProjectId; - console.log(loggedInUser, 'this'); return ( {this.state.showModal && ( diff --git a/src/components/Login/utils.js b/src/components/Login/utils.js index 28a5f8959..1d377381b 100644 --- a/src/components/Login/utils.js +++ b/src/components/Login/utils.js @@ -33,7 +33,7 @@ export const getUserGroups = ({ validatedPayload }) => { if (!validatedPayload) return []; const jwtUser = get(validatedPayload, 'context.user', {}); const groups = jwtUser.groups; - return groups && isArrayLikeObject(groups) ? groups : []; + return groups ? groups : []; }; export const validateJWT = ({ jwt }) => { diff --git a/src/components/VariantDb/LaunchClusterCard.tsx b/src/components/VariantDb/LaunchClusterCard.tsx index 0b70d5493..11a696006 100644 --- a/src/components/VariantDb/LaunchClusterCard.tsx +++ b/src/components/VariantDb/LaunchClusterCard.tsx @@ -3,6 +3,7 @@ import { Button, Col, Modal, Row } from 'antd'; import { DeleteOutlined, LoadingOutlined, RocketOutlined, ToolOutlined } from '@ant-design/icons'; import * as React from 'react'; import { clusterStatus, isInterimState, canBeDeleted } from './store'; +import './index.css'; interface Props { status: string; @@ -40,10 +41,7 @@ const LaunchClusterCard = (props: Props) => { buttonIcon = ; } return ( -
    +
    AppacheZeppelin
    Kids First is providing members with their own SPARK cluster running a web-based Zeppelin @@ -64,7 +62,7 @@ const LaunchClusterCard = (props: Props) => { - {canBeDeleted(status) ? ( + {canBeDeleted(status) && (
    - ) : ( - '' )} diff --git a/src/components/VariantDb/Test/VariantDb.test.tsx b/src/components/VariantDb/Test/VariantDb.test.tsx index ae63544bb..6757e5c82 100644 --- a/src/components/VariantDb/Test/VariantDb.test.tsx +++ b/src/components/VariantDb/Test/VariantDb.test.tsx @@ -3,52 +3,48 @@ import Adapter from 'enzyme-adapter-react-16'; import VariantDb from '../index'; import { jestPatchMatchMedia } from '../../../utils'; import Enzyme, { mount } from 'enzyme'; +import { clusterStatus } from '../store'; -// configure({ adapter: new Adapter() }); Enzyme.configure({ adapter: new Adapter() }); describe('VariantDb', () => { const mockApi = jest.fn(); - // mockApi.mockReturnValueOnce(Promise.resolve({ status: 'STOPPED' })); beforeAll(() => jestPatchMatchMedia()); let wrapper: Enzyme.ShallowWrapper, React.Component<{}, {}, any>>; - it('should render Page', () => { - mockApi.mockReturnValueOnce( - new Promise((resolve) => { - setTimeout(function () { - resolve({ status: 'STOPPED' }); - }, 1000); - }), - ); + beforeEach(() => { wrapper = mount(); + }); + + afterEach(() => { + wrapper.unmount(); + }); + + it('should render Page', () => { + mockApi.mockResolvedValueOnce({ status: clusterStatus.stopped }); expect(wrapper.length).toEqual(1); }); it('should render delete button when required', () => { mockApi - .mockResolvedValue({ status: 'STOPPED' }) - .mockResolvedValueOnce({ status: 'CREATE_IN_PROGRESS' }) - .mockResolvedValueOnce({ status: 'CREATE_COMPLETE' }) - .mockResolvedValueOnce({ status: 'DELETE_IN_PROGRESS' }) - .mockResolvedValueOnce({ status: 'ROLLBACK_COMPLETE' }); + .mockResolvedValue({ status: clusterStatus.stopped }) + .mockResolvedValueOnce({ status: clusterStatus.createInProgress }) + .mockResolvedValueOnce({ status: clusterStatus.createComplete }) + .mockResolvedValueOnce({ status: clusterStatus.deleteInProgress }) + .mockResolvedValueOnce({ status: clusterStatus.rollback }); - wrapper = mount(); - wrapper.setState({ status: 'CREATE_IN_PROGRESS' }); - expect(wrapper.find('#deleteClusterButton').length).toBeGreaterThan(0); + wrapper.setState({ status: clusterStatus.createInProgress }); + expect(wrapper.find('#deleteClusterButton').length).toBe(2); //Ant