Browse Source

add traces and database redirects to local rpc calls (#9745)

Alexey Efimov 5 months ago
parent
commit
e3f4b87679

+ 59 - 75
ydb/core/viewer/json_local_rpc.h

@@ -1,11 +1,8 @@
 #pragma once
 #include "json_pipe_req.h"
-#include "viewer.h"
-#include <library/cpp/json/json_writer.h>
 #include <ydb/core/grpc_services/local_rpc/local_rpc.h>
 
-namespace NKikimr {
-namespace NViewer {
+namespace NKikimr::NViewer {
 
 struct TEvLocalRpcPrivate {
     enum EEv {
@@ -26,27 +23,15 @@ struct TEvLocalRpcPrivate {
     };
 };
 
-using namespace NActors;
-using NSchemeShard::TEvSchemeShard;
-
 template <class TProtoRequest, class TProtoResponse, class TProtoResult, class TProtoService, class TRpcEv>
-class TJsonLocalRpc : public TActorBootstrapped<TJsonLocalRpc<TProtoRequest, TProtoResponse, TProtoResult, TProtoService, TRpcEv>> {
+class TJsonLocalRpc : public TViewerPipeClient {
     using TThis = TJsonLocalRpc<TProtoRequest, TProtoResponse, TProtoResult, TProtoService, TRpcEv>;
-    using TBase = TActorBootstrapped<TThis>;
-
-    using TBase::Send;
-    using TBase::PassAway;
-    using TBase::Become;
+    using TBase = TViewerPipeClient;
 
 protected:
-    IViewer* Viewer;
-    NMon::TEvHttpInfo::TPtr Event;
-    TProtoRequest Request;
+    using TBase::ReplyAndPassAway;
+    std::vector<HTTP_METHOD> AllowedMethods = {};
     TAutoPtr<TEvLocalRpcPrivate::TEvGrpcRequestResult<TProtoResult>> Result;
-
-    TJsonSettings JsonSettings;
-    ui32 Timeout = 0;
-    TString Database;
     NThreading::TFuture<TProtoResponse> RpcFuture;
 
 public:
@@ -54,13 +39,11 @@ public:
         return NKikimrServices::TActivity::VIEWER_HANDLER;
     }
 
-    TJsonLocalRpc(IViewer* viewer, NMon::TEvHttpInfo::TPtr &ev)
-        : Viewer(viewer)
-        , Event(ev)
+    TJsonLocalRpc(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
+        : TBase(viewer, ev, TProtoRequest::descriptor()->name())
     {}
 
-    TProtoRequest Params2Proto(const TCgiParameters& params) {
-        TProtoRequest request;
+    void Params2Proto(const TCgiParameters& params, TProtoRequest& request) {
         using google::protobuf::Descriptor;
         using google::protobuf::Reflection;
         using google::protobuf::FieldDescriptor;
@@ -110,44 +93,52 @@ public:
                 }
             }
         }
-        return request;
     }
 
-    TProtoRequest Params2Proto() {
-        TProtoRequest request;
-        NProtobufJson::TJson2ProtoConfig json2ProtoConfig;
-        auto postData = Event->Get()->Request.GetPostContent();
-        if (!postData.empty()) {
-            try {
-                NProtobufJson::Json2Proto(postData, request, json2ProtoConfig);
-            }
-            catch (const yexception& e) {
-                ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", e.what()));
+    bool ValidateProto(TProtoRequest& request) {
+        using google::protobuf::Descriptor;
+        using google::protobuf::Reflection;
+        using google::protobuf::FieldDescriptor;
+        const Descriptor& descriptor = *TProtoRequest::GetDescriptor();
+        const Reflection& reflection = *TProtoRequest::GetReflection();
+        for (int idx = 0; idx < descriptor.field_count(); ++idx) {
+            const FieldDescriptor* field = descriptor.field(idx);
+            const auto& options(field->options());
+            if (options.HasExtension(Ydb::required)) {
+                if (options.GetExtension(Ydb::required)) {
+                    if (!reflection.HasField(request, field)) {
+                        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", TStringBuilder() << "field '" << field->name() << "' is required"));
+                        return false;
+                    }
+                }
             }
-        } else {
-            const auto& params(Event->Get()->Request.GetParams());
-            return Params2Proto(params);
         }
-        return request;
+        return true;
     }
 
-    bool PostToRequest() {
+    bool Params2Proto(TProtoRequest& request) {
         auto postData = Event->Get()->Request.GetPostContent();
         if (!postData.empty()) {
             try {
-                NProtobufJson::Json2Proto(postData, Request, {});
-                return true;
+                NProtobufJson::Json2Proto(postData, request);
             }
             catch (const yexception& e) {
-                ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", e.what()));
+                ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", e.what()));
                 return false;
             }
+        } else {
+            const auto& params(Event->Get()->Request.GetParams());
+            Params2Proto(params, request);
+        }
+        if (!ValidateProto(request)) {
+            return false;
         }
         return true;
     }
 
-    void SendGrpcRequest() {
-        RpcFuture = NRpcService::DoLocalRpc<TRpcEv>(std::move(Request), Database, Event->Get()->UserToken, TlsActivationContext->ActorSystem());
+    void SendGrpcRequest(TProtoRequest&& request) {
+        // TODO(xenoxeno): pass trace id
+        RpcFuture = NRpcService::DoLocalRpc<TRpcEv>(std::move(request), Database, Event->Get()->UserToken, TlsActivationContext->ActorSystem());
         RpcFuture.Subscribe([actorId = TBase::SelfId(), actorSystem = TlsActivationContext->ActorSystem()]
                             (const NThreading::TFuture<TProtoResponse>& future) {
             auto& response = future.GetValueSync();
@@ -173,14 +164,21 @@ public:
     }
 
     virtual void Bootstrap() {
-        const auto& params(Event->Get()->Request.GetParams());
-        JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool>(params.Get("enums"), true);
-        JsonSettings.UI64AsString = !FromStringWithDefault<bool>(params.Get("ui64"), true);
-        Timeout = FromStringWithDefault<ui32>(params.Get("timeout"), 10000);
-
-        SendGrpcRequest();
-
-        Become(&TThis::StateRequested, TDuration::MilliSeconds(Timeout), new TEvents::TEvWakeup());
+        if (!AllowedMethods.empty() && std::find(AllowedMethods.begin(), AllowedMethods.end(), Event->Get()->Request.GetMethod()) == AllowedMethods.end()) {
+            return ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Method is not allowed"));
+        }
+        if (Database.empty()) {
+            return ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "field 'database' is required"));
+        }
+        if (TBase::NeedToRedirect()) {
+            return;
+        }
+        TProtoRequest request;
+        if (!Params2Proto(request)) {
+            return;
+        }
+        SendGrpcRequest(std::move(request));
+        Become(&TThis::StateRequested, Timeout, new TEvents::TEvWakeup());
     }
 
     void Handle(typename TEvLocalRpcPrivate::TEvGrpcRequestResult<TProtoResult>::TPtr& ev) {
@@ -197,38 +195,24 @@ public:
 
     void ReplyAndPassAway() {
         if (Result && Result->Status) {
-            if (!Result->Status->IsSuccess()) {
+            if (Result->Status->IsSuccess()) {
+                return ReplyAndPassAway(GetHTTPOKJSON(Result->Message));
+            } else {
                 NJson::TJsonValue json;
                 TString message;
                 MakeJsonErrorReply(json, message, Result->Status.value());
                 TStringStream stream;
                 NJson::WriteJson(&stream, &json);
                 if (Result->Status->GetStatus() == NYdb::EStatus::UNAUTHORIZED) {
-                    return ReplyAndPassAway(Viewer->GetHTTPFORBIDDEN(Event->Get(), "application/json", stream.Str()));
+                    return ReplyAndPassAway(GetHTTPFORBIDDEN("application/json", stream.Str()), message);
                 } else {
-                    return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "application/json", stream.Str()));
+                    return ReplyAndPassAway(GetHTTPBADREQUEST("application/json", stream.Str()), message);
                 }
-            } else {
-                TStringStream json;
-                TProtoToJson::ProtoToJson(json, Result->Message, JsonSettings);
-                return ReplyAndPassAway(Viewer->GetHTTPOKJSON(Event->Get(), json.Str()));
             }
         } else {
-            return ReplyAndPassAway(Viewer->GetHTTPINTERNALERROR(Event->Get()));
+            return ReplyAndPassAway(GetHTTPINTERNALERROR("text/plain", "no Result or Status"), "internal error");
         }
     }
-
-
-    void HandleTimeout() {
-        ReplyAndPassAway(Viewer->GetHTTPGATEWAYTIMEOUT(Event->Get()));
-    }
-
-    void ReplyAndPassAway(TString data) {
-        Send(Event->Sender, new NMon::TEvHttpInfoRes(data, 0, NMon::IEvHttpInfoRes::EContentType::Custom));
-        PassAway();
-    }
 };
 
-
-}
-}
+}  // namespace NKikimr::NViewer

+ 40 - 25
ydb/core/viewer/json_pipe_req.cpp

@@ -599,6 +599,9 @@ void TViewerPipeClient::InitConfig(const TCgiParameters& params) {
         Database = params.Get("tenant");
     }
     Direct = FromStringWithDefault<bool>(params.Get("direct"), Direct);
+    JsonSettings.EnumAsNumbers = !FromStringWithDefault<bool>(params.Get("enums"), true);
+    JsonSettings.UI64AsString = !FromStringWithDefault<bool>(params.Get("ui64"), true);
+    Timeout = TDuration::MilliSeconds(FromStringWithDefault<ui32>(params.Get("timeout"), Timeout.MilliSeconds()));
 }
 
 void TViewerPipeClient::InitConfig(const TRequestSettings& settings) {
@@ -655,6 +658,12 @@ TString TViewerPipeClient::GetHTTPOKJSON(const NJson::TJsonValue& response, TIns
     return GetHTTPOKJSON(NJson::WriteJson(response, false), lastModified);
 }
 
+TString TViewerPipeClient::GetHTTPOKJSON(const google::protobuf::Message& response, TInstant lastModified) {
+    TStringStream json;
+    TProtoToJson::ProtoToJson(json, response, JsonSettings);
+    return GetHTTPOKJSON(json.Str(), lastModified);
+}
+
 TString TViewerPipeClient::GetHTTPGATEWAYTIMEOUT(TString contentType, TString response) {
     return Viewer->GetHTTPGATEWAYTIMEOUT(GetRequest(), std::move(contentType), std::move(response));
 }
@@ -696,41 +705,47 @@ void TViewerPipeClient::Handle(TEvTabletPipe::TEvClientConnected::TPtr& ev) {
 }
 
 void TViewerPipeClient::HandleResolveResource(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
-    ResourceNavigateResponse->Set(std::move(ev));
-    if (ResourceNavigateResponse->IsOk()) {
-        TSchemeCacheNavigate::TEntry& entry(ResourceNavigateResponse->Get()->Request->ResultSet.front());
-        SharedDatabase = CanonizePath(entry.Path);
-        if (SharedDatabase == AppData()->TenantName) {
-            Direct = true;
-            return Bootstrap(); // retry bootstrap without redirect this time
+    if (ResourceNavigateResponse) {
+        ResourceNavigateResponse->Set(std::move(ev));
+        if (ResourceNavigateResponse->IsOk()) {
+            TSchemeCacheNavigate::TEntry& entry(ResourceNavigateResponse->Get()->Request->ResultSet.front());
+            SharedDatabase = CanonizePath(entry.Path);
+            if (SharedDatabase == AppData()->TenantName) {
+                Direct = true;
+                return Bootstrap(); // retry bootstrap without redirect this time
+            }
+            DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(SharedDatabase);
+        } else {
+            ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - shared database not found"));
         }
-        DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(SharedDatabase);
-    } else {
-        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - shared database not found"));
     }
 }
 
 void TViewerPipeClient::HandleResolveDatabase(TEvTxProxySchemeCache::TEvNavigateKeySetResult::TPtr& ev) {
-    DatabaseNavigateResponse->Set(std::move(ev));
-    if (DatabaseNavigateResponse->IsOk()) {
-        TSchemeCacheNavigate::TEntry& entry(DatabaseNavigateResponse->Get()->Request->ResultSet.front());
-        if (entry.DomainInfo && entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
-            ResourceNavigateResponse = MakeRequestSchemeCacheNavigate(TPathId(entry.DomainInfo->ResourcesDomainKey));
-            Become(&TViewerPipeClient::StateResolveResource);
-            return;
+    if (DatabaseNavigateResponse) {
+        DatabaseNavigateResponse->Set(std::move(ev));
+        if (DatabaseNavigateResponse->IsOk()) {
+            TSchemeCacheNavigate::TEntry& entry(DatabaseNavigateResponse->Get()->Request->ResultSet.front());
+            if (entry.DomainInfo && entry.DomainInfo->ResourcesDomainKey && entry.DomainInfo->DomainKey != entry.DomainInfo->ResourcesDomainKey) {
+                ResourceNavigateResponse = MakeRequestSchemeCacheNavigate(TPathId(entry.DomainInfo->ResourcesDomainKey));
+                Become(&TViewerPipeClient::StateResolveResource);
+                return;
+            }
+            DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(CanonizePath(entry.Path));
+        } else {
+            ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - not found"));
         }
-        DatabaseBoardInfoResponse = MakeRequestStateStorageEndpointsLookup(CanonizePath(entry.Path));
-    } else {
-        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - not found"));
     }
 }
 
 void TViewerPipeClient::HandleResolve(TEvStateStorage::TEvBoardInfo::TPtr& ev) {
-    DatabaseBoardInfoResponse->Set(std::move(ev));
-    if (DatabaseBoardInfoResponse->IsOk()) {
-        ReplyAndPassAway(MakeForward(GetNodesFromBoardReply(DatabaseBoardInfoResponse->GetRef())));
-    } else {
-        ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - no nodes found"));
+    if (DatabaseBoardInfoResponse) {
+        DatabaseBoardInfoResponse->Set(std::move(ev));
+        if (DatabaseBoardInfoResponse->IsOk()) {
+            ReplyAndPassAway(MakeForward(GetNodesFromBoardReply(DatabaseBoardInfoResponse->GetRef())));
+        } else {
+            ReplyAndPassAway(GetHTTPBADREQUEST("text/plain", "Failed to resolve database - no nodes found"));
+        }
     }
 }
 

+ 3 - 0
ydb/core/viewer/json_pipe_req.h

@@ -45,6 +45,8 @@ protected:
     NWilson::TSpan Span;
     IViewer* Viewer = nullptr;
     NMon::TEvHttpInfo::TPtr Event;
+    TJsonSettings JsonSettings;
+    TDuration Timeout = TDuration::Seconds(10);
 
     struct TPipeInfo {
         TActorId PipeClient;
@@ -289,6 +291,7 @@ protected:
     TString GetHTTPOK(TString contentType = {}, TString response = {}, TInstant lastModified = {});
     TString GetHTTPOKJSON(TString response = {}, TInstant lastModified = {});
     TString GetHTTPOKJSON(const NJson::TJsonValue& response, TInstant lastModified = {});
+    TString GetHTTPOKJSON(const google::protobuf::Message& response, TInstant lastModified = {});
     TString GetHTTPGATEWAYTIMEOUT(TString contentType = {}, TString response = {});
     TString GetHTTPBADREQUEST(TString contentType = {}, TString response = {});
     TString GetHTTPINTERNALERROR(TString contentType = {}, TString response = {});

+ 2 - 29
ydb/core/viewer/operation_cancel.h

@@ -18,35 +18,8 @@ public:
 
     TOperationCancel(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TBase(viewer, ev)
-    {}
-
-    void Bootstrap() override {
-        if (Event->Get()->Request.GetMethod() != HTTP_METHOD_POST) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "Only POST method is allowed"));
-        }
-
-        if (!PostToRequest()) {
-            return;
-        }
-
-        const auto& params(Event->Get()->Request.GetParams());
-        if (params.Has("database")) {
-            Database = params.Get("database");
-        }
-
-        if (Database.empty()) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'database' is required"));
-        }
-
-        if (params.Has("id")) {
-            Request.set_id(params.Get("id"));
-        }
-
-        if (Request.id().empty()) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'id' is required"));
-        }
-
-        TBase::Bootstrap();
+    {
+        AllowedMethods = {HTTP_METHOD_POST};
     }
 
     static YAML::Node GetSwagger() {

+ 2 - 29
ydb/core/viewer/operation_forget.h

@@ -18,35 +18,8 @@ public:
 
     TOperationForget(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TBase(viewer, ev)
-    {}
-
-    void Bootstrap() override {
-        if (Event->Get()->Request.GetMethod() != HTTP_METHOD_POST) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "Only POST method is allowed"));
-        }
-
-        if (!PostToRequest()) {
-            return;
-        }
-
-        const auto& params(Event->Get()->Request.GetParams());
-        if (params.Has("database")) {
-            Database = params.Get("database");
-        }
-
-        if (Database.empty()) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'database' is required"));
-        }
-
-        if (params.Has("id")) {
-            Request.set_id(params.Get("id"));
-        }
-
-        if (Request.id().empty()) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'id' is required"));
-        }
-
-        TBase::Bootstrap();
+    {
+        AllowedMethods = {HTTP_METHOD_POST};
     }
 
     static YAML::Node GetSwagger() {

+ 2 - 20
ydb/core/viewer/operation_get.h

@@ -18,26 +18,8 @@ public:
 
     TOperationGet(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TBase(viewer, ev)
-    {}
-
-    void Bootstrap() override {
-        if (Event->Get()->Request.GetMethod() != HTTP_METHOD_GET) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "Only GET method is allowed"));
-        }
-        const auto& params(Event->Get()->Request.GetParams());
-        if (params.Has("database")) {
-            Database = params.Get("database");
-        } else {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'database' is required"));
-        }
-
-        if (params.Has("id")) {
-            Request.set_id(params.Get("id"));
-        } else {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'id' is required"));
-        }
-
-        TBase::Bootstrap();
+    {
+        AllowedMethods = {HTTP_METHOD_GET};
     }
 
     static YAML::Node GetSwagger() {

+ 9 - 29
ydb/core/viewer/operation_list.h

@@ -18,34 +18,8 @@ public:
 
     TOperationList(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TBase(viewer, ev)
-    {}
-
-    void Bootstrap() override {
-        if (Event->Get()->Request.GetMethod() != HTTP_METHOD_GET) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "Only GET method is allowed"));
-        }
-        const auto& params(Event->Get()->Request.GetParams());
-        if (params.Has("database")) {
-            Database = params.Get("database");
-        } else {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'database' is required"));
-        }
-
-        if (params.Has("kind")) {
-            Request.set_kind(params.Get("kind"));
-        } else {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'kind' is required"));
-        }
-
-        if (params.Has("page_size")) {
-            Request.set_page_size(FromStringWithDefault<ui32>(params.Get("page_size"), 0));
-        }
-
-        if (params.Has("page_token")) {
-            Request.set_page_token(params.Get("page_token"));
-        }
-
-        TBase::Bootstrap();
+    {
+        AllowedMethods = {HTTP_METHOD_GET};
     }
 
     static YAML::Node GetSwagger() {
@@ -63,7 +37,13 @@ public:
                     type: string
                   - name: kind
                     in: query
-                    description: kind
+                    description: >
+                        kind:
+                          * `ss/backgrounds`
+                          * `export`
+                          * `import`
+                          * `buildindex`
+                          * `scriptexec`
                     required: true
                     type: string
                   - name: page_size

+ 0 - 23
ydb/core/viewer/scheme_directory.h

@@ -38,29 +38,6 @@ public:
     TSchemeDirectoryRequest(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TBase(viewer, ev)
     {}
-
-    void Bootstrap() override {
-        if (!TBase::PostToRequest()) {
-            return;
-        }
-
-        const auto& params(TBase::Event->Get()->Request.GetParams());
-        if (params.Has("database")) {
-            TBase::Database = params.Get("database");
-        }
-        if (TBase::Database.empty()) {
-            return TBase::ReplyAndPassAway(TBase::Viewer->GetHTTPBADREQUEST(TBase::Event->Get(), "text/plain", "field 'database' is required"));
-        }
-
-        if (params.Has("path")) {
-            TBase::Request.set_path(params.Get("path"));
-        }
-        if (TBase::Request.path().empty()) {
-            return TBase::ReplyAndPassAway(TBase::Viewer->GetHTTPBADREQUEST(TBase::Event->Get(), "text/plain", "field 'path' is required"));
-        }
-
-        TBase::Bootstrap();
-    }
 };
 
 class TJsonSchemeDirectoryHandler : public TJsonHandler<TSchemeDirectory> {

+ 1 - 1
ydb/core/viewer/viewer.cpp

@@ -598,7 +598,7 @@ void TViewer::FillCORS(TStringBuilder& stream, const TRequestState& request) {
                << "Access-Control-Allow-Headers: Content-Type,Authorization,Origin,Accept,X-Trace-Verbosity,X-Want-Trace,traceparent\r\n"
                << "Access-Control-Expose-Headers: traceresponse,X-Worker-Name\r\n"
                << "Access-Control-Allow-Methods: OPTIONS,GET,POST,PUT,DELETE\r\n"
-               << "Allow: OPTIONS, GET, POST, DELETE\r\n";
+               << "Allow: OPTIONS,GET,POST,DELETE\r\n";
     }
 }
 

+ 4 - 26
ydb/core/viewer/viewer_describe_consumer.h

@@ -18,37 +18,15 @@ public:
 
     TJsonDescribeConsumer(IViewer* viewer, NMon::TEvHttpInfo::TPtr& ev)
         : TBase(viewer, ev)
-    {}
+    {
+        AllowedMethods = {HTTP_METHOD_GET};
+    }
 
     void Bootstrap() override {
-        if (Event->Get()->Request.GetMethod() != HTTP_METHOD_GET) {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "Only GET method is allowed"));
-        }
         const auto& params(Event->Get()->Request.GetParams());
-        if (params.Has("database")) {
-            Database = params.Get("database");
-        } else if (params.Has("database_path")) {
+        if (params.Has("database_path")) {
             Database = params.Get("database_path");
-        } else {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'database' is required"));
         }
-
-        if (params.Has("consumer")) {
-            Request.set_consumer(params.Get("consumer"));
-        } else {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'consumer' is required"));
-        }
-
-        if (params.Has("path")) {
-            Request.set_path(params.Get("path"));
-        } else {
-            return ReplyAndPassAway(Viewer->GetHTTPBADREQUEST(Event->Get(), "text/plain", "field 'path' is required"));
-        }
-
-        if (params.Has("include_stats")) {
-            Request.set_include_stats(FromStringWithDefault<bool>(params.Get("include_stats"), false));
-        }
-
         TBase::Bootstrap();
     }
 

Some files were not shown because too many files changed in this diff