diff options
| author | Andrei Rykov <[email protected]> | 2026-06-23 15:29:43 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-06-23 15:29:43 +0200 |
| commit | a7a91c0eb312b89eff8cd124c043fd58593e4545 (patch) | |
| tree | 14e694bf973f0abe49dfab125a3ec0e475aec773 | |
| parent | 0cc54fc39e3016733005a27fcac08ac787d42d6a (diff) | |
[EXT-2281] Move support links implementation from headers to cpp (#44305)
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_dashboard_common.cpp | 98 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_dashboard_common.h | 86 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_dashboard_search_source.cpp | 231 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_dashboard_search_source.h | 216 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_dashboard_source.cpp | 62 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_dashboard_source.h | 57 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_logging_source.cpp | 200 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/grafana_logging_source.h | 197 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/param_bindings.cpp | 142 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/param_bindings.h | 137 | ||||
| -rw-r--r-- | ydb/mvp/meta/support_links/ya.make | 5 |
11 files changed, 764 insertions, 667 deletions
diff --git a/ydb/mvp/meta/support_links/grafana_dashboard_common.cpp b/ydb/mvp/meta/support_links/grafana_dashboard_common.cpp new file mode 100644 index 00000000000..2ee68d32324 --- /dev/null +++ b/ydb/mvp/meta/support_links/grafana_dashboard_common.cpp @@ -0,0 +1,98 @@ +#include "grafana_dashboard_common.h" + +#include "source_common.h" + +namespace NMVP::NSupportLinks { +namespace { + +constexpr TStringBuf GRAFANA_WORKSPACE_KEY = "k8s_namespace"; +constexpr TStringBuf GRAFANA_DATASOURCE_KEY = "datasource"; + +std::pair<TString, TCgiParameters> BuildGrafanaDashboardUrlParts(TStringBuf grafanaEndpoint, TStringBuf url) { + TString resolvedUrl = IsAbsoluteUrl(url) + ? TString(url) + : JoinUrl(grafanaEndpoint, url); + TString path = TString(TStringBuf(resolvedUrl).Before('?')); + const TStringBuf queryString = TStringBuf(resolvedUrl).After('?'); + + TCgiParameters queryParameters; + if (!queryString.empty()) { + queryParameters.Scan(queryString); + } + + return {std::move(path), std::move(queryParameters)}; +} + +void InsertOrReplaceDashboardVar(TCgiParameters& queryParameters, TStringBuf label, TStringBuf value) { + const TString varName = TStringBuilder() << "var-" << label; + queryParameters.EraseAll(varName); + queryParameters.InsertUnescaped(varName, value); +} + +void ApplyGrafanaDashboardBindingPolicy( + TCgiParameters& queryParameters, + const THashMap<TString, TString>& clusterInfo, + const TCgiParameters& requestQueryParameters, + const TResolvedParamBindings& paramBindings) +{ + for (const auto& [name, value] : BuildNonIdentityRequestParamValues(requestQueryParameters)) { + InsertOrReplaceDashboardVar(queryParameters, name, value); + } + + for (const auto& [label, value] : BuildClusterInfoParamValues(clusterInfo, paramBindings.ClusterInfoMappings)) { + InsertOrReplaceDashboardVar(queryParameters, label, value); + } + + for (const auto& [label, value] : BuildStaticParamValues(paramBindings.StaticMappings)) { + InsertOrReplaceDashboardVar(queryParameters, label, value); + } + + for (const auto& [label, value] : BuildRequestParamValues(requestQueryParameters, paramBindings.RequestMappings)) { + InsertOrReplaceDashboardVar(queryParameters, label, value); + } +} + +} // namespace + +TResolvedParamBindings BuildDefaultGrafanaDashboardParamBindings() { + return TResolvedParamBindings{ + .RequestMappings = { + {"cluster", "cluster"}, + {"database", "database"}, + {"node", "node"}, + {"host", "host"}, + }, + .ClusterInfoMappings = { + {TString(GRAFANA_DATASOURCE_KEY), "ds"}, + {TString(GRAFANA_WORKSPACE_KEY), "workspace"}, + }, + .StaticMappings = {}, + }; +} + +TString BuildGrafanaDashboardUrl( + TStringBuf grafanaEndpoint, + TStringBuf url, + const THashMap<TString, TString>& clusterInfo, + const TCgiParameters& requestQueryParameters, + const TResolvedParamBindings& paramBindings) +{ + auto [path, queryParameters] = BuildGrafanaDashboardUrlParts(grafanaEndpoint, url); + ApplyGrafanaDashboardBindingPolicy(queryParameters, clusterInfo, requestQueryParameters, paramBindings); + + return queryParameters.empty() + ? path + : TStringBuilder() << path << '?' << queryParameters.Print(); +} + +TString BuildGrafanaDashboardUrl( + TStringBuf grafanaEndpoint, + TStringBuf url, + const ILinkSource::TLinkResolveInput& input, + const TResolvedParamBindings& paramBindings) +{ + const TCgiParameters forwardedParameters = BuildForwardedParameters(input.Identity, input.AdditionalRequestParams); + return BuildGrafanaDashboardUrl(grafanaEndpoint, url, input.ClusterInfo, forwardedParameters, paramBindings); +} + +} // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/grafana_dashboard_common.h b/ydb/mvp/meta/support_links/grafana_dashboard_common.h index 331bca39025..984c6ad4dec 100644 --- a/ydb/mvp/meta/support_links/grafana_dashboard_common.h +++ b/ydb/mvp/meta/support_links/grafana_dashboard_common.h @@ -1,97 +1,23 @@ #pragma once -#include "source_common.h" -#include "source.h" -#include "types.h" #include "param_bindings.h" #include <library/cpp/cgiparam/cgiparam.h> +#include <util/generic/strbuf.h> namespace NMVP::NSupportLinks { -inline constexpr TStringBuf GRAFANA_WORKSPACE_KEY = "k8s_namespace"; -inline constexpr TStringBuf GRAFANA_DATASOURCE_KEY = "datasource"; - -inline std::pair<TString, TCgiParameters> BuildGrafanaDashboardUrlParts(TStringBuf grafanaEndpoint, TStringBuf url) { - TString resolvedUrl = IsAbsoluteUrl(url) - ? TString(url) - : JoinUrl(grafanaEndpoint, url); - TString path = TString(TStringBuf(resolvedUrl).Before('?')); - const TStringBuf queryString = TStringBuf(resolvedUrl).After('?'); - - TCgiParameters queryParameters; - if (!queryString.empty()) { - queryParameters.Scan(queryString); - } - - return {std::move(path), std::move(queryParameters)}; -} - -inline void InsertOrReplaceDashboardVar(TCgiParameters& queryParameters, TStringBuf label, TStringBuf value) { - const TString varName = TStringBuilder() << "var-" << label; - queryParameters.EraseAll(varName); - queryParameters.InsertUnescaped(varName, value); -} - -inline TResolvedParamBindings BuildDefaultGrafanaDashboardParamBindings() { - return TResolvedParamBindings{ - .RequestMappings = { - {"cluster", "cluster"}, - {"database", "database"}, - {"node", "node"}, - {"host", "host"}, - }, - .ClusterInfoMappings = { - {TString(GRAFANA_DATASOURCE_KEY), "ds"}, - {TString(GRAFANA_WORKSPACE_KEY), "workspace"}, - }, - .StaticMappings = {}, - }; -} - -inline void ApplyGrafanaDashboardBindingPolicy( - TCgiParameters& queryParameters, - const THashMap<TString, TString>& clusterInfo, - const TCgiParameters& requestQueryParameters, - const TResolvedParamBindings& paramBindings) { - for (const auto& [name, value] : BuildNonIdentityRequestParamValues(requestQueryParameters)) { - InsertOrReplaceDashboardVar(queryParameters, name, value); - } - - for (const auto& [label, value] : BuildClusterInfoParamValues(clusterInfo, paramBindings.ClusterInfoMappings)) { - InsertOrReplaceDashboardVar(queryParameters, label, value); - } - - for (const auto& [label, value] : BuildStaticParamValues(paramBindings.StaticMappings)) { - InsertOrReplaceDashboardVar(queryParameters, label, value); - } - - for (const auto& [label, value] : BuildRequestParamValues(requestQueryParameters, paramBindings.RequestMappings)) { - InsertOrReplaceDashboardVar(queryParameters, label, value); - } -} - -inline TString BuildGrafanaDashboardUrl( +TResolvedParamBindings BuildDefaultGrafanaDashboardParamBindings(); +TString BuildGrafanaDashboardUrl( TStringBuf grafanaEndpoint, TStringBuf url, const THashMap<TString, TString>& clusterInfo, const TCgiParameters& requestQueryParameters, - const TResolvedParamBindings& paramBindings) { - auto [path, queryParameters] = BuildGrafanaDashboardUrlParts(grafanaEndpoint, url); - ApplyGrafanaDashboardBindingPolicy(queryParameters, clusterInfo, requestQueryParameters, paramBindings); - - return queryParameters.empty() - ? path - : TStringBuilder() << path << '?' << queryParameters.Print(); -} - -inline TString BuildGrafanaDashboardUrl( + const TResolvedParamBindings& paramBindings); +TString BuildGrafanaDashboardUrl( TStringBuf grafanaEndpoint, TStringBuf url, const ILinkSource::TLinkResolveInput& input, - const TResolvedParamBindings& paramBindings) { - const TCgiParameters forwardedParameters = BuildForwardedParameters(input.Identity, input.AdditionalRequestParams); - return BuildGrafanaDashboardUrl(grafanaEndpoint, url, input.ClusterInfo, forwardedParameters, paramBindings); -} + const TResolvedParamBindings& paramBindings); } // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/grafana_dashboard_search_source.cpp b/ydb/mvp/meta/support_links/grafana_dashboard_search_source.cpp new file mode 100644 index 00000000000..a617782ad1f --- /dev/null +++ b/ydb/mvp/meta/support_links/grafana_dashboard_search_source.cpp @@ -0,0 +1,231 @@ +#include "grafana_dashboard_search_source.h" + +#include "events.h" +#include "grafana_dashboard_common.h" +#include "source_common.h" + +#include <ydb/mvp/core/appdata.h> +#include <ydb/mvp/meta/mvp.h> + +#include <ydb/library/actors/core/actor.h> +#include <ydb/library/actors/core/actor_bootstrapped.h> +#include <ydb/library/actors/core/events.h> +#include <ydb/library/actors/core/hfunc.h> +#include <ydb/library/actors/http/http.h> + +#include <library/cpp/json/json_reader.h> + +#include <util/generic/yexception.h> +#include <util/string/cast.h> +#include <util/string/strip.h> + +namespace NMVP::NSupportLinks { +namespace { + +class TGrafanaDashboardSearchActor : public NActors::TActorBootstrapped<TGrafanaDashboardSearchActor> { +public: + TGrafanaDashboardSearchActor( + TSupportLinkEntryConfig config, + TString grafanaEndpoint, + TResolvedParamBindings paramBindings, + const ILinkSource::TLinkResolveInput& input, + const ILinkSource::TResolveContext& context) + : Config(std::move(config)) + , GrafanaEndpoint(std::move(grafanaEndpoint)) + , ParamBindings(std::move(paramBindings)) + , ClusterInfo(input.ClusterInfo) + , RequestQueryParameters(BuildForwardedParameters(input.Identity, input.AdditionalRequestParams)) + , Context(context) + {} + + void Bootstrap() { + const TString authHeaderValue = GetAuthorizationHeaderValue(); + NHttp::THttpOutgoingRequestPtr request = NHttp::THttpOutgoingRequest::CreateRequestGet(BuildSearchUrl()); + request->Set("Authorization", authHeaderValue); + + auto event = MakeHolder<NHttp::TEvHttpProxy::TEvHttpOutgoingRequest>(request); + Send(Context.HttpProxyId, event.Release()); + Become(&TGrafanaDashboardSearchActor::StateWork); + } + + STFUNC(StateWork) { + switch (ev->GetTypeRewrite()) { + hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); + cFunc(NActors::TEvents::TEvPoisonPill::EventType, PassAway); + } + } + +private: + TSupportLinkEntryConfig Config; + TString GrafanaEndpoint; + TResolvedParamBindings ParamBindings; + THashMap<TString, TString> ClusterInfo; + TCgiParameters RequestQueryParameters; + ILinkSource::TResolveContext Context; + TVector<TResolvedLink> Links; + TVector<TSupportError> Errors; + + TString GetAuthorizationHeaderValue() { + auto* appData = MVPAppData(); + if (!appData || !appData->Tokenator) { + return {}; + } + return StripString(appData->Tokenator->GetToken(TMVP::MetaDatabaseTokenName)); + } + + TString BuildSearchUrl() const { + TString url = Config.GetUrl().empty() ? TString("/api/search") : Config.GetUrl(); + url = IsAbsoluteUrl(url) ? url : JoinUrl(GrafanaEndpoint, url); + for (size_t i = 0; i < Config.TagSize(); ++i) { + if (!Config.GetTag(i).empty()) { + url = AppendQueryParam(url, "tag", Config.GetTag(i)); + } + } + for (size_t i = 0; i < Config.FolderSize(); ++i) { + if (!Config.GetFolder(i).empty()) { + url = AppendQueryParam(url, "folderUIDs", Config.GetFolder(i)); + } + } + return url; + } + + void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event) { + if (!event->Get()->Error.empty() || !event->Get()->Response || event->Get()->Response->Status != "200") { + TSupportError error; + error.Source = Config.GetSource(); + if (!event->Get()->Error.empty()) { + error.Message = event->Get()->Error; + } else if (event->Get()->Response) { + ui32 status = 0; + if (TryFromString<ui32>(event->Get()->Response->Status, status)) { + error.Status = status; + } + error.Reason = TString(event->Get()->Response->Message); + error.Message = TStringBuilder() << event->Get()->Response->Status << " " << event->Get()->Response->Message; + } else { + error.Message = "Unknown Grafana request error"; + } + Errors.push_back(std::move(error)); + ReplyAndDie(); + return; + } + + ParseSearchResponse(event->Get()->Response->Body); + ReplyAndDie(); + } + + void ParseSearchResponse(TStringBuf body) { + NJson::TJsonValue dashboardsJson; + NJson::TJsonReaderConfig jsonReaderConfig; + if (!NJson::ReadJsonTree(body, &jsonReaderConfig, &dashboardsJson) || dashboardsJson.GetType() != NJson::JSON_ARRAY) { + Errors.emplace_back(TSupportError{ + .Source = Config.GetSource(), + .Message = "Invalid JSON from Grafana Search API", + }); + return; + } + + for (const auto& item : dashboardsJson.GetArray()) { + if (item.GetType() != NJson::JSON_MAP) { + continue; + } + if (!IsDashboardItem(item)) { + continue; + } + + TString title; + TString dashboardUrl; + if (item.Has("title") && item["title"].GetType() == NJson::JSON_STRING) { + title = item["title"].GetString(); + } + if (item.Has("url") && item["url"].GetType() == NJson::JSON_STRING) { + dashboardUrl = item["url"].GetString(); + } else if (item.Has("uri") && item["uri"].GetType() == NJson::JSON_STRING) { + dashboardUrl = item["uri"].GetString(); + } + + if (dashboardUrl.empty()) { + continue; + } + + const TString resolvedUrl = BuildGrafanaDashboardUrl( + GrafanaEndpoint, + dashboardUrl, + ClusterInfo, + RequestQueryParameters, + ParamBindings); + if (!resolvedUrl.empty()) { + Links.emplace_back(TResolvedLink{ + .Title = std::move(title), + .Url = resolvedUrl, + }); + } + } + } + + static bool IsDashboardItem(const NJson::TJsonValue& item) { + if (!item.Has("type") || item["type"].GetType() != NJson::JSON_STRING) { + return false; + } + + const TString type = item["type"].GetString(); + return type == "dash-db" || type == "dashboard"; + } + + void ReplyAndDie() { + Send(Context.Owner, new TEvPrivate::TEvSourceResponse(Context.Place, std::move(Links), std::move(Errors))); + PassAway(); + } +}; + +class TGrafanaDashboardSearchSource : public ILinkSource { +public: + TGrafanaDashboardSearchSource(TSupportLinkEntryConfig config, TString grafanaEndpoint, TResolvedParamBindings paramBindings) + : Config(std::move(config)) + , GrafanaEndpoint(std::move(grafanaEndpoint)) + , ParamBindings(std::move(paramBindings)) + {} + + TResolveOutput Resolve(const ILinkSource::TLinkResolveInput& input, const ILinkSource::TResolveContext& context) const override { + TResolveOutput result{ + .Name = Config.GetSource(), + }; + result.Actor = NActors::TActivationContext::Register( + new TGrafanaDashboardSearchActor( + Config, + GrafanaEndpoint, + ParamBindings, + input, + context), + context.Owner); + return result; + } + +private: + TSupportLinkEntryConfig Config; + TString GrafanaEndpoint; + TResolvedParamBindings ParamBindings; +}; + +} // namespace + +void ValidateGrafanaDashboardSearchSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings) { + if (metaSettings.SupportLinks.GrafanaEndpoint.empty()) { + ythrow yexception() << "grafana.endpoint is required for source=" << config.GetSource(); + } + if (TMVP::MetaDatabaseTokenName.empty()) { + ythrow yexception() << "meta.meta_database_token_name is required for source=" << config.GetSource(); + } + ValidateParamsAreUnique(ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()), config); +} + +std::shared_ptr<ILinkSource> MakeGrafanaDashboardSearchSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings) { + ValidateGrafanaDashboardSearchSourceConfig(config, metaSettings); + auto paramBindings = ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()); + return std::make_shared<TGrafanaDashboardSearchSource>( + std::move(config), + metaSettings.SupportLinks.GrafanaEndpoint, + std::move(paramBindings)); +} + +} // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/grafana_dashboard_search_source.h b/ydb/mvp/meta/support_links/grafana_dashboard_search_source.h index 57cc1fd1eb6..85118841d44 100644 --- a/ydb/mvp/meta/support_links/grafana_dashboard_search_source.h +++ b/ydb/mvp/meta/support_links/grafana_dashboard_search_source.h @@ -1,220 +1,10 @@ #pragma once -#include "events.h" -#include "grafana_dashboard_common.h" -#include "source_common.h" - -#include <ydb/mvp/core/appdata.h> -#include <ydb/mvp/meta/mvp.h> -#include <ydb/mvp/meta/support_links/source.h> - -#include <ydb/library/actors/core/actor.h> -#include <ydb/library/actors/core/actor_bootstrapped.h> -#include <ydb/library/actors/core/events.h> -#include <ydb/library/actors/core/hfunc.h> -#include <ydb/library/actors/http/http.h> - -#include <library/cpp/json/json_reader.h> - -#include <util/generic/yexception.h> -#include <util/string/cast.h> -#include <util/string/strip.h> +#include "source.h" namespace NMVP::NSupportLinks { -class TGrafanaDashboardSearchActor : public NActors::TActorBootstrapped<TGrafanaDashboardSearchActor> { -public: - TGrafanaDashboardSearchActor(TSupportLinkEntryConfig config, TString grafanaEndpoint, TResolvedParamBindings paramBindings, const ILinkSource::TLinkResolveInput& input, const ILinkSource::TResolveContext& context) - : Config(std::move(config)) - , GrafanaEndpoint(std::move(grafanaEndpoint)) - , ParamBindings(std::move(paramBindings)) - , ClusterInfo(input.ClusterInfo) - , RequestQueryParameters(BuildForwardedParameters(input.Identity, input.AdditionalRequestParams)) - , Context(context) - {} - - void Bootstrap() { - const TString authHeaderValue = GetAuthorizationHeaderValue(); - NHttp::THttpOutgoingRequestPtr request = NHttp::THttpOutgoingRequest::CreateRequestGet(BuildSearchUrl()); - request->Set("Authorization", authHeaderValue); - - auto event = MakeHolder<NHttp::TEvHttpProxy::TEvHttpOutgoingRequest>(request); - Send(Context.HttpProxyId, event.Release()); - Become(&TGrafanaDashboardSearchActor::StateWork); - } - - STFUNC(StateWork) { - switch (ev->GetTypeRewrite()) { - hFunc(NHttp::TEvHttpProxy::TEvHttpIncomingResponse, Handle); - cFunc(NActors::TEvents::TEvPoisonPill::EventType, PassAway); - } - } - -private: - TSupportLinkEntryConfig Config; - TString GrafanaEndpoint; - TResolvedParamBindings ParamBindings; - THashMap<TString, TString> ClusterInfo; - TCgiParameters RequestQueryParameters; - ILinkSource::TResolveContext Context; - TVector<TResolvedLink> Links; - TVector<TSupportError> Errors; - - TString GetAuthorizationHeaderValue() { - auto* appData = MVPAppData(); - if (!appData || !appData->Tokenator) { - return {}; - } - return StripString(appData->Tokenator->GetToken(TMVP::MetaDatabaseTokenName)); - } - - TString BuildSearchUrl() const { - TString url = Config.GetUrl().empty() ? TString("/api/search") : Config.GetUrl(); - url = IsAbsoluteUrl(url) ? url : JoinUrl(GrafanaEndpoint, url); - for (size_t i = 0; i < Config.TagSize(); ++i) { - if (!Config.GetTag(i).empty()) { - url = AppendQueryParam(url, "tag", Config.GetTag(i)); - } - } - for (size_t i = 0; i < Config.FolderSize(); ++i) { - if (!Config.GetFolder(i).empty()) { - url = AppendQueryParam(url, "folderUIDs", Config.GetFolder(i)); - } - } - return url; - } - - void Handle(NHttp::TEvHttpProxy::TEvHttpIncomingResponse::TPtr event) { - if (!event->Get()->Error.empty() || !event->Get()->Response || event->Get()->Response->Status != "200") { - TSupportError error; - error.Source = Config.GetSource(); - if (!event->Get()->Error.empty()) { - error.Message = event->Get()->Error; - } else if (event->Get()->Response) { - ui32 status = 0; - if (TryFromString<ui32>(event->Get()->Response->Status, status)) { - error.Status = status; - } - error.Reason = TString(event->Get()->Response->Message); - error.Message = TStringBuilder() << event->Get()->Response->Status << " " << event->Get()->Response->Message; - } else { - error.Message = "Unknown Grafana request error"; - } - Errors.push_back(std::move(error)); - ReplyAndDie(); - return; - } - - ParseSearchResponse(event->Get()->Response->Body); - ReplyAndDie(); - } - - void ParseSearchResponse(TStringBuf body) { - NJson::TJsonValue dashboardsJson; - NJson::TJsonReaderConfig jsonReaderConfig; - if (!NJson::ReadJsonTree(body, &jsonReaderConfig, &dashboardsJson) || dashboardsJson.GetType() != NJson::JSON_ARRAY) { - Errors.emplace_back(TSupportError{ - .Source = Config.GetSource(), - .Message = "Invalid JSON from Grafana Search API", - }); - return; - } - - for (const auto& item : dashboardsJson.GetArray()) { - if (item.GetType() != NJson::JSON_MAP) { - continue; - } - if (!IsDashboardItem(item)) { - continue; - } - - TString title; - TString dashboardUrl; - if (item.Has("title") && item["title"].GetType() == NJson::JSON_STRING) { - title = item["title"].GetString(); - } - if (item.Has("url") && item["url"].GetType() == NJson::JSON_STRING) { - dashboardUrl = item["url"].GetString(); - } else if (item.Has("uri") && item["uri"].GetType() == NJson::JSON_STRING) { - dashboardUrl = item["uri"].GetString(); - } - - if (dashboardUrl.empty()) { - continue; - } - - const TString resolvedUrl = BuildGrafanaDashboardUrl(GrafanaEndpoint, dashboardUrl, ClusterInfo, RequestQueryParameters, ParamBindings); - if (!resolvedUrl.empty()) { - Links.emplace_back(TResolvedLink{ - .Title = std::move(title), - .Url = resolvedUrl, - }); - } - } - } - - static bool IsDashboardItem(const NJson::TJsonValue& item) { - if (!item.Has("type") || item["type"].GetType() != NJson::JSON_STRING) { - return false; - } - - const TString type = item["type"].GetString(); - return type == "dash-db" || type == "dashboard"; - } - - void ReplyAndDie() { - Send(Context.Owner, new TEvPrivate::TEvSourceResponse(Context.Place, std::move(Links), std::move(Errors))); - PassAway(); - } -}; - -} // namespace NMVP::NSupportLinks - -namespace NMVP::NSupportLinks { - -class TGrafanaDashboardSearchSource : public ILinkSource { -public: - TGrafanaDashboardSearchSource(TSupportLinkEntryConfig config, TString grafanaEndpoint, TResolvedParamBindings paramBindings) - : Config(std::move(config)) - , GrafanaEndpoint(std::move(grafanaEndpoint)) - , ParamBindings(std::move(paramBindings)) - {} - - TResolveOutput Resolve(const ILinkSource::TLinkResolveInput& input, const ILinkSource::TResolveContext& context) const override { - TResolveOutput result{ - .Name = Config.GetSource(), - }; - result.Actor = NActors::TActivationContext::Register( - new TGrafanaDashboardSearchActor( - Config, - GrafanaEndpoint, - ParamBindings, - input, - context), - context.Owner); - return result; - } - -private: - TSupportLinkEntryConfig Config; - TString GrafanaEndpoint; - TResolvedParamBindings ParamBindings; -}; - -inline void ValidateGrafanaDashboardSearchSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings) { - if (metaSettings.SupportLinks.GrafanaEndpoint.empty()) { - ythrow yexception() << "grafana.endpoint is required for source=" << config.GetSource(); - } - if (TMVP::MetaDatabaseTokenName.empty()) { - ythrow yexception() << "meta.meta_database_token_name is required for source=" << config.GetSource(); - } - ValidateParamsAreUnique(ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()), config); -} - -inline std::shared_ptr<ILinkSource> MakeGrafanaDashboardSearchSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings) { - ValidateGrafanaDashboardSearchSourceConfig(config, metaSettings); - auto paramBindings = ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()); - return std::make_shared<TGrafanaDashboardSearchSource>(std::move(config), metaSettings.SupportLinks.GrafanaEndpoint, std::move(paramBindings)); -} +void ValidateGrafanaDashboardSearchSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings); +std::shared_ptr<ILinkSource> MakeGrafanaDashboardSearchSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings); } // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/grafana_dashboard_source.cpp b/ydb/mvp/meta/support_links/grafana_dashboard_source.cpp new file mode 100644 index 00000000000..46f722ae5ec --- /dev/null +++ b/ydb/mvp/meta/support_links/grafana_dashboard_source.cpp @@ -0,0 +1,62 @@ +#include "grafana_dashboard_source.h" + +#include "grafana_dashboard_common.h" +#include "source_common.h" + +#include <util/generic/yexception.h> + +namespace NMVP::NSupportLinks { +namespace { + +class TGrafanaDashboardSource : public ILinkSource { +public: + TGrafanaDashboardSource(TString sourceName, TString title, TString url, TString grafanaEndpoint, TResolvedParamBindings paramBindings) + : SourceName(std::move(sourceName)) + , Title(std::move(title)) + , Url(std::move(url)) + , GrafanaEndpoint(std::move(grafanaEndpoint)) + , ParamBindings(std::move(paramBindings)) + {} + + TResolveOutput Resolve(const ILinkSource::TLinkResolveInput& input, const ILinkSource::TResolveContext&) const override { + TString resolvedUrl = BuildGrafanaDashboardUrl(GrafanaEndpoint, Url, input, ParamBindings); + TResolveOutput result{ + .Name = SourceName, + }; + if (!resolvedUrl.empty()) { + result.Links.emplace_back(TResolvedLink{ + .Title = Title, + .Url = std::move(resolvedUrl), + }); + } + return result; + } + +private: + TString SourceName; + TString Title; + TString Url; + TString GrafanaEndpoint; + TResolvedParamBindings ParamBindings; +}; + +} // namespace + +void ValidateGrafanaDashboardSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings) { + if (config.GetUrl().empty()) { + ythrow yexception() << "url is required for source=" << config.GetSource(); + } + if (!IsAbsoluteUrl(config.GetUrl()) && metaSettings.SupportLinks.GrafanaEndpoint.empty()) { + ythrow yexception() << "grafana.endpoint is required for relative url"; + } + ValidateParamsAreUnique(ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()), config); +} + +std::shared_ptr<ILinkSource> MakeGrafanaDashboardSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings) { + ValidateGrafanaDashboardSourceConfig(config, metaSettings); + auto paramBindings = ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()); + return std::make_shared<TGrafanaDashboardSource>( + config.GetSource(), config.GetTitle(), config.GetUrl(), metaSettings.SupportLinks.GrafanaEndpoint, std::move(paramBindings)); +} + +} // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/grafana_dashboard_source.h b/ydb/mvp/meta/support_links/grafana_dashboard_source.h index 05c12e717f8..63b5a9bbd5c 100644 --- a/ydb/mvp/meta/support_links/grafana_dashboard_source.h +++ b/ydb/mvp/meta/support_links/grafana_dashboard_source.h @@ -1,61 +1,10 @@ #pragma once -#include "source_common.h" -#include "grafana_dashboard_common.h" - -#include <ydb/mvp/meta/support_links/source.h> - -#include <util/generic/yexception.h> +#include "source.h" namespace NMVP::NSupportLinks { -class TGrafanaDashboardSource : public ILinkSource { -public: - TGrafanaDashboardSource(TString sourceName, TString title, TString url, TString grafanaEndpoint, TResolvedParamBindings paramBindings) - : SourceName(std::move(sourceName)) - , Title(std::move(title)) - , Url(std::move(url)) - , GrafanaEndpoint(std::move(grafanaEndpoint)) - , ParamBindings(std::move(paramBindings)) - {} - - TResolveOutput Resolve(const ILinkSource::TLinkResolveInput& input, const ILinkSource::TResolveContext&) const override { - TString resolvedUrl = BuildGrafanaDashboardUrl(GrafanaEndpoint, Url, input, ParamBindings); - TResolveOutput result{ - .Name = SourceName, - }; - if (!resolvedUrl.empty()) { - result.Links.emplace_back(TResolvedLink{ - .Title = Title, - .Url = std::move(resolvedUrl), - }); - } - return result; - } - -private: - TString SourceName; - TString Title; - TString Url; - TString GrafanaEndpoint; - TResolvedParamBindings ParamBindings; -}; - -inline void ValidateGrafanaDashboardSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings) { - if (config.GetUrl().empty()) { - ythrow yexception() << "url is required for source=" << config.GetSource(); - } - if (!IsAbsoluteUrl(config.GetUrl()) && metaSettings.SupportLinks.GrafanaEndpoint.empty()) { - ythrow yexception() << "grafana.endpoint is required for relative url"; - } - ValidateParamsAreUnique(ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()), config); -} - -inline std::shared_ptr<ILinkSource> MakeGrafanaDashboardSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings) { - ValidateGrafanaDashboardSourceConfig(config, metaSettings); - auto paramBindings = ResolveParamBindings(config, BuildDefaultGrafanaDashboardParamBindings()); - return std::make_shared<TGrafanaDashboardSource>( - config.GetSource(), config.GetTitle(), config.GetUrl(), metaSettings.SupportLinks.GrafanaEndpoint, std::move(paramBindings)); -} +void ValidateGrafanaDashboardSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings); +std::shared_ptr<ILinkSource> MakeGrafanaDashboardSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings); } // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/grafana_logging_source.cpp b/ydb/mvp/meta/support_links/grafana_logging_source.cpp new file mode 100644 index 00000000000..55c07029719 --- /dev/null +++ b/ydb/mvp/meta/support_links/grafana_logging_source.cpp @@ -0,0 +1,200 @@ +#include "grafana_logging_source.h" + +#include "param_bindings.h" +#include "source_common.h" + +#include <library/cpp/cgiparam/cgiparam.h> +#include <library/cpp/json/json_writer.h> + +#include <util/generic/yexception.h> +#include <util/string/escape.h> + +namespace NMVP::NSupportLinks { +namespace { + +constexpr TStringBuf GRAFANA_LOGGING_DEFAULT_URL = "/explore"; + +TResolvedParamBindings BuildDefaultGrafanaLoggingParamBindings() { + return TResolvedParamBindings{ + .RequestMappings = { + {"database", "database"}, + {"node", "node_id"}, + {"host", "k8s_node_name"}, + }, + .ClusterInfoMappings = { + {"k8s_namespace", "__workspace__"}, + }, + .StaticMappings = { + {"ydb", "__bucket__"}, + }, + }; +} + +TString BuildGrafanaLoggingExpr(const TVector<std::pair<TString, TString>>& bindings) { + TString expr = "{"; + bool first = true; + for (const auto& [name, value] : bindings) { + if (!first) { + expr += ", "; + } + first = false; + expr += TStringBuilder() << name << "=\"" << EscapeC(value) << '"'; + } + expr += "}"; + return expr; +} + +NJson::TJsonValue BuildGrafanaLoggingPanesJson( + const TString& datasource, + const TVector<std::pair<TString, TString>>& bindings) +{ + NJson::TJsonValue panesJson(NJson::JSON_MAP); + NJson::TJsonValue paneJson(NJson::JSON_MAP); + NJson::TJsonValue queryJson(NJson::JSON_MAP); + + queryJson["refId"] = "A"; + queryJson["expr"] = BuildGrafanaLoggingExpr(bindings); + queryJson["queryType"] = "range"; + queryJson["direction"] = "backward"; + queryJson["datasource"]["type"] = "loki"; + queryJson["datasource"]["uid"] = datasource; + + paneJson["datasource"] = datasource; + paneJson["queries"].AppendValue(std::move(queryJson)); + paneJson["range"]["from"] = "now-1h"; + paneJson["range"]["to"] = "now"; + + panesJson["x"] = std::move(paneJson); + return panesJson; +} + +bool TryBuildGrafanaLoggingUrl( + TStringBuf grafanaEndpoint, + TStringBuf url, + const ILinkSource::TLinkResolveInput& input, + const TResolvedParamBindings& paramBindings, + TString& resolvedUrl, + TString& errorMessage) +{ + const TStringBuf path = url.empty() ? GRAFANA_LOGGING_DEFAULT_URL : url; + resolvedUrl = IsAbsoluteUrl(path) ? TString(path) : JoinUrl(grafanaEndpoint, path); + + if (resolvedUrl.Contains('?')) { + errorMessage = "query parameters are not supported in url for source=grafana/logging"; + return false; + } + + TString datasource; + if (const auto datasourceIt = input.ClusterInfo.find("datasource_logging"); + datasourceIt != input.ClusterInfo.end() && !datasourceIt->second.empty()) { + datasource = datasourceIt->second; + } + + const TCgiParameters forwardedParameters = BuildForwardedParameters(input.Identity, input.AdditionalRequestParams); + TVector<std::pair<TString, TString>> bindings = BuildNonIdentityRequestParamValues(forwardedParameters); + for (auto& binding : BuildRequestParamValues(forwardedParameters, paramBindings.RequestMappings)) { + bindings.push_back(std::move(binding)); + } + for (auto& binding : BuildClusterInfoParamValues(input.ClusterInfo, paramBindings.ClusterInfoMappings)) { + bindings.push_back(std::move(binding)); + } + for (auto& binding : BuildStaticParamValues(paramBindings.StaticMappings)) { + bindings.push_back(std::move(binding)); + } + + TVector<std::pair<TString, TString>> resolvedBindings; + resolvedBindings.reserve(bindings.size()); + for (auto& binding : bindings) { + if (binding.first == "datasource") { + datasource = std::move(binding.second); + continue; + } + resolvedBindings.push_back(std::move(binding)); + } + + if (datasource.empty()) { + errorMessage = "datasource_logging is required in cluster info for source=grafana/logging"; + return false; + } + + TCgiParameters queryParameters; + queryParameters.InsertUnescaped("schemaVersion", "1"); + queryParameters.InsertUnescaped("panes", NJson::WriteJson( + BuildGrafanaLoggingPanesJson(datasource, resolvedBindings), + false)); + queryParameters.InsertUnescaped("orgId", "1"); + + resolvedUrl += TStringBuilder() << '?' << queryParameters.Print(); + return true; +} + +class TGrafanaLoggingSource : public ILinkSource { +public: + TGrafanaLoggingSource(TString sourceName, TString title, TString url, TString grafanaEndpoint, TResolvedParamBindings paramBindings) + : SourceName(std::move(sourceName)) + , Title(std::move(title)) + , Url(std::move(url)) + , GrafanaEndpoint(std::move(grafanaEndpoint)) + , ParamBindings(std::move(paramBindings)) + {} + + TResolveOutput Resolve(const ILinkSource::TLinkResolveInput& input, const ILinkSource::TResolveContext&) const override { + TResolveOutput result{ + .Name = SourceName, + }; + + TString resolvedUrl; + TString errorMessage; + if (!TryBuildGrafanaLoggingUrl( + GrafanaEndpoint, + Url, + input, + ParamBindings, + resolvedUrl, + errorMessage)) + { + result.Errors.emplace_back(TSupportError{ + .Source = SourceName, + .Message = std::move(errorMessage), + }); + return result; + } + + result.Links.emplace_back(TResolvedLink{ + .Title = Title, + .Url = std::move(resolvedUrl), + }); + return result; + } + +private: + TString SourceName; + TString Title; + TString Url; + TString GrafanaEndpoint; + TResolvedParamBindings ParamBindings; +}; + +} // namespace + +void ValidateGrafanaLoggingSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings) { + const TStringBuf url = config.GetUrl().empty() + ? GRAFANA_LOGGING_DEFAULT_URL + : TStringBuf(config.GetUrl()); + if (url.Contains('?')) { + ythrow yexception() << "query parameters are not supported in url for source=" << config.GetSource(); + } + if (!IsAbsoluteUrl(url) && metaSettings.SupportLinks.GrafanaEndpoint.empty()) { + ythrow yexception() << "grafana.endpoint is required for relative url"; + } + ValidateParamsAreUnique(ResolveParamBindings(config, BuildDefaultGrafanaLoggingParamBindings()), config); +} + +std::shared_ptr<ILinkSource> MakeGrafanaLoggingSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings) { + ValidateGrafanaLoggingSourceConfig(config, metaSettings); + auto paramBindings = ResolveParamBindings(config, BuildDefaultGrafanaLoggingParamBindings()); + return std::make_shared<TGrafanaLoggingSource>( + config.GetSource(), config.GetTitle(), config.GetUrl(), metaSettings.SupportLinks.GrafanaEndpoint, std::move(paramBindings)); +} + +} // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/grafana_logging_source.h b/ydb/mvp/meta/support_links/grafana_logging_source.h index 7fdec0ccdde..b21d71a0b88 100644 --- a/ydb/mvp/meta/support_links/grafana_logging_source.h +++ b/ydb/mvp/meta/support_links/grafana_logging_source.h @@ -1,201 +1,10 @@ #pragma once -#include "param_bindings.h" -#include "source_common.h" - -#include <ydb/mvp/meta/support_links/source.h> - -#include <library/cpp/cgiparam/cgiparam.h> -#include <library/cpp/json/json_writer.h> - -#include <util/generic/yexception.h> -#include <util/string/escape.h> - -#include <utility> +#include "source.h" namespace NMVP::NSupportLinks { -inline constexpr TStringBuf GRAFANA_LOGGING_DEFAULT_URL = "/explore"; - -inline TResolvedParamBindings BuildDefaultGrafanaLoggingParamBindings() { - return TResolvedParamBindings{ - .RequestMappings = { - {"database", "database"}, - {"node", "node_id"}, - {"host", "k8s_node_name"}, - }, - .ClusterInfoMappings = { - {"k8s_namespace", "__workspace__"}, - }, - .StaticMappings = { - {"ydb", "__bucket__"}, - }, - }; -} - -inline TString BuildGrafanaLoggingExpr(const TVector<std::pair<TString, TString>>& bindings) { - TString expr = "{"; - bool first = true; - for (const auto& [name, value] : bindings) { - if (!first) { - expr += ", "; - } - first = false; - expr += TStringBuilder() << name << "=\"" << EscapeC(value) << '"'; - } - expr += "}"; - return expr; -} - -inline NJson::TJsonValue BuildGrafanaLoggingPanesJson(const TString& datasource, const TVector<std::pair<TString, TString>>& bindings) { - NJson::TJsonValue panesJson(NJson::JSON_MAP); - NJson::TJsonValue paneJson(NJson::JSON_MAP); - NJson::TJsonValue queryJson(NJson::JSON_MAP); - - queryJson["refId"] = "A"; - queryJson["expr"] = BuildGrafanaLoggingExpr(bindings); - queryJson["queryType"] = "range"; - queryJson["direction"] = "backward"; - queryJson["datasource"]["type"] = "loki"; - queryJson["datasource"]["uid"] = datasource; - - paneJson["datasource"] = datasource; - paneJson["queries"].AppendValue(std::move(queryJson)); - paneJson["range"]["from"] = "now-1h"; - paneJson["range"]["to"] = "now"; - - panesJson["x"] = std::move(paneJson); - return panesJson; -} - -inline bool TryBuildGrafanaLoggingUrl( - TStringBuf grafanaEndpoint, - TStringBuf url, - const ILinkSource::TLinkResolveInput& input, - const TResolvedParamBindings& paramBindings, - TString& resolvedUrl, - TString& errorMessage) { - const TStringBuf path = url.empty() ? GRAFANA_LOGGING_DEFAULT_URL : url; - resolvedUrl = IsAbsoluteUrl(path) ? TString(path) : JoinUrl(grafanaEndpoint, path); - - if (resolvedUrl.Contains('?')) { - errorMessage = "query parameters are not supported in url for source=grafana/logging"; - return false; - } - - TString datasource; - if (const auto datasourceIt = input.ClusterInfo.find("datasource_logging"); - datasourceIt != input.ClusterInfo.end() && !datasourceIt->second.empty()) { - datasource = datasourceIt->second; - } - - const TCgiParameters forwardedParameters = BuildForwardedParameters(input.Identity, input.AdditionalRequestParams); - TVector<std::pair<TString, TString>> bindings = BuildNonIdentityRequestParamValues(forwardedParameters); - for (auto& binding : BuildRequestParamValues(forwardedParameters, paramBindings.RequestMappings)) { - bindings.push_back(std::move(binding)); - } - for (auto& binding : BuildClusterInfoParamValues(input.ClusterInfo, paramBindings.ClusterInfoMappings)) { - bindings.push_back(std::move(binding)); - } - for (auto& binding : BuildStaticParamValues(paramBindings.StaticMappings)) { - bindings.push_back(std::move(binding)); - } - - TVector<std::pair<TString, TString>> resolvedBindings; - resolvedBindings.reserve(bindings.size()); - for (auto& binding : bindings) { - if (binding.first == "datasource") { - datasource = std::move(binding.second); - continue; - } - resolvedBindings.push_back(std::move(binding)); - } - - if (datasource.empty()) { - errorMessage = "datasource_logging is required in cluster info for source=grafana/logging"; - return false; - } - - TCgiParameters queryParameters; - queryParameters.InsertUnescaped("schemaVersion", "1"); - queryParameters.InsertUnescaped("panes", NJson::WriteJson( - BuildGrafanaLoggingPanesJson(datasource, resolvedBindings), - false)); - queryParameters.InsertUnescaped("orgId", "1"); - - resolvedUrl += TStringBuilder() << '?' << queryParameters.Print(); - return true; -} - -} // namespace NMVP::NSupportLinks - -namespace NMVP::NSupportLinks { - -class TGrafanaLoggingSource : public ILinkSource { -public: - TGrafanaLoggingSource(TString sourceName, TString title, TString url, TString grafanaEndpoint, TResolvedParamBindings paramBindings) - : SourceName(std::move(sourceName)) - , Title(std::move(title)) - , Url(std::move(url)) - , GrafanaEndpoint(std::move(grafanaEndpoint)) - , ParamBindings(std::move(paramBindings)) - {} - - TResolveOutput Resolve(const ILinkSource::TLinkResolveInput& input, const ILinkSource::TResolveContext&) const override { - TResolveOutput result{ - .Name = SourceName, - }; - - TString resolvedUrl; - TString errorMessage; - if (!TryBuildGrafanaLoggingUrl( - GrafanaEndpoint, - Url, - input, - ParamBindings, - resolvedUrl, - errorMessage)) - { - result.Errors.emplace_back(TSupportError{ - .Source = SourceName, - .Message = std::move(errorMessage), - }); - return result; - } - - result.Links.emplace_back(TResolvedLink{ - .Title = Title, - .Url = std::move(resolvedUrl), - }); - return result; - } - -private: - TString SourceName; - TString Title; - TString Url; - TString GrafanaEndpoint; - TResolvedParamBindings ParamBindings; -}; - -inline void ValidateGrafanaLoggingSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings) { - const TStringBuf url = config.GetUrl().empty() - ? GRAFANA_LOGGING_DEFAULT_URL - : TStringBuf(config.GetUrl()); - if (url.Contains('?')) { - ythrow yexception() << "query parameters are not supported in url for source=" << config.GetSource(); - } - if (!IsAbsoluteUrl(url) && metaSettings.SupportLinks.GrafanaEndpoint.empty()) { - ythrow yexception() << "grafana.endpoint is required for relative url"; - } - ValidateParamsAreUnique(ResolveParamBindings(config, BuildDefaultGrafanaLoggingParamBindings()), config); -} - -inline std::shared_ptr<ILinkSource> MakeGrafanaLoggingSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings) { - ValidateGrafanaLoggingSourceConfig(config, metaSettings); - auto paramBindings = ResolveParamBindings(config, BuildDefaultGrafanaLoggingParamBindings()); - return std::make_shared<TGrafanaLoggingSource>( - config.GetSource(), config.GetTitle(), config.GetUrl(), metaSettings.SupportLinks.GrafanaEndpoint, std::move(paramBindings)); -} +void ValidateGrafanaLoggingSourceConfig(const TSupportLinkEntryConfig& config, const TMetaSettings& metaSettings); +std::shared_ptr<ILinkSource> MakeGrafanaLoggingSource(TSupportLinkEntryConfig config, const TMetaSettings& metaSettings); } // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/param_bindings.cpp b/ydb/mvp/meta/support_links/param_bindings.cpp new file mode 100644 index 00000000000..80f1cc39e83 --- /dev/null +++ b/ydb/mvp/meta/support_links/param_bindings.cpp @@ -0,0 +1,142 @@ +#include "param_bindings.h" + +#include <util/generic/hash_set.h> +#include <util/generic/yexception.h> + +namespace NMVP::NSupportLinks { +namespace { + +TResolvedParamBindings ResolveConfiguredParamMappings(const TSupportLinkEntryConfig& config) { + TResolvedParamBindings paramBindings; + paramBindings.RequestMappings.reserve(config.LinkParameterMappingsSize()); + paramBindings.ClusterInfoMappings.reserve(config.LinkParameterMappingsSize()); + paramBindings.StaticMappings.reserve(config.LinkParameterMappingsSize()); + + const int linkParameterMappingsSize = config.LinkParameterMappingsSize(); + for (int i = 0; i < linkParameterMappingsSize; ++i) { + const auto& mapping = config.GetLinkParameterMappings(i); + if (!mapping.HasParameter() || mapping.GetParameter().empty()) { + ythrow yexception() << "link_parameter_mappings.parameter is required for source=" << config.GetSource(); + } + + switch (mapping.GetSourceValueCase()) { + case TSupportLinkEntryConfig::TLinkParameterMapping::kFromRequest: + paramBindings.RequestMappings.emplace_back(mapping.GetFromRequest(), mapping.GetParameter()); + break; + + case TSupportLinkEntryConfig::TLinkParameterMapping::kFromClusterInfo: + paramBindings.ClusterInfoMappings.emplace_back(mapping.GetFromClusterInfo(), mapping.GetParameter()); + break; + + case TSupportLinkEntryConfig::TLinkParameterMapping::kStaticValue: + paramBindings.StaticMappings.emplace_back(mapping.GetStaticValue(), mapping.GetParameter()); + break; + + case TSupportLinkEntryConfig::TLinkParameterMapping::SOURCEVALUE_NOT_SET: + ythrow yexception() + << "link_parameter_mappings.parameter=" << mapping.GetParameter() + << " must set one of from_request, from_cluster_info or static_value for source=" + << config.GetSource(); + } + } + + return paramBindings; +} + +} // namespace + +TResolvedParamBindings ResolveParamBindings(const TSupportLinkEntryConfig& config, const TResolvedParamBindings& defaultParamBindings) { + if (config.LinkParameterMappingsSize() != 0) { + return ResolveConfiguredParamMappings(config); + } + + return defaultParamBindings; +} + +void ValidateParamsAreUnique(const TResolvedParamBindings& paramBindings, const TSupportLinkEntryConfig& config) { + THashSet<TString> labels; + + for (const auto& mapping : paramBindings.RequestMappings) { + const TString& targetLabel = mapping.second; + if (!labels.insert(targetLabel).second) { + ythrow yexception() + << "duplicate target label '" << targetLabel + << "' in link_parameter_mappings for source=" << config.GetSource(); + } + } + + for (const auto& mapping : paramBindings.ClusterInfoMappings) { + const TString& targetLabel = mapping.second; + if (!labels.insert(targetLabel).second) { + ythrow yexception() + << "duplicate target label '" << targetLabel + << "' in link_parameter_mappings for source=" << config.GetSource(); + } + } + + for (const auto& mapping : paramBindings.StaticMappings) { + const TString& targetLabel = mapping.second; + if (!labels.insert(targetLabel).second) { + ythrow yexception() + << "duplicate target label '" << targetLabel + << "' in link_parameter_mappings for source=" << config.GetSource(); + } + } +} + +TVector<std::pair<TString, TString>> BuildRequestParamValues( + const TCgiParameters& requestParameters, + const TVector<std::pair<TString, TString>>& requestMappings) +{ + TVector<std::pair<TString, TString>> paramValues; + + for (const auto& [requestParamName, targetLabel] : requestMappings) { + const TString value = requestParameters.Get(requestParamName); + if (!value.empty()) { + paramValues.emplace_back(targetLabel, value); + } + } + + return paramValues; +} + +TVector<std::pair<TString, TString>> BuildNonIdentityRequestParamValues(const TCgiParameters& requestParameters) { + TVector<std::pair<TString, TString>> paramValues; + + for (const auto& [name, value] : requestParameters) { + if (!IsIdentityRequestParameter(name) && !value.empty()) { + paramValues.emplace_back(name, value); + } + } + + return paramValues; +} + +TVector<std::pair<TString, TString>> BuildClusterInfoParamValues( + const THashMap<TString, TString>& clusterInfo, + const TVector<std::pair<TString, TString>>& clusterInfoMappings) +{ + TVector<std::pair<TString, TString>> paramValues; + + for (const auto& [clusterInfoField, targetLabel] : clusterInfoMappings) { + const auto it = clusterInfo.find(clusterInfoField); + if (it != clusterInfo.end() && !it->second.empty()) { + paramValues.emplace_back(targetLabel, it->second); + } + } + + return paramValues; +} + +TVector<std::pair<TString, TString>> BuildStaticParamValues(const TVector<std::pair<TString, TString>>& staticMappings) { + TVector<std::pair<TString, TString>> paramValues; + paramValues.reserve(staticMappings.size()); + + for (const auto& [staticValue, targetLabel] : staticMappings) { + paramValues.emplace_back(targetLabel, staticValue); + } + + return paramValues; +} + +} // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/param_bindings.h b/ydb/mvp/meta/support_links/param_bindings.h index b8b94e95d1e..293c709671f 100644 --- a/ydb/mvp/meta/support_links/param_bindings.h +++ b/ydb/mvp/meta/support_links/param_bindings.h @@ -3,9 +3,8 @@ #include "source.h" #include <util/generic/hash.h> -#include <util/generic/hash_set.h> #include <util/generic/vector.h> -#include <util/generic/yexception.h> +#include <utility> namespace NMVP::NSupportLinks { @@ -15,129 +14,15 @@ struct TResolvedParamBindings { TVector<std::pair<TString, TString>> StaticMappings; }; -inline TResolvedParamBindings ResolveConfiguredParamMappings(const TSupportLinkEntryConfig& config) { - TResolvedParamBindings paramBindings; - paramBindings.RequestMappings.reserve(config.LinkParameterMappingsSize()); - paramBindings.ClusterInfoMappings.reserve(config.LinkParameterMappingsSize()); - paramBindings.StaticMappings.reserve(config.LinkParameterMappingsSize()); - - const int linkParameterMappingsSize = config.LinkParameterMappingsSize(); - for (int i = 0; i < linkParameterMappingsSize; ++i) { - const auto& mapping = config.GetLinkParameterMappings(i); - if (!mapping.HasParameter() || mapping.GetParameter().empty()) { - ythrow yexception() << "link_parameter_mappings.parameter is required for source=" << config.GetSource(); - } - - switch (mapping.GetSourceValueCase()) { - case TSupportLinkEntryConfig::TLinkParameterMapping::kFromRequest: - paramBindings.RequestMappings.emplace_back(mapping.GetFromRequest(), mapping.GetParameter()); - break; - - case TSupportLinkEntryConfig::TLinkParameterMapping::kFromClusterInfo: - paramBindings.ClusterInfoMappings.emplace_back(mapping.GetFromClusterInfo(), mapping.GetParameter()); - break; - - case TSupportLinkEntryConfig::TLinkParameterMapping::kStaticValue: - paramBindings.StaticMappings.emplace_back(mapping.GetStaticValue(), mapping.GetParameter()); - break; - - case TSupportLinkEntryConfig::TLinkParameterMapping::SOURCEVALUE_NOT_SET: - ythrow yexception() - << "link_parameter_mappings.parameter=" << mapping.GetParameter() - << " must set one of from_request, from_cluster_info or static_value for source=" - << config.GetSource(); - } - } - - return paramBindings; -} - -inline TResolvedParamBindings ResolveParamBindings(const TSupportLinkEntryConfig& config, const TResolvedParamBindings& defaultParamBindings) { - if (config.LinkParameterMappingsSize() != 0) { - return ResolveConfiguredParamMappings(config); - } - - return defaultParamBindings; -} - -inline void ValidateParamsAreUnique(const TResolvedParamBindings& paramBindings, const TSupportLinkEntryConfig& config) { - THashSet<TString> labels; - - for (const auto& [requestParamName, targetLabel] : paramBindings.RequestMappings) { - Y_UNUSED(requestParamName); - if (!labels.insert(targetLabel).second) { - ythrow yexception() - << "duplicate target label '" << targetLabel - << "' in link_parameter_mappings for source=" << config.GetSource(); - } - } - - for (const auto& [clusterInfoField, targetLabel] : paramBindings.ClusterInfoMappings) { - Y_UNUSED(clusterInfoField); - if (!labels.insert(targetLabel).second) { - ythrow yexception() - << "duplicate target label '" << targetLabel - << "' in link_parameter_mappings for source=" << config.GetSource(); - } - } - - for (const auto& [staticValue, targetLabel] : paramBindings.StaticMappings) { - Y_UNUSED(staticValue); - if (!labels.insert(targetLabel).second) { - ythrow yexception() - << "duplicate target label '" << targetLabel - << "' in link_parameter_mappings for source=" << config.GetSource(); - } - } -} - -inline TVector<std::pair<TString, TString>> BuildRequestParamValues(const TCgiParameters& requestParameters, const TVector<std::pair<TString, TString>>& requestMappings) { - TVector<std::pair<TString, TString>> paramValues; - - for (const auto& [requestParamName, targetLabel] : requestMappings) { - const TString value = requestParameters.Get(requestParamName); - if (!value.empty()) { - paramValues.emplace_back(targetLabel, value); - } - } - - return paramValues; -} - -inline TVector<std::pair<TString, TString>> BuildNonIdentityRequestParamValues(const TCgiParameters& requestParameters) { - TVector<std::pair<TString, TString>> paramValues; - - for (const auto& [name, value] : requestParameters) { - if (!IsIdentityRequestParameter(name) && !value.empty()) { - paramValues.emplace_back(name, value); - } - } - - return paramValues; -} - -inline TVector<std::pair<TString, TString>> BuildClusterInfoParamValues(const THashMap<TString, TString>& clusterInfo, const TVector<std::pair<TString, TString>>& clusterInfoMappings) { - TVector<std::pair<TString, TString>> paramValues; - - for (const auto& [clusterInfoField, targetLabel] : clusterInfoMappings) { - const auto it = clusterInfo.find(clusterInfoField); - if (it != clusterInfo.end() && !it->second.empty()) { - paramValues.emplace_back(targetLabel, it->second); - } - } - - return paramValues; -} - -inline TVector<std::pair<TString, TString>> BuildStaticParamValues(const TVector<std::pair<TString, TString>>& staticMappings) { - TVector<std::pair<TString, TString>> paramValues; - paramValues.reserve(staticMappings.size()); - - for (const auto& [staticValue, targetLabel] : staticMappings) { - paramValues.emplace_back(targetLabel, staticValue); - } - - return paramValues; -} +TResolvedParamBindings ResolveParamBindings(const TSupportLinkEntryConfig& config, const TResolvedParamBindings& defaultParamBindings); +void ValidateParamsAreUnique(const TResolvedParamBindings& paramBindings, const TSupportLinkEntryConfig& config); +TVector<std::pair<TString, TString>> BuildRequestParamValues( + const TCgiParameters& requestParameters, + const TVector<std::pair<TString, TString>>& requestMappings); +TVector<std::pair<TString, TString>> BuildNonIdentityRequestParamValues(const TCgiParameters& requestParameters); +TVector<std::pair<TString, TString>> BuildClusterInfoParamValues( + const THashMap<TString, TString>& clusterInfo, + const TVector<std::pair<TString, TString>>& clusterInfoMappings); +TVector<std::pair<TString, TString>> BuildStaticParamValues(const TVector<std::pair<TString, TString>>& staticMappings); } // namespace NMVP::NSupportLinks diff --git a/ydb/mvp/meta/support_links/ya.make b/ydb/mvp/meta/support_links/ya.make index af813180ad4..ccaf4b1a54a 100644 --- a/ydb/mvp/meta/support_links/ya.make +++ b/ydb/mvp/meta/support_links/ya.make @@ -2,6 +2,11 @@ LIBRARY() SRCS( entity.cpp + grafana_dashboard_common.cpp + grafana_dashboard_search_source.cpp + grafana_dashboard_source.cpp + grafana_logging_source.cpp + param_bindings.cpp response.cpp source.cpp support_links_resolver.cpp |
