Fawkes API Fawkes Development Version
rest_api.h
1
2/***************************************************************************
3 * rest_api.h - Webview REST API
4 *
5 * Created: Fri Mar 16 17:39:57 2018
6 * Copyright 2006-2018 Tim Niemueller [www.niemueller.de]
7 ****************************************************************************/
8
9/* This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Library General Public License for more details.
18 *
19 * Read the full text in the LICENSE.GPL file in the doc directory.
20 */
21
22#ifndef _LIBS_WEBVIEW_REST_API_H_
23#define _LIBS_WEBVIEW_REST_API_H_
24
25#include <core/exception.h>
26#include <logging/logger.h>
27#include <utils/misc/string_split.h>
28#include <webview/reply.h>
29#include <webview/request.h>
30
31#include <algorithm>
32#include <functional>
33#include <map>
34#include <memory>
35#include <regex>
36#include <string>
37#include <vector>
38
39namespace fawkes {
40
41template <typename T>
42class WebviewRouter;
43
44/** REST reply via Webview.
45 * @author Tim Niemueller
46 */
48{
49public:
50 /** Constructor.
51 * @param code HTTP response code
52 * @param body body of reply, usually a JSON document.
53 * @param content_type content type of reply, defaults to application/json.
54 * When sending text error messages, set to text/plain.
55 */
57 const std::string &body = "",
58 const std::string &content_type = "application/json")
60 {
61 add_header("Content-type", content_type);
62 }
63};
64
65/** REST processing exception.
66 * Use to indicate failure with more specific response. The HTTP code
67 * will be used for the static response with the formatted error message.
68 * @author Tim Niemueller
69 */
71{
72public:
73 /** Constructor.
74 * @param code HTTP response code
75 * @param format format string for error message (cf. printf)
76 */
77 explicit WebviewRestException(WebReply::Code code, const char *format, ...)
78 : Exception(), code_(code), content_type_("text/plain")
79 {
80 va_list va;
81 va_start(va, format);
82 append_va(format, va);
83 va_end(va);
84 }
85
86 /** Constructor.
87 * @param code HTTP response code
88 * @param o Object to convert to JSON
89 * @param pretty true to enable pretty printing of the JSON input
90 */
91 template <typename T, typename = std::enable_if_t<std::is_class<T>::value>>
92 WebviewRestException(WebReply::Code code, const T &o, bool pretty = false)
93 : Exception(), code_(code), content_type_("application/json")
94 {
95 append("%s", o.to_json(pretty).c_str());
96 }
97
98 /** Get HTTP response code.
99 * @return HTTP response code
100 */
103 {
104 return code_;
105 }
106
107 /** Get content type of response.
108 * @return HTTP content type
109 */
110 const std::string &
112 {
113 return content_type_;
114 }
115
116private:
117 WebReply::Code code_;
118 std::string content_type_;
119};
120
121/** REST parameters to pass to handlers.
122 * @author Tim Niemueller
123 */
125{
126 /// REST API can call private methods.
127 friend class WebviewRestApi;
128
129public:
130 /** Constructor. */
131 WebviewRestParams() : pretty_json_(false)
132 {
133 }
134
135 /** Get a path argument.
136 * Retrieves a named argument that was a token in the
137 * registration URL, e.g., retrieve "id" for "/item/{id}".
138 * @param what what to retrieve
139 * @return item passed in URL or empty string
140 */
141 std::string
142 path_arg(const std::string &what)
143 {
144 if (path_args_.find(what) != path_args_.end()) {
145 return path_args_[what];
146 } else {
147 return "";
148 }
149 }
150
151 /** Get a query argument.
152 * Retrieves a named query argument that was passed in the
153 * URL, e.g., retrieve "pretty" for "?pretty=true".
154 * @param what what to retrieve
155 * @return item passed in URL or empty string
156 */
157 std::string
158 query_arg(const std::string &what)
159 {
160 if (query_args_.find(what) != query_args_.end()) {
161 return query_args_[what];
162 } else {
163 return "";
164 }
165 }
166
167 /** Check if query argument is set.
168 * Retrieves a named query argument that was passed in the
169 * URL, e.g., retrieve "pretty" for "?pretty".
170 * @param what what to check
171 * @return true if the argument exists (with any value), false otherwise
172 */
173 bool
174 has_query_arg(const std::string &what)
175 {
176 return (query_args_.find(what) != query_args_.end());
177 }
178
179 /** Is pretty-printed JSON enabled?
180 * @return true true to request enabling pretty mode
181 */
182 bool
184 {
185 return pretty_json_;
186 }
187
188 /** Enable or disable pretty printed results.
189 * Note that this only works when using the generated API
190 * interface and classes which support the "pretty" flag.
191 * @param pretty true to enable, false to disable
192 */
193 void
194 set_pretty_json(bool pretty)
195 {
196 pretty_json_ = pretty;
197 }
198
199private:
200 void
201 set_path_args(std::map<std::string, std::string> &&args)
202 {
203 path_args_ = std::move(args);
204 }
205
206 void
207 set_query_args(const std::map<std::string, std::string> &args)
208 {
209 query_args_ = args;
210 }
211
212private:
213 bool pretty_json_;
214 std::map<std::string, std::string> path_args_;
215 std::map<std::string, std::string> query_args_;
216};
217
218class Logger;
219
221{
222public:
223 WebviewRestApi(const std::string &name, fawkes::Logger *logger);
224
225 /** REST API call handler function type. */
226 typedef std::function<std::unique_ptr<WebReply>(std::string, WebviewRestParams &)> Handler;
227
228 const std::string &name() const;
229 void add_handler(WebRequest::Method method, std::string path, Handler handler);
230 void set_pretty_json(bool pretty);
231
232 /** Add simple handler.
233 * For a handler that does not require input parameters and that outputs
234 * a WebviewRestReply instance, for example, only to indicate success.
235 * @param method HTTP method to react to
236 * @param path path (after component base path) to react to
237 * @param handler handler function
238 */
239 void
241 std::string path,
242 std::function<std::unique_ptr<WebReply>(WebviewRestParams &)> handler)
243 {
244 add_handler(method,
245 path,
246 [handler](const std::string &body,
247 WebviewRestParams &m) -> std::unique_ptr<WebReply> {
248 try {
249 return handler(m);
250 } catch (WebviewRestException &e) {
251 return std::make_unique<WebviewRestReply>(e.code(),
253 e.content_type());
254 } catch (Exception &e) {
255 auto r =
256 std::make_unique<WebviewRestReply>(WebReply::HTTP_INTERNAL_SERVER_ERROR);
257 r->append_body("Execution failed: %s", e.what_no_backtrace());
258 r->add_header("Content-type", "text/plain");
259 return r;
260 }
261 });
262 }
263
264 /** Add handler function.
265 * @param method HTTP method to react to
266 * @param path path (after component base path) to react to
267 * @param handler handler function
268 */
269 template <class O, class I>
270 void
272 std::string path,
273 std::function<O(I &, WebviewRestParams &)> handler)
274 {
275 add_handler(method,
276 path,
277 [this, handler](const std::string &body,
278 WebviewRestParams &m) -> std::unique_ptr<WebReply> {
279 I input;
280 input.from_json(body);
281 try {
282 O output{handler(input, m)};
283 try {
284 output.validate();
285 } catch (std::runtime_error &e) {
286 logger_->log_warn(("RestAPI|" + name_).c_str(), "%s", e.what());
287 }
288 if (m.has_query_arg("pretty")) {
289 m.set_pretty_json(true);
290 }
291 return std::make_unique<WebviewRestReply>(WebReply::HTTP_OK,
292 output.to_json(pretty_json_
293 || m.pretty_json()));
294 } catch (WebviewRestException &e) {
295 return std::make_unique<WebviewRestReply>(e.code(),
297 e.content_type());
298 } catch (Exception &e) {
299 auto r =
300 std::make_unique<WebviewRestReply>(WebReply::HTTP_INTERNAL_SERVER_ERROR);
301 r->append_body("Execution failed: %s", e.what_no_backtrace());
302 r->add_header("Content-type", "text/plain");
303 return r;
304 }
305 });
306 }
307
308 /** Add handler function.
309 * @param method HTTP method to react to
310 * @param path path (after component base path) to react to
311 * @param handler handler function
312 */
313 template <class I>
314 void
316 std::string path,
317 std::function<std::unique_ptr<WebReply>(I, WebviewRestParams &)> handler)
318 {
319 add_handler(method,
320 path,
321 [this, handler](const std::string &body,
322 WebviewRestParams &m) -> std::unique_ptr<WebReply> {
323 I input;
324 input.from_json(body);
325 try {
326 return handler(std::forward<I>(input), m);
327 } catch (WebviewRestException &e) {
328 return std::make_unique<WebviewRestReply>(e.code(),
330 e.content_type());
331 } catch (Exception &e) {
332 auto r =
333 std::make_unique<WebviewRestReply>(WebReply::HTTP_INTERNAL_SERVER_ERROR);
334 r->append_body("Execution failed: %s", e.what_no_backtrace());
335 r->add_header("Content-type", "text/plain");
336 return r;
337 }
338 });
339 }
340
341 /** Add handler function.
342 * @param method HTTP method to react to
343 * @param path path (after component base path) to react to
344 * @param handler handler function
345 */
346 template <class O>
347 void
349 std::string path,
350 std::function<O(WebviewRestParams &)> handler)
351 {
352 add_handler(method,
353 path,
354 [this, handler](const std::string &body,
355 WebviewRestParams &m) -> std::unique_ptr<WebReply> {
356 try {
357 O output{handler(m)};
358 try {
359 output.validate();
360 } catch (std::runtime_error &e) {
361 logger_->log_warn(("RestAPI|" + name_).c_str(), "%s", e.what());
362 }
363 if (m.has_query_arg("pretty")) {
364 m.set_pretty_json(true);
365 }
366 return std::make_unique<WebviewRestReply>(WebReply::HTTP_OK,
367 output.to_json(pretty_json_
368 || m.pretty_json()));
369 } catch (WebviewRestException &e) {
370 return std::make_unique<WebviewRestReply>(e.code(),
372 e.content_type());
373 } catch (Exception &e) {
374 auto r =
375 std::make_unique<WebviewRestReply>(WebReply::HTTP_INTERNAL_SERVER_ERROR);
376 r->append_body("Execution failed: %s", e.what_no_backtrace());
377 r->add_header("Content-type", "text/plain");
378 return r;
379 }
380 });
381 }
382
383 WebReply *process_request(const WebRequest *request, const std::string &rest_url);
384
385private:
386 std::string name_;
387 fawkes::Logger * logger_;
388 bool pretty_json_;
389 std::shared_ptr<WebviewRouter<Handler>> router_;
390};
391
392} // namespace fawkes
393
394#endif
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual const char * what_no_backtrace() const noexcept
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
void append_va(const char *format, va_list va) noexcept
Append messages to the message list.
Definition: exception.cpp:353
void append(const char *format,...) noexcept
Append messages to the message list.
Definition: exception.cpp:333
Interface for logging.
Definition: logger.h:42
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
Static web reply.
Definition: reply.h:136
virtual const std::string & body()
Get body.
Definition: reply.cpp:289
Basic web reply.
Definition: reply.h:34
void add_header(const std::string &header, const std::string &content)
Add a HTTP header.
Definition: reply.cpp:123
Code code() const
Get response code.
Definition: reply.cpp:104
Code
HTTP response code.
Definition: reply.h:37
@ HTTP_OK
OK.
Definition: reply.h:42
@ HTTP_INTERNAL_SERVER_ERROR
INTERNAL_SERVER_ERROR.
Definition: reply.h:85
Web request meta data carrier.
Definition: request.h:42
Method
HTTP transfer methods.
Definition: request.h:47
Webview REST API component.
Definition: rest_api.h:221
void add_handler(WebRequest::Method method, std::string path, std::function< std::unique_ptr< WebReply >(WebviewRestParams &)> handler)
Add simple handler.
Definition: rest_api.h:240
void add_handler(WebRequest::Method method, std::string path, std::function< O(WebviewRestParams &)> handler)
Add handler function.
Definition: rest_api.h:348
void set_pretty_json(bool pretty)
Enable or disable pretty JSON printing globally.
Definition: rest_api.cpp:94
WebReply * process_request(const WebRequest *request, const std::string &rest_url)
Process REST API request.
Definition: rest_api.cpp:64
void add_handler(WebRequest::Method method, std::string path, std::function< std::unique_ptr< WebReply >(I, WebviewRestParams &)> handler)
Add handler function.
Definition: rest_api.h:315
const std::string & name() const
Get name of component.
Definition: rest_api.cpp:53
std::function< std::unique_ptr< WebReply >(std::string, WebviewRestParams &)> Handler
REST API call handler function type.
Definition: rest_api.h:226
void add_handler(WebRequest::Method method, std::string path, std::function< O(I &, WebviewRestParams &)> handler)
Add handler function.
Definition: rest_api.h:271
void add_handler(WebRequest::Method method, std::string path, Handler handler)
Add handler function.
Definition: rest_api.cpp:85
WebviewRestApi(const std::string &name, fawkes::Logger *logger)
Constructor.
Definition: rest_api.cpp:41
REST processing exception.
Definition: rest_api.h:71
WebReply::Code code()
Get HTTP response code.
Definition: rest_api.h:102
WebviewRestException(WebReply::Code code, const char *format,...)
Constructor.
Definition: rest_api.h:77
WebviewRestException(WebReply::Code code, const T &o, bool pretty=false)
Constructor.
Definition: rest_api.h:92
const std::string & content_type() const
Get content type of response.
Definition: rest_api.h:111
REST parameters to pass to handlers.
Definition: rest_api.h:125
bool pretty_json()
Is pretty-printed JSON enabled?
Definition: rest_api.h:183
std::string query_arg(const std::string &what)
Get a query argument.
Definition: rest_api.h:158
bool has_query_arg(const std::string &what)
Check if query argument is set.
Definition: rest_api.h:174
std::string path_arg(const std::string &what)
Get a path argument.
Definition: rest_api.h:142
void set_pretty_json(bool pretty)
Enable or disable pretty printed results.
Definition: rest_api.h:194
WebviewRestParams()
Constructor.
Definition: rest_api.h:131
REST reply via Webview.
Definition: rest_api.h:48
WebviewRestReply(WebReply::Code code, const std::string &body="", const std::string &content_type="application/json")
Constructor.
Definition: rest_api.h:56
Fawkes library namespace.