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.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
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
19 * Copyright 2015 Cloudius Systems
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>
28 #include <seastar/util/noncopyable_function.hh>
34 struct api_doc : public json::json_base {
35 json::json_element<std::string> path;
36 json::json_element<std::string> description;
38 void register_params() {
40 add(&description, "description");
46 api_doc(const api_doc & e) {
49 description = e.description;
52 api_doc& operator=(const T& e) {
54 description = e.description;
57 api_doc& operator=(const api_doc& e) {
59 description = e.description;
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;
69 void register_params() {
70 add(&apiVersion, "apiVersion");
71 add(&swaggerVersion, "swaggerVersion");
77 swaggerVersion = "1.2";
80 api_docs(const api_docs & e) {
82 swaggerVersion = "1.2";
86 api_docs& operator=(const T& e) {
90 api_docs& operator=(const api_docs& e) {
96 class api_registry_base : public handler_base {
99 sstring _file_directory;
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(
109 void set_route(handler_base* h) {
110 _routes.put(GET, _base_path, h);
112 virtual ~api_registry_base() = default;
115 class api_registry : public api_registry_base {
118 api_registry(routes& routes, const sstring& file_directory,
119 const sstring& base_path)
120 : api_registry_base(routes, file_directory, base_path) {
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);
128 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
131 void reg(const sstring& api, const sstring& description,
132 const sstring& alternative_path = "") {
134 doc.description = description;
135 doc.path = "/" + api;
136 _docs.apis.push(doc);
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);
146 class api_registry_builder_base {
148 sstring _file_directory;
150 static const sstring DEFAULT_DIR;
151 static const sstring DEFAULT_PATH;
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) {
159 class api_registry_builder : public api_registry_builder_base {
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) {
166 void set_api_doc(routes& r) {
167 new api_registry(r, _file_directory, _base_path);
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);
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);
181 using doc_entry = noncopyable_function<future<>(output_stream<char>&)>;
184 * \brief a helper function that creates a reader from a file
187 doc_entry get_file_reader(sstring file_name);
190 * \brief An api doc that support swagger version 2.0
192 * The result is a unified JSON file with the swagger definitions.
194 * The file content is a concatenation of the doc_entry by the order of
197 * Definitions will be added under the definition section
205 "host": "localhost:10000",
212 "description": "Return a config value",
213 "operationId": "findConfigId",
220 * Closing the entries:
232 std::vector<doc_entry> _apis;
233 content_replace _transform;
234 std::vector<doc_entry> _definitions;
237 future<> write(output_stream<char>&&, std::unique_ptr<request> req);
239 void add_api(doc_entry&& f) {
240 _apis.emplace_back(std::move(f));
243 void add_definition(doc_entry&& f) {
244 _definitions.emplace_back(std::move(f));
248 class api_registry_20 : public api_registry_base {
251 api_registry_20(routes& routes, const sstring& file_directory,
252 const sstring& base_path)
253 : api_registry_base(routes, file_directory, base_path) {
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));
262 return make_ready_future<std::unique_ptr<reply>>(std::move(rep));
265 virtual void reg(doc_entry&& f) {
266 _docs.add_api(std::move(f));
269 virtual void add_definition(doc_entry&& f) {
270 _docs.add_definition(std::move(f));
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);
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);
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) {
291 void set_api_doc(routes& r) {
292 new api_registry_20(r, _file_directory, _base_path);
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.
300 void register_function(routes& r, doc_entry&& f) {
301 auto h = get_register_base(r);
303 h->reg(std::move(f));
307 * \brief register an API
309 void register_api_file(routes& r, const sstring& api) {
310 register_function(r, get_file_reader(_file_directory + "/" + api + ".json"));
315 * Add a footer doc_entry
317 void add_definition(routes& r, doc_entry&& f) {
318 auto h = get_register_base(r);
320 h->add_definition(std::move(f));
326 * Add a definition file
328 void add_definitions_file(routes& r, const sstring& file) {
329 add_definition(r, get_file_reader(_file_directory + file + ".def.json" ));