1import * as Effect from "effect/Effect";
2import * as Layer from "effect/Layer";
3import * as Binding from "../../Binding.ts";
4import * as Output from "../../Output.ts";
5import { unpackEnvValue } from "../../RuntimeContext.ts";
6import { isBindingHost } from "../Lambda/Function.ts";
7import {
8 ConnectReplicationGroup,
9 replicationGroupConnectEnvPrefix,
10 type ConnectReplicationGroupOptions,
11} from "./ConnectReplicationGroup.ts";
12import type { ReplicationGroup } from "./ReplicationGroup.ts";
13
14export const ConnectReplicationGroupHttp = Layer.effect(
15 ConnectReplicationGroup,
16 Effect.gen(function* () {
17 return Effect.fn(function* (
18 group: ReplicationGroup,
19 options?: ConnectReplicationGroupOptions,
20 ) {
21 const prefix = replicationGroupConnectEnvPrefix(group.LogicalId);
22 // Outputs yield a deferred runtime effect. Keep the Output expressions
23 // for deploy-time bindings, where Apply resolves them before Lambda's
24 // API call, and use the deferred values only in the runtime closure.
25 const configurationHost = group.configurationEndpointAddress;
26 const primaryHost = group.primaryEndpointAddress;
27 const configurationPort = group.configurationEndpointPort;
28 const primaryPort = group.primaryEndpointPort;
29 const endpointHost = Output.flatMap(configurationHost, (host) =>
30 host === undefined ? primaryHost : Output.asOutput(host),
31 );
32 const endpointPort = Output.flatMap(configurationPort, (port) =>
33 port === undefined ? primaryPort : Output.asOutput(port),
34 );
35 const readerHost = Output.map(
36 group.readerEndpointAddress,
37 (host) => host ?? "",
38 );
39 const readerPort = Output.map(group.readerEndpointPort, (port) =>
40 port === undefined ? "" : String(port),
41 );
42 if (!globalThis.__ALCHEMY_RUNTIME__) {
43 const host = yield* Binding.Host;
44 if (isBindingHost(host)) {
45 yield* host.bind`Allow(${host}, AWS.ElastiCache.ConnectReplicationGroup(${group}))`(
46 {
47 env: {
48 [`${prefix}_HOST`]: endpointHost,
49 [`${prefix}_PORT`]: Output.interpolate`${endpointPort}`,
50 [`${prefix}_TLS`]: Output.map(
51 group.transitEncryptionEnabled,
52 String,
53 ),
54 [`${prefix}_READER_HOST`]: readerHost,
55 [`${prefix}_READER_PORT`]: readerPort,
56 },
57 ...(options?.subnetIds || options?.securityGroupIds
58 ? {
59 vpc: {
60 subnetIds: options?.subnetIds ?? [],
61 securityGroupIds: options?.securityGroupIds ?? [],
62 },
63 }
64 : {}),
65 },
66 );
67 }
68 }
69 return Effect.gen(function* () {
70 const host = unpackEnvValue<string>(process.env[`${prefix}_HOST`]);
71 if (host !== undefined) {
72 const port = unpackEnvValue<number>(process.env[`${prefix}_PORT`]);
73 const tls = unpackEnvValue<boolean>(process.env[`${prefix}_TLS`]);
74 const readerHost = unpackEnvValue<string>(
75 process.env[`${prefix}_READER_HOST`],
76 );
77 const readerPort = unpackEnvValue<number>(
78 process.env[`${prefix}_READER_PORT`],
79 );
80 if (!host || port === undefined) {
81 return yield* Effect.die(
82 `ElastiCache endpoint for '${group.LogicalId}' is not available`,
83 );
84 }
85 return {
86 host,
87 port,
88 readerHost: readerHost || undefined,
89 readerPort: readerHost ? readerPort : undefined,
90 tls: tls ?? true,
91 };
92 }
93 const endpoint = yield* yield* endpointHost;
94 const port = yield* yield* endpointPort;
95 if (!endpoint || port === undefined) {
96 return yield* Effect.die(
97 `ElastiCache endpoint for '${group.LogicalId}' is not available yet`,
98 );
99 }
100 return {
101 host: endpoint,
102 port,
103 readerHost: yield* yield* group.readerEndpointAddress,
104 readerPort: yield* yield* group.readerEndpointPort,
105 tls: yield* yield* group.transitEncryptionEnabled,
106 };
107 });
108 });
109 }),
110);