summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Rykov <[email protected]>2026-06-23 15:29:43 +0200
committerGitHub <[email protected]>2026-06-23 15:29:43 +0200
commita7a91c0eb312b89eff8cd124c043fd58593e4545 (patch)
tree14e694bf973f0abe49dfab125a3ec0e475aec773
parent0cc54fc39e3016733005a27fcac08ac787d42d6a (diff)
[EXT-2281] Move support links implementation from headers to cpp (#44305)
-rw-r--r--ydb/mvp/meta/support_links/grafana_dashboard_common.cpp98
-rw-r--r--ydb/mvp/meta/support_links/grafana_dashboard_common.h86
-rw-r--r--ydb/mvp/meta/support_links/grafana_dashboard_search_source.cpp231
-rw-r--r--ydb/mvp/meta/support_links/grafana_dashboard_search_source.h216
-rw-r--r--ydb/mvp/meta/support_links/grafana_dashboard_source.cpp62
-rw-r--r--ydb/mvp/meta/support_links/grafana_dashboard_source.h57
-rw-r--r--ydb/mvp/meta/support_links/grafana_logging_source.cpp200
-rw-r--r--ydb/mvp/meta/support_links/grafana_logging_source.h197
-rw-r--r--ydb/mvp/meta/support_links/param_bindings.cpp142
-rw-r--r--ydb/mvp/meta/support_links/param_bindings.h137
-rw-r--r--ydb/mvp/meta/support_links/ya.make5
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