1import * as codeconnections from "@distilled.cloud/aws/codeconnections";
2import * as Effect from "effect/Effect";
3import { diffTags } from "../../Tags.ts";
4
5/**
6 * Shared tag plumbing for the CodeConnections resources (Connection, Host,
7 * RepositoryLink). All three share the same `TagResource`/`UntagResource`/
8 * `ListTagsForResource` wire shape keyed by ARN.
9 *
10 * NOT exported from `index.ts`.
11 */
12
13/** Convert a CodeConnections wire tag list into a plain record. */
14export const toTagRecord = (
15 tags: ReadonlyArray<{ Key?: string; Value?: string }> | undefined,
16): Record<string, string> =>
17 Object.fromEntries(
18 (tags ?? [])
19 .filter(
20 (tag): tag is { Key: string; Value: string } =>
21 typeof tag.Key === "string" && typeof tag.Value === "string",
22 )
23 .map((tag) => [tag.Key, tag.Value]),
24 );
25
26/** Convert a plain record into the CodeConnections wire tag list. */
27export const toTagList = (
28 tags: Record<string, string>,
29): { Key: string; Value: string }[] =>
30 Object.entries(tags).map(([Key, Value]) => ({ Key, Value }));
31
32/** Read the observed cloud tags for a CodeConnections resource ARN. */
33export const fetchObservedTags = Effect.fn(function* (arn: string) {
34 return yield* codeconnections.listTagsForResource({ ResourceArn: arn }).pipe(
35 Effect.map((res) => toTagRecord(res.Tags)),
36 Effect.catchTag("ResourceNotFoundException", () => Effect.succeed({})),
37 );
38});
39
40/**
41 * Converge a CodeConnections resource's tags to `desiredTags`, diffing
42 * against the OBSERVED cloud tags (adoption may bring foreign tags).
43 */
44export const syncResourceTags = Effect.fn(function* (
45 arn: string,
46 desiredTags: Record<string, string>,
47) {
48 const observed = yield* fetchObservedTags(arn);
49 const { removed, upsert } = diffTags(observed, desiredTags);
50 if (upsert.length > 0) {
51 yield* codeconnections.tagResource({ ResourceArn: arn, Tags: upsert });
52 }
53 if (removed.length > 0) {
54 yield* codeconnections.untagResource({
55 ResourceArn: arn,
56 TagKeys: removed,
57 });
58 }
59});