Skip to content

Commit

Permalink
parse URL instances when creating client requests (#2924)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart authored Jun 5, 2024
1 parent 6f3823a commit 22c777d
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/old-doors-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@effect/platform": minor
---

parse URL instances when creating client requests
2 changes: 1 addition & 1 deletion packages/platform-node/src/internal/http/clientUndici.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const make = (dispatcher: Undici.Dispatcher): Client.Client.Default =>
method: request.method,
headers: request.headers,
origin: url.origin,
path: url.pathname + url.search,
path: url.pathname + url.search + url.hash,
body,
// leave timeouts to Effect.timeout etc
headersTimeout: 60 * 60 * 1000,
Expand Down
44 changes: 37 additions & 7 deletions packages/platform/src/Http/ClientRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { ParseOptions } from "@effect/schema/AST"
import type * as Schema from "@effect/schema/Schema"
import type * as Effect from "effect/Effect"
import type { Inspectable } from "effect/Inspectable"
import type * as Option from "effect/Option"
import type { Scope } from "effect/Scope"
import type * as Stream from "effect/Stream"
import type * as PlatformError from "../Error.js"
Expand Down Expand Up @@ -41,6 +42,7 @@ export interface ClientRequest
readonly method: Method
readonly url: string
readonly urlParams: UrlParams.UrlParams
readonly hash: Option.Option<string>
readonly headers: Headers.Headers
readonly body: Body.Body
}
Expand All @@ -51,8 +53,9 @@ export interface ClientRequest
*/
export interface Options {
readonly method?: Method | undefined
readonly url?: string | undefined
readonly url?: string | URL | undefined
readonly urlParams?: UrlParams.Input | undefined
readonly hash?: string | undefined
readonly headers?: Headers.Input | undefined
readonly body?: Body.Body | undefined
readonly accept?: string | undefined
Expand Down Expand Up @@ -204,15 +207,15 @@ export const acceptJson: (self: ClientRequest) => ClientRequest = internal.accep
*/
export const setUrl: {
(url: string | URL): (self: ClientRequest) => ClientRequest
(self: ClientRequest, url: string): ClientRequest
(self: ClientRequest, url: string | URL): ClientRequest
} = internal.setUrl

/**
* @since 1.0.0
* @category combinators
*/
export const prependUrl: {
(path: string | URL): (self: ClientRequest) => ClientRequest
(path: string): (self: ClientRequest) => ClientRequest
(self: ClientRequest, path: string): ClientRequest
} = internal.prependUrl

Expand All @@ -238,25 +241,52 @@ export const updateUrl: {
* @since 1.0.0
* @category combinators
*/
export const setUrlParam = internal.setUrlParam
export const setUrlParam: {
(key: string, value: string): (self: ClientRequest) => ClientRequest
(self: ClientRequest, key: string, value: string): ClientRequest
} = internal.setUrlParam

/**
* @since 1.0.0
* @category combinators
*/
export const setUrlParams: {
(input: UrlParams.Input): (self: ClientRequest) => ClientRequest
(self: ClientRequest, input: UrlParams.Input): ClientRequest
} = internal.setUrlParams

/**
* @since 1.0.0
* @category combinators
*/
export const setUrlParams = internal.setUrlParams
export const appendUrlParam: {
(key: string, value: string): (self: ClientRequest) => ClientRequest
(self: ClientRequest, key: string, value: string): ClientRequest
} = internal.appendUrlParam

/**
* @since 1.0.0
* @category combinators
*/
export const appendUrlParams: {
(input: UrlParams.Input): (self: ClientRequest) => ClientRequest
(self: ClientRequest, input: UrlParams.Input): ClientRequest
} = internal.appendUrlParams

/**
* @since 1.0.0
* @category combinators
*/
export const appendUrlParam = internal.appendUrlParam
export const setHash: {
(hash: string): (self: ClientRequest) => ClientRequest
(self: ClientRequest, hash: string): ClientRequest
} = internal.setHash

/**
* @since 1.0.0
* @category combinators
*/
export const appendUrlParams = internal.appendUrlParams
export const removeHash: (self: ClientRequest) => ClientRequest = internal.removeHash

/**
* @since 1.0.0
Expand Down
6 changes: 4 additions & 2 deletions packages/platform/src/Http/UrlParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ export const toString = (self: UrlParams): string => new URLSearchParams(self as
* @since 1.0.0
* @category constructors
*/
export const makeUrl = (url: string, params: UrlParams): Either.Either<URL, Error> => {
export const makeUrl = (url: string, params: UrlParams, hash: Option.Option<string>): Either.Either<URL, Error> => {
try {
const urlInstance = new URL(url, baseUrl())
for (let i = 0; i < params.length; i++) {
Expand All @@ -216,7 +216,9 @@ export const makeUrl = (url: string, params: UrlParams): Either.Either<URL, Erro
urlInstance.searchParams.append(key, value)
}
}

if (hash._tag === "Some") {
urlInstance.hash = hash.value
}
return Either.right(urlInstance)
} catch (e) {
return Either.left(e as Error)
Expand Down
2 changes: 1 addition & 1 deletion packages/platform/src/internal/http/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export const makeDefault = (
const scope = Context.unsafeGet(fiber.getFiberRef(FiberRef.currentContext), Scope.Scope)
const controller = new AbortController()
const addAbort = Scope.addFinalizer(scope, Effect.sync(() => controller.abort()))
const urlResult = UrlParams.makeUrl(request.url, request.urlParams)
const urlResult = UrlParams.makeUrl(request.url, request.urlParams, request.hash)
if (urlResult._tag === "Left") {
return Effect.fail(new Error.RequestError({ request, reason: "InvalidUrl", error: urlResult.left }))
}
Expand Down
85 changes: 75 additions & 10 deletions packages/platform/src/internal/http/clientRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as Effect from "effect/Effect"
import * as Effectable from "effect/Effectable"
import { dual } from "effect/Function"
import * as Inspectable from "effect/Inspectable"
import * as Option from "effect/Option"
import type * as Stream from "effect/Stream"
import type * as PlatformError from "../../Error.js"
import type * as FileSystem from "../../FileSystem.js"
Expand Down Expand Up @@ -35,6 +36,7 @@ const Proto = {
method: this.method,
url: this.url,
urlParams: this.urlParams,
hash: this.hash,
headers: this.headers,
body: this.body.toJSON()
}
Expand All @@ -45,13 +47,15 @@ function makeInternal(
method: Method,
url: string,
urlParams: UrlParams.UrlParams,
hash: Option.Option<string>,
headers: Headers.Headers,
body: Body.Body
): ClientRequest.ClientRequest {
const self = Object.create(Proto)
self.method = method
self.url = url
self.urlParams = urlParams
self.hash = hash
self.headers = headers
self.body = body
return self
Expand All @@ -66,6 +70,7 @@ export const empty: ClientRequest.ClientRequest = makeInternal(
"GET",
"",
UrlParams.empty,
Option.none(),
Headers.empty,
internalBody.empty
)
Expand All @@ -78,7 +83,7 @@ export const make = <M extends Method>(method: M) =>
) =>
modify(empty, {
method,
url: url.toString(),
url,
...(options ?? undefined)
})

Expand Down Expand Up @@ -122,6 +127,9 @@ export const modify = dual<
if (options.urlParams) {
result = setUrlParams(result, options.urlParams)
}
if (options.hash) {
result = setHash(result, options.hash)
}
if (options.body) {
result = setBody(result, options.body)
}
Expand All @@ -144,6 +152,7 @@ export const setHeader = dual<
self.method,
self.url,
self.urlParams,
self.hash,
Headers.set(self.headers, key, value),
self.body
))
Expand All @@ -157,6 +166,7 @@ export const setHeaders = dual<
self.method,
self.url,
self.urlParams,
self.hash,
Headers.setAll(self.headers, input),
self.body
))
Expand Down Expand Up @@ -191,22 +201,40 @@ export const setMethod = dual<
method,
self.url,
self.urlParams,
self.hash,
self.headers,
self.body
))

