Skip to content

Commit

Permalink
Create observability for when we're unable to load a name from the OR…
Browse files Browse the repository at this point in the history
…CID API

Refs #976
  • Loading branch information
thewilkybarkid committed May 31, 2023
1 parent 5b64ba4 commit 49ea0a6
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 16 deletions.
41 changes: 25 additions & 16 deletions src/orcid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import * as F from 'fetch-fp-ts'
import * as E from 'fp-ts/Either'
import * as J from 'fp-ts/Json'
import * as RTE from 'fp-ts/ReaderTaskEither'
import { identity, pipe } from 'fp-ts/function'
import { flow, pipe } from 'fp-ts/function'
import { Status } from 'hyper-ts'
import * as D from 'io-ts/Decoder'
import * as L from 'logger-fp-ts'
import type { Orcid } from 'orcid-id-ts'
import { match } from 'ts-pattern'
import { NonEmptyStringC } from './string'
Expand All @@ -17,29 +18,37 @@ const JsonD = {
),
}

const PersonalDetailsD = D.struct({
name: D.struct({
'given-names': D.struct({
value: NonEmptyStringC,
const PersonalDetailsD = pipe(
JsonD,
D.compose(
D.struct({
name: D.struct({
'given-names': D.struct({
value: NonEmptyStringC,
}),
'family-name': D.struct({
value: NonEmptyStringC,
}),
}),
}),
'family-name': D.struct({
value: NonEmptyStringC,
}),
}),
})
),
)

export const getNameFromOrcid = (orcid: Orcid): RTE.ReaderTaskEither<F.FetchEnv, 'not-found' | 'unavailable', string> =>
export const getNameFromOrcid = (
orcid: Orcid,
): RTE.ReaderTaskEither<F.FetchEnv & L.LoggerEnv, 'not-found' | 'unavailable', string> =>
match(orcid)
.with('0000-0002-6109-0367' as Orcid, () =>
pipe(
'https://pub.orcid.org/v3.0/0000-0002-6109-0367/personal-details',
'https://pub.orcid.org/v3.0/0000-0002-6109-0367/personal-detailss',
F.Request('GET'),
F.setHeader('Accept', 'application/json'),
F.send,
RTE.filterOrElseW(F.hasStatus(Status.OK), identity),
RTE.chainTaskEitherK(F.getText(identity)),
RTE.chainEitherKW(JsonD.decode),
RTE.chainEitherKW(PersonalDetailsD.decode),
RTE.mapLeft(() => 'network-error' as const),
RTE.filterOrElseW(F.hasStatus(Status.OK), () => 'non-200-response' as const),
RTE.chainTaskEitherKW(F.getText(() => 'no-text' as const)),
RTE.chainEitherKW(flow(PersonalDetailsD.decode, E.mapLeft(D.draw))),
RTE.orElseFirstW(RTE.fromReaderIOK(flow(error => ({ error }), L.errorP('Failed to get name from ORCID')))),
RTE.bimap(
() => 'unavailable' as const,
personalDetails =>
Expand Down
10 changes: 10 additions & 0 deletions test/orcid.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { test } from '@fast-check/jest'
import { describe, expect } from '@jest/globals'
import { SystemClock } from 'clock-ts'
import fetchMock from 'fetch-mock'
import * as E from 'fp-ts/Either'
import * as IO from 'fp-ts/IO'
import { Status } from 'hyper-ts'
import type { Orcid } from 'orcid-id-ts'
import * as _ from '../src/orcid'
Expand All @@ -11,9 +13,11 @@ describe('getNameFromOrcid', () => {
describe('when the ORCID iD is 0000-0002-6109-0367', () => {
test('when the request succeeds', async () => {
const actual = await _.getNameFromOrcid('0000-0002-6109-0367' as Orcid)({
clock: SystemClock,
fetch: fetchMock.sandbox().get('*', {
body: { name: { 'given-names': { value: 'Daniela' }, 'family-name': { value: 'Saderi' } } },
}),
logger: () => IO.of(undefined),
})()

expect(actual).toStrictEqual(E.right('Daniela Saderi'))
Expand All @@ -23,7 +27,9 @@ describe('getNameFromOrcid', () => {
'when the request fails',
async status => {
const actual = await _.getNameFromOrcid('0000-0002-6109-0367' as Orcid)({
clock: SystemClock,
fetch: fetchMock.sandbox().get('*', { status }),
logger: () => IO.of(undefined),
})()

expect(actual).toStrictEqual(E.left('unavailable'))
Expand All @@ -32,7 +38,9 @@ describe('getNameFromOrcid', () => {

test('when the network fails', async () => {
const actual = await _.getNameFromOrcid('0000-0002-6109-0367' as Orcid)({
clock: SystemClock,
fetch: () => Promise.reject('network error'),
logger: () => IO.of(undefined),
})()

expect(actual).toStrictEqual(E.left('unavailable'))
Expand All @@ -43,7 +51,9 @@ describe('getNameFromOrcid', () => {
'when the ORCID iD is not 0000-0002-6109-0367',
async orcid => {
const actual = await _.getNameFromOrcid(orcid)({
clock: SystemClock,
fetch: () => Promise.reject('should not be called'),
logger: () => IO.of(undefined),
})()

expect(actual).toStrictEqual(E.left('not-found'))
Expand Down

0 comments on commit 49ea0a6

Please sign in to comment.