1import {
2 AuthOrchestrationReadScope,
3 EnvironmentHttpApi,
4 requiredScopesForProjectMutation,
5} from "@t3tools/contracts";
6import * as Effect from "effect/Effect";
7import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
8
9import {
10 annotateEnvironmentRequest,
11 failEnvironmentInternal,
12 failEnvironmentInvalidRequest,
13 requireEnvironmentScope,
14} from "../auth/http.ts";
15import { traceLocalHandlerWork } from "../cloud/traceRelayRequest.ts";
16import * as ServerRuntimeStartup from "../serverRuntimeStartup.ts";
17import * as ProjectCloneTracker from "./ProjectCloneTracker.ts";
18import * as ProjectService from "./ProjectService.ts";
19import { mutateProjectWithClone } from "./ProjectMutation.ts";
20
21const failProjectMutation = Effect.fn("environment.projects.failMutation")(function* (
22 cause: ProjectService.ProjectServiceError | ServerRuntimeStartup.ServerRuntimeStartupError,
23) {
24 if (
25 cause._tag === "ProjectNotFoundError" ||
26 cause._tag === "ProjectConflictError" ||
27 cause._tag === "ProjectNotEmptyError" ||
28 cause._tag === "ProjectInvalidError"
29 ) {
30 return yield* failEnvironmentInvalidRequest("invalid_command");
31 }
32 return yield* failEnvironmentInternal("project_mutation_failed", cause);
33});
34
35export const layer = HttpApiBuilder.group(
36 EnvironmentHttpApi,
37 "projects",
38 Effect.fnUntraced(function* (handlers) {
39 const projects = yield* ProjectService.ProjectService;
40 const startup = yield* ServerRuntimeStartup.ServerRuntimeStartup;
41 const projectClones = yield* ProjectCloneTracker.ProjectCloneTracker;
42
43 return handlers
44 .handle(
45 "snapshot",
46 Effect.fn("environment.projects.snapshot")(function* (args) {
47 yield* annotateEnvironmentRequest(args.endpoint.name);
48 yield* requireEnvironmentScope(AuthOrchestrationReadScope);
49 return yield* projects.snapshot.pipe(
50 traceLocalHandlerWork,
51 Effect.catch((cause) => failEnvironmentInternal("project_snapshot_failed", cause)),
52 );
53 }),
54 )
55 .handle(
56 "mutate",
57 Effect.fn("environment.projects.mutate")(function* (args) {
58 yield* annotateEnvironmentRequest(args.endpoint.name);
59 for (const scope of requiredScopesForProjectMutation(args.payload)) {
60 yield* requireEnvironmentScope(scope);
61 }
62 const operation = mutateProjectWithClone(projects, projectClones, args.payload);
63 return yield* startup
64 .enqueueCommand(operation)
65 .pipe(traceLocalHandlerWork, Effect.catch(failProjectMutation));
66 }),
67 );
68 }),
69);