Skip to content

Commit

Permalink
move fetch options to a FiberRef
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart committed Apr 3, 2024
1 parent 3cad21d commit 6caa77a
Show file tree
Hide file tree
Showing 16 changed files with 236 additions and 124 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-pumpkins-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/platform-node": patch
---

allow specifying undici options through a fiber ref
5 changes: 5 additions & 0 deletions .changeset/rotten-mirrors-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/platform": minor
---

rename auto-scoped ClientResponse apis from *Effect to *Scoped
20 changes: 20 additions & 0 deletions .changeset/stale-gorillas-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"@effect/platform": minor
"@effect/rpc-http": minor
---

move fetch options to a FiberRef

This change makes adjusting options to fetch more composable. You can now do:

```ts
import { pipe } from "effect";
import * as Http from "@effect/platform/HttpClient";

pipe(
Http.request.get("https://example.com"),
Http.client.fetchOk,
Http.client.withFetchOptions({ credentials: "include" }),
Http.response.text
);
```
19 changes: 19 additions & 0 deletions packages/platform-node/src/NodeHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type * as Client from "@effect/platform/Http/Client"
import type * as Context from "effect/Context"
import type * as Effect from "effect/Effect"
import type * as FiberRef from "effect/FiberRef"
import type * as Layer from "effect/Layer"
import type * as Scope from "effect/Scope"
import type * as Http from "node:http"
Expand Down Expand Up @@ -109,6 +110,24 @@ export const dispatcherLayer: Layer.Layer<Dispatcher> = internalUndici.dispatche
*/
export const dispatcherLayerGlobal: Layer.Layer<Dispatcher> = internalUndici.dispatcherLayerGlobal

/**
* @since 1.0.0
* @category undici
*/
export const currentUndiciOptions: FiberRef.FiberRef<Partial<Undici.Dispatcher.RequestOptions>> =
internalUndici.currentUndiciOptions

/**
* @since 1.0.0
* @category undici
*/
export const withUndiciOptions: {
(
options: Partial<Undici.Dispatcher.RequestOptions>
): <R, E, A>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
<R, E, A>(effect: Effect.Effect<A, E, R>, options: Partial<Undici.Dispatcher.RequestOptions>): Effect.Effect<A, E, R>
} = internalUndici.withUndiciOptions

/**
* @since 1.0.0
* @category constructors
Expand Down
25 changes: 23 additions & 2 deletions packages/platform-node/src/internal/http/clientUndici.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import * as IncomingMessage from "@effect/platform/Http/IncomingMessage"
import * as UrlParams from "@effect/platform/Http/UrlParams"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as FiberRef from "effect/FiberRef"
import { dual } from "effect/Function"
import { globalValue } from "effect/GlobalValue"
import * as Inspectable from "effect/Inspectable"
import * as Layer from "effect/Layer"
import * as Option from "effect/Option"
Expand All @@ -20,7 +23,7 @@ import * as NodeStream from "../../NodeStream.js"

/** @internal */
export const Dispatcher = Context.GenericTag<NodeClient.Dispatcher, Undici.Dispatcher>(
"@effect/platform-node/Http/NodeClient/Dispatcher"
"@effect/platform-node/NodeHttpClient/Dispatcher"
)

/** @internal */
Expand All @@ -43,9 +46,26 @@ const makeAbortSignal = Effect.map(
(_) => _.signal
)

/** @internal */
export const currentUndiciOptions = globalValue(
Symbol.for("@effect/platform-node/NodeHttpClient/currentUndici"),
() => FiberRef.unsafeMake<Partial<Undici.Dispatcher.RequestOptions>>({})
)

/** @internal */
export const withUndiciOptions = dual<
(
options: Partial<Undici.Dispatcher.RequestOptions>
) => <R, E, A>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>,
<R, E, A>(
effect: Effect.Effect<A, E, R>,
options: Partial<Undici.Dispatcher.RequestOptions>
) => Effect.Effect<A, E, R>
>(2, (self, options) => Effect.locally(self, currentUndiciOptions, options))

