1import * as grafana from "@distilled.cloud/aws/grafana";
2import * as Effect from "effect/Effect";
3import * as Redacted from "effect/Redacted";
4import { diffTags } from "../../Tags.ts";
5
6/** Unwrap a possibly-redacted string field returned by the Grafana API. */
7export const unredact = (
8 value: string | Redacted.Redacted<string> | undefined,
9): string | undefined =>
10 value === undefined
11 ? undefined
12 : typeof value === "string"
13 ? value
14 : Redacted.value(value);
15
16/**
17 * Coerce a Grafana wire tag map (values are `string | undefined`) into a
18 * plain `Record<string, string>`.
19 */
20export const toTagRecord = (
21 tags: { [key: string]: string | undefined } | undefined,
22): Record<string, string> =>
23 Object.fromEntries(
24 Object.entries(tags ?? {}).filter(
25 (entry): entry is [string, string] => typeof entry[1] === "string",
26 ),
27 );
28
29/** Read the observed tags of a Grafana resource by ARN (best-effort). */
30export const readGrafanaTags = Effect.fn(function* (arn: string) {
31 const response = yield* grafana
32 .listTagsForResource({ resourceArn: arn })
33 .pipe(Effect.catch(() => Effect.succeed(undefined)));
34 return toTagRecord(response?.tags);
35});
36
37/**
38 * Sync tags on a Grafana resource: diff OBSERVED cloud tags against the
39 * desired set and apply only the delta. Grafana's `tagResource` takes a tag
40 * map, so the `upsert` delta is folded back into a record.
41 */
42export const syncGrafanaTags = Effect.fn(function* (
43 arn: string,
44 desiredTags: Record<string, string>,
45) {
46 const observedTags = yield* readGrafanaTags(arn);
47 const { removed, upsert } = diffTags(observedTags, desiredTags);
48 if (upsert.length > 0) {
49 yield* grafana.tagResource({
50 resourceArn: arn,
51 tags: Object.fromEntries(upsert.map((t) => [t.Key, t.Value])),
52 });
53 }
54 if (removed.length > 0) {
55 yield* grafana.untagResource({ resourceArn: arn, tagKeys: removed });
56 }
57});