From eb89ed96f08fcf0602f4ef0eacc924936870f40d Mon Sep 17 00:00:00 2001 From: Ann Catton Date: Thu, 25 Feb 2021 16:28:24 -0500 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=9A=B8=20Add=20config=20error=20handl?= =?UTF-8?q?ing=20for=20repository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pages/repository/index.tsx | 184 +++++++++++++++++++++----- 1 file changed, 154 insertions(+), 30 deletions(-) diff --git a/components/pages/repository/index.tsx b/components/pages/repository/index.tsx index 689b9046..ca278a35 100644 --- a/components/pages/repository/index.tsx +++ b/components/pages/repository/index.tsx @@ -7,6 +7,7 @@ import { RepoFiltersType } from './sqonTypes'; import { getConfig } from '../../../global/config'; import { css } from '@emotion/core'; import createArrangerFetcher from '../../utils/arrangerFetcher'; +import { useEffect, useState } from 'react'; const Arranger = dynamic( () => import('@arranger/components/dist/Arranger').then((comp) => comp.Arranger), @@ -36,6 +37,126 @@ export interface PageContentProps { const arrangerFetcher = createArrangerFetcher({}); +const projectsQuery = ` + query { + projects { + id + active + indices { + id + esIndex + graphqlField + } + } + } +`; + +type Project = { + id: string; + active: boolean; + indices: [{ id: string; esIndex: string; graphqlField: string }]; +}; + +const ConfigErrorDisplay = ({ children }: { children: React.ReactNode }) => ( +
+
+ css` + padding: 2rem; + border: 1px solid ${theme.colors.grey_3}; + border-radius: 5px; + ${theme.shadow.default}; + ${theme.typography.heading}; + max-width: 600px; + ` + } + > + {children} +
+
+); + +const getConfigError = ({ + availableProjects, + projectId, + index, + graphqlField, +}: { + availableProjects: Project[]; + projectId: string; + index: string; + graphqlField: string; +}) => { + const Missing = ( + + css` + color: ${theme.colors.error_dark}; + ` + } + > + Missing + + ); + if (!(projectId && index && graphqlField)) { + return ( + + One or more of project ID, Elasticsearch index, and index alias (graphQL field) values + required by the DMS portal do not exist. Make sure the values are specified in the + config.yaml file during DMS installation and have been used to create your project in the + Arranger Admin UI. + + + ); + } + if (!availableProjects.filter((project) => project.active).length) { + return ( + + No active projects for the DMS portal exist. Make sure the project specified in the + config.yaml file during DMS installation has been created in the Arranger Admin UI. + + ); + } + const foundProject = + availableProjects.length && availableProjects.find((project) => project.id === projectId); + if (!foundProject) { + return ( + + The project ID "{projectId}" configured for the DMS portal does not match any existing + project. Make sure the project ID specified in the config.yaml file during DMS installation + has been created in the Arranger Admin UI. + + ); + } + + const aliasFromList = foundProject.indices.find((i) => i.id.match(projectId))?.graphqlField; + const matchesConfiguredAlias = graphqlField === aliasFromList; + + if (!matchesConfiguredAlias) { + return ( +
+ The Elasticsearch alias name (graphQL field) "{aliasFromList}" required by the DMS portal + for project ID "{projectId}" does not match your configured alias name "{graphqlField}" . + Make sure the value specified in the config.yaml file during DMS installation has been used + to create your project in the Arranger Admin UI. +
+ ); + } + return null; +}; + const RepositoryPage = () => { const { NEXT_PUBLIC_ARRANGER_PROJECT_ID, @@ -43,38 +164,41 @@ const RepositoryPage = () => { NEXT_PUBLIC_ARRANGER_INDEX, } = getConfig(); + const [availableProjects, setAvailableProjects] = useState([]); + useEffect(() => { + fetch(`https://arranger.qa.overture.bio/admin/graphql`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + variables: {}, + query: projectsQuery, + }), + }) + .then((res) => { + if (res.status !== 200) { + throw new Error('Could not retrieve available projects from Arranger server!'); + } + return res.json(); + }) + .then(({ data: { projects } }: { data: { projects: Project[] } }) => { + setAvailableProjects(projects); + }) + .catch((err) => { + console.warn(err); + }); + }, []); + + const ConfigError = getConfigError({ + availableProjects, + projectId: NEXT_PUBLIC_ARRANGER_PROJECT_ID, + index: NEXT_PUBLIC_ARRANGER_INDEX, + graphqlField: NEXT_PUBLIC_ARRANGER_GRAPHQL_FIELD, + }); + return ( - {/* TODO: arranger config error handling tbd */} - {!( - NEXT_PUBLIC_ARRANGER_PROJECT_ID && - NEXT_PUBLIC_ARRANGER_GRAPHQL_FIELD && - NEXT_PUBLIC_ARRANGER_INDEX - ) ? ( -
-
- css` - ${theme.typography.subheading} - ` - } - > - Arranger is missing configuration values. Please check your ".env" file. -
    -
  • Project ID: {NEXT_PUBLIC_ARRANGER_PROJECT_ID || 'missing'}
  • -
  • GraphQL Field: {NEXT_PUBLIC_ARRANGER_GRAPHQL_FIELD || 'missing'}
  • -
  • Index: {NEXT_PUBLIC_ARRANGER_INDEX || 'missing'}
  • -
-
-
+ {ConfigError ? ( + {ConfigError} ) : ( Date: Fri, 26 Feb 2021 15:18:55 -0500 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=92=84=20Styling=20for=20error=20mess?= =?UTF-8?q?ages,=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/ErrorContainer.tsx | 53 +++++ .../pages/repository/getConfigError.tsx | 211 ++++++++++++++++++ components/pages/repository/index.tsx | 117 +--------- components/theme/colors.ts | 2 + components/theme/icons/error.tsx | 45 ++++ components/theme/icons/index.tsx | 4 + components/theme/icons/warning.tsx | 37 +++ 7 files changed, 361 insertions(+), 108 deletions(-) create mode 100644 components/ErrorContainer.tsx create mode 100644 components/pages/repository/getConfigError.tsx create mode 100644 components/theme/icons/error.tsx create mode 100644 components/theme/icons/warning.tsx diff --git a/components/ErrorContainer.tsx b/components/ErrorContainer.tsx new file mode 100644 index 00000000..0a6bae04 --- /dev/null +++ b/components/ErrorContainer.tsx @@ -0,0 +1,53 @@ +import { css } from '@emotion/core'; +import { Error } from './theme/icons'; + +const ErrorContainer = ({ children, title }: { children: React.ReactNode; title: string }) => ( +
+
+ css` + padding: 1rem 2rem; + border: 1px solid ${theme.colors.error_2}; + border-radius: 5px; + ${theme.shadow.default}; + ${theme.typography.subheading}; + font-weight: normal; + line-height: 26px; + max-width: 600px; + background-color: ${theme.colors.error_1}; + color: ${theme.colors.accent_dark}; + ` + } + > +

+ {' '} + {title} +

+ {children} +
+
+); + +export default ErrorContainer; diff --git a/components/pages/repository/getConfigError.tsx b/components/pages/repository/getConfigError.tsx new file mode 100644 index 00000000..3ac48065 --- /dev/null +++ b/components/pages/repository/getConfigError.tsx @@ -0,0 +1,211 @@ +import { css } from '@emotion/core'; + +import { Checkmark, Warning } from '../../theme/icons'; +import theme from '../../theme/'; +import StyledLink from '../../Link'; + +import { getConfig } from '../../../global/config'; +import { Project } from './'; + +const ArrangerAdminUILink = () => { + const { NEXT_PUBLIC_ARRANGER_API } = getConfig(); + const splitApi: string[] = NEXT_PUBLIC_ARRANGER_API.split('//'); + const adminUiUrl: string = [splitApi[0], '//ui.', splitApi[1]].join(''); + return ( + + Arranger Admin UI + + ); +}; + +const WarningListItem = ({ fieldName }: { fieldName: string }) => ( +
  • + css` + color: ${theme.colors.error_dark}; + display: flex; + align-items: center; + ` + } + > + + + {fieldName}:{' '} + + Missing + + +
  • +); + +const ListItem = ({ fieldName, value }: { fieldName: string; value: string }) => ( +
  • + + + {fieldName}:{' '} + + {value} + + +
  • +); + +const getConfigError = ({ + availableProjects, + projectId, + index, + graphqlField, +}: { + availableProjects: Project[]; + projectId: string; + index: string; + graphqlField: string; +}) => { + if (!(projectId && index && graphqlField)) { + return ( + + One or more of the following values required by the DMS portal do not exist. Please make + sure the values are specified in the DMS{' '} + + config.yaml + {' '} + file during installation and have been used to create your project in the{' '} + . +
      + {[ + { field: 'Project ID', value: projectId }, + { field: 'Alias name', value: graphqlField }, + { field: 'Elasticsearch index', value: index }, + ].map(({ field, value }) => { + return value ? ( + + ) : ( + + ); + })} +
    +
    + ); + } + + if (!availableProjects.filter((project) => project.active).length) { + return ( + + No active projects for the DMS portal exist. Please make sure the project specified in the + DMS{' '} + + config.yaml + {' '} + file during installation has been created in the . + + ); + } + + const foundProject = + availableProjects.length && availableProjects.find((project) => project.id === projectId); + + if (!foundProject) { + return ( + + The project ID:{' '} + + "{projectId}" + {' '} + configured for the DMS portal does not match any existing project. Please make sure the + project ID specified in the DMS{' '} + + config.yaml + {' '} + file during installation has been created in the . + + ); + } + + const aliasFromList = foundProject.indices.find((i) => i.id.match(projectId))?.graphqlField; + const matchesConfiguredAlias = graphqlField === aliasFromList; + + if (!matchesConfiguredAlias) { + return ( +
    + The Elasticsearch alias name (graphQL field):{' '} + + "{aliasFromList}" + {' '} + required by the DMS portal for project ID:{' '} + + "{projectId}" + {' '} + does not match your configured alias name:{' '} + + "{graphqlField}" + + . Please make sure the value specified in the DMS{' '} + + config.yaml + {' '} + file during installation has been used to create your project in the + . +
    + ); + } +}; + +export default getConfigError; diff --git a/components/pages/repository/index.tsx b/components/pages/repository/index.tsx index ca278a35..112b35af 100644 --- a/components/pages/repository/index.tsx +++ b/components/pages/repository/index.tsx @@ -5,9 +5,10 @@ import PageLayout from '../../PageLayout'; import { RepoFiltersType } from './sqonTypes'; import { getConfig } from '../../../global/config'; -import { css } from '@emotion/core'; import createArrangerFetcher from '../../utils/arrangerFetcher'; import { useEffect, useState } from 'react'; +import ErrorContainer from '../../ErrorContainer'; +import getConfigError from './getConfigError'; const Arranger = dynamic( () => import('@arranger/components/dist/Arranger').then((comp) => comp.Arranger), @@ -35,6 +36,12 @@ export interface PageContentProps { fetchData?: (projectId: string) => Promise; } +export type Project = { + id: string; + active: boolean; + indices: [{ id: string; esIndex: string; graphqlField: string }]; +}; + const arrangerFetcher = createArrangerFetcher({}); const projectsQuery = ` @@ -51,112 +58,6 @@ const projectsQuery = ` } `; -type Project = { - id: string; - active: boolean; - indices: [{ id: string; esIndex: string; graphqlField: string }]; -}; - -const ConfigErrorDisplay = ({ children }: { children: React.ReactNode }) => ( -
    -
    - css` - padding: 2rem; - border: 1px solid ${theme.colors.grey_3}; - border-radius: 5px; - ${theme.shadow.default}; - ${theme.typography.heading}; - max-width: 600px; - ` - } - > - {children} -
    -
    -); - -const getConfigError = ({ - availableProjects, - projectId, - index, - graphqlField, -}: { - availableProjects: Project[]; - projectId: string; - index: string; - graphqlField: string; -}) => { - const Missing = ( - - css` - color: ${theme.colors.error_dark}; - ` - } - > - Missing - - ); - if (!(projectId && index && graphqlField)) { - return ( - - One or more of project ID, Elasticsearch index, and index alias (graphQL field) values - required by the DMS portal do not exist. Make sure the values are specified in the - config.yaml file during DMS installation and have been used to create your project in the - Arranger Admin UI. -
      -
    • Project ID: {projectId || Missing}
    • -
    • Alias name: {graphqlField || Missing}
    • -
    • Elasticsearch index: {index || Missing}
    • -
    -
    - ); - } - if (!availableProjects.filter((project) => project.active).length) { - return ( - - No active projects for the DMS portal exist. Make sure the project specified in the - config.yaml file during DMS installation has been created in the Arranger Admin UI. - - ); - } - const foundProject = - availableProjects.length && availableProjects.find((project) => project.id === projectId); - if (!foundProject) { - return ( - - The project ID "{projectId}" configured for the DMS portal does not match any existing - project. Make sure the project ID specified in the config.yaml file during DMS installation - has been created in the Arranger Admin UI. - - ); - } - - const aliasFromList = foundProject.indices.find((i) => i.id.match(projectId))?.graphqlField; - const matchesConfiguredAlias = graphqlField === aliasFromList; - - if (!matchesConfiguredAlias) { - return ( -
    - The Elasticsearch alias name (graphQL field) "{aliasFromList}" required by the DMS portal - for project ID "{projectId}" does not match your configured alias name "{graphqlField}" . - Make sure the value specified in the config.yaml file during DMS installation has been used - to create your project in the Arranger Admin UI. -
    - ); - } - return null; -}; - const RepositoryPage = () => { const { NEXT_PUBLIC_ARRANGER_PROJECT_ID, @@ -198,7 +99,7 @@ const RepositoryPage = () => { return ( {ConfigError ? ( - {ConfigError} + {ConfigError} ) : ( { + return ( + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default Error; diff --git a/components/theme/icons/index.tsx b/components/theme/icons/index.tsx index 7cf6348d..264361f6 100644 --- a/components/theme/icons/index.tsx +++ b/components/theme/icons/index.tsx @@ -11,6 +11,8 @@ import OvertureLogoWithText from './overture_logo_with_text'; import OvertureUser from './overture_user'; import Checkmark from './checkmark'; import Spinner from './spinner'; +import Error from './error'; +import Warning from './warning'; export { GoogleLogo, @@ -26,4 +28,6 @@ export { OvertureUser, Checkmark, Spinner, + Error, + Warning, }; diff --git a/components/theme/icons/warning.tsx b/components/theme/icons/warning.tsx new file mode 100644 index 00000000..de7e75fc --- /dev/null +++ b/components/theme/icons/warning.tsx @@ -0,0 +1,37 @@ +import { css } from '@emotion/core'; +import { IconProps } from './types'; +import theme from '../'; + +const Warning = ({ height, width, style, fill = theme.colors.error_dark }: IconProps) => { + return ( + + + + + + + + + + + + + + + + ); +}; + +export default Warning; From f7f5b1d607e3ee49159147d6d2793edf22e799d9 Mon Sep 17 00:00:00 2001 From: Ann Catton Date: Wed, 3 Mar 2021 10:03:01 -0500 Subject: [PATCH 3/5] pr feedback --- .../pages/repository/getConfigError.tsx | 45 ++++++++----------- components/pages/repository/index.tsx | 4 +- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/components/pages/repository/getConfigError.tsx b/components/pages/repository/getConfigError.tsx index 3ac48065..862ee431 100644 --- a/components/pages/repository/getConfigError.tsx +++ b/components/pages/repository/getConfigError.tsx @@ -1,3 +1,4 @@ +import { ReactNode } from 'react'; import { css } from '@emotion/core'; import { Checkmark, Warning } from '../../theme/icons'; @@ -18,17 +19,28 @@ const ArrangerAdminUILink = () => { ); }; -const WarningListItem = ({ fieldName }: { fieldName: string }) => ( +const ListItem = ({ + Icon, + value, + fieldName, +}: { + Icon?: ReactNode; + value: string; + fieldName: string; +}) => (
  • css` - color: ${theme.colors.error_dark}; display: flex; align-items: center; + ${value === 'Missing' && + css` + color: ${theme.colors.error_dark}; + `} ` } > - + {Icon || } ( font-weight: bold; `} > - Missing + {value}
  • ); -const ListItem = ({ fieldName, value }: { fieldName: string; value: string }) => ( -
  • - - - {fieldName}:{' '} - - {value} - - -
  • +const WarningListItem = ({ fieldName }: { fieldName: string }) => ( + } fieldName={fieldName} value={'Missing'} /> ); const getConfigError = ({ diff --git a/components/pages/repository/index.tsx b/components/pages/repository/index.tsx index 112b35af..0e2df98b 100644 --- a/components/pages/repository/index.tsx +++ b/components/pages/repository/index.tsx @@ -1,4 +1,5 @@ import dynamic from 'next/dynamic'; +import urlJoin from 'url-join'; import PageContent from './PageContent'; import PageLayout from '../../PageLayout'; @@ -67,7 +68,8 @@ const RepositoryPage = () => { const [availableProjects, setAvailableProjects] = useState([]); useEffect(() => { - fetch(`https://arranger.qa.overture.bio/admin/graphql`, { + const { NEXT_PUBLIC_ARRANGER_API } = getConfig(); + fetch(urlJoin(NEXT_PUBLIC_ARRANGER_API, 'admin/graphql'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ From 126a05ab155cbe0adc47be2d285836097cea72c8 Mon Sep 17 00:00:00 2001 From: Ann Catton Date: Wed, 3 Mar 2021 14:21:32 -0500 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=9A=B8=20logout=20redirect=20to=20/re?= =?UTF-8?q?pository?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- global/hooks/useAuthContext.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global/hooks/useAuthContext.tsx b/global/hooks/useAuthContext.tsx index 5a9ff755..e3addeca 100644 --- a/global/hooks/useAuthContext.tsx +++ b/global/hooks/useAuthContext.tsx @@ -38,7 +38,7 @@ export const AuthProvider = ({ const logout = () => { removeToken(); - router.push('/login'); + router.push('/repository'); }; if (token !== egoJwt) { From 303fa7fef9e9940662a2734938c19a34b98cfb09 Mon Sep 17 00:00:00 2001 From: Ann Catton Date: Thu, 4 Mar 2021 17:03:51 -0500 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=94=96=20rc=200.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a543d7e..e9c0b8de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dms-ui", - "version": "0.5.0", + "version": "0.6.0", "private": true, "scripts": { "dev": "next dev",