HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NET_WebAPIHandler.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: NET_WebAPIHandler.h (NET Library, C++)
7  *
8  * COMMENTS: Object that defines a command. What arguments it has and the
9  *default values it would have.
10  */
11 
12 #ifndef __NET_WebAPIHandler_H__
13 #define __NET_WebAPIHandler_H__
14 
15 #include "NET_API.h"
16 
17 #include "NET_ConvertToType.h"
18 #include "NET_HTTPRequest.h"
19 #include "NET_WebAPI.h"
20 #include "NET_HTTPDefines.h"
22 
23 #include <UT/UT_ArrayStringMap.h>
24 #include <UT/UT_Assert.h>
25 #include <UT/UT_JSONValue.h>
26 #include <UT/UT_JSONValueArray.h>
27 #include <UT/UT_JSONValueMap.h>
28 #include <UT/UT_Optional.h>
29 #include <UT/UT_StringArray.h>
30 #include <UT/UT_StringHolder.h>
31 #include <UT/UT_UniquePtr.h>
32 #include <UT/UT_WorkBuffer.h>
33 
34 #include <functional>
35 #include <stdexcept>
36 #include <tuple>
37 #include <type_traits>
38 
39 class NET_API NET_APIHandlerValueException : public std::runtime_error
40 {
41 public:
43  : std::runtime_error("No acceptable value found for function argument.")
44  {
45  }
46  explicit NET_APIHandlerValueException(const char *what_arg)
47  : std::runtime_error(what_arg)
48  {
49  }
50 };
51 
52 // Base Callback for NET Commands which takes in a variadic set of arguments.
53 // NOTE: when executing the callback all parameters of the function are called
54 // and therefore it is not necessary to keep the map of commands and the
55 // function itself default parameters in-sync. The default parameters are
56 // used and therefore these are the ones that should be updated.
57 class NET_API NET_WebAPIHandler
58 {
59 public:
61  class NET_API NET_Param
62  {
63  public:
64  explicit NET_Param(const UT_StringHolder &name) : myName(name) {}
65  explicit NET_Param(
66  const UT_StringHolder &name,
67  const UT_StringHolder &def)
68  : myName(name), myDefault(def)
69  {
70  }
73  };
74 
75  using ParamIdx = unsigned;
77 
78  virtual ~NET_WebAPIHandler() = default;
79 
80  virtual void exec(NET_HTTPRequest &req, NET_WebResponse &resp)
81  {
82  UT_ASSERT_P(!"Handler not implemented for this api type");
83  }
84  virtual void exec(
85  NET_HTTPRequest &req,
86  NET_WebResponse &resp,
89  {
90  UT_ASSERT_P(!"Handler not implemented for this api type.");
91  }
92  virtual void call(
96  {
97  UT_ASSERT_P(!"Handler not implemented for this api type.");
98  }
99  const UT_StringHolder &getName() const { return myName; }
100  // Get the parameters index based on its name
102  {
103  for (int i = 0; i < myParams.entries(); ++i)
104  {
105  if (myParams[i].myName == name)
106  return i;
107  }
108  return UT_Optional<ParamIdx>();
109  }
110  // Get the parameters default argument based on its name
112  const UT_String &name) const
113  {
114  for (int i = 0; i < myParams.entries(); ++i)
115  {
116  const NET_Param &param = myParams[i];
117  if (param.myName == name)
118  {
119  if (param.myDefault)
120  return param.myDefault.get();
122  }
123  }
125  }
126 
127  explicit operator bool() const { return isValid(); }
128  virtual bool isValid() const = 0;
129 
130 protected:
131  NET_WebAPIHandler(const UT_StringHolder &name, const ParamArray &param_arr);
132 
133  // The commands name
136 };
137 
138 namespace NET_WebHelpers
139 {
140 struct NET_API ParamDetail
141 {
142  bool myIsDefault = false;
143  UT_StringHolder myValue = "";
144 };
145 
146 template <class T>
147 T
149 {
150  // See if this is a default value and convert it to a json object for
151  // serialization.
152  if (detail.myIsDefault)
153  {
154  if (detail.myValue.isEmpty())
155  return T{};
156 
157  UT_JSONValue json;
158  if (!json.parseValue(detail.myValue))
160  T value{};
161  if (!NETserialize<T>(json, value))
163  return value;
164  }
165 
166  return convert<T, UT_StringHolder>(detail.myValue);
167 }
168 
169 // Convert an array to a tuple implementation.
170 template <class... Args, std::size_t... Idxs>
171 std::tuple<Args...>
173  const UT_Array<ParamDetail> &arr,
174  std::index_sequence<Idxs...>)
175 {
176  return std::make_tuple(convertArrayValue<Args>(arr[Idxs])...);
177 }
178 
179 // Convert an array to a tuple
180 template <class... Args>
181 std::tuple<Args...>
183 {
184  UT_ASSERT(arr.entries() >= (sizeof...(Args)));
185  return convertArrayToTupleImpl<Args...>(
186  arr, std::make_index_sequence<sizeof...(Args)>{});
187 }
188 
189 #define _NET_IS_STRING(T) \
190  ( std::is_same<UT_StringHolder, T>::value || std::is_same<std::string, T>::value )
191 
192 #define _NET_IS_INTEGRAL(T) \
193  ( std::is_integral<T>::value && !std::is_same<bool, T>::value )
194 
195 #define _NET_REQUIRES(T) \
196  typename std::enable_if< T >::type * = nullptr
197 
198 template<class T, _NET_REQUIRES(!_NET_IS_STRING(T) && !_NET_IS_INTEGRAL(T) ) >
199 bool
200 netConvertDefault(T& value, const UT_StringRef& def_value)
201 {
202  return false;
203 }
204 
205 template <class T, _NET_REQUIRES( _NET_IS_STRING(T) && !_NET_IS_INTEGRAL(T) ) >
206 bool
208 {
209  value = def_value;
210  return true;
211 }
212 
213 template <class T, _NET_REQUIRES( _NET_IS_INTEGRAL(T) && !_NET_IS_STRING(T)) >
214 bool
215 netConvertDefault(T& value, const UT_StringRef& def_value)
216 {
217  value = def_value.toInt();
218  return true;
219 }
220 
221 template <class T>
222 T
227  std::size_t idx)
228 {
229  // See if the current index can be specified by the arg
230  if (idx < args->entries())
231  {
232  UT_JSONValue *json = args->get(idx);
233  if (json != nullptr)
234  {
235  T value{};
236  if (!NETserialize<T>(*json, value))
238  return value;
239  }
240  }
241  // Check that we have enough param specified
242  if (idx >= params.entries())
244  const NET_WebAPIHandler::NET_Param &param = params[idx];
245  // See if the kwargs has it specified
246  UT_JSONValue *json = kwargs->get(param.myName);
247  if (json != nullptr)
248  {
249  T value{};
250  if (!NETserialize<T>(*json, value))
252  return value;
253  }
254 
255  // Try and fallback to the default
256  if (!param.myDefault)
258 
259  // Check if the default is empty. An empty default means to instantiate the
260  // default object.
261  if (param.myDefault.get().isEmpty())
262  return T{};
263 
264  T value{};
265  // Since we dont have if constexpr use a helper function that returns false
266  // for all types except ones that dont need to go through json
267  // (i.e. strings)
268  if (netConvertDefault<T>(value, param.myDefault.get()))
269  return value;
270 
271  UT_JSONValue def_json;
272  if (!def_json.parseValue(param.myDefault.get()))
274 
275  if (!NETserialize<T>(def_json, value))
277 
278  return value;
279 }
280 
281 // Convert json array element to tuple.
282 template <class... Args, std::size_t... Idxs>
283 std::tuple<Args...>
288  std::index_sequence<Idxs...>)
289 {
290  return std::make_tuple(
291  convertJSONValue<Args>(params, args, kwargs, Idxs)...);
292 }
293 
294 // Convert a json array to tuple
295 
296 template <class... Args, std::size_t Size = sizeof...(Args)>
297 auto
302  typename std::enable_if_t<Size == 0, std::tuple<Args...>>
303 {
304  (void)args;
305  (void)kwargs;
306  return std::make_tuple();
307 }
308 
309 template <class... Args, std::size_t Size = sizeof...(Args)>
310 auto
313  UT_JSONValueArray *args,
314  UT_JSONValueMap *kwargs) ->
315  typename std::enable_if_t<Size != 0, std::tuple<Args...>>
316 {
317  return convertJSONArrayToTupleImpl<Args...>(
318  params, args, kwargs, std::make_index_sequence<Size>{});
319 }
320 
321 // C++17 implementation of std::apply()
322 template <class F, class ReqObj, class Tuple, std::size_t... I>
323 decltype(auto)
324 applyImpl(F &&f, ReqObj& req, Tuple &&t, std::index_sequence<I...>)
325 {
326  return std::forward<F>(f)(req, std::get<I>(std::forward<Tuple>(t))...);
327 }
328 
329 template <class F, class ReqObj, class Tuple>
330 decltype(auto)
331 applyTuple(F &&f, ReqObj& req, Tuple &&tuple)
332 {
333  constexpr std::size_t size =
335  return applyImpl(
336  std::forward<F>(f), req, std::forward<Tuple>(tuple),
337  std::make_index_sequence<size>());
338 }
339 } // namespace NET_WebHelpers
340 
341 // Callback for NET_WebAPI requests which takes in a variadic set of arguments.
342 template <typename... Args>
344 {
345 public:
346  static constexpr std::size_t Size = sizeof...(Args);
347  using Callback =
348  std::function<NET_WebResponse(NET_HTTPRequest &req, Args...)>;
349 
351  const UT_StringHolder &name,
352  const ParamArray &param_arr,
353  Callback clb)
354  : NET_WebAPIHandler(name, param_arr), myCallback(clb)
355  {
356  }
357 
358  // Execute the callback based on http request
359  void exec(NET_HTTPRequest &req, NET_WebResponse &resp) override
360  {
361  try
362  {
363  using namespace NET_WebHelpers;
364  // The number of params must be identical to the size parameter
365  if (myParams.size() != Size)
366  {
367  UT_ASSERT(!"Number of parameters specified does not match the "
368  "number of parameters for the function.");
369 
370  UT_WorkBuffer errors(
371  "The number of parameters specified does not "
372  "match the number of parameters for the function.");
373 
374  // Send a response back informing caller of incorrect request
375  resp = NET_WebResponse(NET_HTTPBadRequest, errors);
376  return;
377  }
378 
379  // Create vector in order of parameters for function
380  UT_Array<ParamDetail> param_values(Size, Size);
381  for (int i = 0; i < myParams.entries(); ++i)
382  {
383  const NET_Param &param = myParams[i];
385  req.getVariable(param.myName);
386  // if the value was not specified then see if there was a
387  // default value. if the value was not found and it does not
388  // have a default value then we have hit an error.
389  if (!value)
390  {
391  if (!param.myDefault)
392  {
393  UT_ASSERT(!"A required parameter was not found and a "
394  "default"
395  " was not specified.");
396 
397  UT_WorkBuffer errors;
398  errors.format(
399  "Internal Error: Required parameter \"{}\" was"
400  " not found and a default was not specified.",
401  param.myName);
402  resp = NET_WebResponse(NET_HTTPBadRequest, errors);
403  return;
404  }
405  // Insert the default value into the map
406  param_values[i].myValue = param.myDefault.get();
407  param_values[i].myIsDefault = true;
408  }
409  else
410  {
411  // Insert the request value into the map
412  param_values[i].myValue = value.get();
413  param_values[i].myIsDefault = false;
414  }
415  }
416 
417  // convert array to tuple which will be forwarded to the execution
418  // callback. NOTE: we remove the reference from the tuple values.
419  std::tuple<std::decay_t<Args>...> tuple_values =
420  convertArrayToTuple<std::decay_t<Args>...>(param_values);
421 
422  // finally call the callback
423  resp = applyTuple(myCallback, req, tuple_values);
424  }
425  catch (const NET_APIHandlerValueException &e)
426  {
427  resp.myData = e.what();
429  }
430  catch (...)
431  {
432  resp.myData = "Unknown exception occurred.";
434  };
435  }
436 
437  void exec(
438  NET_HTTPRequest &req,
439  NET_WebResponse &resp,
440  UT_JSONValueArray *args,
441  UT_JSONValueMap *kwargs) override
442  {
443  UT_ASSERT_P(args != nullptr);
444 
445  using namespace NET_WebHelpers;
446  using tuple_t = std::tuple<std::decay_t<Args>...>;
447 
448  try
449  {
450  tuple_t tuple_values =
451  convertJSONArrayToTuple<std::decay_t<Args>...>(
452  myParams, args, kwargs);
453 
454  // finally call the callback
455  resp = applyTuple(myCallback, req, tuple_values);
456  }
457  catch (const NET_APIHandlerValueException &e)
458  {
459  resp.myData = e.what();
461  }
462  catch (...)
463  {
464  resp.myData = "Unknown exception occurred.";
466  };
467  }
468 
469  operator bool() const { return isValid(); }
470  bool isValid() const override { return true; }
471 
472 private:
473  Callback myCallback;
474 };
475 
476 template <typename... Args>
478 {
479 public:
480  using Callback =
481  std::function<void(NET_WebSocketAPIRequest &req, Args...)>;
482 
484  const UT_StringHolder &name,
485  const ParamArray &param_arr,
486  Callback clb)
487  : NET_WebAPIHandler(name, param_arr), myCallback(clb)
488  {
489  }
490 
492  UT_JSONValueArray* args,
493  UT_JSONValueMap* kwargs) override
494  {
495  UT_ASSERT_P(args != nullptr);
496 
497  using namespace NET_WebHelpers;
498  using tuple_t = std::tuple<std::decay_t<Args>...>;
499 
500  try
501  {
502  tuple_t tuple_values =
503  convertJSONArrayToTuple<std::decay_t<Args>...>(
504  myParams, args, kwargs);
505 
506  applyTuple(myCallback, req, tuple_values);
507  }
508  catch (const NET_APIHandlerValueException& e)
509  {
510  req.setError(e.what(), NET_HTTPUnprocessableEntity);
511  }
512  catch (...)
513  {
514  req.setError("Unknown exception occurred.", NET_HTTPUnprocessableEntity);
515  };
516  }
517 
518  operator bool() const { return isValid(); }
519  bool isValid() const override { return true; }
520 
521 private:
522  Callback myCallback;
523 };
524 
525 #endif // __NET_WebAPIHandler_H__
std::function< void(NET_WebSocketAPIRequest &req, Args...)> Callback
T convertJSONValue(NET_WebAPIHandler::ParamArray &params, UT_JSONValueArray *args, UT_JSONValueMap *kwargs, std::size_t idx)
NET_WebAPIHandlerImpl(const UT_StringHolder &name, const ParamArray &param_arr, Callback clb)
T convertArrayValue(const ParamDetail &detail)
UT_Optional< ParamIdx > getParamIndex(const UT_String &name) const
GLsizeiptr size
Definition: glew.h:1681
const UT_StringHolder myName
UT_JSONValueMap stores a map/dictionary of UT_JSONValue objects.
Response object used for responding to request in the server.
GLuint const GLchar * name
Definition: glew.h:1814
bool parseValue(UT_JSONParser &parser, UT_IStream *is=0, bool record_source_offsets=false)
void exec(NET_HTTPRequest &req, NET_WebResponse &resp, UT_JSONValueArray *args, UT_JSONValueMap *kwargs) override
const Args & args
Definition: printf.h:628
SYS_NO_DISCARD_RESULT const UT_JSONValue * get(int64 i) const
Access a const entry by index.
NET_WebSocketAPIHandlerImpl(const UT_StringHolder &name, const ParamArray &param_arr, Callback clb)
bool isValid() const override
UT_JSONValueArray stores a list of UT_JSONValue objects.
bool isEmpty() const
UT_StringHolder myData
std::tuple< Args...> convertArrayToTupleImpl(const UT_Array< ParamDetail > &arr, std::index_sequence< Idxs...>)
NET_Param(const UT_StringHolder &name)
exint size() const
Definition: UT_Array.h:458
virtual void exec(NET_HTTPRequest &req, NET_WebResponse &resp, UT_JSONValueArray *args, UT_JSONValueMap *kwargs)
GLfloat param
Definition: glew.h:1530
std::tuple< Args...> convertArrayToTuple(const UT_Array< ParamDetail > &arr)
std::function< NET_WebResponse(NET_HTTPRequest &req, Args...)> Callback
NET_Param(const UT_StringHolder &name, const UT_StringHolder &def)
bool netConvertDefault(T &value, const UT_StringRef &def_value)
hboost::optional< T > UT_Optional
Definition: UT_Optional.h:16
GLclampf f
Definition: glew.h:3499
decltype(auto) applyImpl(F &&f, ReqObj &req, Tuple &&t, std::index_sequence< I...>)
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:134
UT_StringHolder myName
void
Definition: png.h:1083
virtual void call(NET_WebSocketAPIRequest &req, UT_JSONValueArray *args, UT_JSONValueMap *kwargs)
virtual void exec(NET_HTTPRequest &req, NET_WebResponse &resp)
UT_Optional< const UT_StringHolder & > getParamDefault(const UT_String &name) const
NET_HTTPStatusCode myStatus
static constexpr std::size_t Size
exint entries() const
Alias of size(). size() is preferred.
Definition: UT_Array.h:460
auto convertJSONArrayToTuple(NET_WebAPIHandler::ParamArray &params, UT_JSONValueArray *args, UT_JSONValueMap *kwargs) -> typename std::enable_if_t< Size==0
size_t format(const char *fmt, const Args &...args)
void call(NET_WebSocketAPIRequest &req, UT_JSONValueArray *args, UT_JSONValueMap *kwargs) override
const UT_StringHolder & getName() const
Class to store JSON objects as C++ objects.
Definition: UT_JSONValue.h:77
decltype(auto) applyTuple(F &&f, ReqObj &req, Tuple &&tuple)
const GLfloat * params
Definition: glew.h:1531
bool isValid() const override
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:135
int toInt() const
void setError(Args &&...args)
UT_Optional< const UT_StringHolder & > getVariable(const char *name) const
UT_Optional< UT_StringHolder > ParamDefault
std::tuple< Args...> convertJSONArrayToTupleImpl(NET_WebAPIHandler::ParamArray &params, UT_JSONValueArray *args, UT_JSONValueMap *kwargs, std::index_sequence< Idxs...>)
GLsizei const GLfloat * value
Definition: glew.h:1849
GLdouble GLdouble t
Definition: glew.h:1398
void exec(NET_HTTPRequest &req, NET_WebResponse &resp) override
NET_APIHandlerValueException(const char *what_arg)
SYS_FORCE_INLINE const UT_JSONValue * get(int64 i) const
Access a const entry by index.
type
Definition: core.h:528