/** @internal */
export const make = (dispatcher: Undici.Dispatcher): Client.Client.Default =>
Client.makeDefault((request) =>
Client.makeDefault((request, fiber) =>
Effect.Do.pipe(
Effect.bind("url", () =>
UrlParams.makeUrl(request.url, request.urlParams, (_) =>
Expand All @@ -60,6 +80,7 @@ export const make = (dispatcher: Undici.Dispatcher): Client.Client.Default =>
Effect.tryPromise({
try: () =>
dispatcher.request({
...(fiber.getFiberRef(currentUndiciOptions)),
signal,
method: request.method,
headers: request.headers,
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-node/test/Http/NodeClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const JsonPlaceholderLive = Layer.effect(JsonPlaceholder, makeJsonPlaceholder)
const response = yield* _(
Http.request.head("https://jsonplaceholder.typicode.com/todos"),
client,
Http.response.schemaJsonEffect(Schema.struct({ status: Schema.literal(200) }))
Http.response.schemaJsonScoped(Schema.struct({ status: Schema.literal(200) }))
)
expect(response).toEqual({ status: 200 })
}).pipe(Effect.provide(layer)))
Expand Down
24 changes: 12 additions & 12 deletions packages/platform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ import { Console, Effect } from "effect";

const getPostAsJson = Http.request
.get("https://jsonplaceholder.typicode.com/posts/1")
.pipe(Http.client.fetch(), Http.response.json);
.pipe(Http.client.fetch, Http.response.json);

NodeRuntime.runMain(
getPostAsJson.pipe(Effect.andThen((post) => Console.log(typeof post, post)))
Expand Down Expand Up @@ -302,7 +302,7 @@ import { Console, Effect } from "effect";

const getPostAsText = Http.request
.get("https://jsonplaceholder.typicode.com/posts/1")
.pipe(Http.client.fetch(), Http.response.text);
.pipe(Http.client.fetch, Http.response.text);

NodeRuntime.runMain(
getPostAsText.pipe(Effect.andThen((post) => Console.log(typeof post, post)))
Expand Down Expand Up @@ -349,7 +349,7 @@ const getPost = Http.request
"Content-type": "application/json; charset=UTF-8",
Foo: "Bar",
}),
Http.client.fetch()
Http.client.fetch
);
```

Expand Down Expand Up @@ -377,7 +377,7 @@ const getPostAndValidate: Effect.Effect<{
const getPostAndValidate = Http.request
.get("https://jsonplaceholder.typicode.com/posts/1")
.pipe(
Http.client.fetch(),
Http.client.fetch,
Effect.andThen(Http.response.schemaBodyJson(Post)),
Effect.scoped
);
Expand Down Expand Up @@ -411,7 +411,7 @@ import { Console, Effect } from "effect";

const getText = Http.request
.get("https://jsonplaceholder.typicode.com/non-existing-page")
.pipe(Http.client.fetch(), Http.response.text);
.pipe(Http.client.fetch, Http.response.text);

NodeRuntime.runMain(getText.pipe(Effect.andThen(Console.log)));
/*
Expand All @@ -429,7 +429,7 @@ import { Console, Effect } from "effect";

const getText = Http.request
.get("https://jsonplaceholder.typicode.com/non-existing-page")
.pipe(Http.client.filterStatusOk(Http.client.fetch()), Http.response.text);
.pipe(Http.client.filterStatusOk(Http.client.fetch), Http.response.text);

NodeRuntime.runMain(getText.pipe(Effect.andThen(Console.log)));
/*
Expand All @@ -438,12 +438,12 @@ timestamp=2024-03-25T10:21:16.972Z level=ERROR fiber=#0 cause="ResponseError: St
*/
```

Note that you can use `Http.client.fetchOk` as a shortcut for `Http.client.filterStatusOk(Http.client.fetch())`:
Note that you can use `Http.client.fetchOk` as a shortcut for `Http.client.filterStatusOk(Http.client.fetch)`:

```ts
const getText = Http.request
.get("https://jsonplaceholder.typicode.com/non-existing-page")
.pipe(Http.client.fetchOk(), Http.response.text);
.pipe(Http.client.fetchOk, Http.response.text);
```

You can also create your own status-based filters. In fact, `Http.client.filterStatusOk` is just a shortcut for the following filter:
Expand All @@ -453,7 +453,7 @@ const getText = Http.request
.get("https://jsonplaceholder.typicode.com/non-existing-page")
.pipe(
Http.client.filterStatus(
Http.client.fetch(),
Http.client.fetch,
(status) => status >= 200 && status < 300
),
Http.response.text
Expand All @@ -477,7 +477,7 @@ const addPost = Http.request
body: "bar",
userId: 1,
}),
Effect.andThen(Http.client.fetch()),
Effect.andThen(Http.client.fetch),
Http.response.json
);

Expand Down Expand Up @@ -508,7 +508,7 @@ const addPost = Http.request
}),
"application/json; charset=UTF-8"
),
Http.client.fetch(),
Http.client.fetch,
Http.response.json
);

