1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import * as Output from "../../Output.ts";
4import { isBindingHost } from "../Lambda/Function.ts";
5import type { Application } from "./Application.ts";
6import type { AttributeGroup } from "./AttributeGroup.ts";
7
8/**
9 * Shared scaffolding for AppRegistry HTTP bindings.
10 *
11 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
12 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the three
13 * builders below. Everything except the operation, the IAM action list, and
14 * the injected identifier is boilerplate.
15 *
16 * AppRegistry IAM actions live under the `servicecatalog:` service prefix.
17 */
18
19/**
20 * Build the impl Effect for an application-scoped operation: the runtime
21 * callable injects the bound {@link Application}'s ID as `application` and
22 * the deploy-time half grants `actions` on the application ARN.
23 */
24export const makeApplicationScopedHttpBinding = <
25 I extends { application: string },
26 A,
27 E,
28 R,
29>(options: {
30 /** Fully-qualified binding tag, e.g. `AWS.AppRegistry.GetApplication`. */
31 tag: string;
32 /** The distilled operation; `application` is injected from the resource. */
33 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
34 /** IAM actions granted on the application ARN. */
35 actions: readonly string[];
36}) =>
37 Effect.gen(function* () {
38 const op = yield* options.operation;
39
40 return Effect.fn(function* (application: Application) {
41 const ApplicationId = yield* application.applicationId;
42 if (!globalThis.__ALCHEMY_RUNTIME__) {
43 const host = yield* Binding.Host;
44 if (isBindingHost(host)) {
45 yield* host.bind`Allow(${host}, ${options.tag}(${application}))`({
46 policyStatements: [
47 {
48 Effect: "Allow",
49 Action: [...options.actions],
50 Resource: [Output.interpolate`${application.applicationArn}`],
51 },
52 ],
53 });
54 }
55 }
56 return Effect.fn(`${options.tag}(${application.LogicalId})`)(function* (
57 request?: Omit<I, "application">,
58 ) {
59 return yield* op({
60 ...request,
61 application: yield* ApplicationId,
62 } as I);
63 });
64 });
65 });
66
67/**
68 * Build the impl Effect for an attribute-group-scoped operation: the runtime
69 * callable injects the bound {@link AttributeGroup}'s ID as `attributeGroup`
70 * and the deploy-time half grants `actions` on the attribute group ARN.
71 */
72export const makeAttributeGroupScopedHttpBinding = <
73 I extends { attributeGroup: string },
74 A,
75 E,
76 R,
77>(options: {
78 /** Fully-qualified binding tag, e.g. `AWS.AppRegistry.GetAttributeGroup`. */
79 tag: string;
80 /** The distilled operation; `attributeGroup` is injected from the resource. */
81 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
82 /** IAM actions granted on the attribute group ARN. */
83 actions: readonly string[];
84}) =>
85 Effect.gen(function* () {
86 const op = yield* options.operation;
87
88 return Effect.fn(function* (attributeGroup: AttributeGroup) {
89 const AttributeGroupId = yield* attributeGroup.attributeGroupId;
90 if (!globalThis.__ALCHEMY_RUNTIME__) {
91 const host = yield* Binding.Host;
92 if (isBindingHost(host)) {
93 yield* host.bind`Allow(${host}, ${options.tag}(${attributeGroup}))`({
94 policyStatements: [
95 {
96 Effect: "Allow",
97 Action: [...options.actions],
98 Resource: [
99 Output.interpolate`${attributeGroup.attributeGroupArn}`,
100 ],
101 },
102 ],
103 });
104 }
105 }
106 return Effect.fn(`${options.tag}(${attributeGroup.LogicalId})`)(
107 function* (request?: Omit<I, "attributeGroup">) {
108 return yield* op({
109 ...request,
110 attributeGroup: yield* AttributeGroupId,
111 } as I);
112 },
113 );
114 });
115 });
116
117/**
118 * Build the impl Effect for an account-level operation (no target resource).
119 * The deploy-time half grants `actions` on `*`.
120 */
121export const makeAppRegistryAccountHttpBinding = <I, A, E, R>(options: {
122 /** Fully-qualified binding tag, e.g. `AWS.AppRegistry.SyncResource`. */
123 tag: string;
124 /** The distilled operation, invoked with the caller's request as-is. */
125 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
126 /** IAM actions granted on `*`. */
127 actions: readonly string[];
128}) =>
129 Effect.gen(function* () {
130 const op = yield* options.operation;
131
132 return Effect.fn(function* () {
133 if (!globalThis.__ALCHEMY_RUNTIME__) {
134 const host = yield* Binding.Host;
135 if (isBindingHost(host)) {
136 yield* host.bind`Allow(${host}, ${options.tag}())`({
137 policyStatements: [
138 {
139 Effect: "Allow",
140 Action: [...options.actions],
141 Resource: ["*"],
142 },
143 ],
144 });
145 }
146 }
147 return Effect.fn(options.tag)(function* (request?: I) {
148 return yield* op((request ?? {}) as I);
149 });
150 });
151 });