1import * as Effect from "effect/Effect";
2import * as Binding from "../../Binding.ts";
3import { isBindingHost } from "../Lambda/Function.ts";
4import type { WirelessDevice } from "./WirelessDevice.ts";
5import type { WirelessGateway } from "./WirelessGateway.ts";
6
7/**
8 * Shared HTTP scaffolding for the AWS IoT Wireless runtime bindings.
9 *
10 * NOT exported from `index.ts` — every `{Op}Http.ts` in this service is a
11 * thin `Layer.effect(Cap, make…HttpBinding({ … }))` over one of the builders
12 * below. Everything except the operation, the IAM action, and the request
13 * shaping is boilerplate.
14 */
15
16/**
17 * Build the impl Effect for an IoT Wireless operation scoped to one
18 * {@link WirelessDevice}: the deploy-time half grants `iamActions` on the
19 * bound device's ARN, and the runtime half injects the device's
20 * server-assigned id into every request via `prepare`.
21 */
22export const makeIotWirelessDeviceHttpBinding = <I, A, E, R, Req>(options: {
23 /**
24 * Short capability name used in the binding sid and runtime span, e.g.
25 * `"SendDataToWirelessDevice"`.
26 */
27 capability: string;
28 /** IAM actions granted on the bound device's ARN. */
29 iamActions: readonly string[];
30 /** The distilled operation implementing the capability. */
31 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
32 /** Map the public request shape + bound device id onto the wire request. */
33 prepare: (request: Req, wirelessDeviceId: string) => I;
34 /**
35 * IAM resource scope. Defaults to the bound device's ARN. The position
36 * operations (`GetResourcePosition` / `UpdateResourcePosition`) must use
37 * `"any"`: IoT Wireless authorizes them against a type-level ARN
38 * (`…:WirelessDevice/WirelessDevice`, built from the `resourceType`
39 * query parameter), which the device ARN never matches.
40 */
41 resourceScope?: "device" | "any";
42}) =>
43 Effect.gen(function* () {
44 const op = yield* options.operation;
45
46 return Effect.fn(function* (device: WirelessDevice) {
47 // Output yields a DEFERRED effect — resolve again per invocation below.
48 const DeviceId = yield* device.wirelessDeviceId;
49 if (!globalThis.__ALCHEMY_RUNTIME__) {
50 const host = yield* Binding.Host;
51 if (isBindingHost(host)) {
52 yield* host.bind`Allow(${host}, AWS.IoTWireless.${options.capability}(${device}))`(
53 {
54 policyStatements: [
55 {
56 Effect: "Allow",
57 Action: [...options.iamActions],
58 Resource:
59 options.resourceScope === "any"
60 ? ["*"]
61 : [device.wirelessDeviceArn],
62 },
63 ],
64 },
65 );
66 }
67 }
68 return Effect.fn(
69 `AWS.IoTWireless.${options.capability}(${device.LogicalId})`,
70 )(function* (request: Req) {
71 const wirelessDeviceId = yield* DeviceId;
72 return yield* op(options.prepare(request, wirelessDeviceId));
73 });
74 });
75 });
76
77/**
78 * Build the impl Effect for an IoT Wireless operation scoped to one
79 * {@link WirelessGateway}: the deploy-time half grants `iamActions` on the
80 * bound gateway's ARN, and the runtime half injects the gateway's
81 * server-assigned id into every request via `prepare`.
82 */
83export const makeIotWirelessGatewayHttpBinding = <I, A, E, R, Req>(options: {
84 /** Short capability name used in the binding sid and runtime span. */
85 capability: string;
86 /** IAM actions granted on the bound gateway's ARN. */
87 iamActions: readonly string[];
88 /** The distilled operation implementing the capability. */
89 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
90 /** Map the public request shape + bound gateway id onto the wire request. */
91 prepare: (request: Req, wirelessGatewayId: string) => I;
92}) =>
93 Effect.gen(function* () {
94 const op = yield* options.operation;
95
96 return Effect.fn(function* (gateway: WirelessGateway) {
97 const GatewayId = yield* gateway.wirelessGatewayId;
98 if (!globalThis.__ALCHEMY_RUNTIME__) {
99 const host = yield* Binding.Host;
100 if (isBindingHost(host)) {
101 yield* host.bind`Allow(${host}, AWS.IoTWireless.${options.capability}(${gateway}))`(
102 {
103 policyStatements: [
104 {
105 Effect: "Allow",
106 Action: [...options.iamActions],
107 Resource: [gateway.wirelessGatewayArn],
108 },
109 ],
110 },
111 );
112 }
113 }
114 return Effect.fn(
115 `AWS.IoTWireless.${options.capability}(${gateway.LogicalId})`,
116 )(function* (request: Req) {
117 const wirelessGatewayId = yield* GatewayId;
118 return yield* op(options.prepare(request, wirelessGatewayId));
119 });
120 });
121 });
122
123/**
124 * Build the impl Effect for an account-level IoT Wireless operation (e.g.
125 * `GetServiceEndpoint` or `GetPositionEstimate`, which are not scoped to a
126 * single resource). Grants `iamActions` on `Resource: ["*"]`.
127 */
128export const makeIotWirelessAccountHttpBinding = <I, A, E, R, Req>(options: {
129 /** Short capability name used in the binding sid and runtime span. */
130 capability: string;
131 /** IAM actions granted on `Resource: ["*"]`. */
132 iamActions: readonly string[];
133 /** The distilled operation implementing the capability. */
134 operation: Effect.Effect<(input: I) => Effect.Effect<A, E>, never, R>;
135 /** Map the public request shape onto the wire request. */
136 prepare: (request: Req) => I;
137}) =>
138 Effect.gen(function* () {
139 const op = yield* options.operation;
140
141 return Effect.fn(function* () {
142 if (!globalThis.__ALCHEMY_RUNTIME__) {
143 const host = yield* Binding.Host;
144 if (isBindingHost(host)) {
145 yield* host.bind`Allow(${host}, AWS.IoTWireless.${options.capability}())`(
146 {
147 policyStatements: [
148 {
149 Effect: "Allow",
150 Action: [...options.iamActions],
151 Resource: ["*"],
152 },
153 ],
154 },
155 );
156 }
157 }
158 return Effect.fn(`AWS.IoTWireless.${options.capability}`)(function* (
159 request: Req,
160 ) {
161 return yield* op(options.prepare(request));
162 });
163 });
164 });