Skip to content

Commit

Permalink
Refactor parser function into callback in ListResources [#870]
Browse files Browse the repository at this point in the history
Convert the two existing uses into the callback form.

This does not add any new functionality, it only re-arranges
implementation of existing functionality.
  • Loading branch information
genehack committed Jun 13, 2024
1 parent ed02850 commit ccc7701
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 43 deletions.
24 changes: 15 additions & 9 deletions static-site/src/components/ListResources/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<readonly FilterOption[]>([]);
const [sortMethod, changeSortMethod] = useState("alphabetical");
Expand All @@ -48,7 +54,7 @@ function ListResources({

if (dataFetchError) {
return (
<ErrorContainer>
<ErrorContainer>
{"Whoops - listing resources isn't working!"}
<br/>
{'Please '}<a href="/contact" style={{fontWeight: 300}}>get in touch</a>{" if this keeps happening"}
Expand Down Expand Up @@ -100,14 +106,14 @@ function ListResources({


interface ListResourcesResponsiveProps {
sourceId: string
versioned: boolean
quickLinks: QuickLink[]

/** Should the group name itself be a url? (which we let the server redirect) */
defaultGroupLinks: boolean
groupDisplayNames: GroupDisplayNames
showcase: Card[]
resourceListingCallback: () => Promise<ResourceListingInfo>;
}

/**
Expand Down Expand Up @@ -152,7 +158,7 @@ function SortOptions({sortMethod, changeSortMethod}) {
}
return (
<SortContainer>
Sort pathogens by:
Sort pathogens by:
<input id="alphabetical" type="radio" onChange={onChangeValue} value="alphabetical"
checked={"alphabetical"===sortMethod} style={{cursor: "alphabetical"===sortMethod ? 'default' : 'pointer'}}/>
<TooltipWrapper description={'Pathogen groups ordered alphabetically. ' +
Expand Down Expand Up @@ -222,4 +228,4 @@ const SortContainer = styled.div`
margin-left: 20px;
margin-right: 5px;
}
`
`
5 changes: 5 additions & 0 deletions static-site/src/components/ListResources/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export interface ResourceDisplayName {
default: string
}

export interface ResourceListingInfo {
pathPrefix: string
pathVersions: PathVersions
}

/**
* Mapping from group name -> display name
*/
Expand Down
61 changes: 30 additions & 31 deletions static-site/src/components/ListResources/useDataFetch.ts
Original file line number Diff line number Diff line change
@@ -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<ResourceListingInfo>,
) : {groups: Group[] | undefined, dataFetchError: boolean} {
const [groups, setGroups] = useState<Group[]>();
const [dataFetchError, setDataFetchError] = useState<boolean>();
useEffect(() => {
const url = `/list-resources/${sourceId}`;
const [dataFetchError, setDataFetchError] = useState<boolean>(false);

async function fetchAndParse() {
let pathVersions: PathVersions, pathPrefix: string;
useEffect((): void => {
async function fetchAndParse(): Promise<void> {
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}
}

Expand Down
20 changes: 18 additions & 2 deletions static-site/src/sections/pathogens.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -35,9 +47,13 @@ class Index extends React.Component {
</FlexCenter>

<HugeSpacer/>
<ListResources sourceId="core" resourceType="dataset"

<ListResources resourceType="dataset"
showcase={coreShowcase}
quickLinks={coreQuickLinks} defaultGroupLinks groupDisplayNames={coreGroupDisplayNames}/>
quickLinks={coreQuickLinks} defaultGroupLinks
groupDisplayNames={coreGroupDisplayNames}
resourceListingCallback={resourceListingCallback}/>

<HugeSpacer/>
</GenericPage>
);
Expand Down
15 changes: 14 additions & 1 deletion static-site/src/sections/staging-page.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -62,7 +74,8 @@ class Index extends React.Component {
</FlexCenter>
<HugeSpacer />

<ListResources sourceId="staging" resourceType="dataset" versioned={false}/>
<ListResources resourceType="dataset" versioned={false}
resourceListingCallback={resourceListingCallback}/>

<HugeSpacer />

Expand Down

0 comments on commit ccc7701

Please sign in to comment.