| 1 | import { |
| 2 | EnvironmentAuthInvalidError, |
| 3 | type PullRequestDiffInput, |
| 4 | type PullRequestDiffResult, |
| 5 | } from "@t3tools/contracts"; |
| 6 | import * as Context from "effect/Context"; |
| 7 | import * as Effect from "effect/Effect"; |
| 8 | import * as Layer from "effect/Layer"; |
| 9 | import * as Option from "effect/Option"; |
| 10 | import * as Schema from "effect/Schema"; |
| 11 | import { HttpClient } from "effect/http"; |
| 12 | |
| 13 | import * as RemoteEnvironmentAuthorization from "../authorization/service.ts"; |
| 14 | import type { PreparedConnection } from "../connection/model.ts"; |
| 15 | import * as ManagedRelay from "../relay/managedRelay.ts"; |
| 16 | import { type RemoteEnvironmentRequestError } from "../rpc/http.ts"; |
| 17 | import { executeAuthenticatedEnvironmentHttpRequest } from "./environmentHttpAuth.ts"; |
| 18 | |
| 19 | const DEFAULT_PULL_REQUEST_DIFF_TIMEOUT_MS = 60_000; |
| 20 | |
| 21 | export class PullRequestDiffCredentialRejectedError extends Schema.TaggedError<PullRequestDiffCredentialRejectedError>()( |
| 22 | "PullRequestDiffCredentialRejectedError", |
| 23 | { |
| 24 | repository: Schema.String, |
| 25 | number: Schema.Number, |
| 26 | traceId: Schema.String, |
| 27 | cause: EnvironmentAuthInvalidError, |
| 28 | }, |
| 29 | ) { |
| 30 | override get message(): string { |
| 31 | return "This environment session is no longer valid (invalid_credential). Refresh the page or quit and reopen The Knife."; |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | export type PullRequestDiffLoadError = |
| 36 | | RemoteEnvironmentRequestError |
| 37 | | PullRequestDiffCredentialRejectedError; |
| 38 | |
| 39 | export const fetchEnvironmentPullRequestDiff = Effect.fn( |
| 40 | "clientRuntime.state.fetchEnvironmentPullRequestDiff", |
| 41 | )(function* (input: { |
| 42 | readonly prepared: PreparedConnection; |
| 43 | readonly diff: PullRequestDiffInput; |
| 44 | readonly signer: Option.Option<ManagedRelay.ManagedRelayDpopSigner["Service"]>; |
| 45 | readonly remoteAuthorization?: Option.Option< |
| 46 | RemoteEnvironmentAuthorization.RemoteEnvironmentAuthorization["Service"] |
| 47 | >; |
| 48 | readonly timeoutMs?: number; |
| 49 | }) { |
| 50 | return yield* executeAuthenticatedEnvironmentHttpRequest({ |
| 51 | ...input, |
| 52 | group: "pullRequests", |
| 53 | method: "POST", |
| 54 | url: (urls) => urls.diff(), |
| 55 | timeoutMs: input.timeoutMs ?? DEFAULT_PULL_REQUEST_DIFF_TIMEOUT_MS, |
| 56 | request: ({ client, headers }) => client.diff({ payload: input.diff, headers }), |
| 57 | }).pipe( |
| 58 | Effect.mapError((error) => |
| 59 | error._tag === "EnvironmentAuthInvalidError" && error.reason === "invalid_credential" |
| 60 | ? new PullRequestDiffCredentialRejectedError({ |
| 61 | repository: input.diff.repository, |
| 62 | number: input.diff.number, |
| 63 | traceId: error.traceId, |
| 64 | cause: error, |
| 65 | }) |
| 66 | : error, |
| 67 | ), |
| 68 | ); |
| 69 | }); |
| 70 | |
| 71 | export class PullRequestDiffLoader extends Context.Service< |
| 72 | PullRequestDiffLoader, |
| 73 | { |
| 74 | readonly load: ( |
| 75 | prepared: PreparedConnection, |
| 76 | input: PullRequestDiffInput, |
| 77 | ) => Effect.Effect<PullRequestDiffResult, PullRequestDiffLoadError>; |
| 78 | } |
| 79 | >()("@t3tools/client-runtime/state/pullRequestDiffHttp/PullRequestDiffLoader") {} |
| 80 | |
| 81 | export const layer: Layer.Layer<PullRequestDiffLoader, never, HttpClient.HttpClient> = Layer.effect( |
| 82 | PullRequestDiffLoader, |
| 83 | Effect.gen(function* () { |
| 84 | const httpClient = yield* HttpClient.HttpClient; |
| 85 | const signer = yield* Effect.serviceOption(ManagedRelay.ManagedRelayDpopSigner); |
| 86 | const remoteAuthorization = yield* Effect.serviceOption( |
| 87 | RemoteEnvironmentAuthorization.RemoteEnvironmentAuthorization, |
| 88 | ); |
| 89 | return PullRequestDiffLoader.of({ |
| 90 | load: (prepared, input) => |
| 91 | fetchEnvironmentPullRequestDiff({ |
| 92 | prepared, |
| 93 | diff: input, |
| 94 | signer, |
| 95 | remoteAuthorization, |
| 96 | }).pipe(Effect.provideService(HttpClient.HttpClient, httpClient)), |
| 97 | }); |
| 98 | }), |
| 99 | ); |