1import type { GcpOpContext } from "@distilled.cloud/gcp/composer_v1";
2import * as Effect from "effect/Effect";
3import type { Output } from "../../Output.ts";
4import type { Environment } from "./Environment.ts";
5import type { EnvironmentsUserWorkloadsConfigMap } from "./EnvironmentsUserWorkloadsConfigMap.ts";
6import type { EnvironmentsUserWorkloadsSecret } from "./EnvironmentsUserWorkloadsSecret.ts";
7import { bindGcpHost } from "../Host.ts";
8import { type BindingIam, grantFor } from "../HttpBinding.ts";
9
10type GcpHttpOp<I, A, E> = Effect.Effect<
11 (input: I) => Effect.Effect<A, E>,
12 never,
13 GcpOpContext
14> &
15 ((input: I) => Effect.Effect<A, E, GcpOpContext>);
16
17type NamedResource = {
18 readonly name: Output<string, never>;
19 readonly LogicalId: string;
20};
21
22/**
23 * Shared HTTP scaffolding for Cloud Composer bindings whose request
24 * identifies the resource by a single name path parameter.
25 * Distilled ops are OperationMethods: yield them once at Layer
26 * construction (after providing Credentials + HttpClient) so the inner
27 * runtime Effect is `Effect<A, E>` and does not leak `GcpOpContext`.
28 * NOT exported from index.ts.
29 */
30export const makeNamedHttpBinding = <
31 Resource extends NamedResource,
32 I,
33 A,
34 E,
35>(options: {
36 tag: string;
37 iam: BindingIam;
38 nameKey?: string;
39 operation: GcpHttpOp<I, A, E>;
40}) =>
41 Effect.gen(function* () {
42 const run = yield* options.operation;
43 const nameKey = options.nameKey ?? "name";
44 return Effect.fn(function* (resource: Resource) {
45 yield* bindGcpHost({
46 tag: options.tag,
47 resource: resource,
48 iam: [grantFor(options.iam, resource.name)],
49 });
50 const name = yield* resource.name;
51 return Effect.fn(`${options.tag}(${resource.LogicalId})`)(function* (
52 request?: object,
53 ) {
54 const resourceName = yield* name;
55 return yield* run({
56 ...(request ?? {}),
57 [nameKey]: resourceName,
58 } as I);
59 });
60 });
61 });
62
63/**
64 * Shared HTTP scaffolding for Cloud Composer environment bindings.
65 * NOT exported from index.ts.
66 */
67export const makeEnvironmentHttpBinding = <I, A, E>(options: {
68 tag: string;
69 iam: BindingIam;
70 nameKey: string;
71 operation: GcpHttpOp<I, A, E>;
72}) =>
73 makeNamedHttpBinding<Environment, I, A, E>({
74 tag: options.tag,
75 iam: options.iam,
76 nameKey: options.nameKey,
77 operation: options.operation,
78 });
79
80export const makeUserWorkloadsConfigMapHttpBinding = <I, A, E>(options: {
81 tag: string;
82 iam: BindingIam;
83 operation: GcpHttpOp<I, A, E>;
84}) =>
85 makeNamedHttpBinding<EnvironmentsUserWorkloadsConfigMap, I, A, E>({
86 tag: options.tag,
87 iam: options.iam,
88 nameKey: "name",
89 operation: options.operation,
90 });
91
92export const makeUserWorkloadsSecretHttpBinding = <I, A, E>(options: {
93 tag: string;
94 iam: BindingIam;
95 operation: GcpHttpOp<I, A, E>;
96}) =>
97 makeNamedHttpBinding<EnvironmentsUserWorkloadsSecret, I, A, E>({
98 tag: options.tag,
99 iam: options.iam,
100 nameKey: "name",
101 operation: options.operation,
102 });