]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/http/api_docs.hh
import quincy beta 17.1.0
[ceph.git] / ceph / src / seastar / include / seastar / http / api_docs.hh
CommitLineData
11fdf7f2
TL
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
30namespace seastar {
31
32namespace httpd {
33
34struct 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 }
9f95a23c
TL
46 api_doc(const api_doc & e)
47 : json::json_base()
48 {
11fdf7f2
TL
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
66struct api_docs : public json::json_base {
67 json::json_element<std::string> apiVersion;
68 json::json_element<std::string> swaggerVersion;
69 json::json_list<api_doc> apis;
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 }
9f95a23c
TL
82 api_docs(const api_docs & e)
83 : json::json_base()
84 {
11fdf7f2
TL
85 apiVersion = "0.0.1";
86 swaggerVersion = "1.2";
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
100class api_registry_base : public handler_base {
101protected:
102 sstring _base_path;
103 sstring _file_directory;
104 routes& _routes;
105
106public:
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
119class api_registry : public api_registry_base {
120 api_docs _docs;
121public:
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
128 future<std::unique_ptr<reply>> handle(const sstring& path,
129 std::unique_ptr<request> req, std::unique_ptr<reply> rep) override {
130 rep->_content = json::formatter::to_json(_docs);
131 rep->done("json");
132 return make_ready_future<std::unique_ptr<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
150class api_registry_builder_base {
151protected:
152 sstring _file_directory;
153 sstring _base_path;
154 static const sstring DEFAULT_DIR;
155 static const sstring DEFAULT_PATH;
156public:
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
163class api_registry_builder : public api_registry_builder_base {
164public:
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
185using doc_entry = noncopyable_function<future<>(output_stream<char>&)>;
186
187/*!
188 * \brief a helper function that creates a reader from a file
189 */
190
191doc_entry get_file_reader(sstring file_name);
192
193/*!
194 * \brief An api doc that support swagger version 2.0
195 *
196 * The result is a unified JSON file with the swagger definitions.
197 *
198 * The file content is a concatenation of the doc_entry by the order of
199 * their entry.
200 *
201 * Definitions will be added under the definition section
202 *
203 * typical usage:
204 *
205 * First entry:
206 *
207 {
208 "swagger": "2.0",
209 "host": "localhost:10000",
210 "basePath": "/v2",
211 "paths": {
212
213 * entry:
214 "/config/{id}": {
215 "get": {
216 "description": "Return a config value",
217 "operationId": "findConfigId",
218 "produces": [
219 "application/json"
220 ],
221 }
222 }
223 *
224 * Closing the entries:
225 },
226
227 "definitions": {
228 .....
229
230 .....
231 }
232}
233 *
234 */
235class api_docs_20 {
236 std::vector<doc_entry> _apis;
237 content_replace _transform;
238 std::vector<doc_entry> _definitions;
239
240public:
241 future<> write(output_stream<char>&&, std::unique_ptr<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
252class api_registry_20 : public api_registry_base {
253 api_docs_20 _docs;
254public:
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
261 future<std::unique_ptr<reply>> handle(const sstring& path,
262 std::unique_ptr<request> req, std::unique_ptr<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<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
278class api_registry_builder20 : public api_registry_builder_base {
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
289public:
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
299 /*!
300 * \brief register a doc_entry
301 * This doc_entry can be used to either take the definition from a file
302 * or generate them dynamically.
303 */
304 void register_function(routes& r, doc_entry&& f) {
305 auto h = get_register_base(r);
306 if (h) {
307 h->reg(std::move(f));
308 }
309 }
310 /*!
311 * \brief register an API
312 */
313 void register_api_file(routes& r, const sstring& api) {
314 register_function(r, get_file_reader(_file_directory + "/" + api + ".json"));
315 }
316
317
318 /*!
319 * Add a footer doc_entry
320 */
321 void add_definition(routes& r, doc_entry&& f) {
322 auto h = get_register_base(r);
323 if (h) {
324 h->add_definition(std::move(f));
325 }
326
327 }
328
329 /*!
330 * Add a definition file
331 */
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}