Expand Down Expand Up @@ -542,7 +542,7 @@ const addPost = Http.request
body: "bar",
userId: 1,
}),
Effect.andThen(Http.client.fetch()),
Effect.andThen(Http.client.fetch),
Effect.andThen(Http.response.schemaBodyJson(Post)),
Effect.scoped
);
Expand Down
23 changes: 19 additions & 4 deletions packages/platform/src/Http/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,13 @@ export const layer: Layer.Layer<Client.Default> = internal.layer
* @since 1.0.0
* @category constructors
*/
export const fetch: (options?: RequestInit) => Client.Default = internal.fetch
export const fetch: Client.Default = internal.fetch

/**
* @since 1.0.0
* @category constructors
*/
export const fetchOk: (options?: RequestInit) => Client.Default = internal.fetchOk
export const fetchOk: Client.Default = internal.fetchOk

/**
* @since 1.0.0
Expand Down Expand Up @@ -437,14 +437,14 @@ export const withCookiesRef: {
} = internal.withCookiesRef

/**
* @since 2.0.0
* @since 1.0.0
* @category fiber refs
*/
export const currentTracerDisabledWhen: FiberRef.FiberRef<Predicate.Predicate<ClientRequest.ClientRequest>> =
internal.currentTracerDisabledWhen

/**
* @since 2.0.0
* @since 1.0.0
* @category fiber refs
*/
export const withTracerDisabledWhen: {
Expand All @@ -456,3 +456,18 @@ export const withTracerDisabledWhen: {
predicate: Predicate.Predicate<ClientRequest.ClientRequest>
): Effect.Effect<A, E, R>
} = internal.withTracerDisabledWhen

/**
* @since 1.0.0
* @category fiber refs
*/
export const currentFetchOptions: FiberRef.FiberRef<RequestInit> = internal.currentFetchOptions

/**
* @since 1.0.0
* @category fiber refs
*/
export const withFetchOptions: {
(options: RequestInit): <R, E, A>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
<R, E, A>(effect: Effect.Effect<A, E, R>, options: RequestInit): Effect.Effect<A, E, R>
} = internal.withFetchOptions
14 changes: 7 additions & 7 deletions packages/platform/src/Http/ClientResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export {
* @since 1.0.0
* @category schema
*/
schemaBodyJsonEffect,
schemaBodyJsonScoped,
/**
* @since 1.0.0
* @category schema
Expand All @@ -34,7 +34,7 @@ export {
* @since 1.0.0
* @category schema
*/
schemaBodyUrlParamsEffect,
schemaBodyUrlParamsScoped,
/**
* @since 1.0.0
* @category schema
Expand All @@ -44,7 +44,7 @@ export {
* @since 1.0.0
* @category schema
*/
schemaHeadersEffect
schemaHeadersScoped
} from "./IncomingMessage.js"

/**
Expand Down Expand Up @@ -161,7 +161,7 @@ export const urlParamsBody: <E, R>(
* @since 1.0.0
* @category schema
*/
export const schemaJsonEffect: <
export const schemaJsonScoped: <
R,
I extends {
readonly status?: number | undefined
Expand All @@ -178,13 +178,13 @@ export const schemaJsonEffect: <
A,
E | Error.ResponseError | ParseResult.ParseError,
Exclude<R, Scope.Scope> | Exclude<R2, Scope.Scope>
> = internal.schemaJsonEffect
> = internal.schemaJsonScoped

/**
* @since 1.0.0
* @category schema
*/
export const schemaNoBodyEffect: <
export const schemaNoBodyScoped: <
R,
I extends {
readonly status?: number | undefined
Expand All @@ -197,4 +197,4 @@ export const schemaNoBodyEffect: <
) => <E, R2>(
effect: Effect.Effect<ClientResponse, E, R2>
) => Effect.Effect<A, E | ParseResult.ParseError, Exclude<R, Scope.Scope> | Exclude<R2, Scope.Scope>> =
internal.schemaNoBodyEffect
internal.schemaNoBodyScoped
Loading

0 comments on commit 6caa77a

Please sign in to comment.