Skip to content

Commit ddea4fb

Browse files
authored
Plan2svg converter viewer handler (#10334)
1 parent ec35b6a commit ddea4fb

File tree

4 files changed

+157
-0
lines changed

4 files changed

+157
-0
lines changed

ydb/core/viewer/json_handlers_viewer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "viewer_netinfo.h"
2222
#include "viewer_nodelist.h"
2323
#include "viewer_nodes.h"
24+
#include "viewer_plan2svg.h"
2425
#include "viewer_pqconsumerinfo.h"
2526
#include "viewer_query.h"
2627
#include "viewer_render.h"
@@ -270,6 +271,10 @@ void InitViewerFeatureFlagsJsonHandler(TJsonHandlers& handlers) {
270271
handlers.AddHandler("/viewer/feature_flags", new TJsonHandler<TJsonFeatureFlags>(TJsonFeatureFlags::GetSwagger()), 2);
271272
}
272273

274+
void InitViewerPlan2SvgJsonHandler(TJsonHandlers& handlers) {
275+
handlers.AddHandler("/viewer/plan2svg", new TJsonHandler<TJsonPlanToSvg>(TJsonPlanToSvg::GetSwagger()));
276+
}
277+
273278
void InitViewerJsonHandlers(TJsonHandlers& jsonHandlers) {
274279
InitViewerCapabilitiesJsonHandler(jsonHandlers);
275280
InitViewerNodelistJsonHandler(jsonHandlers);
@@ -309,6 +314,7 @@ void InitViewerJsonHandlers(TJsonHandlers& jsonHandlers) {
309314
InitViewerAutocompleteJsonHandler(jsonHandlers);
310315
InitViewerCheckAccessJsonHandler(jsonHandlers);
311316
InitViewerFeatureFlagsJsonHandler(jsonHandlers);
317+
InitViewerPlan2SvgJsonHandler(jsonHandlers);
312318
}
313319

314320
}

ydb/core/viewer/viewer_plan2svg.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#pragma once
2+
#include "json_pipe_req.h"
3+
#include "viewer.h"
4+
#include <ydb/library/yaml_config/yaml_config.h>
5+
6+
#include <ydb/public/lib/ydb_cli/common/plan2svg.h>
7+
8+
namespace NKikimr::NViewer {
9+
10+
using namespace NActors;
11+
12+
class TJsonPlanToSvg : public TActorBootstrapped<TJsonPlanToSvg> {
13+
14+
IViewer* Viewer = nullptr;
15+
NMon::TEvHttpInfo::TPtr Event;
16+
17+
public:
18+
TJsonPlanToSvg(IViewer* viewer, NMon::TEvHttpInfo::TPtr &ev)
19+
: Viewer(viewer)
20+
, Event(ev)
21+
{}
22+
23+
void Bootstrap() {
24+
TString plan(Event->Get()->Request.GetPostContent());
25+
TString result;
26+
27+
TRequestState state(Event->Get());
28+
try {
29+
TPlanVisualizer planViz;
30+
planViz.LoadPlans(plan);
31+
result = Viewer->GetHTTPOK(state, "image/svg+xml", planViz.PrintSvg());
32+
} catch (std::exception& e) {
33+
result = Viewer->GetHTTPBADREQUEST(state, "text/plain", TStringBuilder() << "Conversion error: " << e.what());
34+
}
35+
36+
Send(Event->Sender, new NMon::TEvHttpInfoRes(result, 0, NMon::IEvHttpInfoRes::EContentType::Custom));
37+
PassAway();
38+
}
39+
40+
static YAML::Node GetSwagger() {
41+
TSimpleYamlBuilder yaml({
42+
.Method = "post",
43+
.Tag = "viewer",
44+
.Summary = "Plan2svg converter",
45+
.Description = "Renders plan json to svg image"
46+
});
47+
return yaml;
48+
}
49+
};
50+
51+
}

ydb/core/viewer/viewer_ut.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,4 +1935,103 @@ Y_UNIT_TEST_SUITE(Viewer) {
19351935
UNIT_ASSERT_EQUAL_C(rows.size(), ROWS_LIMIT, response);
19361936
}
19371937

1938+
Y_UNIT_TEST(Plan2SvgOK) {
1939+
TPortManager tp;
1940+
ui16 port = tp.GetPort(2134);
1941+
ui16 grpcPort = tp.GetPort(2135);
1942+
ui16 monPort = tp.GetPort(8765);
1943+
auto settings = TServerSettings(port);
1944+
settings.InitKikimrRunConfig()
1945+
.SetNodeCount(1)
1946+
.SetUseRealThreads(true)
1947+
.SetDomainName("Root")
1948+
.SetUseSectorMap(true)
1949+
.SetMonitoringPortOffset(monPort, true);
1950+
1951+
TServer server(settings);
1952+
server.EnableGRpc(grpcPort);
1953+
TClient client(settings);
1954+
1955+
TString tinyPlan = R"json({
1956+
"Plan" : {
1957+
"Node Type" : "Query",
1958+
"PlanNodeType" : "Query",
1959+
"Plans" : [
1960+
{
1961+
"Node Type" : "ResultSet",
1962+
"PlanNodeType" : "ResultSet",
1963+
"Plans" : [
1964+
{
1965+
"Node Type" : "Limit"
1966+
}
1967+
]
1968+
}
1969+
]
1970+
}
1971+
})json";
1972+
1973+
TKeepAliveHttpClient httpClient("localhost", monPort);
1974+
TStringStream responseStream;
1975+
TKeepAliveHttpClient::THeaders headers;
1976+
headers["Content-Type"] = "application/json";
1977+
headers["Authorization"] = "test_ydb_token";
1978+
const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoPost("/viewer/plan2svg", tinyPlan, &responseStream, headers);
1979+
const TString response = responseStream.ReadAll();
1980+
UNIT_ASSERT_EQUAL_C(statusCode, HTTP_OK, statusCode << ": " << response);
1981+
UNIT_ASSERT_C(response.StartsWith("<svg"), response);
1982+
}
1983+
1984+
Y_UNIT_TEST(Plan2SvgBad) {
1985+
TPortManager tp;
1986+
ui16 port = tp.GetPort(2134);
1987+
ui16 grpcPort = tp.GetPort(2135);
1988+
ui16 monPort = tp.GetPort(8765);
1989+
auto settings = TServerSettings(port);
1990+
settings.InitKikimrRunConfig()
1991+
.SetNodeCount(1)
1992+
.SetUseRealThreads(true)
1993+
.SetDomainName("Root")
1994+
.SetUseSectorMap(true)
1995+
.SetMonitoringPortOffset(monPort, true);
1996+
1997+
TServer server(settings);
1998+
server.EnableGRpc(grpcPort);
1999+
TClient client(settings);
2000+
2001+
TString brokenPlan = R"json({
2002+
"Plan" : {
2003+
"Node Type" : "Query",
2004+
"PlanNodeType" : "Query",
2005+
"Plans" : [
2006+
{
2007+
"Node Type" : "ResultSet",
2008+
"PlanNodeType" : "ResultSet",
2009+
"Plans" : [
2010+
{
2011+
"Node Type" : "Limit",
2012+
"Plans" : [
2013+
{
2014+
"Node Type" : "Merge",
2015+
"CTE Name": "TableFullScan_15",
2016+
"PlanNodeType" : "Connection"
2017+
}
2018+
]
2019+
}
2020+
]
2021+
}
2022+
]
2023+
}
2024+
})json";
2025+
2026+
TKeepAliveHttpClient httpClient("localhost", monPort);
2027+
TStringStream responseStream;
2028+
TKeepAliveHttpClient::THeaders headers;
2029+
headers["Content-Type"] = "application/json";
2030+
headers["Authorization"] = "test_ydb_token";
2031+
const TKeepAliveHttpClient::THttpCode statusCode = httpClient.DoPost("/viewer/plan2svg", brokenPlan, &responseStream, headers);
2032+
const TString response = responseStream.ReadAll();
2033+
UNIT_ASSERT_EQUAL_C(statusCode, HTTP_BAD_REQUEST, statusCode << ": " << response);
2034+
UNIT_ASSERT_C(response.StartsWith("Conversion error"), response);
2035+
}
2036+
19382037
}

ydb/core/viewer/ya.make

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ PEERDIR(
579579
ydb/public/api/protos
580580
ydb/public/lib/deprecated/kicli
581581
ydb/public/lib/json_value
582+
ydb/public/lib/ydb_cli/common
582583
ydb/public/api/grpc
583584
ydb/public/sdk/cpp/client/ydb_types
584585
contrib/libs/yaml-cpp

0 commit comments

Comments
 (0)