aboutsummaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
authorneodarz <neodarz@neodarz.net>2019-09-06 18:04:40 +0200
committerneodarz <neodarz@neodarz.net>2019-09-06 18:04:40 +0200
commit1b922f7319f821fe3daf302a8c68131aaa94f586 (patch)
treedebf82b507174f158b9cb4e030b3f82d31c44a64 /api
parent3185dd695d58bb96672d4f33c4528bf8b361707f (diff)
downloadumosapicpp-1b922f7319f821fe3daf302a8c68131aaa94f586.tar.xz
umosapicpp-1b922f7319f821fe3daf302a8c68131aaa94f586.zip
Change code to use Restbed
Diffstat (limited to '')
-rw-r--r--api/umosapi.cpp480
-rw-r--r--api/umosapi.h105
2 files changed, 462 insertions, 123 deletions
diff --git a/api/umosapi.cpp b/api/umosapi.cpp
index 83965a3..af58b37 100644
--- a/api/umosapi.cpp
+++ b/api/umosapi.cpp
@@ -17,6 +17,12 @@
#include "../db/mongo_access.h"
#include "../db/uobject.h"
+#include <regex>
+#include <sstream>
+#include <iomanip>
+#include <fstream>
+#include <streambuf>
+
mongo_access mongo;
@@ -30,133 +36,395 @@ using bsoncxx::builder::stream::open_document;
using json = nlohmann::json;
using namespace std;
-using namespace Pistache;
+using namespace restbed;
-namespace Generic {
- void handleReady(const Rest::Request&, Http::ResponseWriter response) {
- response.send(Http::Code::Ok, "1");
- }
-}
+UmosapiService::UmosapiService() {}
-UmosapiService::UmosapiService(Address addr)
- : httpEndpoint(std::make_shared<Http::Endpoint>(addr))
- , desc("Unity Mongo Save API", "0.1")
-{ }
+void UmosapiService::init() {
-void UmosapiService::init(size_t thr = 2) {
auto uri = mongocxx::uri{config["mongoURI"]};
mongo.configure(std::move(uri));
- auto opts = Http::Endpoint::options()
- .threads(thr);
- httpEndpoint->init(opts);
- createDescription();
-}
-
-void UmosapiService::start(std::string swaggerui) {
- router.initFromDescription(desc);
-
- Rest::Swagger swagger(desc);
- swagger
- .uiPath("/doc")
- .uiDirectory(swaggerui)
- .apiPath("/api")
- .serializer(&Rest::Serializer::rapidJson)
- .install(router);
-
- httpEndpoint->setHandler(router.handler());
- httpEndpoint->serve();
-}
-
-void UmosapiService::createDescription() {
- desc
- .info()
- .license("Apache", "https://neodarz.net");
-
- auto backendErrorResponse =
- desc.response(Http::Code::Internal_Server_Error, "Backend is dead, it's the end of the world");
-
- desc
- .schemes(Rest::Scheme::Http)
- .basePath("/v1")
- .produces(MIME(Application, Json))
- .consumes(MIME(Application, Json));
-
- desc
- .route(desc.get("/ready"))
- .bind(&Generic::handleReady)
- .response(Http::Code::Ok, "Api started")
- .response(backendErrorResponse);
-
- auto versionPath = desc.path("/v1");
-
- versionPath
- .route(desc.get("/:mcollection"))
- .bind(&UmosapiService::UmosapiService::retrieveAll, this)
- .produces(MIME(Application, Json))
- .parameter<Rest::Type::String>("mcollection", "Name of the collection where the uobjects are located")
- .response(Http::Code::Ok, "List of uobjects")
- .response(backendErrorResponse);
-
- versionPath
- .route(desc.post("/:mcollection"))
- .bind(&UmosapiService::UmosapiService::addUObject, this)
- .produces(MIME(Application, Json))
- .parameter<Rest::Type::String>("mcollection", "Name of the collection where the uobjects are located")
- .response(Http::Code::Ok, "Uobject created")
- .response(backendErrorResponse);
-
- versionPath
- .route(desc.del("/:mcollection/:oid"))
- .bind(&UmosapiService::UmosapiService::deleteUObject, this)
- .produces(MIME(Application, Json))
- .parameter<Rest::Type::String>("mcollection", "Name of the collection where the uobjects are located")
- .parameter<Rest::Type::String>("oid", "MongoDB oid of the uobject")
- .response(Http::Code::Ok, "Uobject deleted")
- .response(backendErrorResponse);
-
- versionPath
- .route(desc.get("/:mcollection/:key/:value"))
- .bind(&UmosapiService::UmosapiService::searchUObjectByKeyValue, this)
- .produces(MIME(Application, Json))
- .parameter<Rest::Type::String>("mcollection", "Name of the collection where the uobjects are located")
- .parameter<Rest::Type::String>("key", "Key of uobject to search, ex.: kil or total.kill")
- .parameter<Rest::Type::String>("value", "Value of uobject to search, ex.: 12")
- .response(Http::Code::Ok, "Uobject found")
- .response(backendErrorResponse);
-}
-
-void UmosapiService::retrieveAll(const Rest::Request& request, Http::ResponseWriter response) {
- auto jsonObjects = json_object_new_array();
- auto json_string = uobject::retrieveAll(request.param(":mcollection").as<string>(), jsonObjects);
- json_object_put(jsonObjects);
+ createResource();
+ ofstream swagger_json;
+ swagger_json.open(config["swaggerui"] + "/swagger.json");
+ swagger_json << _swagger.dump();
+ swagger_json.close();
+}
- response.send(Http::Code::Ok, json_string, MIME(Application, Json));
+void UmosapiService::start(int port, int thr) {
+ auto settings = make_shared< Settings >();
+ settings->set_port( port );
+ settings->set_worker_limit( thr );
+ settings->set_default_header("Connection", "close");
+ _service.start(settings);
}
-void UmosapiService::addUObject(const Rest::Request& request, Http::ResponseWriter response) {
- auto jsonObject = json_object_new_object();
+void service_error_handler( const int, const exception& e, const shared_ptr< Session > session )
+{
+ std::string message = "Backend Service is dead: ";
+ message += e.what();
+ if ( session->is_open( ) )
+ session->close( 500, message, { { "Content-Length", ::to_string(message.length()) } } );
+ fprintf( stderr, "ERROR: %s.\n", message.c_str() );
+}
- auto json_string = uobject::add(request.param(":mcollection").as<string>(), jsonObject, request.body().c_str());
+void resource_error_handler( const int, const exception& e, const shared_ptr< Session > session )
+{
+ std::string message = "Backend Resource is dead: ";
+ message += e.what();
+ if ( session->is_open( ) )
+ session->close( 500, message, { { "Content-Length", ::to_string(message.length()) } } );
+ fprintf( stderr, "ERROR: %s.\n", message.c_str() );
+}
- response.send(Http::Code::Ok, json_string, MIME(Application, Json));
- json_object_put(jsonObject);
+void faulty_method_handler( const shared_ptr< Session > )
+{
+ throw SERVICE_UNAVAILABLE;
+}
+
+void is_ready(const shared_ptr<Session> session)
+{
+ session->close( OK, "1", { { "Content-Length", "1"}});
+}
+
+/*
+ * Add new route in service
+ * service: the service pointer
+ * route: path of the route
+ * http_word: word http who defin REST request
+ * callback: function to call when the route is requested
+ * error_callback: error to show if everything is broken
+ * tags: array of tags
+ */
+void UmosapiService::desc(std::string route, std::string http_word, const std::function< void ( const std::shared_ptr< Session > ) >& callback, const std::function< void(int, const std::exception&, std::shared_ptr<restbed::Session>) >& error_callback, tag tags[]) {
+ auto resource = make_shared< Resource > ();
+ resource->set_path(route);
+ resource->set_method_handler(http_word, callback);
+ resource->set_error_handler( error_callback );
+
+ _service.publish(resource);
+}
+
+void UmosapiService::description(std::string description) {
+ _swagger["info"]["description"] = description;
+}
+
+void UmosapiService::title(std::string title) {
+ _swagger["info"]["title"] = title;
+}
+
+void UmosapiService::version(std::string version) {
+ _swagger["info"]["version"] = version;
+}
+
+void UmosapiService::basePath(std::string basePath) {
+ _swagger["swagger"] = "2.0";
+ _swagger["basePath"] = basePath;
+}
+
+void UmosapiService::host(std::string host) {
+ _swagger["host"] = host;
+}
+
+void UmosapiService::atag(std::string name, std::string description) {
+ struct tag the_tag;
+ the_tag.name = name;
+ the_tag.description = description;
+
+ _tags.push_back(the_tag);
+ _swagger["tags"].push_back({ {"name",the_tag.name},{"description", the_tag.description} });
+
+}
+
+void UmosapiService::scheme(std::string scheme) {
+ _swagger["schemes"].push_back(scheme);
+}
+
+void UmosapiService::set_path(std::string route) {
+ _resource = make_shared< Resource > ();
+ _resource->set_path(route);
+ std::regex parameter(":.*?}");
+ std::regex base_path("^/v2");
+ auto tmp_route = std::regex_replace (route,parameter,"}");
+ auto final_route = std::regex_replace (tmp_route,base_path,"");
+ _path = Path{final_route};
+ _swagger["paths"][final_route] = {};
+}
+
+void UmosapiService::set_method_handler(std::string http_word, const std::function< void ( const std::shared_ptr< Session > ) >& callback) {
+ _resource->set_method_handler(http_word, callback);
+ std::locale loc;
+ for (auto& c : http_word) {
+ c = tolower(c);
+ }
+ _path.words.push_back(HttpWord{http_word});
+ _swagger["paths"][_path.name][http_word]["description"] = "";
+ _swagger["paths"][_path.name][http_word]["operationId"] = "";
+ _swagger["paths"][_path.name][http_word]["summary"] = "";
+}
+
+void UmosapiService::set_error_handler(const std::function< void(int, const std::exception&, std::shared_ptr<restbed::Session>) >& error_callback) {
+ _resource->set_error_handler( error_callback );
+}
+
+void UmosapiService::publish() {
+ for (auto& http_word: _path.words) {
+ auto responses = _swagger["paths"][_path.name][http_word.name]["responses"];
+ if (responses.find("200") == responses.end()) {
+ _swagger["paths"][_path.name][http_word.name]["responses"]["200"]["description"] = "All is fine.";
+ }
+ }
+ _service.publish(_resource);
+}
+
+void UmosapiService::definition(std::string name, std::string type) {
+ _definition = Definition{name, type};
+ _definitions.defs.push_back(_definition);
+ _swagger["definitions"][name]["type"] = type;
+}
+
+void UmosapiService::propertie(std::string name, std::string format, std::string type, std::string required) {
+ _definition.props.push_back(Propertie{name, format, type, required});
+ _swagger["definitions"][_definition.name]["properties"][name]["format"] = format;
+ _swagger["definitions"][_definition.name]["properties"][name]["type"] = type;
+ if (required == "true") {
+ _swagger["definitions"][_definition.name]["required"].push_back(name);
+ }
+}
+
+void UmosapiService::consume(std::string consume) {
+ _swagger["paths"][_path.name][_path.words.back().name]["consumes"].push_back(consume);
+}
+
+void UmosapiService::produce(std::string produce) {
+ _swagger["paths"][_path.name][_path.words.back().name]["produces"].push_back(produce);
+}
+
+void UmosapiService::parameter(std::string name, std::string description, std::string schema = "") {
+ json parameter;
+ parameter["name"] = name;
+ parameter["description"] = description;
+ parameter["required"] = true;
+ if (schema == "") {
+ parameter["type"] = "string";
+ parameter["in"] = "path";
+ } else {
+ parameter["in"] = "body";
+ for (auto& def: _definitions.defs) {
+ if (def.name == schema ) {
+ std::string schema_path = "#/definitions/";
+ parameter["schema"]["$ref"] = schema_path.append(schema);
+ }
+ }
+ }
+ _swagger["paths"][_path.name][_path.words.back().name]["parameters"].push_back(parameter);
+}
+
+void UmosapiService::response(std::string http_code, std::string description, std::string definition) {
+ std::string schema = "#/definitions/";
+ _swagger["paths"][_path.name][_path.words.back().name]["responses"][http_code]["description"] = description;
+ _swagger["paths"][_path.name][_path.words.back().name]["responses"][http_code]["schema"]["items"]["$ref"] = schema.append(definition);
+ _swagger["paths"][_path.name][_path.words.back().name]["responses"][http_code]["schema"]["type"] = "array";
+}
+
+void UmosapiService::swagger(std::string ui_path, std::string swagger_dir, std::string api_path) {
+ _resource = make_shared< Resource > ();
+ _resource->set_path(ui_path);
+ _resource->set_method_handler("GET", swaggerEndpoint);
+ _service.publish(_resource);
+
+ _resource = make_shared< Resource > ();
+ _resource->set_path(ui_path + "/{filename: .*}");
+ _resource->set_method_handler("GET", swaggerEndpointResources);
+ _service.publish(_resource);
+
+ _resource = make_shared< Resource > ();
+ _resource->set_path(api_path);
+ _resource->set_method_handler("GET", swaggerEndpointApi);
+ _service.publish(_resource);
+}
+
+
+void UmosapiService::createResource() {
+ basePath("/v2");
+ description("Umosapi rest api");
+ version("0.1");
+ title("UMOSAPI");
+ host("127.0.0.1:"+config["port"]);
+
+ swagger("/doc", config["swaggerui"], "/api");
+
+ atag("uobject", "Everything about your UObjec");
+
+ scheme("http");
+ scheme("https");
+
+ definition("UObject", "object");
+ propertie("id", "int64", "integer", "true");
+ propertie("value", "string", "object", "true");
+
+ definition("UObjectSended", "object");
+
+ set_path("/v2/ready");
+ set_method_handler("GET", is_ready);
+ produce("application/json");
+ publish();
+
+ set_path("/v2/{mcollection: .*}");
+ set_method_handler("GET", retrieveAll);
+ produce("application/json");
+ parameter("mcollection", "Name of the collection where the uobject are located");
+ set_error_handler(resource_error_handler);
+ set_method_handler("POST", addUObject);
+ consume("application/json");
+ parameter("mcollection", "Name of the collection where the uobject are located");
+ parameter("body", "UObject to add", "UObjectSended");
+ set_error_handler(resource_error_handler);
+ publish();
+
+ set_path("/v2/{mcollection: .*}/{oid: .*}");
+ set_method_handler("DELETE", deleteUObject);
+ produce("application/json");
+ parameter("mcollection", "Name of the collection where the uobject are located" );
+ parameter("oid", "MongoDB oid of the uobject");
+ set_error_handler(resource_error_handler);
+ publish();
+
+ set_path("/v2/{mcollection: .*}/{key: .*}/{value: .*}");
+ set_method_handler("GET", searchUObjectByKeyValue);
+ produce("application/json");
+ parameter("mcollection", "Name of the collection where the uobject are located");
+ parameter("key", "Key of uobject to search, ex: kill or total.kill");
+ parameter("value", "Value of uobject to search, ex: 42");
+ set_error_handler(resource_error_handler);
+ publish();
+
+ _service.set_error_handler( service_error_handler );
+}
+
+void UmosapiService::retrieveAll( const shared_ptr<Session> session ){
+ auto jsonObjects = json_object_new_array();
+ const auto& request = session->get_request( );
+ auto json_string = uobject::retrieveAll(request->get_path_parameter( "mcollection" ), jsonObjects);
+ json_object_put(jsonObjects);
+
+ session->close( OK, json_string, {
+ { "Content-Length", ::to_string(json_string.length()) },
+ { "Content-Type", "application/json" }
+ });
+}
+
+void UmosapiService::addUObject( const shared_ptr<Session> session ){
+ const auto request = session->get_request();
+
+ size_t content_length = request->get_header( "Content-Length", 0 );
+
+ session->fetch( content_length, [ request ]( const shared_ptr< Session > session, const Bytes & body )
+ {
+ auto jsonObject = json_object_new_object();
+ char bodyData[body.size()+1];
+ memset(bodyData, 0, sizeof(bodyData));
+ snprintf(bodyData, sizeof(bodyData), "%.*s", ( int ) body.size( ), body.data());
+ auto json_string = uobject::add(request->get_path_parameter("mcollection"), jsonObject, bodyData);
+ session->close( OK, json_string, {
+ { "Content-Length", ::to_string(json_string.length()) },
+ { "Content-Type", "application/json" }
+ });
+ json_object_put(jsonObject);
+ } );
}
-void UmosapiService::deleteUObject(const Rest::Request& request, Http::ResponseWriter response) {
+void UmosapiService::deleteUObject( const shared_ptr<Session> session ){
auto jsonObject = json_object_new_object();
+ const auto request = session->get_request();
- auto json_string = uobject::remove(request.param(":mcollection").as<string>(), request.param(":oid").as<string>(), jsonObject);
+ auto json_string = uobject::remove(request->get_path_parameter("mcollection"), request->get_path_parameter("oid"), jsonObject);
- response.send(Http::Code::Ok, json_string, MIME(Application, Json));
+ session->close( OK, json_string, {
+ { "Content-Length", ::to_string(json_string.length()) },
+ { "Content-Type", "application/json" }
+ });
json_object_put(jsonObject);
}
-void UmosapiService::searchUObjectByKeyValue(const Rest::Request& request, Http::ResponseWriter response) {
+void UmosapiService::searchUObjectByKeyValue( const shared_ptr<Session> session ){
auto jsonObject = json_object_new_array();
+ const auto request = session->get_request();
- auto json_string = uobject::searchKeyValue(request.param(":mcollection").as<string>(), request.param(":key").as<string>(), request.param(":value").as<string>(), jsonObject);
+ auto json_string = uobject::searchKeyValue(request->get_path_parameter("mcollection"), request->get_path_parameter("key"), request->get_path_parameter("value"), jsonObject);
- response.send(Http::Code::Ok, json_string, MIME(Application, Json));
+ session->close( OK, json_string, {
+ { "Content-Length", ::to_string(json_string.length()) },
+ { "Content-Type", "application/json" }
+ });
json_object_put(jsonObject);
}
+
+void UmosapiService::swaggerEndpoint( const shared_ptr<Session> session ){
+ const auto request = session->get_request();
+
+ ifstream stream(config["swaggerui"] + "/index.html", ifstream::in );
+
+ if ( stream.is_open() ) {
+ const string body = string( istreambuf_iterator< char >(stream), istreambuf_iterator< char>());
+
+ const multimap< string, string> headers {
+ { "Content-Type", "text/html" },
+ { "Content-Length", ::to_string( body.length() ) }
+ };
+ session->close(OK, body, headers);
+ } else {
+ session->close( NOT_FOUND );
+ }
+}
+
+void UmosapiService::swaggerEndpointResources( const shared_ptr< Session > session )
+{
+ const auto request = session->get_request( );
+ const string filename = request->get_path_parameter( "filename" );
+
+ ifstream stream( config["swaggerui"] + "/" + filename, ifstream::in );
+
+ if ( stream.is_open( ) )
+ {
+ const string body = string( istreambuf_iterator< char >( stream ), istreambuf_iterator< char >( ) );
+
+ const multimap< string, string > headers
+ {
+ { "Content-Length", ::to_string( body.length( ) ) }
+ };
+
+ session->close( OK, body, headers );
+ }
+ else
+ {
+ session->close( NOT_FOUND );
+ }
+}
+
+void UmosapiService::swaggerEndpointApi( const shared_ptr< Session > session )
+{
+ const auto request = session->get_request( );
+ const string filename = request->get_path_parameter( "filename" );
+ const string path = request->get_path();
+
+ ifstream stream( config["swaggerui"] + "/swagger.json", ifstream::in );
+
+ if ( stream.is_open( ) )
+ {
+ const string body = string( istreambuf_iterator< char >( stream ), istreambuf_iterator< char >( ) );
+
+ const multimap< string, string > headers
+ {
+ { "Content-Type", "application/json" },
+ { "Content-Length", ::to_string( body.length( ) ) }
+ };
+
+ session->close( OK, body, headers );
+ }
+ else
+ {
+ session->close( NOT_FOUND );
+ }
+}
diff --git a/api/umosapi.h b/api/umosapi.h
index 20c6d7b..a1765d7 100644
--- a/api/umosapi.h
+++ b/api/umosapi.h
@@ -1,37 +1,108 @@
#ifndef UmosapiService_H_
#define UmosapiService_H_
-#include <pistache/http.h>
-#include <pistache/description.h>
-#include <pistache/endpoint.h>
+//#include <pistache/http.h>
+//#include <pistache/description.h>
+//#include <pistache/endpoint.h>
-#include <pistache/serializer/rapidjson.h>
+#include <restbed>
-using namespace Pistache;
+//#include <pistache/serializer/rapidjson.h>
+
+#include <nlohmann/json.hpp>
+
+using namespace restbed;
+using namespace std;
+
+using json = nlohmann::json;
+
+struct tag {
+ std::string name;
+ std::string description;
+};
+
+struct Propertie {
+
+ std::string name;
+ std::string format;
+ std::string type;
+ std::string required;
+};
+
+struct Definition {
+
+ std::string name;
+ std::string type;
+ std::vector<Propertie> props;
+};
+
+struct Definitions {
+ std::vector<Definition> defs;
+};
+
+struct HttpWord {
+ std::string name;
+};
+
+struct Path {
+ std::string name;
+ std::vector<HttpWord> words;
+};
+
+struct Paths {
+ std::vector<Path> paths;
+};
class UmosapiService {
public:
- UmosapiService(Address addr);
+ UmosapiService();
virtual ~UmosapiService() {};
- void init(size_t thr);
+ void init();
- void start(std::string swaggerui);
+ void start(int, int);
+ Service _service;
+ json _swagger;
+ vector<tag> _tags;
private:
- void createDescription();
-
- void retrieveAll(const Rest::Request& request, Http::ResponseWriter response);
+ void desc(std::string route, std::string http_word, const std::function< void ( const std::shared_ptr< Session > ) >& callback, const std::function< void(int, const std::exception&, std::shared_ptr<restbed::Session>) >& error_callback, tag tags[]);
+ void createResource();
- void addUObject(const Rest::Request& request, Http::ResponseWriter response);
+ static void retrieveAll( const shared_ptr<Session> session );
+ static void addUObject( const shared_ptr<Session> session );
+ static void deleteUObject( const shared_ptr<Session> session );
+ static void searchUObjectByKeyValue( const shared_ptr<Session> session );
+ static void swaggerEndpoint( const shared_ptr<Session> session );
+ static void swaggerEndpointResources( const shared_ptr<Session> session );
+ static void swaggerEndpointApi( const shared_ptr<Session> session );
- void deleteUObject(const Rest::Request& request, Http::ResponseWriter response);
+ shared_ptr< Resource> _resource;
+ Definition _definition;
+ Definitions _definitions;
+ Path _path;
+ Paths _paths;
+ void set_path();
+ void set_path(std::string route);
+ void set_method_handler(std::string http_word, const std::function< void ( const std::shared_ptr< Session > ) >& callback);
+ void set_error_handler(const std::function< void(int, const std::exception&, std::shared_ptr<restbed::Session>) >& error_callback);
+ void produce(std::string);
+ void consume(std::string);
+ void parameter(std::string, std::string, std::string);
+ void response(std::string err_code, std::string description, std::string schema);
+ void publish();
+ void basePath(std::string basePath);
+ void description(std::string description);
+ void title(std::string title);
+ void version(std::string version);
+ void host(std::string host);
+ void atag(std::string name, std::string description);
+ void scheme(std::string scheme);
- void searchUObjectByKeyValue(const Rest::Request& request, Http::ResponseWriter response);
+ void definition(std::string name, std::string type);
+ void propertie(std::string name, std::string format, std::string type, std::string required);
+ void swagger(std::string ui_path, std::string swagger_dir, std::string api_path);
- std::shared_ptr<Http::Endpoint> httpEndpoint;
- Rest::Description desc;
- Rest::Router router;
};
#endif