Commit 37c872c6 authored by octal's avatar octal

Added Swagger implementation

parent dfd57229
[submodule "thirdparty/rapidjson"]
path = thirdparty/rapidjson
url = https://github.com/miloyip/rapidjson.git
...@@ -16,6 +16,9 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include) ...@@ -16,6 +16,9 @@ include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
file(GLOB HEADER_FILES "include/*.h") file(GLOB HEADER_FILES "include/*.h")
install(FILES ${HEADER_FILES} DESTINATION include/rest) install(FILES ${HEADER_FILES} DESTINATION include/rest)
add_subdirectory (thirdparty)
include_directories(${RapidJSON_SOURCE_DIR}/include)
add_subdirectory (src) add_subdirectory (src)
include_directories (src) include_directories (src)
......
...@@ -37,6 +37,13 @@ public: ...@@ -37,6 +37,13 @@ public:
void start() { void start() {
router.initFromDescription(desc); router.initFromDescription(desc);
Rest::Swagger swagger(desc);
swagger
.uiPath("/doc")
.uiDirectory("/home/octal/code/web/swagger-ui-2.1.4/dist/")
.apiPath("/banker-api.json")
.install(router);
httpEndpoint->setHandler(router.handler()); httpEndpoint->setHandler(router.handler());
httpEndpoint->serve(); httpEndpoint->serve();
} }
...@@ -97,6 +104,7 @@ private: ...@@ -97,6 +104,7 @@ private:
.produces(MIME(Application, Json)) .produces(MIME(Application, Json))
.response(Http::Code::Ok, "Budget has been added to the account") .response(Http::Code::Ok, "Budget has been added to the account")
.response(backendErrorResponse); .response(backendErrorResponse);
} }
void retrieveAllAccounts(const Rest::Request& req, Http::ResponseWriter response) { void retrieveAllAccounts(const Rest::Request& req, Http::ResponseWriter response) {
......
...@@ -57,23 +57,6 @@ private: ...@@ -57,23 +57,6 @@ private:
void setupRoutes() { void setupRoutes() {
using namespace Net::Rest; using namespace Net::Rest;
#if 0
Rest::Description apiDescription("my-api");
auto versionPath = desc.path("/v1");
versionPath
.route(desc.get("/record/:name/:value?"))
.bind(&StatsEndpoint::doRecordMetric, this)
.produces(MIME(Text, Plain))
.consumes(MIME(Star, Star))
.parameter<Rest::Type::String>("name", "The name of the metric to record", Rest::Flag::Required)
.parameter<Rest::Type::String>("value", "The value to record", Rest::Flag::Optional)
.response(Http::Code::Created, "The metric has been created")
.response(Http::Code::Not_Found, "The creation of the metric failed");
#endif
Routes::Post(router, "/record/:name/:value?", Routes::bind(&StatsEndpoint::doRecordMetric, this)); Routes::Post(router, "/record/:name/:value?", Routes::bind(&StatsEndpoint::doRecordMetric, this));
Routes::Get(router, "/value/:name", Routes::bind(&StatsEndpoint::doGetMetric, this)); Routes::Get(router, "/value/:name", Routes::bind(&StatsEndpoint::doGetMetric, this));
Routes::Get(router, "/ready", Routes::bind(&Generic::handleReady)); Routes::Get(router, "/ready", Routes::bind(&Generic::handleReady));
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "mime.h" #include "mime.h"
#include "optional.h" #include "optional.h"
#include "router.h" #include "router.h"
#include "iterator_adapter.h"
namespace Net { namespace Net {
...@@ -115,6 +116,27 @@ struct Info { ...@@ -115,6 +116,27 @@ struct Info {
Optional<Contact> contact; Optional<Contact> contact;
Optional<License> license; Optional<License> license;
template<typename Writer>
void serialize(Writer& writer) const {
writer.String("info");
writer.StartObject();
{
writer.String("title");
writer.String(title.c_str());
writer.String("version");
writer.String(version.c_str());
if (!description.empty()) {
writer.String("description");
writer.String(description.c_str());
}
if (!termsOfService.empty()) {
writer.String("termsOfService");
writer.String(termsOfService.c_str());
}
}
writer.EndObject();
}
}; };
struct InfoBuilder { struct InfoBuilder {
...@@ -161,10 +183,27 @@ struct Parameter { ...@@ -161,10 +183,27 @@ struct Parameter {
return p; return p;
} }
template<typename Writer>
void serialize(Writer& writer) const {
writer.StartObject();
{
writer.String("name");
writer.String(name.c_str());
writer.String("description");
writer.String(description.c_str());
writer.String("required");
writer.Bool(required);
writer.String("type");
writer.String(type->typeName());
}
writer.EndObject();
}
std::string name; std::string name;
std::string description; std::string description;
bool required; bool required;
std::shared_ptr<DataType> type; std::shared_ptr<DataType> type;
}; };
struct Response { struct Response {
...@@ -172,6 +211,18 @@ struct Response { ...@@ -172,6 +211,18 @@ struct Response {
Http::Code statusCode; Http::Code statusCode;
std::string description; std::string description;
template<typename Writer>
void serialize(Writer& writer) const {
auto code = std::to_string(static_cast<uint32_t>(statusCode));
writer.String(code.c_str());
writer.StartObject();
{
writer.String("description");
writer.String(description.c_str());
}
writer.EndObject();
}
}; };
struct ResponseBuilder { struct ResponseBuilder {
...@@ -203,6 +254,104 @@ struct Path { ...@@ -203,6 +254,104 @@ struct Path {
std::vector<Response> responses; std::vector<Response> responses;
Route::Handler handler; Route::Handler handler;
template<typename Writer>
void serialize(Writer& writer) const {
auto serializeMimes = [&](const char* name, const std::vector<Http::Mime::MediaType>& mimes) {
if (!mimes.empty()) {
writer.String(name);
writer.StartArray();
{
for (const auto& mime: mimes) {
auto str = mime.toString();
writer.String(str.c_str());
}
}
writer.EndArray();
}
};
writer.String(methodString(method));
writer.StartObject();
{
writer.String("description");
writer.String(description.c_str());
serializeMimes("consumes", consumeMimes);
serializeMimes("produces", produceMimes);
// @Todo: create a template to serialize vectors in a generic way
if (!parameters.empty()) {
writer.String("parameters");
writer.StartArray();
{
for (const auto& parameter: parameters) {
parameter.serialize(writer);
}
}
writer.EndArray();
}
if (!responses.empty()) {
writer.String("responses");
writer.StartArray();
for (const auto& response: responses) {
response.serialize(writer);
}
writer.EndArray();
}
}
writer.EndObject();
}
};
class PathGroup {
public:
typedef std::unordered_map<std::string, std::vector<Path>> Map;
typedef Map::iterator iterator;
typedef Map::const_iterator const_iterator;
typedef std::vector<Path>::iterator group_iterator;
typedef FlatMapIteratorAdapter<Map> flat_iterator;
bool hasPath(const std::string& name, Http::Method method) const;
bool hasPath(const Path& path) const;
std::vector<Path> paths(const std::string& name) const;
Optional<Path> path(const std::string& name, Http::Method method) const;
group_iterator add(Path path);
template<typename... Args>
group_iterator emplace(Args&& ...args) {
return add(Path(std::forward<Args>(args)...));
}
const_iterator begin() const;
const_iterator end() const;
flat_iterator flatBegin() const;
flat_iterator flatEnd() const;
template<typename Writer>
void serialize(Writer& writer) const {
writer.String("paths");
writer.StartArray();
{
for (const auto& group: groups) {
writer.String(group.first.c_str());
writer.StartObject();
{
for (const auto& path: group.second) {
path.serialize(writer);
}
}
writer.EndObject();
}
}
writer.EndArray();
}
private:
Map groups;
}; };
struct PathBuilder { struct PathBuilder {
...@@ -246,6 +395,8 @@ struct PathBuilder { ...@@ -246,6 +395,8 @@ struct PathBuilder {
path_->handler = [=](const Rest::Request& request, Http::ResponseWriter response) { path_->handler = [=](const Rest::Request& request, Http::ResponseWriter response) {
CALL_MEMBER_FN(obj, func)(request, std::move(response)); CALL_MEMBER_FN(obj, func)(request, std::move(response));
return Route::Result::Ok;
}; };
#undef CALL_MEMBER_FN #undef CALL_MEMBER_FN
...@@ -258,6 +409,8 @@ struct PathBuilder { ...@@ -258,6 +409,8 @@ struct PathBuilder {
path_->handler = [=](const Rest::Request& request, Http::ResponseWriter response) { path_->handler = [=](const Rest::Request& request, Http::ResponseWriter response) {
func(request, std::move(response)); func(request, std::move(response));
return Route::Result::Ok;
}; };
return *this; return *this;
...@@ -269,7 +422,7 @@ private: ...@@ -269,7 +422,7 @@ private:
}; };
struct SubPath { struct SubPath {
SubPath(std::string prefix, std::vector<Path>* paths); SubPath(std::string prefix, PathGroup* paths);
PathBuilder route(std::string path, Http::Method method, std::string description = ""); PathBuilder route(std::string path, Http::Method method, std::string description = "");
PathBuilder route(PathFragment fragment, std::string description = ""); PathBuilder route(PathFragment fragment, std::string description = "");
...@@ -283,7 +436,7 @@ struct SubPath { ...@@ -283,7 +436,7 @@ struct SubPath {
std::string prefix; std::string prefix;
std::vector<Parameter> parameters; std::vector<Parameter> parameters;
std::vector<Path>* paths; PathGroup* paths;
}; };
} // namespace Schema } // namespace Schema
...@@ -316,14 +469,58 @@ public: ...@@ -316,14 +469,58 @@ public:
Schema::ResponseBuilder response(Http::Code statusCode, std::string description); Schema::ResponseBuilder response(Http::Code statusCode, std::string description);
std::vector<Schema::Path> paths() const { return paths_; } Schema::PathGroup paths() const { return paths_; }
template<typename Writer>
void serialize(Writer& writer) const {
writer.StartObject();
{
info_.serialize(writer);
if (!host_.empty()) {
writer.String("host");
writer.String(host_.c_str());
}
if (!schemes_.empty()) {
writer.String("schemes");
writer.StartArray();
{
for (const auto& scheme: schemes_) {
writer.String(scheme.c_str());
}
}
writer.EndArray();
}
paths_.serialize(writer);
}
writer.EndObject();
}
private: private:
Schema::Info info_; Schema::Info info_;
std::string host_; std::string host_;
std::vector<std::string> schemes_; std::vector<std::string> schemes_;
std::vector<Schema::Path> paths_; Schema::PathGroup paths_;
};
class Swagger {
public:
Swagger(const Description& description)
: description_(description)
{ }
Swagger& uiPath(std::string path);
Swagger& uiDirectory(std::string dir);
Swagger& apiPath(std::string path);
void install(Rest::Router& router);
private:
Description description_;
std::string uiPath_;
std::string uiDirectory_;
std::string apiPath_;
}; };
} // namespace Rest } // namespace Rest
......
...@@ -352,6 +352,23 @@ private: ...@@ -352,6 +352,23 @@ private:
Net::Port port_; Net::Port port_;
}; };
class Location : public Header {
public:
NAME("Location")
Location() { }
explicit Location(const std::string& location);
void parse(const std::string& data);
void write(std::ostream& os) const;
std::string location() const { return location_; }
private:
std::string location_;
};
class Server : public Header { class Server : public Header {
public: public:
NAME("Server") NAME("Server")
......
/*
Mathieu Stefani, 28 février 2016
A collection of sample iterator adapters
*/
#pragma once
template<typename Map>
struct FlatMapIteratorAdapter {
typedef typename Map::key_type Key;
typedef typename Map::mapped_type Value;
typedef typename Map::const_iterator const_iterator;
FlatMapIteratorAdapter(const_iterator it)
: it(it)
{ }
FlatMapIteratorAdapter operator++() {
++it;
return FlatMapIteratorAdapter(it);
}
const Value& operator*() {
return it->second;
}
bool operator==(FlatMapIteratorAdapter other) {
return other.it == it;
}
bool operator!=(FlatMapIteratorAdapter other) {
return !(*this == other);
}
private:
const_iterator it;
};
template<typename Map>
FlatMapIteratorAdapter<Map>
makeFlatMapIterator(const Map&, typename Map::const_iterator it) {
return FlatMapIteratorAdapter<Map>(it);
}
...@@ -61,7 +61,9 @@ private: ...@@ -61,7 +61,9 @@ private:
class Request; class Request;
struct Route { struct Route {
typedef std::function<void (const Request&, Net::Http::ResponseWriter)> Handler; enum class Result { Ok, Failure };
typedef std::function<Result (const Request&, Net::Http::ResponseWriter)> Handler;
Route(std::string resource, Http::Method method, Handler handler) Route(std::string resource, Http::Method method, Handler handler)
: resource_(std::move(resource)) : resource_(std::move(resource))
...@@ -130,10 +132,42 @@ private: ...@@ -130,10 +132,42 @@ private:
}; };
namespace Private { namespace Private {
class RouterHandler;
}
class Router {
public:
enum class Status { Match, NotFound };
static Router fromDescription(const Rest::Description& desc);
std::shared_ptr<Private::RouterHandler>
handler() const;
void initFromDescription(const Rest::Description& desc);
void get(std::string resource, Route::Handler handler);
void post(std::string resource, Route::Handler handler);
void put(std::string resource, Route::Handler handler);
void del(std::string resource, Route::Handler handler);
void addCustomHandler(Route::Handler handler);
Status route(const Http::Request& request, Http::ResponseWriter response);
private:
void addRoute(Http::Method method, std::string resource, Route::Handler handler);
std::unordered_map<Http::Method, std::vector<Route>> routes;
std::vector<Route::Handler> customHandlers;
};
class HttpHandler : public Net::Http::Handler { namespace Private {
class RouterHandler : public Net::Http::Handler {
public: public:
HttpHandler(const std::unordered_map<Http::Method, std::vector<Route>>& routes); RouterHandler(const Rest::Router& router);
void onRequest( void onRequest(
const Http::Request& req, const Http::Request& req,
...@@ -141,16 +175,16 @@ namespace Private { ...@@ -141,16 +175,16 @@ namespace Private {
private: private:
std::shared_ptr<Net::Tcp::Handler> clone() const { std::shared_ptr<Net::Tcp::Handler> clone() const {
return std::make_shared<HttpHandler>(routes); return std::make_shared<RouterHandler>(router);
} }
std::unordered_map<Http::Method, std::vector<Route>> routes; Rest::Router router;
}; };
} }
class Request : public Http::Request { class Request : public Http::Request {
public: public:
friend class Private::HttpHandler; friend class Router;
bool hasParam(std::string name) const; bool hasParam(std::string name) const;
TypedParam param(std::string name) const; TypedParam param(std::string name) const;
...@@ -168,27 +202,6 @@ private: ...@@ -168,27 +202,6 @@ private:
std::vector<TypedParam> splats_; std::vector<TypedParam> splats_;
}; };
class Router {
public:
static Router fromDescription(const Rest::Description& desc);
std::shared_ptr<Private::HttpHandler>
handler() const {
return std::make_shared<Private::HttpHandler>(routes);
}
void initFromDescription(const Rest::Description& desc);
void get(std::string resource, Route::Handler handler);
void post(std::string resource, Route::Handler handler);
void put(std::string resource, Route::Handler handler);
void del(std::string resource, Route::Handler handler);
private:
void addRoute(Http::Method method, std::string resource, Route::Handler handler);
std::unordered_map<Http::Method, std::vector<Route>> routes;
};
namespace Routes { namespace Routes {
...@@ -231,6 +244,8 @@ namespace Routes { ...@@ -231,6 +244,8 @@ namespace Routes {
return [=](const Rest::Request& request, Http::ResponseWriter response) { return [=](const Rest::Request& request, Http::ResponseWriter response) {
CALL_MEMBER_FN(obj, func)(request, std::move(response)); CALL_MEMBER_FN(obj, func)(request, std::move(response));
return Route::Result::Ok;
}; };
#undef CALL_MEMBER_FN #undef CALL_MEMBER_FN
...@@ -242,6 +257,8 @@ namespace Routes { ...@@ -242,6 +257,8 @@ namespace Routes {
return [=](const Rest::Request& request, Http::ResponseWriter response) { return [=](const Rest::Request& request, Http::ResponseWriter response) {
func(request, std::move(response)); func(request, std::move(response));
return Route::Result::Ok;
}; };
} }
......
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
*/ */
#include "description.h" #include "description.h"
#include "http_header.h"
#include <sstream> #include <sstream>
#include <algorithm>
#include "rapidjson/prettywriter.h"
namespace Net { namespace Net {
...@@ -27,10 +30,10 @@ License::License( ...@@ -27,10 +30,10 @@ License::License(
{ } { }
Info::Info( Info::Info(
std::string title, std::string description, std::string version) std::string title, std::string version, std::string description)
: title(std::move(title)) : title(std::move(title))
, description(std::move(description))
, version(std::move(version)) , version(std::move(version))
, description(std::move(description))
{ } { }
PathFragment::PathFragment( PathFragment::PathFragment(
...@@ -46,12 +49,77 @@ Path::Path( ...@@ -46,12 +49,77 @@ Path::Path(
, description(std::move(description)) , description(std::move(description))
{ } { }
bool
PathGroup::hasPath(const std::string& name, Http::Method method) const {
auto group = paths(name);
auto it = std::find_if(std::begin(group), std::end(group), [&](const Path& p) {
return p.method == method;
});
return it != std::end(group);
}
bool
PathGroup::hasPath(const Path& path) const {
return hasPath(path.value, path.method);
}
std::vector<Path>
PathGroup::paths(const std::string& name) const {
auto it = groups.find(name);
if (it == std::end(groups))
return std::vector<Path> { };
return it->second;
}
Optional<Path>
PathGroup::path(const std::string& name, Http::Method method) const {
auto group = paths(name);
auto it = std::find_if(std::begin(group), std::end(group), [&](const Path& p) {
return p.method == method;
});
if (it != std::end(group)) {
return Some(*it);
}
return None();
}
PathGroup::group_iterator
PathGroup::add(Path path) {
if (hasPath(path))
return PathGroup::group_iterator { };
auto &group = groups[path.value];
return group.insert(group.end(), std::move(path));
}
PathGroup::const_iterator
PathGroup::begin() const {
return groups.begin();
}
PathGroup::const_iterator
PathGroup::end() const {
return groups.end();
}
PathGroup::flat_iterator
PathGroup::flatBegin() const {
return makeFlatMapIterator(groups, begin());
}
PathGroup::flat_iterator
PathGroup::flatEnd() const {
return makeFlatMapIterator(groups, end());
}
PathBuilder::PathBuilder(Path* path) PathBuilder::PathBuilder(Path* path)
: path_(path) : path_(path)
{ } { }
SubPath::SubPath( SubPath::SubPath(
std::string prefix, std::vector<Path>* paths) std::string prefix, PathGroup* paths)
: prefix(std::move(prefix)) : prefix(std::move(prefix))
, paths(paths) , paths(paths)
{ } { }
...@@ -62,9 +130,9 @@ SubPath::route(std::string name, Http::Method method, std::string description) { ...@@ -62,9 +130,9 @@ SubPath::route(std::string name, Http::Method method, std::string description) {
Path path(std::move(fullPath), method, std::move(description)); Path path(std::move(fullPath), method, std::move(description));
std::copy(std::begin(parameters), std::end(parameters), std::back_inserter(path.parameters)); std::copy(std::begin(parameters), std::end(parameters), std::back_inserter(path.parameters));
paths->push_back(std::move(path)); auto it = paths->add(std::move(path));
return PathBuilder(&paths->back()); return PathBuilder(&*it);
} }
PathBuilder PathBuilder
...@@ -164,8 +232,8 @@ Description::path(std::string name) { ...@@ -164,8 +232,8 @@ Description::path(std::string name) {
Schema::PathBuilder Schema::PathBuilder
Description::route(std::string name, Http::Method method, std::string description) { Description::route(std::string name, Http::Method method, std::string description) {
paths_.emplace_back(std::move(name), method, std::move(description)); auto it = paths_.emplace(std::move(name), method, std::move(description));
return Schema::PathBuilder(&paths_.back()); return Schema::PathBuilder(&*it);
} }
Schema::PathBuilder Schema::PathBuilder
...@@ -179,6 +247,127 @@ Description::response(Http::Code statusCode, std::string description) { ...@@ -179,6 +247,127 @@ Description::response(Http::Code statusCode, std::string description) {
return builder; return builder;
} }
Swagger&
Swagger::uiPath(std::string path) {
uiPath_ = std::move(path);
return *this;
}
Swagger&
Swagger::uiDirectory(std::string dir) {
uiDirectory_ = std::move(dir);
return *this;
}
Swagger&
Swagger::apiPath(std::string path) {
apiPath_ = std::move(path);
return *this;
}
void
Swagger::install(Rest::Router& router) {
Route::Handler uiHandler = [=](const Rest::Request& req, Http::ResponseWriter response) {
auto res = req.resource();
/*
* @Export might be useful for routing also. Make it public
*/
struct Path {
Path(const std::string& value)
: value(value)
, trailingSlashValue(value)
{
if (trailingSlashValue.back() != '/')
trailingSlashValue += '/';
}
static bool hasTrailingSlash(const Rest::Request& req) {
auto res = req.resource();
return res.back() == '/';
}
std::string stripPrefix(const Rest::Request& req) {
auto res = req.resource();
if (!res.compare(0, value.size(), value)) {
return res.substr(value.size());
}
return res;
}
bool matches(const Rest::Request& req) {
auto res = req.resource();
if (value == res)
return true;
if (res == trailingSlashValue)
return true;
return false;
}
bool isPrefix(const Rest::Request& req) {
auto res = req.resource();
return !res.compare(0, value.size(), value);
}
const std::string& withTrailingSlash() const {
return trailingSlashValue;
}
std::string join(const std::string& value) const {
std::string val;
if (value[0] == '/') val = value.substr(1);
else val = value;
return trailingSlashValue + val;
}
private:
std::string value;
std::string trailingSlashValue;
};
Path ui(uiPath_);
Path uiDir(uiDirectory_);
if (ui.matches(req)) {
if (!Path::hasTrailingSlash(req)) {
response
.headers()
.add<Http::Header::Location>(uiPath_ + '/');
response.send(Http::Code::Moved_Permanently);
} else {
auto index = uiDir.join("index.html");
Http::serveFile(response, index.c_str());
}
return Route::Result::Ok;
}
else if (ui.isPrefix(req)) {
auto file = ui.stripPrefix(req);
auto path = uiDir.join(file);
Http::serveFile(response, path.c_str());
return Route::Result::Ok;
}
else if (res == apiPath_) {
rapidjson::StringBuffer sb;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(sb);
description_.serialize(writer);
response.send(Http::Code::Ok, sb.GetString(), MIME(Application, Json));
return Route::Result::Ok;
}
return Route::Result::Failure;
};
router.addCustomHandler(uiHandler);
}
} // namespace Rest } // namespace Rest
......
...@@ -359,6 +359,20 @@ Host::write(std::ostream& os) const { ...@@ -359,6 +359,20 @@ Host::write(std::ostream& os) const {
} }
} }
Location::Location(const std::string& location)
: location_(location)
{ }
void
Location::parse(const std::string& data) {
location_ = data;
}
void
Location::write(std::ostream& os) const {
os << location_;
}
void void
UserAgent::parse(const std::string& data) { UserAgent::parse(const std::string& data) {
ua_ = data; ua_ = data;
......
...@@ -31,6 +31,7 @@ RegisterHeader(ContentType); ...@@ -31,6 +31,7 @@ RegisterHeader(ContentType);
RegisterHeader(Date); RegisterHeader(Date);
RegisterHeader(Expect); RegisterHeader(Expect);
RegisterHeader(Host); RegisterHeader(Host);
RegisterHeader(Location);
RegisterHeader(Server); RegisterHeader(Server);
RegisterHeader(UserAgent); RegisterHeader(UserAgent);
......
...@@ -210,30 +210,17 @@ Route::match(const std::string& req) const ...@@ -210,30 +210,17 @@ Route::match(const std::string& req) const
namespace Private { namespace Private {
HttpHandler::HttpHandler( RouterHandler::RouterHandler(const Rest::Router& router)
const std::unordered_map<Http::Method, std::vector<Route>>& routes) : router(router)
: routes(routes)
{ {
} }
void void
HttpHandler::onRequest( RouterHandler::onRequest(
const Http::Request& req, const Http::Request& req,
Http::ResponseWriter response) Http::ResponseWriter response)
{ {
auto& r = routes[req.method()]; router.route(req, std::move(response));
for (const auto& route: r) {
bool match;
std::vector<TypedParam> params;
std::vector<TypedParam> splats;
std::tie(match, params, splats) = route.match(req);
if (match) {
route.invokeHandler(Request(req, std::move(params), std::move(splats)), std::move(response));
return;
}
}
response.send(Http::Code::Not_Found, "Could not find a matching route");
} }
} // namespace Private } // namespace Private
...@@ -245,12 +232,20 @@ Router::fromDescription(const Rest::Description& desc) { ...@@ -245,12 +232,20 @@ Router::fromDescription(const Rest::Description& desc) {
return router; return router;
} }
std::shared_ptr<Private::RouterHandler>
Router::handler() const {
return std::make_shared<Private::RouterHandler>(*this);
}
void void
Router::initFromDescription(const Rest::Description& desc) { Router::initFromDescription(const Rest::Description& desc) {
auto paths = desc.paths(); auto paths = desc.paths();
for (auto it = paths.flatBegin(), end = paths.flatEnd(); it != end; ++it) {
const auto& paths = *it;
for (const auto& path: paths) { for (const auto& path: paths) {
addRoute(path.method, std::move(path.value), std::move(path.handler)); addRoute(path.method, std::move(path.value), std::move(path.handler));
} }
}
} }
void void
...@@ -273,6 +268,40 @@ Router::del(std::string resource, Route::Handler handler) { ...@@ -273,6 +268,40 @@ Router::del(std::string resource, Route::Handler handler) {
addRoute(Http::Method::Delete, std::move(resource), std::move(handler)); addRoute(Http::Method::Delete, std::move(resource), std::move(handler));
} }
void
Router::addCustomHandler(Route::Handler handler) {
customHandlers.push_back(std::move(handler));
}
Router::Status
Router::route(const Http::Request& req, Http::ResponseWriter response) {
auto& r = routes[req.method()];
for (const auto& route: r) {
bool match;
std::vector<TypedParam> params;
std::vector<TypedParam> splats;
std::tie(match, params, splats) = route.match(req);
if (match) {
route.invokeHandler(Request(req, std::move(params), std::move(splats)), std::move(response));
return Router::Status::Match;
}
}
/* @Major @FixMe:
* original response object should not be moved here. Instead, we should provide some sort of clone() method
* to explicit request a copy and then move that clone
*/
for (const auto& handler: customHandlers) {
auto result = handler(Request(req, std::vector<TypedParam>(), std::vector<TypedParam>()), std::move(response));
if (result == Route::Result::Ok) return Router::Status::Match;
}
throw std::runtime_error("Fix me");
response.send(Http::Code::Not_Found, "Could not find a matching route");
return Router::Status::NotFound;
}
void void
Router::addRoute(Http::Method method, std::string resource, Route::Handler handler) Router::addRoute(Http::Method method, std::string resource, Route::Handler handler)
{ {
......
...@@ -75,7 +75,7 @@ bool matchSplat( ...@@ -75,7 +75,7 @@ bool matchSplat(
Rest::Route Rest::Route
makeRoute(std::string value) { makeRoute(std::string value) {
auto noop = [](const Net::Http::Request&, Net::Http::Response) { }; auto noop = [](const Net::Http::Request&, Net::Http::Response) { return Rest::Route::Result::Ok; };
return Rest::Route(value, Net::Http::Method::Get, noop); return Rest::Route(value, Net::Http::Method::Get, noop);
} }
......
add_subdirectory(rapidjson)
Subproject commit ef5e74a200f5feab8714619dc1136ca9e96de004
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment