diff options
author | neodarz <neodarz@neodarz.net> | 2019-09-06 18:04:40 +0200 |
---|---|---|
committer | neodarz <neodarz@neodarz.net> | 2019-09-06 18:04:40 +0200 |
commit | 1b922f7319f821fe3daf302a8c68131aaa94f586 (patch) | |
tree | debf82b507174f158b9cb4e030b3f82d31c44a64 /api | |
parent | 3185dd695d58bb96672d4f33c4528bf8b361707f (diff) | |
download | umosapicpp-1b922f7319f821fe3daf302a8c68131aaa94f586.tar.xz umosapicpp-1b922f7319f821fe3daf302a8c68131aaa94f586.zip |
Change code to use Restbed
Diffstat (limited to 'api')
-rw-r--r-- | api/umosapi.cpp | 480 | ||||
-rw-r--r-- | api/umosapi.h | 105 |
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 |