/** @internal */
export const setUrl = dual<
(url: string | URL) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
(self: ClientRequest.ClientRequest, url: string) => ClientRequest.ClientRequest
>(2, (self, url) =>
makeInternal(
(self: ClientRequest.ClientRequest, url: string | URL) => ClientRequest.ClientRequest
>(2, (self, url) => {
if (typeof url === "string") {
return makeInternal(
self.method,
url,
self.urlParams,
self.hash,
self.headers,
self.body
)
}
const clone = new URL(url.toString())
const urlParams = UrlParams.fromInput(clone.searchParams)
const hash = clone.hash ? Option.some(clone.hash.slice(1)) : Option.none()
clone.search = ""
clone.hash = ""
return makeInternal(
self.method,
url.toString(),
self.urlParams,
clone.toString(),
urlParams,
hash,
self.headers,
self.body
))
)
})

/** @internal */
export const appendUrl = dual<
Expand All @@ -215,21 +243,27 @@ export const appendUrl = dual<
>(2, (self, url) =>
makeInternal(
self.method,
self.url + url,
self.url.endsWith("/") && url.startsWith("/") ?
self.url + url.slice(1) :
self.url + url,
self.urlParams,
self.hash,
self.headers,
self.body
))

/** @internal */
export const prependUrl = dual<
(path: string | URL) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
(path: string) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
(self: ClientRequest.ClientRequest, path: string) => ClientRequest.ClientRequest
>(2, (self, url) =>
makeInternal(
self.method,
url.toString() + self.url,
url.endsWith("/") && self.url.startsWith("/") ?
url + self.url.slice(1) :
url + self.url,
self.urlParams,
self.hash,
self.headers,
self.body
))
Expand All @@ -243,6 +277,7 @@ export const updateUrl = dual<
self.method,
f(self.url),
self.urlParams,
self.hash,
self.headers,
self.body
))
Expand All @@ -256,6 +291,7 @@ export const appendUrlParam = dual<
self.method,
self.url,
UrlParams.append(self.urlParams, key, value),
self.hash,
self.headers,
self.body
))
Expand All @@ -269,6 +305,7 @@ export const appendUrlParams = dual<
self.method,
self.url,
UrlParams.appendAll(self.urlParams, input),
self.hash,
self.headers,
self.body
))
Expand All @@ -282,6 +319,7 @@ export const setUrlParam = dual<
self.method,
self.url,
UrlParams.set(self.urlParams, key, value),
self.hash,
self.headers,
self.body
))
Expand All @@ -295,10 +333,36 @@ export const setUrlParams = dual<
self.method,
self.url,
UrlParams.setAll(self.urlParams, input),
self.hash,
self.headers,
self.body
))

/** @internal */
export const setHash = dual<
(hash: string) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
(self: ClientRequest.ClientRequest, hash: string) => ClientRequest.ClientRequest
>(2, (self, hash) =>
makeInternal(
self.method,
self.url,
self.urlParams,
Option.some(hash),
self.headers,
self.body
))

/** @internal */
export const removeHash = (self: ClientRequest.ClientRequest): ClientRequest.ClientRequest =>
makeInternal(
self.method,
self.url,
self.urlParams,
Option.none(),
self.headers,
self.body
)

/** @internal */
export const setBody = dual<
(body: Body.Body) => (self: ClientRequest.ClientRequest) => ClientRequest.ClientRequest,
Expand All @@ -322,6 +386,7 @@ export const setBody = dual<
self.method,
self.url,
self.urlParams,
self.hash,
headers,
body
)
Expand Down
Loading

0 comments on commit 22c777d

Please sign in to comment.