]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/include/seastar/http/api_docs.hh
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / seastar / include / seastar / http / 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 {
35 json::json_element<std::string> path;
36 json::json_element<std::string> description;
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 register_params();
48 path = e.path;
49 description = e.description;
50 }
51 template<class T>
52 api_doc& operator=(const T& e) {
53 path = e.path;
54 description = e.description;
55 return *this;
56 }
57 api_doc& operator=(const api_doc& e) {
58 path = e.path;
59 description = e.description;
60 return *this;
61 }
62 };
63
64 struct api_docs : public json::json_base {
65 json::json_element<std::string> apiVersion;
66 json::json_element<std::string> swaggerVersion;
67 json::json_list<api_doc> apis;
68
69 void register_params() {
70 add(&apiVersion, "apiVersion");
71 add(&swaggerVersion, "swaggerVersion");
72 add(&apis, "apis");
73
74 }
75 api_docs() {
76 apiVersion = "0.0.1";
77 swaggerVersion = "1.2";
78 register_params();
79 }
80 api_docs(const api_docs & e) {
81 apiVersion = "0.0.1";
82 swaggerVersion = "1.2";
83 register_params();
84 }
85 template<class T>
86 api_docs& operator=(const T& e) {
87 apis = e.apis;
88 return *this;
89 }
90 api_docs& operator=(const api_docs& e) {
91 apis = e.apis;
92 return *this;
93 }
94 };
95
96 class api_registry_base : public handler_base {
97 protected:
98 sstring _base_path;
99 sstring _file_directory;
100 routes& _routes;
101
102 public:
103 api_registry_base(routes& routes, const sstring& file_directory,
104 const sstring& base_path)
105 : _base_path(base_path), _file_directory(file_directory), _routes(
106 routes) {
107 }
108
109 void set_route(handler_base* h) {
110 _routes.put(GET, _base_path, h);
111 }
112 virtual ~api_registry_base() = default;
113 };
114
115 class api_registry : public api_registry_base {
116 api_docs _docs;
117 public:
118 api_registry(routes& routes, const sstring& file_directory,
119 const sstring& base_path)
120 : api_registry_base(routes, file_directory, base_path) {
121 set_route(this);
122 }
123
124 future<std::unique_ptr<reply>> handle(const sstring& path,
125 std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
126 rep->_content = json::formatter::to_json(_docs);
127 rep->done("json");
128 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
129 }
130
131 void reg(const sstring& api, const sstring& description,
132 const sstring& alternative_path = "") {
133 api_doc doc;
134 doc.description = description;
135 doc.path = "/" + api;
136 _docs.apis.push(doc);
137 sstring path =
138 (alternative_path == "") ?
139 _file_directory + api + ".json" : alternative_path;
140 file_handler* index = new file_handler(path,
141 new content_replace("json"));
142 _routes.put(GET, _base_path + "/" + api, index);
143 }
144 };
145
146 class api_registry_builder_base {
147 protected:
148 sstring _file_directory;
149 sstring _base_path;
150 static const sstring DEFAULT_DIR;
151 static const sstring DEFAULT_PATH;
152 public:
153 api_registry_builder_base(const sstring& file_directory = DEFAULT_DIR,
154 const sstring& base_path = DEFAULT_PATH)
155 : _file_directory(file_directory), _base_path(base_path) {
156 }
157 };
158
159 class api_registry_builder : public api_registry_builder_base {
160 public:
161 api_registry_builder(const sstring& file_directory = DEFAULT_DIR,
162 const sstring& base_path = DEFAULT_PATH)
163 : api_registry_builder_base(file_directory, base_path) {
164 }
165
166 void set_api_doc(routes& r) {
167 new api_registry(r, _file_directory, _base_path);
168 }
169
170 void register_function(routes& r, const sstring& api,
171 const sstring& description, const sstring& alternative_path = "") {
172 auto h = r.get_exact_match(GET, _base_path);
173 if (h) {
174 // if a handler is found, it was added there by the api_registry_builder
175 // with the set_api_doc method, so we know it's the type
176 static_cast<api_registry*>(h)->reg(api, description, alternative_path);
177 };
178 }
179 };
180
181 using doc_entry = noncopyable_function<future<>(output_stream<char>&)>;
182
183 /*!
184 * \brief a helper function that creates a reader from a file
185 */
186
187 doc_entry get_file_reader(sstring file_name);
188
189 /*!
190 * \brief An api doc that support swagger version 2.0
191 *
192 * The result is a unified JSON file with the swagger definitions.
193 *
194 * The file content is a concatenation of the doc_entry by the order of
195 * their entry.
196 *
197 * Definitions will be added under the definition section
198 *
199 * typical usage:
200 *
201 * First entry:
202 *
203 {
204 "swagger": "2.0",
205 "host": "localhost:10000",
206 "basePath": "/v2",
207 "paths": {
208
209 * entry:
210 "/config/{id}": {
211 "get": {
212 "description": "Return a config value",
213 "operationId": "findConfigId",
214 "produces": [
215 "application/json"
216 ],
217 }
218 }
219 *
220 * Closing the entries:
221 },
222
223 "definitions": {
224 .....
225
226 .....
227 }
228 }
229 *
230 */
231 class api_docs_20 {
232 std::vector<doc_entry> _apis;
233 content_replace _transform;
234 std::vector<doc_entry> _definitions;
235
236 public:
237 future<> write(output_stream<char>&&, std::unique_ptr<request> req);
238
239 void add_api(doc_entry&& f) {
240 _apis.emplace_back(std::move(f));
241 }
242
243 void add_definition(doc_entry&& f) {
244 _definitions.emplace_back(std::move(f));
245 }
246 };
247
248 class api_registry_20 : public api_registry_base {
249 api_docs_20 _docs;
250 public:
251 api_registry_20(routes& routes, const sstring& file_directory,
252 const sstring& base_path)
253 : api_registry_base(routes, file_directory, base_path) {
254 set_route(this);
255 }
256
257 future<std::unique_ptr<reply>> handle(const sstring& path,
258 std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
259 rep->write_body("json", [this, req = std::move(req)] (output_stream<char>&& os) mutable {
260 return _docs.write(std::move(os), std::move(req));
261 });
262 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
263 }
264
265 virtual void reg(doc_entry&& f) {
266 _docs.add_api(std::move(f));
267 }
268
269 virtual void add_definition(doc_entry&& f) {
270 _docs.add_definition(std::move(f));
271 }
272 };
273
274 class api_registry_builder20 : public api_registry_builder_base {
275 api_registry_20* get_register_base(routes& r) {
276 auto h = r.get_exact_match(GET, _base_path);
277 if (h) {
278 // if a handler is found, it was added there by the api_registry_builder
279 // with the set_api_doc method, so we know it's the type
280 return static_cast<api_registry_20*>(h);
281 }
282 return nullptr;
283 }
284
285 public:
286 api_registry_builder20(const sstring& file_directory = DEFAULT_DIR,
287 const sstring& base_path = DEFAULT_PATH)
288 : api_registry_builder_base(file_directory, base_path) {
289 }
290
291 void set_api_doc(routes& r) {
292 new api_registry_20(r, _file_directory, _base_path);
293 }
294
295 /*!
296 * \brief register a doc_entry
297 * This doc_entry can be used to either take the definition from a file
298 * or generate them dynamically.
299 */
300 void register_function(routes& r, doc_entry&& f) {
301 auto h = get_register_base(r);
302 if (h) {
303 h->reg(std::move(f));
304 }
305 }
306 /*!
307 * \brief register an API
308 */
309 void register_api_file(routes& r, const sstring& api) {
310 register_function(r, get_file_reader(_file_directory + "/" + api + ".json"));
311 }
312
313
314 /*!
315 * Add a footer doc_entry
316 */
317 void add_definition(routes& r, doc_entry&& f) {
318 auto h = get_register_base(r);
319 if (h) {
320 h->add_definition(std::move(f));
321 }
322
323 }
324
325 /*!
326 * Add a definition file
327 */
328 void add_definitions_file(routes& r, const sstring& file) {
329 add_definition(r, get_file_reader(_file_directory + file + ".def.json" ));
330 }
331
332 };
333
334 }
335
336 }