From ccc77016e2a82bc7f689881fd02d313bf9aa12e7 Mon Sep 17 00:00:00 2001 From: John SJ Anderson Date: Tue, 11 Jun 2024 17:33:10 -0700 Subject: [PATCH] Refactor parser function into callback in ListResources [#870] Convert the two existing uses into the callback form. This does not add any new functionality, it only re-arranges implementation of existing functionality. --- .../src/components/ListResources/index.tsx | 24 +++++--- .../src/components/ListResources/types.ts | 5 ++ .../components/ListResources/useDataFetch.ts | 61 +++++++++---------- static-site/src/sections/pathogens.jsx | 20 +++++- static-site/src/sections/staging-page.jsx | 15 ++++- 5 files changed, 82 insertions(+), 43 deletions(-) diff --git a/static-site/src/components/ListResources/index.tsx b/static-site/src/components/ListResources/index.tsx index 5170ebe85..4417460f5 100644 --- a/static-site/src/components/ListResources/index.tsx +++ b/static-site/src/components/ListResources/index.tsx @@ -12,7 +12,7 @@ import { ErrorContainer } from "../../pages/404"; import { TooltipWrapper } from "./IndividualResource"; import {ResourceModal, SetModalResourceContext} from "./Modal"; import { Showcase, useShowcaseCards} from "./Showcase"; -import { Card, FilterOption, Group, GroupDisplayNames, QuickLink, Resource } from './types'; +import { Card, FilterOption, Group, GroupDisplayNames, QuickLink, Resource, ResourceListingInfo } from './types'; interface ListResourcesProps extends ListResourcesResponsiveProps { elWidth: number @@ -21,23 +21,29 @@ interface ListResourcesProps extends ListResourcesResponsiveProps { export const LIST_ANCHOR = "list"; /** - * A React component to fetch data and display the available resources, - * including past versions ("snapshots"). + * A React component that uses a callback to fetch data about + * available resources, and then display them, including past versions + * ("snapshots"), if those exist. * * Note that currently this only uses 'dataset' resources. In the future this * will be expanded. Similarly, we define versioned: boolean here in the UI whereas * this may be better expressed as a property of the API response. */ function ListResources({ - sourceId, versioned=true, elWidth, quickLinks, defaultGroupLinks=false, groupDisplayNames, showcase, + resourceListingCallback: resourceListingCallback, }: ListResourcesProps) { - const {groups, dataFetchError} = useDataFetch(sourceId, versioned, defaultGroupLinks, groupDisplayNames); + const {groups, dataFetchError} = useDataFetch( + versioned, + defaultGroupLinks, + groupDisplayNames, + resourceListingCallback, + ); const showcaseCards = useShowcaseCards(showcase, groups); const [selectedFilterOptions, setSelectedFilterOptions] = useState([]); const [sortMethod, changeSortMethod] = useState("alphabetical"); @@ -48,7 +54,7 @@ function ListResources({ if (dataFetchError) { return ( - + {"Whoops - listing resources isn't working!"}
{'Please '}get in touch{" if this keeps happening"} @@ -100,7 +106,6 @@ function ListResources({ interface ListResourcesResponsiveProps { - sourceId: string versioned: boolean quickLinks: QuickLink[] @@ -108,6 +113,7 @@ interface ListResourcesResponsiveProps { defaultGroupLinks: boolean groupDisplayNames: GroupDisplayNames showcase: Card[] + resourceListingCallback: () => Promise; } /** @@ -152,7 +158,7 @@ function SortOptions({sortMethod, changeSortMethod}) { } return ( - Sort pathogens by: + Sort pathogens by: display name */ diff --git a/static-site/src/components/ListResources/useDataFetch.ts b/static-site/src/components/ListResources/useDataFetch.ts index ef68e585e..23f81d215 100644 --- a/static-site/src/components/ListResources/useDataFetch.ts +++ b/static-site/src/components/ListResources/useDataFetch.ts @@ -1,53 +1,52 @@ import { useState, useEffect } from 'react'; -import { Group, GroupDisplayNames, PathVersions, Resource } from './types'; +import { Group, GroupDisplayNames, PathVersions, Resource, ResourceListingInfo } from './types'; /** - * Fetches the datasets for the provided `sourceId` and parses the data into an - * array of groups, each representing a "pathogen" and detailing the available - * resources for each, and the available versions (snapshots) for each of those - * resources. + * Uses the provided callback to fetch resources and parse those + * resources into the `pathVersions` and `pathPrefix` values. The + * callback is expected (and encouraged!) to throw() on any errors. * - * The current implementation defines `versioned: boolean` for the entire API - * response, however in the future we may shift this to the API response and it - * may vary across the resources returned. + * Continues on to parse the `pathVersions`/`pathPrefix` data + * structures into an array of groups, each representing a "pathogen" + * and detailing the available resources for each, and the available + * versions (snapshots) for each of those resources. In the case of + * un-versioned resources, versions will be a zero-length array (i.e., + * `[]`) + * + * The current implementation defines `versioned: boolean` for the + * entire API response, however in the future we may shift this to the + * API response and it may vary across the resources returned. */ -export function useDataFetch(sourceId: string, versioned: boolean, defaultGroupLinks: boolean, groupDisplayNames: GroupDisplayNames) { +export function useDataFetch( + versioned: boolean, + defaultGroupLinks: boolean, + groupDisplayNames: GroupDisplayNames, + resourceListingCallback: () => Promise, +) : {groups: Group[] | undefined, dataFetchError: boolean} { const [groups, setGroups] = useState(); - const [dataFetchError, setDataFetchError] = useState(); - useEffect(() => { - const url = `/list-resources/${sourceId}`; + const [dataFetchError, setDataFetchError] = useState(false); - async function fetchAndParse() { - let pathVersions: PathVersions, pathPrefix: string; + useEffect((): void => { + async function fetchAndParse(): Promise { try { - const response = await fetch(url, {headers: {accept: "application/json"}}); - if (response.status !== 200) { - console.error(`ERROR: fetching data from "${url}" returned status code ${response.status}`); - return setDataFetchError(true); - } - ({ pathVersions, pathPrefix } = (await response.json()).dataset[sourceId]); - } catch (err) { - console.error(`Error while fetching data from "${url}"`); - console.error(err); - return setDataFetchError(true); - } + const { pathPrefix, pathVersions } = await resourceListingCallback(); - /* group/partition the resources by pathogen (the first word of the - resource path). This grouping is constant for all UI options so we do it a - single time following the data fetch */ - try { + /* group/partition the resources by pathogen (the first word + of the resource path). This grouping is constant for all UI + options so we do it a single time following the data fetch */ const partitions = partitionByPathogen(pathVersions, pathPrefix, versioned); setGroups(groupsFrom(partitions, pathPrefix, defaultGroupLinks, groupDisplayNames)); } catch (err) { - console.error(`Error while parsing fetched data`); + console.error(`Error while fetching and/or parsing data`); console.error(err); return setDataFetchError(true); } } fetchAndParse(); - }, [sourceId, versioned, defaultGroupLinks, groupDisplayNames]); + }, [versioned, defaultGroupLinks, groupDisplayNames, resourceListingCallback]); + return {groups, dataFetchError} } diff --git a/static-site/src/sections/pathogens.jsx b/static-site/src/sections/pathogens.jsx index 90feae2b6..e610077a2 100644 --- a/static-site/src/sections/pathogens.jsx +++ b/static-site/src/sections/pathogens.jsx @@ -21,6 +21,18 @@ const abstract = ( ); +const resourceListingCallback = async () => { + const sourceId = "core" + const sourceUrl = `list-resources/${sourceId}`; + + const response = await fetch(sourceUrl, {headers: {accept: "application/json"}}); + if (response.status !== 200) { + throw new Error(`fetching data from "${sourceUrl}" returned status code ${response.status}`); + } + + return (await response.json()).dataset[sourceId]; +}; + class Index extends React.Component { render() { return ( @@ -35,9 +47,13 @@ class Index extends React.Component { - + quickLinks={coreQuickLinks} defaultGroupLinks + groupDisplayNames={coreGroupDisplayNames} + resourceListingCallback={resourceListingCallback}/> + ); diff --git a/static-site/src/sections/staging-page.jsx b/static-site/src/sections/staging-page.jsx index 1b87b68f4..bfbe22f88 100644 --- a/static-site/src/sections/staging-page.jsx +++ b/static-site/src/sections/staging-page.jsx @@ -18,6 +18,18 @@ const abstract = ( ); +const resourceListingCallback = async () => { + const sourceId = "staging" + const sourceUrl = `list-resources/${sourceId}`; + + const response = await fetch(sourceUrl, {headers: {accept: "application/json"}}); + if (response.status !== 200) { + throw new Error(`fetching data from "${sourceUrl}" returned status code ${response.status}`); + } + + return (await response.json()).dataset[sourceId]; +}; + class Index extends React.Component { constructor(props) { super(props); @@ -62,7 +74,8 @@ class Index extends React.Component { - +