Skip to content

Commit

Permalink
Lazily update Zenodo records
Browse files Browse the repository at this point in the history
We can update the metadata on Zenodo records, but this doesn't often happen. This change means we use stale versions from the HTTP cache but revalidate them in the background. As a result, the page will load faster but might temporarily contain out-of-date information.

The behaviour is similar to if Zenodo were to return an HTTP response with a stale-while-revalidate directive.

Note that the 'local' function from fp-ts can't infer the argument type, so I've used function overloading to avoid having to declare it in multiple places.

Refs #249, https://doi.org/10.17487/RFC5861, gcanti/fp-ts#1761
  • Loading branch information
thewilkybarkid committed Sep 5, 2022
1 parent 6105f8f commit f76f852
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 3 deletions.
28 changes: 25 additions & 3 deletions src/infrastructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as RTE from 'fp-ts/ReaderTaskEither'
import * as RA from 'fp-ts/ReadonlyArray'
import { compose } from 'fp-ts/Refinement'
import * as TE from 'fp-ts/TaskEither'
import { flow, identity, pipe } from 'fp-ts/function'
import { constVoid, flow, identity, pipe } from 'fp-ts/function'
import { isString } from 'fp-ts/string'
import { NotFound } from 'http-errors'
import { Status } from 'hyper-ts'
Expand All @@ -26,6 +26,7 @@ import {
Record,
SubmittedDeposition,
ZenodoAuthenticatedEnv,
ZenodoEnv,
createDeposition,
getRecord,
publishDeposition,
Expand Down Expand Up @@ -53,6 +54,8 @@ export const getPreprintTitle = flow(

export const getPrereview = flow(
getRecord,
RTE.local(revalidateIfStale),
RTE.local(useStaleCache),
RTE.filterOrElseW(isInCommunity, () => new NotFound()),
RTE.chain(recordToPrereview),
)
Expand Down Expand Up @@ -284,6 +287,25 @@ function detectLanguage<L extends LanguageCode>(...languages: ReadonlyArray<L>):
)
}

function useStaleCache({ fetch }: F.FetchEnv): F.FetchEnv {
return { fetch: (url, init) => fetch(url, { cache: 'force-cache', ...init }) }
function useStaleCache(env: ZenodoEnv): ZenodoEnv
function useStaleCache(env: F.FetchEnv): F.FetchEnv
function useStaleCache<E extends F.FetchEnv>(env: E): E {
return { ...env, fetch: (url, init) => env.fetch(url, { cache: 'force-cache', ...init }) }
}

function revalidateIfStale(env: ZenodoEnv): ZenodoEnv
function revalidateIfStale(env: F.FetchEnv): F.FetchEnv
function revalidateIfStale<E extends F.FetchEnv>(env: E): E {
return {
...env,
fetch: async (url, init) => {
const response = await env.fetch(url, init)

if (response.headers.get('x-local-cache-status') === 'stale') {
void env.fetch(url, { ...init, cache: 'no-cache' }).catch(constVoid)
}

return response
},
}
}
81 changes: 81 additions & 0 deletions test/infrastructure.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,87 @@ describe('infrastructure', () => {
)
})

test('revalidates if the PREreview is stale', async () => {
await fc.assert(
fc.asyncProperty(
fc.integer(),
fc.record({
doi: fc.preprintDoi(),
language: fc.languageCode(),
title: fc.html(),
}),
async (id, preprint) => {
const record: Record = {
conceptdoi: '10.5072/zenodo.1061863' as Doi,
conceptrecid: 1061863,
files: [
{
links: {
self: new URL('http://example.com/file'),
},
key: 'review.html',
type: 'html',
size: 58,
},
],
id,
links: {
latest: new URL('http://example.com/latest'),
latest_html: new URL('http://example.com/latest_html'),
},
metadata: {
communities: [{ id: 'prereview-reviews' }],
creators: [{ name: 'PREreviewer' }],
description: 'Description',
doi: '10.5281/zenodo.1061864' as Doi,
license: {
id: 'CC-BY-4.0',
},
publication_date: new Date('2022-07-05'),
related_identifiers: [
{
scheme: 'doi',
identifier: preprint.doi,
relation: 'reviews',
resource_type: 'publication-preprint',
},
],
resource_type: {
type: 'publication',
subtype: 'article',
},
title: 'Title',
},
}

const fetch = fetchMock
.sandbox()
.getOnce((url, { cache }) => url === `https://zenodo.org/api/records/${id}` && cache === 'force-cache', {
body: RecordC.encode(record),
headers: { 'X-Local-Cache-Status': 'stale' },
})
.getOnce((url, { cache }) => url === `https://zenodo.org/api/records/${id}` && cache === 'no-cache', {
throws: new Error('Network error'),
})
.getOnce('http://example.com/file', { body: 'Some text' })

const actual = await _.getPrereview(id)({ fetch, getPreprintTitle: () => TE.right(preprint) })()

expect(actual).toStrictEqual(
E.right({
authors: [{ name: 'PREreviewer' }],
doi: '10.5281/zenodo.1061864' as Doi,
postedDate: PlainDate.from('2022-07-05'),
preprint,
text: rawHtml('Some text'),
}),
)
expect(fetch.done()).toBeTruthy()
},
),
)
})

test('when the review is not found', async () => {
await fc.assert(
fc.asyncProperty(
Expand Down

0 comments on commit f76f852

Please sign in to comment.