Skip to content

Commit

Permalink
Make a HTTP request
Browse files Browse the repository at this point in the history
Note the response is unused

Refs #2209
  • Loading branch information
thewilkybarkid committed Jan 29, 2025
1 parent 2c06889 commit f871ce3
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 2 deletions.
13 changes: 13 additions & 0 deletions src/JapanLinkCenter/Record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { HttpClient } from '@effect/platform'
import type * as Doi from 'doi-ts'
import { Data, Effect, pipe } from 'effect'

class RecordIsUnavailable extends Data.TaggedError('RecordIsUnavailable')<{ cause?: unknown }> {}

export const getRecord = (doi: Doi.Doi): Effect.Effect<never, RecordIsUnavailable, HttpClient.HttpClient> =>
pipe(
HttpClient.get(new URL(encodeURIComponent(encodeURIComponent(doi)), 'https://api.japanlinkcenter.org/dois/')),
Effect.mapError(error => new RecordIsUnavailable({ cause: error })),
Effect.andThen(response => new RecordIsUnavailable({ cause: response })),
Effect.scoped,
)
9 changes: 7 additions & 2 deletions src/JapanLinkCenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DeprecatedSleepEnv } from '../Context.js'
import { revalidateIfStale, timeoutRequest, useStaleCache } from '../fetch.js'
import * as Preprint from '../preprint.js'
import type { JapanLinkCenterPreprintId } from './PreprintId.js'
import { getRecord } from './Record.js'

export { isJapanLinkCenterPreprintDoi, type JapanLinkCenterPreprintId } from './PreprintId.js'

Expand All @@ -13,9 +14,13 @@ export const getPreprintFromJapanLinkCenter: (
Preprint.Preprint,
Preprint.PreprintIsNotFound | Preprint.PreprintIsUnavailable,
FetchHttpClient.Fetch | DeprecatedSleepEnv
> = () =>
> = id =>
pipe(
Effect.fail(new Preprint.PreprintIsUnavailable()),
getRecord(id.value),
Effect.andThen(() => Effect.fail(new Preprint.PreprintIsUnavailable())),
Effect.catchTags({
RecordIsUnavailable: () => new Preprint.PreprintIsUnavailable(),
}),
Effect.provide(FetchHttpClient.layer),
Effect.provideServiceEffect(
FetchHttpClient.Fetch,
Expand Down
92 changes: 92 additions & 0 deletions test/JapanLinkCenter/Record.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { HttpClient, HttpClientError, HttpClientRequest, HttpClientResponse } from '@effect/platform'
import { test } from '@fast-check/jest'
import { describe, expect, jest } from '@jest/globals'
import { Doi } from 'doi-ts'
import { Effect, pipe, TestContext, Tuple } from 'effect'
import * as _ from '../../src/JapanLinkCenter/Record.js'
import * as fc from '../fc.js'

describe('getRecord', () => {
test.prop(
[
fc
.doi()
.map(doi =>
Tuple.make(
doi,
new URL(encodeURIComponent(encodeURIComponent(doi)), 'https://api.japanlinkcenter.org/dois/').href,
),
),
],
{
examples: [
[[Doi('10.51094/jxiv.1041'), 'https://api.japanlinkcenter.org/dois/10.51094%252Fjxiv.1041']],
[[Doi('10.51094/jxiv.1041/1'), 'https://api.japanlinkcenter.org/dois/10.51094%252Fjxiv.1041%252F1']],
],
},
)('calls the record API', ([doi, expectedUrl]) =>
Effect.gen(function* () {
const clientSpy = jest.fn((_: HttpClientRequest.HttpClientRequest) => new Response())
const client = stubbedClient(clientSpy)

yield* pipe(Effect.flip(_.getRecord(doi)), Effect.provideService(HttpClient.HttpClient, client))

expect(clientSpy).toHaveBeenCalledWith(HttpClientRequest.get(expectedUrl))
}).pipe(Effect.provide(TestContext.TestContext), Effect.runPromise),
)

describe('with a response', () => {
test.prop([fc.doi(), fc.statusCode().filter(status => status >= 200)])('always fails', (doi, status) =>
Effect.gen(function* () {
const client = stubbedClient(() => new Response(null, { status }))

const actual = yield* pipe(Effect.flip(_.getRecord(doi)), Effect.provideService(HttpClient.HttpClient, client))

expect(actual._tag).toStrictEqual('RecordIsUnavailable')
}).pipe(Effect.provide(TestContext.TestContext), Effect.runPromise),
)
})

describe('with a request error', () => {
test.prop([fc.doi(), fc.constantFrom('Transport', 'Encode', 'InvalidUrl')])('returns unavailable', (doi, reason) =>
Effect.gen(function* () {
const client = stubbedFailingClient(request => new HttpClientError.RequestError({ request, reason }))
const actual = yield* pipe(Effect.flip(_.getRecord(doi)), Effect.provideService(HttpClient.HttpClient, client))

expect(actual._tag).toStrictEqual('RecordIsUnavailable')
expect(actual.cause).toStrictEqual(expect.objectContaining({ _tag: 'RequestError', reason }))
}).pipe(Effect.provide(TestContext.TestContext), Effect.runPromise),
)
})

describe('with a response error', () => {
test.prop([fc.doi(), fc.constantFrom('StatusCode', 'Decode', 'EmptyBody')])('returns unavailable', (doi, reason) =>
Effect.gen(function* () {
const client = stubbedFailingClient(
request =>
new HttpClientError.ResponseError({
request,
response: HttpClientResponse.fromWeb(request, new Response()),
reason,
}),
)
const actual = yield* pipe(Effect.flip(_.getRecord(doi)), Effect.provideService(HttpClient.HttpClient, client))

expect(actual._tag).toStrictEqual('RecordIsUnavailable')
expect(actual.cause).toStrictEqual(expect.objectContaining({ _tag: 'ResponseError', reason }))
}).pipe(Effect.provide(TestContext.TestContext), Effect.runPromise),
)
})
})

const stubbedClient = (f: (request: HttpClientRequest.HttpClientRequest) => Response) =>
HttpClient.makeWith<never, never, never, never>(
Effect.andThen(request => HttpClientResponse.fromWeb(request, f(request))),
Effect.succeed,
)

const stubbedFailingClient = (f: (request: HttpClientRequest.HttpClientRequest) => HttpClientError.HttpClientError) =>
HttpClient.makeWith<never, never, HttpClientError.HttpClientError, never>(
Effect.andThen(request => Effect.fail(f(request))),
Effect.succeed,
)

0 comments on commit f871ce3

Please sign in to comment.