Seastar
High performance C++ framework for concurrent servers
api_docs.hh
1 /*
2  * This file is open source software, licensed to you under the terms
3  * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4  * distributed with this work for additional information regarding copyright
5  * ownership. You may not use this file except in compliance with the License.
6  *
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing,
12  * software distributed under the License is distributed on an
13  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14  * KIND, either express or implied. See the License for the
15  * specific language governing permissions and limitations
16  * under the License.
17  */
18 /*
19  * Copyright 2015 Cloudius Systems
20  */
21 
22 #pragma once
23 #include <seastar/json/json_elements.hh>
24 #include <seastar/json/formatter.hh>
25 #include <seastar/http/routes.hh>
26 #include <seastar/http/transformers.hh>
27 #include <string>
28 #include <seastar/util/noncopyable_function.hh>
29 
30 namespace seastar {
31 
32 namespace httpd {
33 
34 struct api_doc : public json::json_base {
37 
38  void register_params() {
39  add(&path, "path");
40  add(&description, "description");
41 
42  }
43  api_doc() {
44  register_params();
45  }
46  api_doc(const api_doc & e)
47  : json::json_base()
48  {
49  register_params();
50  path = e.path;
51  description = e.description;
52  }
53  template<class T>
54  api_doc& operator=(const T& e) {
55  path = e.path;
56  description = e.description;
57  return *this;
58  }
59  api_doc& operator=(const api_doc& e) {
60  path = e.path;
61  description = e.description;
62  return *this;
63  }
64 };
65 
66 struct api_docs : public json::json_base {
68  json::json_element<std::string> swaggerVersion;
70 
71  void register_params() {
72  add(&apiVersion, "apiVersion");
73  add(&swaggerVersion, "swaggerVersion");
74  add(&apis, "apis");
75 
76  }
77  api_docs() {
78  apiVersion = "0.0.1";
79  swaggerVersion = "1.2";
80  register_params();
81  }
82  api_docs(const api_docs & e)
83  : apiVersion{e.apiVersion}
84  , swaggerVersion{e.swaggerVersion}
85  , apis{e.apis}
86  {
87  register_params();
88  }
89  template<class T>
90  api_docs& operator=(const T& e) {
91  apis = e.apis;
92  return *this;
93  }
94  api_docs& operator=(const api_docs& e) {
95  apis = e.apis;
96  return *this;
97  }
98 };
99 
101 protected:
102  sstring _base_path;
103  sstring _file_directory;
104  routes& _routes;
105 
106 public:
107  api_registry_base(routes& routes, const sstring& file_directory,
108  const sstring& base_path)
109  : _base_path(base_path), _file_directory(file_directory), _routes(
110  routes) {
111  }
112 
113  void set_route(handler_base* h) {
114  _routes.put(GET, _base_path, h);
115  }
116  virtual ~api_registry_base() = default;
117 };
118 
120  api_docs _docs;
121 public:
122  api_registry(routes& routes, const sstring& file_directory,
123  const sstring& base_path)
124  : api_registry_base(routes, file_directory, base_path) {
125  set_route(this);
126  }
127 
129  std::unique_ptr<http::request> req, std::unique_ptr<http::reply> rep) override {
130  rep->_content = json::formatter::to_json(_docs);
131  rep->done("json");
132  return make_ready_future<std::unique_ptr<http::reply>>(std::move(rep));
133  }
134 
135  void reg(const sstring& api, const sstring& description,
136  const sstring& alternative_path = "") {
137  api_doc doc;
138  doc.description = description;
139  doc.path = "/" + api;
140  _docs.apis.push(doc);
141  sstring path =
142  (alternative_path == "") ?
143  _file_directory + api + ".json" : alternative_path;
144  file_handler* index = new file_handler(path,
145  new content_replace("json"));
146  _routes.put(GET, _base_path + "/" + api, index);
147  }
148 };
149 
151 protected:
152  sstring _file_directory;
153  sstring _base_path;
154  static const sstring DEFAULT_DIR;
155  static const sstring DEFAULT_PATH;
156 public:
157  api_registry_builder_base(const sstring& file_directory = DEFAULT_DIR,
158  const sstring& base_path = DEFAULT_PATH)
159  : _file_directory(file_directory), _base_path(base_path) {
160  }
161 };
162 
164 public:
165  api_registry_builder(const sstring& file_directory = DEFAULT_DIR,
166  const sstring& base_path = DEFAULT_PATH)
167  : api_registry_builder_base(file_directory, base_path) {
168  }
169 
170  void set_api_doc(routes& r) {
171  new api_registry(r, _file_directory, _base_path);
172  }
173 
174  void register_function(routes& r, const sstring& api,
175  const sstring& description, const sstring& alternative_path = "") {
176  auto h = r.get_exact_match(GET, _base_path);
177  if (h) {
178  // if a handler is found, it was added there by the api_registry_builder
179  // with the set_api_doc method, so we know it's the type
180  static_cast<api_registry*>(h)->reg(api, description, alternative_path);
181  };
182  }
183 };
184 
186 
191 doc_entry get_file_reader(sstring file_name);
192 
235 class api_docs_20 {
236  std::vector<doc_entry> _apis;
237  content_replace _transform;
238  std::vector<doc_entry> _definitions;
239 
240 public:
241  future<> write(output_stream<char>&&, std::unique_ptr<http::request> req);
242 
243  void add_api(doc_entry&& f) {
244  _apis.emplace_back(std::move(f));
245  }
246 
247  void add_definition(doc_entry&& f) {
248  _definitions.emplace_back(std::move(f));
249  }
250 };
251 
253  api_docs_20 _docs;
254 public:
255  api_registry_20(routes& routes, const sstring& file_directory,
256  const sstring& base_path)
257  : api_registry_base(routes, file_directory, base_path) {
258  set_route(this);
259  }
260 
262  std::unique_ptr<http::request> req, std::unique_ptr<http::reply> rep) override {
263  rep->write_body("json", [this, req = std::move(req)] (output_stream<char>&& os) mutable {
264  return _docs.write(std::move(os), std::move(req));
265  });
266  return make_ready_future<std::unique_ptr<http::reply>>(std::move(rep));
267  }
268 
269  virtual void reg(doc_entry&& f) {
270  _docs.add_api(std::move(f));
271  }
272 
273  virtual void add_definition(doc_entry&& f) {
274  _docs.add_definition(std::move(f));
275  }
276 };
277 
279  api_registry_20* get_register_base(routes& r) {
280  auto h = r.get_exact_match(GET, _base_path);
281  if (h) {
282  // if a handler is found, it was added there by the api_registry_builder
283  // with the set_api_doc method, so we know it's the type
284  return static_cast<api_registry_20*>(h);
285  }
286  return nullptr;
287  }
288 
289 public:
290  api_registry_builder20(const sstring& file_directory = DEFAULT_DIR,
291  const sstring& base_path = DEFAULT_PATH)
292  : api_registry_builder_base(file_directory, base_path) {
293  }
294 
295  void set_api_doc(routes& r) {
296  new api_registry_20(r, _file_directory, _base_path);
297  }
298 
305  auto h = get_register_base(r);
306  if (h) {
307  h->reg(std::move(f));
308  }
309  }
313  void register_api_file(routes& r, const sstring& api) {
314  register_function(r, get_file_reader(_file_directory + "/" + api + ".json"));
315  }
316 
317 
322  auto h = get_register_base(r);
323  if (h) {
324  h->add_definition(std::move(f));
325  }
326 
327  }
328 
332  void add_definitions_file(routes& r, const sstring& file) {
333  add_definition(r, get_file_reader(_file_directory + file + ".def.json" ));
334  }
335 
336 };
337 
338 }
339 
340 }
Definition: file.hh:194
A representation of a possibly not-yet-computed value.
Definition: future.hh:1238
An api doc that support swagger version 2.0.
Definition: api_docs.hh:235
Definition: api_docs.hh:252
future< std::unique_ptr< http::reply > > handle(const sstring &path, std::unique_ptr< http::request > req, std::unique_ptr< http::reply > rep) override
Definition: api_docs.hh:261
Definition: api_docs.hh:100
Definition: api_docs.hh:278
void add_definitions_file(routes &r, const sstring &file)
Definition: api_docs.hh:332
void register_api_file(routes &r, const sstring &api)
register an API
Definition: api_docs.hh:313
void register_function(routes &r, doc_entry &&f)
register a doc_entry This doc_entry can be used to either take the definition from a file or generate...
Definition: api_docs.hh:304
void add_definition(routes &r, doc_entry &&f)
Definition: api_docs.hh:321
Definition: api_docs.hh:163
Definition: api_docs.hh:119
future< std::unique_ptr< http::reply > > handle(const sstring &path, std::unique_ptr< http::request > req, std::unique_ptr< http::reply > rep) override
Definition: api_docs.hh:128
Definition: transformers.hh:44
Definition: file_handler.hh:150
Definition: handlers.hh:42
Definition: routes.hh:81
handler_base * get_exact_match(operation_type type, const sstring &url) const
Definition: routes.hh:152
routes & put(operation_type type, const sstring &url, handler_base *handler)
Definition: json_elements.hh:160
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
Definition: noncopyable_function.hh:37
Definition: api_docs.hh:34
Definition: api_docs.hh:66
Definition: json_elements.hh:228
virtual void add(json_base_element *element, std::string name, bool mandatory=false)