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 10, 2024
1 parent ed02850 commit 99a5f43
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 55 deletions.
23 changes: 16 additions & 7 deletions static-site/src/components/ListResources/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ 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';
import { HugeSpacer } from "../../layouts/generalComponents";

interface ListResourcesProps extends ListResourcesResponsiveProps {
elWidth: number
Expand All @@ -29,15 +30,22 @@ export const LIST_ANCHOR = "list";
* this may be better expressed as a property of the API response.
*/
function ListResources({
sourceId,
sourceUrl,
versioned=true,
elWidth,
quickLinks,
defaultGroupLinks=false,
groupDisplayNames,
showcase,
parseResourceListingCallback: parseResourceListingCallback,
}: ListResourcesProps) {
const {groups, dataFetchError} = useDataFetch(sourceId, versioned, defaultGroupLinks, groupDisplayNames);
const {groups, dataFetchError} = useDataFetch(
sourceUrl,
versioned,
defaultGroupLinks,
groupDisplayNames,
parseResourceListingCallback,
);
const showcaseCards = useShowcaseCards(showcase, groups);
const [selectedFilterOptions, setSelectedFilterOptions] = useState<readonly FilterOption[]>([]);
const [sortMethod, changeSortMethod] = useState("alphabetical");
Expand All @@ -48,7 +56,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 +108,15 @@ function ListResources({


interface ListResourcesResponsiveProps {
sourceId: string
sourceUrl: 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[]
parseResourceListingCallback: (response: Response) => Promise<ResourceListingInfo>;
}

/**
Expand Down Expand Up @@ -152,7 +161,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 +231,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
46 changes: 28 additions & 18 deletions static-site/src/components/ListResources/useDataFetch.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
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.
* Fetches the resources from the provided `sourceUrl` and uses the
* provided `parseResourceListingCallback` function to parse those
* resources into the `pathVersions` and `pathPrefix` values.
*
* 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 one-element array with a
* value of `[undefined]`
*
* 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(
sourceUrl: string,
versioned: boolean,
defaultGroupLinks: boolean,
groupDisplayNames: GroupDisplayNames,
parseResourceListingCallback: (response: Response) => Promise<ResourceListingInfo>,
) : {groups: Group[] | undefined, dataFetchError: boolean | undefined} {
const [groups, setGroups] = useState<Group[]>();
const [dataFetchError, setDataFetchError] = useState<boolean>();
useEffect(() => {
const url = `/list-resources/${sourceId}`;

async function fetchAndParse() {
useEffect((): void => {
async function fetchAndParse(): Promise<void> {
let pathVersions: PathVersions, pathPrefix: string;
try {
const response = await fetch(url, {headers: {accept: "application/json"}});
const response = await fetch(sourceUrl, {headers: {accept: "application/json"}});
if (response.status !== 200) {
console.error(`ERROR: fetching data from "${url}" returned status code ${response.status}`);
console.error(`ERROR: fetching data from "${sourceUrl}" returned status code ${response.status}`);
return setDataFetchError(true);
}
({ pathVersions, pathPrefix } = (await response.json()).dataset[sourceId]);
({ pathVersions, pathPrefix } = await parseResourceListingCallback(response));
} catch (err) {
console.error(`Error while fetching data from "${url}"`);
console.error(`Error while fetching data from "${sourceUrl}"`);
console.error(err);
return setDataFetchError(true);
}
Expand All @@ -47,7 +57,7 @@ export function useDataFetch(sourceId: string, versioned: boolean, defaultGroupL
}

fetchAndParse();
}, [sourceId, versioned, defaultGroupLinks, groupDisplayNames]);
}, [sourceUrl, versioned, defaultGroupLinks, groupDisplayNames, parseResourceListingCallback]);
return {groups, dataFetchError}
}

Expand Down
59 changes: 32 additions & 27 deletions static-site/src/pages/groups.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,32 @@ import { UserContext } from "../layouts/userDataWrapper";
import { DataFetchErrorParagraph } from "../components/splash/errorMessages";
import { groupsTitle, GroupsAbstract } from "../../data/SiteConfig";

const datasetColumns = ({isNarrative}) => [
{
name: isNarrative ? "Narrative" : "Dataset",
value: (d) => d.request.replace("/groups/", "").replace(/\//g, " / "),
url: (d) => d.url
},
{
name: "Group Name",
value: (d) => d.request.split("/")[2],
url: (d) => `/groups/${d.request.split("/")[2]}`
}
const sourceId = "charon/groups";
async function parseResourceListingCallback(response) {
const datasets = (await response.json()).datasets;

// Use "" instead of fake date because downstream components can
// conditionalize display choices based on `== ""` or `!== ""`
const pathVersions = Object.assign(
...datasets.map((ds) => ({
[ds.request.replace(/^\/groups\//, "")]: [""]
})),
);

return { pathPrefix: "groups/", pathVersions };
}

const datasetColumns = [
{
name: "Narrative",
value: (d) => d.request.replace("/groups/", "").replace(/\//g, " / "),
url: (d) => d.url,
},
{
name: "Group Name",
value: (d) => d.request.split("/")[2],
url: (d) => `/groups/${d.request.split("/")[2]}`,
},
];

const GroupListingInfo = () => {
Expand Down Expand Up @@ -92,22 +107,12 @@ class GroupsPage extends React.Component {
<GroupCards squashed/>
<HugeSpacer />

<ScrollableAnchor id={'datasets'}>
<splashStyles.H2>Available Datasets</splashStyles.H2>
</ScrollableAnchor>
<FlexCenter>
<splashStyles.CenteredFocusParagraph>
{`Note that this listing is refreshed every ~6 hours.
To see a current listing, visit the page for that group by clicking on that group's tile, above.`}
</splashStyles.CenteredFocusParagraph>
</FlexCenter>
<HugeSpacer />
{this.state.dataLoaded && (
<DatasetSelect
datasets={this.state.datasets}
columns={datasetColumns({isNarrative: false})}
/>
)}
<ListResources
sourceId={sourceId}
resourceType="dataset"
versioned={false}
parseResourceListingCallback={parseResourceListingCallback}/>

<HugeSpacer />
<ScrollableAnchor id={'narratives'}>
<splashStyles.H2>Available Narratives</splashStyles.H2>
Expand Down
11 changes: 9 additions & 2 deletions static-site/src/sections/pathogens.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const abstract = (
</>
);

const sourceId = "core"
const sourceUrl = `list-resources/${sourceId}`;
const parseResourceListingCallback = async (response) => (await response.json()).dataset[sourceId];

class Index extends React.Component {
render() {
return (
Expand All @@ -35,9 +39,12 @@ class Index extends React.Component {
</FlexCenter>

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

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

<HugeSpacer/>
</GenericPage>
);
Expand Down
7 changes: 6 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,10 @@ const abstract = (
</>
);

const sourceId = "staging";
const sourceUrl = `list-resources/${sourceId}`;
const parseResourceListingCallback = async (response) => (await response.json()).dataset[sourceId];

class Index extends React.Component {
constructor(props) {
super(props);
Expand Down Expand Up @@ -62,7 +66,8 @@ class Index extends React.Component {
</FlexCenter>
<HugeSpacer />

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

<HugeSpacer />

Expand Down

0 comments on commit 99a5f43

Please sign in to comment.