Skip to content

Commit

Permalink
HttpClient refactor & simplification (#3746)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart committed Oct 8, 2024
1 parent e0a5dad commit 90ceeab
Show file tree
Hide file tree
Showing 26 changed files with 613 additions and 685 deletions.
7 changes: 7 additions & 0 deletions .changeset/early-otters-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@effect/platform-browser": minor
"@effect/platform-node": minor
"@effect/platform": minor
---

remove HttpClient.Service type
7 changes: 7 additions & 0 deletions .changeset/stale-shirts-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@effect/platform-browser": minor
"@effect/platform-node": minor
"@effect/platform": minor
---

constrain HttpClient success type to HttpClientResponse
7 changes: 7 additions & 0 deletions .changeset/tricky-ears-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@effect/platform-browser": minor
"@effect/platform-node": minor
"@effect/platform": minor
---

add HttpClient accessor apis
2 changes: 1 addition & 1 deletion packages/platform-browser/src/BrowserHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import * as internal from "./internal/httpClient.js"
* @since 1.0.0
* @category layers
*/
export const layerXMLHttpRequest: Layer.Layer<HttpClient.HttpClient.Service> = internal.layerXMLHttpRequest
export const layerXMLHttpRequest: Layer.Layer<HttpClient.HttpClient> = internal.layerXMLHttpRequest

/**
* @since 1.0.0
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-browser/src/internal/httpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const withXHRArrayBuffer = <A, E, R>(effect: Effect.Effect<A, E, R>): Eff

const makeXhr = () => new XMLHttpRequest()

const makeXMLHttpRequest = Client.makeService((request, url, signal, fiber) =>
const makeXMLHttpRequest = Client.make((request, url, signal, fiber) =>
Effect.suspend(() => {
const xhr = Context.getOrElse(
fiber.getFiberRef(FiberRef.currentContext),
Expand Down
10 changes: 5 additions & 5 deletions packages/platform-browser/test/BrowserHttpClient.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cookies, HttpClientRequest } from "@effect/platform"
import { Cookies, HttpClient } from "@effect/platform"
import { BrowserHttpClient } from "@effect/platform-browser"
import { assert, describe, it } from "@effect/vitest"
import { Chunk, Effect, Layer, Stream } from "effect"
Expand All @@ -15,7 +15,7 @@ const layer = (...args: Parameters<typeof MXHR.newServer>) =>
describe("BrowserHttpClient", () => {
it.effect("json", () =>
Effect.gen(function*() {
const body = yield* HttpClientRequest.get("http://localhost:8080/my/url").pipe(
const body = yield* HttpClient.get("http://localhost:8080/my/url").pipe(
Effect.flatMap((_) => _.json),
Effect.scoped
)
Expand All @@ -29,7 +29,7 @@ describe("BrowserHttpClient", () => {

it.effect("stream", () =>
Effect.gen(function*() {
const body = yield* HttpClientRequest.get("http://localhost:8080/my/url").pipe(
const body = yield* HttpClient.get("http://localhost:8080/my/url").pipe(
Effect.map((_) =>
_.stream.pipe(
Stream.decodeText(),
Expand All @@ -49,7 +49,7 @@ describe("BrowserHttpClient", () => {

it.effect("cookies", () =>
Effect.gen(function*() {
const cookies = yield* HttpClientRequest.get("http://localhost:8080/my/url").pipe(
const cookies = yield* HttpClient.get("http://localhost:8080/my/url").pipe(
Effect.map((res) => res.cookies),
Effect.scoped
)
Expand All @@ -67,7 +67,7 @@ describe("BrowserHttpClient", () => {

it.effect("arrayBuffer", () =>
Effect.gen(function*() {
const body = yield* HttpClientRequest.get("http://localhost:8080/my/url").pipe(
const body = yield* HttpClient.get("http://localhost:8080/my/url").pipe(
Effect.flatMap((_) => _.arrayBuffer),
Effect.scoped,
BrowserHttpClient.withXHRArrayBuffer
Expand Down
2 changes: 1 addition & 1 deletion packages/platform-bun/src/BunHttpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const layer: (
* @category layers
*/
export const layerTest: Layer.Layer<
| HttpClient.HttpClient.Service
| HttpClient.HttpClient
| Server.HttpServer
| Platform.HttpPlatform
| Etag.Generator
Expand Down
12 changes: 6 additions & 6 deletions packages/platform-node/src/NodeHttpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,19 @@ export const makeAgentLayer: (options?: Https.AgentOptions) => Layer.Layer<HttpA
* @since 1.0.0
* @category constructors
*/
export const make: Effect.Effect<Client.HttpClient.Service, never, HttpAgent> = internal.make
export const make: Effect.Effect<Client.HttpClient, never, HttpAgent> = internal.make

/**
* @since 1.0.0
* @category layers
*/
export const layer: Layer.Layer<Client.HttpClient.Service> = internal.layer
export const layer: Layer.Layer<Client.HttpClient> = internal.layer

/**
* @since 1.0.0
* @category layers
*/
export const layerWithoutAgent: Layer.Layer<Client.HttpClient.Service, never, HttpAgent> = internal.layerWithoutAgent
export const layerWithoutAgent: Layer.Layer<Client.HttpClient, never, HttpAgent> = internal.layerWithoutAgent

/**
* @since 1.0.0
Expand Down Expand Up @@ -122,17 +122,17 @@ export class UndiciRequestOptions extends Context.Tag(internalUndici.undiciOptio
* @since 1.0.0
* @category constructors
*/
export const makeUndici: (dispatcher: Undici.Dispatcher) => Client.HttpClient.Service = internalUndici.make
export const makeUndici: (dispatcher: Undici.Dispatcher) => Client.HttpClient = internalUndici.make

/**
* @since 1.0.0
* @category layers
*/
export const layerUndici: Layer.Layer<Client.HttpClient.Service> = internalUndici.layer
export const layerUndici: Layer.Layer<Client.HttpClient> = internalUndici.layer

/**
* @since 1.0.0
* @category layers
*/
export const layerUndiciWithoutDispatcher: Layer.Layer<Client.HttpClient.Service, never, Dispatcher> =
export const layerUndiciWithoutDispatcher: Layer.Layer<Client.HttpClient, never, Dispatcher> =
internalUndici.layerWithoutDispatcher
6 changes: 3 additions & 3 deletions packages/platform-node/src/NodeHttpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,21 @@ export const layerConfig: (
* with prepended url of the running http server.
*
* @example
* import { HttpClientRequest, HttpRouter, HttpServer } from "@effect/platform"
* import { HttpClient, HttpRouter, HttpServer } from "@effect/platform"
* import { NodeHttpServer } from "@effect/platform-node"
* import { Effect } from "effect"
*
* Effect.gen(function*() {
* yield* HttpServer.serveEffect(HttpRouter.empty)
* const response = yield* HttpClientRequest.get("/")
* const response = yield* HttpClient.get("/")
* assert.strictEqual(response.status, 404)
* }).pipe(Effect.provide(NodeHttpServer.layerTest))
*
* @since 1.0.0
* @category layers
*/
export const layerTest: Layer.Layer<
| HttpClient.HttpClient.Service
| HttpClient.HttpClient
| Server.HttpServer
| Platform.HttpPlatform
| Etag.Generator
Expand Down
4 changes: 2 additions & 2 deletions packages/platform-node/src/internal/httpClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ export const makeAgentLayer = (options?: Https.AgentOptions): Layer.Layer<NodeCl
/** @internal */
export const agentLayer = makeAgentLayer()

const fromAgent = (agent: NodeClient.HttpAgent): Client.HttpClient.Service =>
Client.makeService((request, url, signal) => {
const fromAgent = (agent: NodeClient.HttpAgent): Client.HttpClient =>
Client.make((request, url, signal) => {
const nodeRequest = url.protocol === "https:" ?
Https.request(url, {
agent: agent.https,
Expand Down
4 changes: 2 additions & 2 deletions packages/platform-node/src/internal/httpClientUndici.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export const dispatcherLayerGlobal = Layer.sync(Dispatcher, () => Undici.getGlob
export const undiciOptionsTagKey = "@effect/platform-node/NodeHttpClient/undiciOptions"

/** @internal */
export const make = (dispatcher: Undici.Dispatcher): Client.HttpClient.Service =>
Client.makeService((request, url, signal, fiber) => {
export const make = (dispatcher: Undici.Dispatcher): Client.HttpClient =>
Client.make((request, url, signal, fiber) => {
const context = fiber.getFiberRef(FiberRef.currentContext)
const options: Undici.Dispatcher.RequestOptions = context.unsafeMap.get(undiciOptionsTagKey) ?? {}
return convertBody(request.body).pipe(
Expand Down
8 changes: 5 additions & 3 deletions packages/platform-node/test/HttpApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe("HttpApi", () => {
describe("errors", () => {
it.scoped("empty errors have no body", () =>
Effect.gen(function*() {
const response = yield* HttpClientRequest.get("/groups/0")
const response = yield* HttpClient.get("/groups/0")
assert.strictEqual(response.status, 418)
const text = yield* response.text
assert.strictEqual(text, "")
Expand All @@ -91,7 +91,8 @@ describe("HttpApi", () => {
it.scoped("default to 500 status code", () =>
Effect.gen(function*() {
const response = yield* HttpClientRequest.get("/users").pipe(
HttpClientRequest.setHeaders({ page: "0" })
HttpClientRequest.setHeaders({ page: "0" }),
HttpClient.execute
)
assert.strictEqual(response.status, 500)
const body = yield* response.json
Expand All @@ -103,7 +104,8 @@ describe("HttpApi", () => {
it.scoped("class level annotations", () =>
Effect.gen(function*() {
const response = yield* HttpClientRequest.post("/users").pipe(
HttpClientRequest.bodyUnsafeJson({ name: "boom" })
HttpClientRequest.bodyUnsafeJson({ name: "boom" }),
HttpClient.execute
)
assert.strictEqual(response.status, 400)
}).pipe(Effect.provide(HttpLive)))
Expand Down
31 changes: 18 additions & 13 deletions packages/platform-node/test/HttpClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { HttpClient, HttpClientRequest, HttpClientResponse } from "@effect/platf
import * as NodeClient from "@effect/platform-node/NodeHttpClient"
import * as Schema from "@effect/schema/Schema"
import { describe, expect, it } from "@effect/vitest"
import { Struct } from "effect"
import * as Context from "effect/Context"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
Expand All @@ -13,23 +14,24 @@ const Todo = Schema.Struct({
title: Schema.String,
completed: Schema.Boolean
})
const TodoWithoutId = Schema.Struct({
...Struct.omit(Todo.fields, "id")
})

const makeJsonPlaceholder = Effect.gen(function*(_) {
const defaultClient = yield* _(HttpClient.HttpClient)
const client = defaultClient.pipe(
HttpClient.mapRequest(HttpClientRequest.prependUrl("https://jsonplaceholder.typicode.com"))
)
const todoClient = client.pipe(
HttpClient.mapEffect(HttpClientResponse.schemaBodyJson(Todo)),
HttpClient.scoped
)
const createTodo = HttpClient.schemaFunction(
todoClient,
Todo.pipe(Schema.omit("id"))
)(HttpClientRequest.post("/todos"))
const createTodo = (todo: typeof TodoWithoutId.Type) =>
HttpClientRequest.post("/todos").pipe(
HttpClientRequest.schemaBodyJson(TodoWithoutId)(todo),
Effect.flatMap(client.execute),
Effect.flatMap(HttpClientResponse.schemaBodyJson(Todo)),
Effect.scoped
)
return {
client,
todoClient,
createTodo
} as const
})
Expand All @@ -50,7 +52,7 @@ const JsonPlaceholderLive = Layer.effect(JsonPlaceholder, makeJsonPlaceholder)
it.effect("google", () =>
Effect.gen(function*(_) {
const response = yield* _(
HttpClientRequest.get("https://www.google.com/"),
HttpClient.get("https://www.google.com/"),
Effect.flatMap((_) => _.text),
Effect.scoped
)
Expand Down Expand Up @@ -83,13 +85,16 @@ const JsonPlaceholderLive = Layer.effect(JsonPlaceholder, makeJsonPlaceholder)
it.effect("jsonplaceholder", () =>
Effect.gen(function*() {
const jp = yield* JsonPlaceholder
const response = yield* jp.todoClient.get("/todos/1")
const response = yield* jp.client.get("/todos/1").pipe(
Effect.flatMap(HttpClientResponse.schemaBodyJson(Todo)),
Effect.scoped
)
expect(response.id).toBe(1)
}).pipe(Effect.provide(JsonPlaceholderLive.pipe(
Layer.provide(layer)
))))

it.effect("jsonplaceholder schemaFunction", () =>
it.effect("jsonplaceholder schemaBodyJson", () =>
Effect.gen(function*() {
const jp = yield* JsonPlaceholder
const response = yield* jp.createTodo({
Expand Down Expand Up @@ -130,7 +135,7 @@ const JsonPlaceholderLive = Layer.effect(JsonPlaceholder, makeJsonPlaceholder)
it.effect("close early", () =>
Effect.gen(function*(_) {
const response = yield* _(
HttpClientRequest.get("https://www.google.com/"),
HttpClient.get("https://www.google.com/"),
Effect.scoped
)
expect(response.status).toBe(200)
Expand Down
40 changes: 17 additions & 23 deletions packages/platform-node/test/HttpServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
import { NodeHttpServer } from "@effect/platform-node"
import * as Schema from "@effect/schema/Schema"
import { assert, describe, expect, it } from "@effect/vitest"
import { Deferred, Duration, Fiber, flow, Stream } from "effect"
import { Deferred, Duration, Fiber, Stream } from "effect"
import * as Effect from "effect/Effect"
import * as Option from "effect/Option"
import * as Tracer from "effect/Tracer"
Expand All @@ -33,14 +33,6 @@ const IdParams = Schema.Struct({
})
const todoResponse = HttpServerResponse.schemaJson(Todo)

const makeTodoClient = Effect.map(
HttpClient.HttpClient,
flow(
HttpClient.mapEffect(HttpClientResponse.schemaBodyJson(Todo)),
HttpClient.scoped
)
)

describe("HttpServer", () => {
it.scoped("schema", () =>
Effect.gen(function*() {
Expand All @@ -54,8 +46,10 @@ describe("HttpServer", () => {
),
HttpServer.serveEffect()
)
const client = yield* makeTodoClient
const todo = yield* client.get("/todos/1")
const todo = yield* HttpClient.get("/todos/1").pipe(
Effect.flatMap(HttpClientResponse.schemaBodyJson(Todo)),
Effect.scoped
)
expect(todo).toEqual({ id: 1, title: "test" })
}).pipe(Effect.provide(NodeHttpServer.layerTest)))

Expand Down Expand Up @@ -306,10 +300,10 @@ describe("HttpServer", () => {
),
HttpServer.serveEffect()
)
const client = yield* makeTodoClient
const todo = yield* HttpClientRequest.post("/todos").pipe(
HttpClientRequest.bodyUrlParams({ id: "1", title: "test" }),
client.execute,
HttpClient.execute,
Effect.flatMap(HttpClientResponse.schemaBodyJson(Todo)),
Effect.scoped
)
expect(todo).toEqual({ id: 1, title: "test" })
Expand Down Expand Up @@ -674,10 +668,10 @@ describe("HttpServer", () => {
Effect.gen(function*() {
yield* HttpRouter.concat(routerA, routerB).pipe(HttpServer.serveEffect())
const [responseA, responseMountA, responseB, responseMountB] = yield* Effect.all([
HttpClientRequest.get("/a"),
HttpClientRequest.get("/ma"),
HttpClientRequest.get("/b"),
HttpClientRequest.get("/mb")
HttpClient.get("/a"),
HttpClient.get("/ma"),
HttpClient.get("/b"),
HttpClient.get("/mb")
])
expect(yield* responseA.text).toEqual("a")
expect(yield* responseMountA.text).toEqual("ma")
Expand All @@ -689,10 +683,10 @@ describe("HttpServer", () => {
Effect.gen(function*() {
yield* HttpRouter.concatAll(routerA, routerB).pipe(HttpServer.serveEffect())
const [responseA, responseMountA, responseB, responseMountB] = yield* Effect.all([
HttpClientRequest.get("/a"),
HttpClientRequest.get("/ma"),
HttpClientRequest.get("/b"),
HttpClientRequest.get("/mb")
HttpClient.get("/a"),
HttpClient.get("/ma"),
HttpClient.get("/b"),
HttpClient.get("/mb")
])
expect(yield* responseA.text).toEqual("a")
expect(yield* responseMountA.text).toEqual("ma")
Expand All @@ -706,9 +700,9 @@ describe("HttpServer", () => {
HttpRouter.get("/:param", HttpServerResponse.empty()),
HttpServer.serveEffect()
)
let res = yield* HttpClientRequest.get("/123456").pipe(Effect.scoped)
let res = yield* HttpClient.get("/123456").pipe(Effect.scoped)
assert.strictEqual(res.status, 404)
res = yield* HttpClientRequest.get("/12345").pipe(Effect.scoped)
res = yield* HttpClient.get("/12345").pipe(Effect.scoped)
assert.strictEqual(res.status, 204)
}).pipe(
Effect.provide(NodeHttpServer.layerTest),
Expand Down
Loading

0 comments on commit 90ceeab

Please sign in to comment.