Current section

Files

Jump to
caffeine_lang src caffeine_lang@codegen@datadog.erl
Raw

src/caffeine_lang@codegen@datadog.erl

-module(caffeine_lang@codegen@datadog).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/codegen/datadog.gleam").
-export([resolve_indicators/1, window_to_timeframe/1, ir_to_terraform_resource/1, generate_resources/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/caffeine_lang/codegen/datadog.gleam", 31).
?DOC(false).
-spec resolve_indicators(
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:deps_validated())
) -> {ok,
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved())} |
{error, caffeine_lang@errors:compilation_error()}.
resolve_indicators(Ir) ->
Values_index = caffeine_lang@helpers:index_value_tuples(
erlang:element(4, Ir)
),
gleam@result:'try'(
begin
_pipe = gleam_stdlib:map_get(Values_index, <<"indicators"/utf8>>),
gleam@result:replace_error(
_pipe,
caffeine_lang@errors:semantic_analysis_template_resolution_error(
<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(Ir))/binary>>/binary,
"' - missing 'indicators' field in IR"/utf8>>
)
)
end,
fun(Indicators_value_tuple) ->
gleam@result:'try'(
begin
_pipe@1 = caffeine_lang@value:extract_string_dict(
erlang:element(4, Indicators_value_tuple)
),
gleam@result:map_error(
_pipe@1,
fun(_) ->
caffeine_lang@errors:semantic_analysis_template_resolution_error(
<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary>>/binary,
"' - failed to decode indicators"/utf8>>
)
end
)
end,
fun(Indicators_dict) ->
Identifier = caffeine_lang@linker@ir:ir_to_identifier(Ir),
gleam@result:'try'(
begin
_pipe@2 = Indicators_dict,
_pipe@3 = maps:to_list(_pipe@2),
gleam@list:try_map(
_pipe@3,
fun(Pair) ->
{Key, Indicator} = Pair,
gleam@result:map(
caffeine_lang@codegen@datadog_template:parse_and_resolve_query_template(
Indicator,
erlang:element(4, Ir),
Identifier
),
fun(Resolved) -> {Key, Resolved} end
)
end
)
end,
fun(Resolved_indicators) ->
Resolved_indicators_value = begin
_pipe@4 = Resolved_indicators,
_pipe@5 = gleam@list:map(
_pipe@4,
fun(Pair@1) ->
{erlang:element(1, Pair@1),
{string_value,
erlang:element(2, Pair@1)}}
end
),
_pipe@6 = maps:from_list(_pipe@5),
{dict_value, _pipe@6}
end,
New_indicators_value_tuple = {value_tuple,
<<"indicators"/utf8>>,
{collection_type,
{dict,
{primitive_type, string},
{primitive_type, string}}},
Resolved_indicators_value},
Evaluation_tuple_result = gleam_stdlib:map_get(
Values_index,
<<"evaluation"/utf8>>
),
gleam@result:'try'(case Evaluation_tuple_result of
{error, _} ->
{ok, none};
{ok, Evaluation_tuple} ->
gleam@result:'try'(
begin
_pipe@7 = caffeine_lang@value:extract_string(
erlang:element(
4,
Evaluation_tuple
)
),
gleam@result:map_error(
_pipe@7,
fun(_) ->
caffeine_lang@errors:semantic_analysis_template_resolution_error(
<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary>>/binary,
"' - failed to decode 'evaluation' field as string"/utf8>>
)
end
)
end,
fun(Evaluation_string) ->
_pipe@8 = caffeine_lang@codegen@datadog_template:parse_and_resolve_query_template(
Evaluation_string,
erlang:element(4, Ir),
Identifier
),
gleam@result:map(
_pipe@8,
fun(Resolved_evaluation) ->
{some,
{value_tuple,
<<"evaluation"/utf8>>,
{primitive_type,
string},
{string_value,
Resolved_evaluation}}}
end
)
end
)
end, fun(Resolved_evaluation_tuple) ->
New_values = begin
_pipe@9 = erlang:element(4, Ir),
gleam@list:map(
_pipe@9,
fun(Vt) ->
case erlang:element(2, Vt) of
<<"indicators"/utf8>> ->
New_indicators_value_tuple;
<<"evaluation"/utf8>> ->
case Resolved_evaluation_tuple of
{some,
New_evaluation} ->
New_evaluation;
none ->
Vt
end;
_ ->
Vt
end
end
)
end,
Resolved_indicators_dict = begin
_pipe@10 = Resolved_indicators,
maps:from_list(_pipe@10)
end,
Resolved_eval = case Resolved_evaluation_tuple of
{some, Vt@1} ->
_pipe@11 = caffeine_lang@value:extract_string(
erlang:element(4, Vt@1)
),
gleam@option:from_result(_pipe@11);
none ->
erlang:element(
5,
erlang:element(5, Ir)
)
end,
New_ir = caffeine_lang@linker@ir:map_slo(
{intermediate_representation,
erlang:element(2, Ir),
erlang:element(3, Ir),
New_values,
erlang:element(5, Ir),
erlang:element(6, Ir)},
fun(Slo) ->
{slo_fields,
erlang:element(2, Slo),
Resolved_indicators_dict,
erlang:element(4, Slo),
Resolved_eval,
erlang:element(6, Slo),
erlang:element(7, Slo),
erlang:element(8, Slo),
erlang:element(9, Slo),
erlang:element(10, Slo),
erlang:element(11, Slo)}
end
),
{ok,
caffeine_lang@linker@ir:promote(New_ir)}
end)
end
)
end
)
end
).
-file("src/caffeine_lang/codegen/datadog.gleam", 345).
-spec description_text_to_expr(binary()) -> terra_madre@hcl:expr().
description_text_to_expr(Text) ->
case gleam_stdlib:contains_string(Text, <<"\n"/utf8>>) of
true ->
{heredoc,
<<"EOT"/utf8>>,
true,
[{literal_part, <<Text/binary, "\n"/utf8>>}]};
false ->
{string_literal, Text}
end.
-file("src/caffeine_lang/codegen/datadog.gleam", 331).
?DOC(
" Combine the SLO description (from `###` doc comments) with the runbook\n"
" link, if either is present. Multi-line descriptions render as an HCL\n"
" heredoc; single-line descriptions render as a plain string literal.\n"
).
-spec build_description_expr(
gleam@option:option(binary()),
gleam@option:option(binary())
) -> gleam@option:option(terra_madre@hcl:expr()).
build_description_expr(Description, Runbook) ->
Runbook_link = gleam@option:map(
Runbook,
fun(Url) -> <<<<"[Runbook]("/utf8, Url/binary>>/binary, ")"/utf8>> end
),
Combined = case {Description, Runbook_link} of
{none, none} ->
none;
{{some, D}, none} ->
{some, D};
{none, {some, R}} ->
{some, R};
{{some, D@1}, {some, R@1}} ->
{some, <<<<D@1/binary, "\n\n"/utf8>>/binary, R@1/binary>>}
end,
gleam@option:map(Combined, fun description_text_to_expr/1).
-file("src/caffeine_lang/codegen/datadog.gleam", 378).
?DOC(false).
-spec window_to_timeframe(integer()) -> {ok, binary()} |
{error, caffeine_lang@errors:compilation_error()}.
window_to_timeframe(Days) ->
Days_string = erlang:integer_to_binary(Days),
case Days of
7 ->
{ok, <<Days_string/binary, "d"/utf8>>};
30 ->
{ok, <<Days_string/binary, "d"/utf8>>};
90 ->
{ok, <<Days_string/binary, "d"/utf8>>};
_ ->
{error,
caffeine_lang@codegen@generator_utils:resolution_error(
<<"datadog"/utf8>>,
<<<<"Illegal window_in_days value: "/utf8,
Days_string/binary>>/binary,
". Accepted values are 7, 30, or 90."/utf8>>
)}
end.
-file("src/caffeine_lang/codegen/datadog.gleam", 354).
?DOC(
" Build dependency relation tag pairs from the relations dict.\n"
" Generates pairs like #(\"soft_dependency\", \"target1,target2\").\n"
).
-spec build_dependency_tags(
gleam@dict:dict(caffeine_lang@linker@dependency:dependency_relation_type(), list(binary()))
) -> list({binary(), binary()}).
build_dependency_tags(Relations) ->
_pipe = Relations,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(
caffeine_lang@linker@dependency:relation_type_to_string(
erlang:element(1, A)
),
caffeine_lang@linker@dependency:relation_type_to_string(
erlang:element(1, B)
)
)
end
),
gleam@list:map(
_pipe@2,
fun(Pair) ->
{Relation_type, Targets} = Pair,
Sorted_targets = begin
_pipe@3 = Targets,
gleam@list:sort(_pipe@3, fun gleam@string:compare/2)
end,
{<<(caffeine_lang@linker@dependency:relation_type_to_string(
Relation_type
))/binary,
"_dependency"/utf8>>,
gleam@string:join(Sorted_targets, <<","/utf8>>)}
end
).
-file("src/caffeine_lang/codegen/datadog.gleam", 175).
?DOC(false).
-spec ir_to_terraform_resource(
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved())
) -> {ok, {terra_madre@terraform:resource(), list(binary())}} |
{error, caffeine_lang@errors:compilation_error()}.
ir_to_terraform_resource(Ir) ->
Resource_name = terra_madre@common:sanitize_terraform_identifier(
erlang:element(3, Ir)
),
Slo = erlang:element(5, Ir),
Threshold = erlang:element(2, Slo),
Window_in_days = erlang:element(4, Slo),
Indicators = erlang:element(3, Slo),
Evaluation_expr = begin
_pipe = erlang:element(5, Slo),
gleam@option:unwrap(_pipe, <<"numerator / denominator"/utf8>>)
end,
Runbook = erlang:element(7, Slo),
Description = erlang:element(9, Slo),
gleam@result:'try'(
begin
_pipe@1 = caffeine_lang@codegen@datadog_cql:resolve_slo_to_hcl(
Evaluation_expr,
Indicators,
erlang:element(10, Slo)
),
gleam@result:map_error(
_pipe@1,
fun(Err) ->
caffeine_lang@errors:generator_slo_query_resolution_error(
<<<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary>>/binary,
"' - failed to resolve SLO query: "/utf8>>/binary,
Err/binary>>
)
end
)
end,
fun(_use0) ->
{resolved_slo_hcl, Slo_type, Slo_blocks} = _use0,
Dependency_tags = case erlang:element(8, Slo) of
{some, Relations} ->
build_dependency_tags(Relations);
none ->
[]
end,
User_tag_pairs = erlang:element(6, Slo),
System_tag_pairs = begin
_pipe@2 = caffeine_lang@helpers:build_system_tag_pairs(
erlang:element(3, erlang:element(2, Ir)),
erlang:element(6, erlang:element(2, Ir)),
erlang:element(4, erlang:element(2, Ir)),
erlang:element(5, erlang:element(2, Ir)),
erlang:element(2, erlang:element(2, Ir)),
erlang:element(7, erlang:element(2, Ir))
),
lists:append(_pipe@2, Dependency_tags)
end,
System_tag_keys = begin
_pipe@3 = System_tag_pairs,
_pipe@4 = gleam@list:map(
_pipe@3,
fun(Pair) -> erlang:element(1, Pair) end
),
gleam@set:from_list(_pipe@4)
end,
User_tag_keys = begin
_pipe@5 = User_tag_pairs,
_pipe@6 = gleam@list:map(
_pipe@5,
fun(Pair@1) -> erlang:element(1, Pair@1) end
),
gleam@set:from_list(_pipe@6)
end,
Overlapping_keys = gleam@set:intersection(
System_tag_keys,
User_tag_keys
),
{Final_system_tag_pairs, Overshadowing_warnings} = case gleam@set:size(
Overlapping_keys
)
> 0 of
true ->
Warn_msgs = begin
_pipe@7 = Overlapping_keys,
_pipe@8 = gleam@set:to_list(_pipe@7),
_pipe@9 = gleam@list:sort(
_pipe@8,
fun gleam@string:compare/2
),
gleam@list:map(
_pipe@9,
fun(Key) ->
<<<<<<(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary,
" - user tag '"/utf8>>/binary,
Key/binary>>/binary,
"' overshadows system tag"/utf8>>
end
)
end,
Filtered = begin
_pipe@10 = System_tag_pairs,
gleam@list:filter(
_pipe@10,
fun(Pair@2) ->
not gleam@set:contains(
Overlapping_keys,
erlang:element(1, Pair@2)
)
end
)
end,
{Filtered, Warn_msgs};
false ->
{System_tag_pairs, []}
end,
At_prefixed_attrs = begin
_pipe@11 = Indicators,
_pipe@12 = maps:values(_pipe@11),
_pipe@13 = lists:append(_pipe@12, [Evaluation_expr]),
_pipe@14 = gleam@list:flat_map(
_pipe@13,
fun datadog_query@lint:at_prefixed_attrs/1
),
gleam@list:unique(_pipe@14)
end,
At_prefix_warnings = begin
_pipe@15 = At_prefixed_attrs,
_pipe@16 = gleam@list:sort(_pipe@15, fun gleam@string:compare/2),
gleam@list:map(
_pipe@16,
fun(Attr) ->
<<<<<<<<<<(caffeine_lang@linker@ir:ir_to_identifier(Ir))/binary,
" - query references '"/utf8>>/binary,
Attr/binary>>/binary,
"' which Datadog rejects in metric SLO filters; use bare '"/utf8>>/binary,
(gleam@string:drop_start(Attr, 1))/binary>>/binary,
"' instead (Datadog strips '@' from log-based metric attribute names)"/utf8>>
end
)
end,
Warnings = lists:append(Overshadowing_warnings, At_prefix_warnings),
Tags = begin
_pipe@17 = lists:append(Final_system_tag_pairs, User_tag_pairs),
_pipe@18 = gleam@list:map(
_pipe@17,
fun(Pair@3) ->
{string_literal,
datadog_query@filter:tag(
erlang:element(1, Pair@3),
erlang:element(2, Pair@3)
)}
end
),
{list_expr, _pipe@18}
end,
Identifier = caffeine_lang@linker@ir:ir_to_identifier(Ir),
gleam@result:'try'(
begin
_pipe@19 = window_to_timeframe(Window_in_days),
gleam@result:map_error(
_pipe@19,
fun(Err@1) ->
caffeine_lang@errors:prefix_error(Err@1, Identifier)
end
)
end,
fun(Window_in_days_string) ->
Thresholds_block = terra_madre@hcl:simple_block(
<<"thresholds"/utf8>>,
[{<<"timeframe"/utf8>>,
{string_literal, Window_in_days_string}},
{<<"target"/utf8>>, {float_literal, Threshold}}]
),
Type_str = case Slo_type of
time_slice_slo ->
<<"time_slice"/utf8>>;
metric_slo ->
<<"metric"/utf8>>
end,
Base_attributes = [{<<"name"/utf8>>,
{string_literal,
erlang:element(
2,
erlang:element(2, erlang:element(2, Ir))
)}},
{<<"type"/utf8>>, {string_literal, Type_str}},
{<<"tags"/utf8>>, Tags}],
Attributes = case build_description_expr(
Description,
Runbook
) of
{some, Desc_expr} ->
[{<<"description"/utf8>>, Desc_expr} |
Base_attributes];
none ->
Base_attributes
end,
{ok,
{{resource,
<<"datadog_service_level_objective"/utf8>>,
Resource_name,
maps:from_list(Attributes),
lists:append(Slo_blocks, [Thresholds_block]),
terra_madre@hcl:empty_meta(),
none},
Warnings}}
end
)
end
).
-file("src/caffeine_lang/codegen/datadog.gleam", 158).
?DOC(false).
-spec generate_resources(
list(caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved()))
) -> {ok, {list(terra_madre@terraform:resource()), list(binary())}} |
{error, caffeine_lang@errors:compilation_error()}.
generate_resources(Irs) ->
_pipe = Irs,
_pipe@1 = gleam@list:try_fold(
_pipe,
{[], []},
fun(Acc, Ir) ->
{Resources, Warning_lists} = Acc,
gleam@result:'try'(
ir_to_terraform_resource(Ir),
fun(_use0) ->
{Resource, Ir_warnings} = _use0,
{ok,
{[Resource | Resources], [Ir_warnings | Warning_lists]}}
end
)
end
),
gleam@result:map(
_pipe@1,
fun(Pair) ->
{lists:reverse(erlang:element(1, Pair)),
lists:append(lists:reverse(erlang:element(2, Pair)))}
end
).