NCBI C++ ToolKit
psg_client_cgi.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: psg_client_cgi.cpp 101384 2023-12-07 15:43:23Z sadyrovr $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Rafael Sadyrov
27  *
28  */
29 
30 #include <ncbi_pch.hpp>
31 
32 #include <cgi/cgiapp.hpp>
33 #include <cgi/cgictx.hpp>
34 #include <cgi/cgi_exception.hpp>
35 #include <html/html.hpp>
36 
37 #include "processing.hpp"
38 #include "performance.hpp"
39 
41 
43 {
45 
47 
48  bool Has(const string& key) const { return entries.find(key) != entries.end(); }
49 
50  template <typename TF>
51  invoke_result_t<TF, const string&> Get(const string& key, TF f, invoke_result_t<TF, const string&> def_value) const;
52 
53  template <typename T>
54  T GetNumeric(const string& key) const { return Get(key, [](const auto& v) { return NStr::StringToNumeric<T>(v); }, T()); }
55  const string& GetString(const string& key) const { return Get(key, [](const auto& v) -> const auto& { return v; }, kEmptyStr); }
56  auto GetStringList(const string& key) const;
57 };
58 
59 template <typename TF>
60 invoke_result_t<TF, const string&> SPsgCgiEntries::Get(const string& key, TF f, invoke_result_t<TF, const string&> def_value) const
61 {
62  auto found = entries.find(key);
63  return found == entries.end() ? def_value : f(found->second.GetValue());
64 }
65 
66 auto SPsgCgiEntries::GetStringList(const string& key) const
67 {
68  auto range = entries.equal_range(key);
69  vector<string> rv;
70  transform(range.first, range.second, back_inserter(rv), [](const auto& p) { return p.second.GetValue(); });
71  return rv;
72 }
73 
74 namespace NParamsBuilder
75 {
76 
77 template <class TParams>
78 struct SBase : TParams
79 {
80  template <class... TInitArgs>
81  SBase(const SPsgCgiEntries& entries, TInitArgs&&... init_args) :
82  TParams{
83  {},
85  SPSG_UserArgs(),
86  std::forward<TInitArgs>(init_args)...
87  }
88  {
89  auto l = [](const auto& v) { EDiagSev s; return CNcbiDiag::StrToSeverityLevel(v.c_str(), s) ? s : eDiag_Warning; };
90  TParams::min_severity = entries.Get("min-severity", l, eDiag_Warning);
91  TParams::verbose = entries.Has("verbose");
92  }
93 };
94 
95 struct SOneRequest : SBase<SOneRequestParams>
96 {
98  SBase{
99  entries,
101  false,
102  entries.Has("blob-only") || entries.Has("annot-only"),
103  true,
104  GetDataOnlyOutputFormat(entries)
105  }
106  {
107  }
108 
110  {
111  const auto& format = entries.GetString("output-fmt");
112 
113  if (format == "asn") return eSerial_AsnText;
114  if (format == "asnb") return eSerial_AsnBinary;
115  if (format == "xml") return eSerial_Xml;
116  if (format == "json") return eSerial_Json;
117 
118  return eSerial_None;
119  }
120 };
121 
122 template <class TParams>
123 struct SParallelProcessing : SBase<TParams>
124 {
125  template <class... TInitArgs>
126  SParallelProcessing(const SPsgCgiEntries& entries, TInitArgs&&... init_args) :
127  SBase<TParams>{
128  entries,
129  0,
130  1,
131  false,
132  false,
133  std::forward<TInitArgs>(init_args)...
134  }
135  {
136  }
137 };
138 
139 struct SBatchResolve : SParallelProcessing<SBatchResolveParams>
140 {
143  entries,
145  }
146  {
147  }
148 };
149 
150 using TIpgBatchResolve = SParallelProcessing<TIpgBatchResolveParams>;
151 
152 }
153 
154 template <>
156 {
158 
160 
161  TSpecified GetSpecified() const;
162  auto GetBioIdType() const { return input.Get("type", SRequestBuilder::GetBioIdType, CPSG_BioId::TType()); }
163  CPSG_BioId GetBioId() const { return { input.GetString("id"), GetBioIdType() }; }
164  CPSG_BioIds GetBioIds() const;
165  CPSG_BlobId GetBlobId() const;
166  CPSG_ChunkId GetChunkId() const;
167  vector<string> GetNamedAnnots() const { return input.GetStringList("na"); }
168  auto GetAccSubstitution() const { return SRequestBuilder::GetAccSubstitution(input.GetString("acc-substitution")); }
171  void ForEachTSE(TExclude exclude) const;
172  auto GetProtein() const { return input.GetString("protein"); }
173  auto GetIpg() const { return input.Get("ipg", [](const auto& v) { return NStr::StringToInt8(v); }, 0); }
174  auto GetNucleotide() const { return input.Get("nucleotide", [](const auto& v) { return CPSG_Request_IpgResolve::TNucleotide(v); }, null); }
175  auto GetSNPScaleLimit() const { return objects::CSeq_id::GetSNPScaleLimit_Value(input.GetString("snp-scale-limit")); }
176  auto GetAbsPathRef() const { NCBI_THROW(CPSG_Exception, eParameterMissing, "request cannot be empty"); return string(); } // Imitating unknown request
177  void SetRequestFlags(shared_ptr<CPSG_Request> request) const;
178  SPSG_UserArgs GetUserArgs() const { return NStr::URLDecode(input.GetString("user-args")); }
179 };
180 
182 {
183  return [&](const string& name) {
184  return input.Has(name);
185  };
186 }
187 
189 {
190  CPSG_BioIds rv;
191 
192  auto ids = input.GetStringList("id");
193 
194  for (const auto& id : ids) {
195  if (rv.empty()) {
196  rv.emplace_back(id, GetBioIdType());
197  } else {
198  rv.emplace_back(id);
199  }
200  }
201 
202  return rv;
203 }
204 
206 {
207  const auto& id = input.GetString("id");
208  return input.Get("last-modified", [&](const auto& v) { return CPSG_BlobId(id, NStr::StringToInt8(v)); }, id);
209 }
210 
212 {
213  return { input.Get("id2-chunk", [](const auto& v) { return NStr::StringToInt(v); }, 0), input.GetString("id2-info") };
214 }
215 
217 {
218  auto blob_ids = input.GetStringList("exclude-blob");
219 
220  for (const auto& blob_id : blob_ids) {
221  exclude(blob_id);
222  }
223 }
224 
225 void SRequestBuilder::SReader<SPsgCgiEntries>::SetRequestFlags(shared_ptr<CPSG_Request> request) const
226 {
227  if (input.Has("include-hup")) {
228  request->SetFlags(CPSG_Request::fIncludeHUP);
229  }
230 }
231 
232 struct SResponse
233 {
234  SResponse(CCgiResponse& response) :
235  m_Response(response),
236  m_Out(cout),
237  m_Err(cerr)
238  {}
239 
240  void DataOnly(const SOneRequestParams::SDataOnly& data_only)
241  {
242  if (data_only.enabled) {
243  m_Format = data_only.output_format;
244  }
245  }
246 
247  void operator()(int status)
248  {
249  m_Out.Reset();
250  m_Err.Reset();
251  return status != CRequestStatus::e200_Ok ? Error(status) : Success();
252  }
253 
254 private:
255  struct SSsR : stringstream, SIoRedirector
256  {
257  SSsR(ios& what) : SIoRedirector(what, *this) {}
258  };
259 
260  void Success();
261  void Error(int status);
262 
266  optional<ESerialDataFormat> m_Format;
267 };
268 
270 {
271  switch (m_Format.value_or(eSerial_Json))
272  {
273  case eSerial_None:
274  m_Response.SetContentType("application/octet-stream");
275  break;
276 
277  case eSerial_AsnBinary:
278  m_Response.SetContentType("x-ncbi-data/x-asn-binary");
279  break;
280 
281  case eSerial_AsnText:
282  m_Response.SetContentType("x-ncbi-data/x-asn-text");
283  break;
284 
285  case eSerial_Xml:
286  m_Response.SetContentType("text/xml");
287  break;
288 
289  case eSerial_Json:
290  m_Response.SetContentType("application/json");
291  break;
292  }
293 
294  m_Response.WriteHeader() << m_Out.rdbuf();
295 
296  // Output debug if there is any
297  cerr << m_Err.rdbuf();
298 }
299 
300 void SResponse::Error(int status)
301 {
302  // If it's data-only
303  if (m_Format) {
304  m_Response.SetContentType("application/problem+json");
305  CJson_Document doc;
306  auto obj = doc.SetObject();
307  obj["status"].SetValue().SetInt8(status);
308  obj["detail"].SetValue().SetString(m_Err.str());
309  m_Response.WriteHeader() << doc;
310  } else {
311  m_Response.WriteHeader() << m_Err.rdbuf();
312  }
313 }
314 
316 {
317  void Init() override;
318  int ProcessRequest(CCgiContext& ctx) override;
319  int Help(const string& request, bool json, CCgiResponse& response);
320 
321  static int GetStatus(int rv);
322  static void AddParamsTable(CHTML_body* body, const string& name, const CJson_ConstObject_pair& params);
323 
324  template <class TChild, class TParent, class... TArgs>
325  static TChild* NewChild(TParent* parent, TArgs&&... args)
326  {
327  _ASSERT(parent);
328  auto child = new TChild(std::forward<TArgs>(args)...);
329  parent->AppendChild(child);
330  return child;
331  }
332 
333 private:
335 };
336 
338 {
341 
342  m_ApiLock = CPSG_Queue::GetApiLock();
343 }
344 
346 {
347  const auto& request = ctx.GetRequest();
348  SPsgCgiEntries entries = request.GetEntries();
349  const auto type = entries.GetString("request");
350 
351  if (type.empty() || entries.Has("help")) {
352  return Help(type, entries.Has("json"), ctx.GetResponse());
353  }
354 
355  int rv;
356  SResponse response = ctx.GetResponse();
357 
358  if (auto is = request.GetRequestMethod() == CCgiRequest::eMethod_POST ? request.GetInputStream() : nullptr) {
359  if (type == "resolve") {
361  } else if (type == "ipg_resolve") {
363  } else {
364  rv = int(EPSG_Status::eError);
365  }
366  } else {
367  const auto builder = NParamsBuilder::SOneRequest(entries);
368 
369  response.DataOnly(builder.data_only);
371  }
372 
373  const auto status = GetStatus(rv);
374  SetHTTPStatus(status);
375  response(status);
376  return rv;
377 }
378 
379 int CPsgCgiApp::Help(const string& request, bool json, CCgiResponse& response)
380 {
381  CJson_Document help_doc;
382 
383  if (!help_doc.Read("pubseq_gateway.json")) {
385  response.WriteHeader() << "Missing 'pubseq_gateway.json'\n";
386  return 0;
387  }
388 
389  auto help_obj = help_doc.SetObject();
390  auto request_obj = help_obj["request"].SetObject();
391 
392  // Do not display unrelated requests
393  if (request_obj.has(request)) {
394  auto i = request_obj.begin();
395 
396  while (i != request_obj.end()) {
397  if (i->name != request) {
398  i = request_obj.erase(i);
399  } else {
400  ++i;
401  }
402  }
403  }
404 
405  if (json) {
406  response.SetContentType("application/json");
407  response.WriteHeader() << help_doc;
408  return 0;
409  }
410 
411  CRef<CHTML_html> html(new CHTML_html);
412  NewChild<CHTML_title>(html.GetPointer())->AppendPlainText("help");
413  auto body = NewChild<CHTML_body>(html.GetPointer());
414 
415  NewChild<CHTML_h2>(body)->AppendPlainText("pubseq_gateway.cgi");
416  NewChild<CHTMLPlainText>(body, "A user-friendly CGI gateway to PSG.");
417 
418  auto i = help_obj.find("Common Parameters");
419 
420  if (i != help_obj.end()) {
421  AddParamsTable(body, i->name, *i);
422  }
423 
424  for (const auto& r : request_obj) {
425  NewChild<CHTML_hr>(body);
426 
427  auto h2 = NewChild<CHTML_h2>(body);
428  NewChild<CHTML_i>(h2)->AppendPlainText("request");
429  h2->AppendPlainText(string("=") + r.name);
430 
431  auto r_obj = r.value.GetObject();
432  auto k = r_obj.find("method");
433 
434  if (k != r_obj.end()) {
435  for (const auto& m : k->value.GetObject()) {
436  auto m_obj = m.value.GetObject();
437  NewChild<CHTML_h3>(body, m.name + string(":"));
438  NewChild<CHTMLPlainText>(body, m_obj["description"].GetValue().GetString());
439 
440  auto j = m_obj.find("Method-specific Parameters");
441 
442  if (j != m_obj.end()) {
443  AddParamsTable(body, m.name + string("-specific Parameters"), *j);
444  }
445  }
446  } else {
447  NewChild<CHTMLPlainText>(body, r_obj["description"].GetValue().GetString());
448  }
449 
450  auto j = r_obj.find("Request-specific Parameters");
451 
452  if (j != r_obj.end()) {
453  AddParamsTable(body, j->name, *j);
454  }
455  }
456 
457  html->Print(response.WriteHeader());
458  response.Flush();
459  return 0;
460 }
461 
463 {
464  switch (rv)
465  {
469  default: return CRequestStatus::e400_BadRequest;
470  }
471 }
472 
473 void CPsgCgiApp::AddParamsTable(CHTML_body* body, const string& name, const CJson_ConstObject_pair& params)
474 {
475  NewChild<CHTML_h4>(body, name);
476  auto table = NewChild<CHTML_table>(body);
477 
478  auto r = 0;
479 
480  for (const auto& type : params.value.GetObject()) {
481  auto header = table->HeaderCell(r, 0);
482  header->AppendPlainText(type.name);
483  header->SetColSpan(2);
484  ++r;
485 
486  for (const auto& param : type.value.GetObject()) {
487  NewChild<CHTML_i>(table->DataCell(r, 0))->AppendPlainText(param.name);
488  table->DataCell(r, 1)->AppendPlainText(param.value.GetValue().GetString());
489  ++r;
490  }
491  }
492 }
493 
494 int main(int argc, const char* argv[])
495 {
496  return CPsgCgiApp().AppMain(argc, argv);
497 }
Exception classes used by the NCBI CGI framework.
void transform(Container &c, UnaryFunction *op)
Definition: chainer.hpp:86
CJson_ConstObject GetObject(void) const
Get JSON object contents of the node.
const CJson_ConstNode value
bool Read(std::istream &in)
Read JSON data from a stream.
CJson_Object SetObject(void)
Get JSON object contents of the node.
CJson_Value SetValue(void)
Get JSON value contents of the node.
CJson_Value & SetString(const TStringType &value)
CJson_Value & SetInt8(Int8 value)
Bio-id (such as accession)
Definition: psg_client.hpp:175
Blob unique ID.
Definition: psg_client.hpp:226
Chunk unique ID.
Definition: psg_client.hpp:265
shared_ptr< void > TApiLock
Get an API lock.
static TApiLock GetApiLock()
CNullable< string > TNucleotide
Definition: psg_client.hpp:600
static int OneRequest(const SOneRequestParams &params, shared_ptr< CPSG_Request > request)
Definition: processing.cpp:810
static int ParallelProcessing(const TParams &params, istream &is=cin)
Definition: processing.hpp:349
int Help(const string &request, bool json, CCgiResponse &response)
void Init() override
This method is called on the CGI application initialization – before starting to process a HTTP reque...
static int GetStatus(int rv)
static TChild * NewChild(TParent *parent, TArgs &&... args)
static void AddParamsTable(CHTML_body *body, const string &name, const CJson_ConstObject_pair &params)
int ProcessRequest(CCgiContext &ctx) override
This is the method you should override.
CPSG_Queue::TApiLock m_ApiLock
CRef –.
Definition: ncbiobj.hpp:618
CTimeout – Timeout interval.
Definition: ncbitime.hpp:1693
const_iterator_pair equal_range(const key_type &key) const
Definition: map.hpp:296
const_iterator find(const key_type &key) const
Definition: map.hpp:293
const_iterator end() const
Definition: map.hpp:292
#define T(s)
Definition: common.h:230
CS_CONTEXT * ctx
Definition: t0006.c:12
#define true
Definition: bool.h:35
#define false
Definition: bool.h:36
static void Init(void)
Definition: cursor6.c:76
int AppMain(int argc, const char *const *argv, const char *const *envp=0, EAppDiagStream diag=eDS_Default, const char *conf=NcbiEmptyCStr, const string &name=NcbiEmptyString)
Main function (entry point) for the NCBI application.
Definition: ncbiapp.cpp:819
string
Definition: cgiapp.hpp:687
void Flush(void) const
Flush output stream.
Definition: ncbicgir.cpp:529
void SetContentType(const string &type)
Set content type (text/html by default if not provided)
Definition: ncbicgir.hpp:337
CNcbiOstream & WriteHeader(void) const
Write HTTP response header to the output stream.
Definition: ncbicgir.hpp:396
@ fDoNotParseContent
do not automatically parse the request's content body (from "istr")
Definition: ncbicgi.hpp:712
@ fDisableParsingAsIndex
Disable parsing input as 'indexed' query (RFC3875) even if no '=' is present.
Definition: ncbicgi.hpp:740
EDiagSev
Severity level for the posted diagnostics.
Definition: ncbidiag.hpp:650
static bool StrToSeverityLevel(const char *str_sev, EDiagSev &sev)
Get severity from string.
Definition: ncbidiag.cpp:7904
@ eDiag_Warning
Warning message.
Definition: ncbidiag.hpp:652
void Error(CExceptionArgs_Base &args)
Definition: ncbiexpt.hpp:1197
#define NCBI_THROW(exception_class, err_code, message)
Generic macro to throw an exception, given the exception class, error code and message string.
Definition: ncbiexpt.hpp:704
ESerialDataFormat
Data file format.
Definition: serialdef.hpp:71
@ eSerial_AsnText
ASN.1 text.
Definition: serialdef.hpp:73
@ eSerial_Xml
XML.
Definition: serialdef.hpp:75
@ eSerial_Json
JSON.
Definition: serialdef.hpp:76
@ eSerial_None
Definition: serialdef.hpp:72
@ eSerial_AsnBinary
ASN.1 binary.
Definition: serialdef.hpp:74
TObjectType * GetPointer(void) THROWS_NONE
Get pointer,.
Definition: ncbiobj.hpp:998
#define kEmptyStr
Definition: ncbistr.hpp:123
static int StringToInt(const CTempString str, TStringToNumFlags flags=0, int base=10)
Convert string to int.
Definition: ncbistr.cpp:630
static Int8 StringToInt8(const CTempString str, TStringToNumFlags flags=0, int base=10)
Convert string to Int8.
Definition: ncbistr.cpp:793
static string URLDecode(const CTempString str, EUrlDecode flag=eUrlDec_All)
URL-decode string.
Definition: ncbistr.cpp:6214
@ eDefault
Default timeout (to be interpreted by the client code)
Definition: ncbitime.hpp:1698
E_Choice
Choice variants.
Definition: Seq_id_.hpp:93
unsigned int
A callback function used to compare two keys in a database.
Definition: types.hpp:1210
HTML classes.
<!DOCTYPE HTML >< html > n< header > n< title > PubSeq Gateway Help Page</title > n< style > n table
static int input()
int i
SParallelProcessing< TIpgBatchResolveParams > TIpgBatchResolve
range(_Ty, _Ty) -> range< _Ty >
const struct ncbi::grid::netcache::search::fields::KEY key
static Format format
Definition: njn_ioutil.cpp:53
double r(size_t dimension_, const Int4 *score_, const double *prob_, double theta_)
double f(double x_, const double &y_)
Definition: njn_root.hpp:188
true_type verbose
Definition: processing.cpp:890
@ eSuccess
Successfully retrieved.
@ eForbidden
User is not authorized for the retrieval.
@ eError
An error was encountered while trying to send request or to read and to process the reply.
@ eNotFound
Not found.
vector< CPSG_BioId > CPSG_BioIds
Definition: psg_client.hpp:206
EPSG_BioIdResolution
Whether to try to resolve provided seq-ids before use.
Definition: psg_client.hpp:299
@ Resolve
Try to resolve provided seq-ids.
@ NoResolve
Use provided seq-ids as is.
int main(int argc, const char *argv[])
USING_NCBI_SCOPE
SBase(const SPsgCgiEntries &entries, TInitArgs &&... init_args)
SBatchResolve(const SPsgCgiEntries &entries)
SOneRequest(const SPsgCgiEntries &entries)
static ESerialDataFormat GetDataOnlyOutputFormat(const SPsgCgiEntries &entries)
SParallelProcessing(const SPsgCgiEntries &entries, TInitArgs &&... init_args)
const ESerialDataFormat output_format
Definition: processing.hpp:194
Arbitrary request URL arguments.
Definition: psg_client.hpp:79
T GetNumeric(const string &key) const
bool Has(const string &key) const
SPsgCgiEntries(const TCgiEntries &e)
auto GetStringList(const string &key) const
invoke_result_t< TF, const string & > Get(const string &key, TF f, invoke_result_t< TF, const string & > def_value) const
const TCgiEntries & entries
const string & GetString(const string &key) const
EPSG_BioIdResolution GetBioIdResolution() const
static CPSG_BioId::TType GetBioIdType(const string &type)
function< void(string)> TExclude
Definition: processing.hpp:404
static shared_ptr< TRequest > Build(const TInput &input, TArgs &&... args)
Definition: processing.hpp:503
static EPSG_AccSubstitution GetAccSubstitution(const string &acc_substitution)
Definition: processing.hpp:631
static SResolveParams GetResolveParams(const TInput &input)
Definition: processing.hpp:495
function< bool(const string &)> TSpecified
Definition: processing.hpp:403
void DataOnly(const SOneRequestParams::SDataOnly &data_only)
void Error(int status)
CCgiResponse & m_Response
void operator()(int status)
optional< ESerialDataFormat > m_Format
SResponse(CCgiResponse &response)
Definition: type.c:6
const char * name
Definition: type.c:8
#define _ASSERT
static wxAcceleratorEntry entries[3]
Modified on Sun Apr 14 05:26:16 2024 by modify_doxy.py rev. 669887