diff --git a/.changeset/early-games-enjoy.md b/.changeset/early-games-enjoy.md new file mode 100644 index 0000000000..032b43f919 --- /dev/null +++ b/.changeset/early-games-enjoy.md @@ -0,0 +1,26 @@ +--- +"effect": patch +--- + +add RequestResolver.aroundRequests api + +This can be used to run side effects that introspect the requests being +executed. + +Example: + +```ts +import { Effect, Request, RequestResolver } from "effect"; + +interface GetUserById extends Request.Request { + readonly id: number; +} + +declare const resolver: RequestResolver.RequestResolver; + +RequestResolver.aroundRequests( + resolver, + (requests) => Effect.log(`got ${requests.length} requests`), + (requests, _) => Effect.log(`finised running ${requests.length} requests`), +); +``` diff --git a/packages/effect/src/RequestResolver.ts b/packages/effect/src/RequestResolver.ts index a026f793dd..645944ff61 100644 --- a/packages/effect/src/RequestResolver.ts +++ b/packages/effect/src/RequestResolver.ts @@ -160,6 +160,43 @@ export const around: { ): RequestResolver } = internal.around +/** + * A data source aspect that executes requests between two effects, `before` + * and `after`, where the result of `before` can be used by `after`. + * + * The `before` and `after` effects are provided with the requests being executed. + * + * @since 2.0.0 + * @category combinators + * @example + * import { Effect, Request, RequestResolver } from "effect" + * + * interface GetUserById extends Request.Request { + * readonly id: number + * } + * + * const resolver = RequestResolver.fromFunction( + * (request: GetUserById) => ({ id: request.id, name: "John" }) + * ) + * + * RequestResolver.aroundRequests( + * resolver, + * (requests) => Effect.log(`got ${requests.length} requests`), + * (requests, _) => Effect.log(`finised running ${requests.length} requests`) + * ) + */ +export const aroundRequests: { + ( + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect<_, never, R3> + ): (self: RequestResolver) => RequestResolver + ( + self: RequestResolver, + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect<_, never, R3> + ): RequestResolver +} = internal.aroundRequests + /** * Returns a data source that executes at most `n` requests in parallel. * diff --git a/packages/effect/src/internal/dataSource.ts b/packages/effect/src/internal/dataSource.ts index 85972230fc..2db14516ba 100644 --- a/packages/effect/src/internal/dataSource.ts +++ b/packages/effect/src/internal/dataSource.ts @@ -7,6 +7,7 @@ import { dual, pipe } from "../Function.js" import * as RA from "../ReadonlyArray.js" import type * as Request from "../Request.js" import type * as RequestResolver from "../RequestResolver.js" +import type { NoInfer } from "../Types.js" import * as core from "./core.js" import { invokeWithInterrupt, zipWithOptions } from "./fiberRuntime.js" import { complete } from "./request.js" @@ -71,6 +72,32 @@ export const around = dual< Chunk.make("Around", self, before, after) )) +/** @internal */ +export const aroundRequests = dual< + ( + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect<_, never, R3> + ) => ( + self: RequestResolver.RequestResolver + ) => RequestResolver.RequestResolver, + ( + self: RequestResolver.RequestResolver, + before: (requests: ReadonlyArray>) => Effect.Effect, + after: (requests: ReadonlyArray>, _: A2) => Effect.Effect<_, never, R3> + ) => RequestResolver.RequestResolver +>(3, (self, before, after) => + new core.RequestResolverImpl( + (requests) => { + const flatRequests = requests.flatMap((chunk) => chunk.map((entry) => entry.request)) + return core.acquireUseRelease( + before(flatRequests), + () => self.runAll(requests), + (a2) => after(flatRequests, a2) + ) + }, + Chunk.make("AroundRequests", self, before, after) + )) + /** @internal */ export const batchN = dual< (n: number) => (