Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
Logic for trimming/revising the links works
Corresponding specs are failing (`Cannot read properties of null (reading 'useMemo'`)
Also need to set up protocols API auth flow
  • Loading branch information
NickAkhmetov committed Jul 11, 2023
1 parent d3ad26e commit 3ac9107
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,16 @@ function ProtocolLink({ title, resolverHostnameAndDOI }) {
}

function Protocol({ protocol_url }) {
const matchedDoiSuffix = protocol_url.match(/\w*$/)[0];

const protocolData = useProtocolData(matchedDoiSuffix, 1);

const title = protocolData?.protocol?.title;
const resolverHostnameAndDOI = protocolData?.protocol?.doi;
const protocolData = useProtocolData(protocol_url, 1);

return (
<DetailPageSection id="protocols">
<SectionHeader>Protocols</SectionHeader>
<Divider />
<StyledPaper>
<ProtocolLink title={title} resolverHostnameAndDOI={resolverHostnameAndDOI} />
{protocolData.map(({ title, resolverHostnameAndDOI }) => (
<ProtocolLink key={title} title={title} resolverHostnameAndDOI={resolverHostnameAndDOI} />
))}
</StyledPaper>
</DetailPageSection>
);
Expand Down
5 changes: 5 additions & 0 deletions context/app/static/js/helpers/multiFetcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Fetcher function that lets SWR fetch multiple urls at once
export const multiFetcher = (...urls: string[]) => {
const f = (url: string) => fetch(url).then((response) => response.json());
return Promise.all(urls.map((url) => f(url)));
};
52 changes: 35 additions & 17 deletions context/app/static/js/hooks/useProtocolData.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
import React from 'react';
import { useMemo } from 'react';
import useSWR from 'swr';

function useProtocolData(doiSuffix, lastVersion = 1) {
const [protocol, setProtocol] = React.useState({});
React.useEffect(() => {
async function getAndSetProtocol() {
const url = `https://www.protocols.io/api/v3/protocols/${doiSuffix}?last_version=${lastVersion}`;
const response = await fetch(url);
if (!response.ok) {
console.error('Protocol API failed:', url, response);
return;
}
const data = await response.json();
setProtocol(data);
}
getAndSetProtocol();
}, [doiSuffix, lastVersion]);
import { multiFetcher } from 'js/helpers/multiFetcher';

return protocol;
export function useFormattedProtocolUrls(protocolUrls, lastVersion) {
return useMemo(() => {
// Handle case with multiple URLs provided in one string
// If only one string is provided, it will be returned as an array
const protocols = protocolUrls.split(',');
// Strip `http://` and `https://` from the beginning of the URL if it exists
// https://dx.doi.org/10.17504/protocols.io.btnfnmbn -> dx.doi.org/10.17504/protocols.io.btnfnmbn
const noHttpPrefix = protocols.map((url) => url.replace(/^(?:https?:\/\/)?/i, ''));
// Strip `dx.doi.org/` from the beginning of the URL if it exists
// dx.doi.org/10.17504/protocols.io.btnfnmbn -> 10.17504/protocols.io.btnfnmbn
const noDomainPrefix = noHttpPrefix.map((url) => url.replace(/^dx.doi.org\//i, ''));
// Strip version number from end of the URL if it exists
// 10.17504/protocols.io.btnfnmbn/v1 -> 10.17504/protocols.io.btnfnmbn
const noVersionSuffix = noDomainPrefix.map((url) => url.replace(/\/v\d+$/, ''));
// Format into the API call URL
// 10.17504/protocols.io.btnfnmbn -> https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=1
const formattedUrls = noVersionSuffix.map(
// TODO: Update to v4 API (see HMP-254)
(doi) => `https://www.protocols.io/api/v3/protocols/${doi}?last_version=${lastVersion}`,
);
return formattedUrls;
}, [protocolUrls, lastVersion]);
}

function useProtocolData(protocolUrls, lastVersion = 1) {
const urls = useFormattedProtocolUrls(protocolUrls, lastVersion);

const protocols = useSWR(urls, multiFetcher, {
revalidateOnFocus: false,
});

return protocols.data ?? [];
}

export default useProtocolData;
76 changes: 76 additions & 0 deletions context/app/static/js/hooks/useProtocolData.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { renderHook } from '@testing-library/react-hooks';

import { useFormattedProtocolUrls } from './useProtocolData';

describe('useFormattedProtocolUrls', () => {
it('should format a single URL with no version number', () => {
const protocolUrls = 'https://dx.doi.org/10.17504/protocols.io.btnfnmbn';
const lastVersion = 1;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual(['https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=1']);
});

it('should format multiple URLs with version numbers', () => {
const protocolUrls =
'https://dx.doi.org/10.17504/protocols.io.btnfnmbn/v1,https://dx.doi.org/10.17504/protocols.io.7d5h6en/v2';
const lastVersion = 2;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual([
'https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=2',
'https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.7d5h6en?last_version=2',
]);
});

it('should handle URLs with http:// prefix', () => {
const protocolUrls = 'http://dx.doi.org/10.17504/protocols.io.btnfnmbn';
const lastVersion = 1;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual(['https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=1']);
});

it('should handle URLs with https:// prefix', () => {
const protocolUrls = 'https://dx.doi.org/10.17504/protocols.io.btnfnmbn';
const lastVersion = 1;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual(['https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=1']);
});

it('should handle URLs with dx.doi.org/ prefix', () => {
const protocolUrls = 'dx.doi.org/10.17504/protocols.io.btnfnmbn';
const lastVersion = 1;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual(['https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=1']);
});

it('should handle URLs with multiple prefixes', () => {
const protocolUrls =
'https://dx.doi.org/10.17504/protocols.io.btnfnmbn/v1,http://dx.doi.org/10.17504/protocols.io.7d5h6en/v2';
const lastVersion = 2;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual([
'https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=2',
'https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.7d5h6en?last_version=2',
]);
});

it('should handle URLs with no http or https prefix', () => {
const protocolUrls = 'dx.doi.org/10.17504/protocols.io.btnfnmbn/v1';
const lastVersion = 1;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual(['https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=1']);
});

it('should handle URLs with no version number', () => {
const protocolUrls = 'https://dx.doi.org/10.17504/protocols.io.btnfnmbn';
const lastVersion = 2;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual(['https://www.protocols.io/api/v3/protocols/10.17504/protocols.io.btnfnmbn?last_version=2']);
});

it('should handle empty input', () => {
const protocolUrls = '';
const lastVersion = 1;
const { result } = renderHook(useFormattedProtocolUrls(protocolUrls, lastVersion));
expect(result).toEqual([]);
});
});

0 comments on commit 3ac9107

Please sign in to comment.