diff options
Diffstat (limited to 'api/umosapi.cpp')
-rw-r--r-- | api/umosapi.cpp | 493 |
1 files changed, 378 insertions, 115 deletions
diff --git a/api/umosapi.cpp b/api/umosapi.cpp index 83965a3..f5d7003 100644 --- a/api/umosapi.cpp +++ b/api/umosapi.cpp @@ -17,146 +17,409 @@ #include "../db/mongo_access.h" #include "../db/uobject.h" -mongo_access mongo; +#include <regex> +#include <sstream> +#include <iomanip> +#include <fstream> +#include <streambuf> - -using bsoncxx::builder::stream::close_array; -using bsoncxx::builder::stream::close_document; -using bsoncxx::builder::stream::document; -using bsoncxx::builder::stream::finalize; -using bsoncxx::builder::stream::open_array; -using bsoncxx::builder::stream::open_document; +UmosapiService::mongo_access mongo; using json = nlohmann::json; -using namespace std; -using namespace Pistache; +UmosapiService::Api::Api() {} + +void UmosapiService::Api::init() { + + auto uri = mongocxx::uri{config["mongoURI"]}; + mongo.configure(std::move(uri)); + UmosapiService::Api::createResource(); + std::ofstream swagger_json; + swagger_json.open(config["swaggerui"] + "/swagger.json"); + swagger_json << UmosapiService::Api::_swagger.dump(); + swagger_json.close(); +} -namespace Generic { - void handleReady(const Rest::Request&, Http::ResponseWriter response) { - response.send(Http::Code::Ok, "1"); +void UmosapiService::Api::start(int port, int thr) { + auto settings = std::make_shared< restbed::Settings >(); + settings->set_port( port ); + settings->set_worker_limit( thr ); + settings->set_default_header("Connection", "close"); + UmosapiService::Api::_service.start(settings); +} + +void service_error_handler( const int, const std::exception& e, const std::shared_ptr< restbed::Session > session ) +{ + std::string message = "Backend Service is dead: "; + message += e.what(); + if ( session ) { + if ( session->is_open( ) ) { + session->close( 500, message, { { "Content-Length", std::to_string(message.length()) } } ); + } } + fprintf( stderr, "ERROR: %s.\n", message.c_str() ); } -UmosapiService::UmosapiService(Address addr) - : httpEndpoint(std::make_shared<Http::Endpoint>(addr)) - , desc("Unity Mongo Save API", "0.1") -{ } +void resource_error_handler( const int, const std::exception& e, const std::shared_ptr< restbed::Session > session ) +{ + std::string message = "Backend Resource is dead: "; + message += e.what(); + if ( session->is_open( ) ) + session->close( 500, message, { { "Content-Length", std::to_string(message.length()) } } ); + fprintf( stderr, "ERROR: %s.\n", message.c_str() ); +} -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); +void faulty_method_handler( const std::shared_ptr< restbed::Session > ) +{ + throw restbed::SERVICE_UNAVAILABLE; +} - response.send(Http::Code::Ok, json_string, MIME(Application, Json)); +void is_ready(const std::shared_ptr< restbed::Session > session) +{ + session->close( restbed::OK, "1", { { "Content-Length", "1"}}); } -void UmosapiService::addUObject(const Rest::Request& request, Http::ResponseWriter response) { - auto jsonObject = json_object_new_object(); +/* + * 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::Api::desc(std::string route, std::string http_word, const std::function< void ( const std::shared_ptr< restbed::Session > ) >& callback, const std::function< void(int, const std::exception&, std::shared_ptr< restbed::Session >) >& error_callback, tag tags[]) { + auto resource = std::make_shared< restbed::Resource > (); + resource->set_path(route); + resource->set_method_handler(http_word, callback); + resource->set_error_handler( error_callback ); + + UmosapiService::Api::_service.publish(resource); +} - auto json_string = uobject::add(request.param(":mcollection").as<string>(), jsonObject, request.body().c_str()); +void UmosapiService::Api::description(std::string description) { + UmosapiService::Api::_swagger["info"]["description"] = description; +} - response.send(Http::Code::Ok, json_string, MIME(Application, Json)); - json_object_put(jsonObject); +void UmosapiService::Api::title(std::string title) { + UmosapiService::Api::_swagger["info"]["title"] = title; +} + +void UmosapiService::Api::version(std::string version) { + UmosapiService::Api::_swagger["info"]["version"] = version; +} + +void UmosapiService::Api::basePath(std::string basePath) { + UmosapiService::Api::_swagger["swagger"] = "2.0"; + UmosapiService::Api::_swagger["basePath"] = basePath; +} + +void UmosapiService::Api::host(std::string host) { + UmosapiService::Api::_swagger["host"] = host; +} + +void UmosapiService::Api::atag(std::string name, std::string description) { + struct tag the_tag; + the_tag.name = name; + the_tag.description = description; + + UmosapiService::Api::_tags.push_back(the_tag); + UmosapiService::Api::_swagger["tags"].push_back({ {"name",the_tag.name},{"description", the_tag.description} }); + +} + +void UmosapiService::Api::scheme(std::string scheme) { + UmosapiService::Api::_swagger["schemes"].push_back(scheme); +} + +void UmosapiService::Api::set_path(std::string route) { + UmosapiService::Api::_resource = std::make_shared< restbed::Resource > (); + UmosapiService::Api::_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,""); + UmosapiService::Api::_path = Path{final_route}; + UmosapiService::Api::_swagger["paths"][final_route] = {}; +} + +void UmosapiService::Api::set_method_handler(std::string http_word, const std::function< void ( const std::shared_ptr< restbed::Session > ) >& callback) { + UmosapiService::Api::_resource->set_method_handler(http_word, callback); + std::locale loc; + for (auto& c : http_word) { c = tolower(c); } + UmosapiService::Api::_path.words.push_back(HttpWord{http_word}); + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][http_word]["description"] = ""; + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][http_word]["operationId"] = ""; + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][http_word]["summary"] = ""; +} + +void UmosapiService::Api::set_error_handler(const std::function< void(int, const std::exception&, std::shared_ptr< restbed::Session >) >& error_callback) { + UmosapiService::Api::_resource->set_error_handler( error_callback ); +} + +void UmosapiService::Api::publish() { + for (const auto& http_word: UmosapiService::Api::_path.words) { + auto responses = UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][http_word.name]["responses"]; + if (responses.find("200") == responses.end()) { + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][http_word.name]["responses"]["200"]["description"] = "All is fine."; + } + } + UmosapiService::Api::_service.publish(_resource); +} + +void UmosapiService::Api::definition(std::string name, std::string type) { + UmosapiService::Api::_definition = Definition{name, type}; + UmosapiService::Api::_definitions.defs.push_back(_definition); + UmosapiService::Api::_swagger["definitions"][name]["type"] = type; +} + +void UmosapiService::Api::propertie(std::string name, std::string format, std::string type, std::string required) { + UmosapiService::Api::_definition.props.push_back(Propertie{name, format, type, required}); + UmosapiService::Api::_swagger["definitions"][UmosapiService::Api::_definition.name]["properties"][name]["format"] = format; + UmosapiService::Api::_swagger["definitions"][UmosapiService::Api::_definition.name]["properties"][name]["type"] = type; + if (required == "true") { + UmosapiService::Api::_swagger["definitions"][UmosapiService::Api::_definition.name]["required"].push_back(name); + } } -void UmosapiService::deleteUObject(const Rest::Request& request, Http::ResponseWriter response) { +void UmosapiService::Api::consume(std::string consume) { + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][UmosapiService::Api::_path.words.back().name]["consumes"].push_back(consume); +} + +void UmosapiService::Api::produce(std::string produce) { + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][UmosapiService::Api::_path.words.back().name]["produces"].push_back(produce); +} + +void UmosapiService::Api::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 (const auto& def: UmosapiService::Api::_definitions.defs) { + if (def.name == schema ) { + std::string schema_path = "#/definitions/"; + parameter["schema"]["$ref"] = schema_path.append(schema); + } + } + } + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][UmosapiService::Api::_path.words.back().name]["parameters"].push_back(parameter); +} + +void UmosapiService::Api::response(std::string http_code, std::string description, std::string definition) { + std::string schema = "#/definitions/"; + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][UmosapiService::Api::_path.words.back().name]["responses"][http_code]["description"] = description; + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][UmosapiService::Api::_path.words.back().name]["responses"][http_code]["schema"]["items"]["$ref"] = schema.append(definition); + UmosapiService::Api::_swagger["paths"][UmosapiService::Api::_path.name][UmosapiService::Api::_path.words.back().name]["responses"][http_code]["schema"]["type"] = "array"; +} + +void UmosapiService::Api::swagger(std::string ui_path, std::string swagger_dir, std::string api_path) { + UmosapiService::Api::_resource = std::make_shared< restbed::Resource > (); + UmosapiService::Api::_resource->set_path(ui_path); + UmosapiService::Api::_resource->set_method_handler("GET", swaggerEndpoint); + UmosapiService::Api::_service.publish(_resource); + + UmosapiService::Api::_resource = std::make_shared< restbed::Resource > (); + UmosapiService::Api::_resource->set_path(ui_path + "/{filename: .*}"); + UmosapiService::Api::_resource->set_method_handler("GET", swaggerEndpointResources); + UmosapiService::Api::_service.publish(_resource); + + UmosapiService::Api::_resource = std::make_shared< restbed::Resource > (); + UmosapiService::Api::_resource->set_path(api_path); + UmosapiService::Api::_resource->set_method_handler("GET", swaggerEndpointApi); + UmosapiService::Api::_service.publish(_resource); +} + + +void UmosapiService::Api::createResource() { + UmosapiService::Api::basePath("/v2"); + UmosapiService::Api::description("Umosapi rest api"); + UmosapiService::Api::version("0.1"); + UmosapiService::Api::title("UMOSAPI"); + UmosapiService::Api::host("127.0.0.1:"+config["port"]); + + UmosapiService::Api::swagger("/doc", config["swaggerui"], "/api"); + + UmosapiService::Api::atag("uobject", "Everything about your UObjec"); + + UmosapiService::Api::scheme("http"); + UmosapiService::Api::scheme("https"); + + UmosapiService::Api::definition("UObject", "object"); + UmosapiService::Api::propertie("id", "int64", "integer", "true"); + UmosapiService::Api::propertie("value", "string", "object", "true"); + + UmosapiService::Api::definition("UObjectSended", "object"); + + UmosapiService::Api::set_path("/v2/ready"); + UmosapiService::Api::set_method_handler("GET", is_ready); + UmosapiService::Api::produce("application/json"); + UmosapiService::Api::publish(); + + UmosapiService::Api::set_path("/v2/{mcollection: .*}"); + UmosapiService::Api::set_method_handler("GET", retrieveAll); + UmosapiService::Api::produce("application/json"); + UmosapiService::Api::parameter("mcollection", "Name of the collection where the uobject are located"); + UmosapiService::Api::set_error_handler(&resource_error_handler); + UmosapiService::Api::set_method_handler("POST", addUObject); + UmosapiService::Api::consume("application/json"); + UmosapiService::Api::parameter("mcollection", "Name of the collection where the uobject are located"); + UmosapiService::Api::parameter("body", "UObject to add", "UObjectSended"); + UmosapiService::Api::set_error_handler(&resource_error_handler); + UmosapiService::Api::publish(); + + UmosapiService::Api::set_path("/v2/{mcollection: .*}/{oid: .*}"); + UmosapiService::Api::set_method_handler("DELETE", deleteUObject); + UmosapiService::Api::produce("application/json"); + UmosapiService::Api::parameter("mcollection", "Name of the collection where the uobject are located" ); + UmosapiService::Api::parameter("oid", "MongoDB oid of the uobject"); + UmosapiService::Api::set_error_handler(&resource_error_handler); + UmosapiService::Api::publish(); + + UmosapiService::Api::set_path("/v2/{mcollection: .*}/{key: .*}/{value: .*}"); + UmosapiService::Api::set_method_handler("GET", searchUObjectByKeyValue); + UmosapiService::Api::produce("application/json"); + UmosapiService::Api::parameter("mcollection", "Name of the collection where the uobject are located"); + UmosapiService::Api::parameter("key", "Key of uobject to search, ex: kill or total.kill"); + UmosapiService::Api::parameter("value", "Value of uobject to search, ex: 42"); + UmosapiService::Api::set_error_handler(&resource_error_handler); + UmosapiService::Api::publish(); + + UmosapiService::Api::_service.set_error_handler( service_error_handler ); +} + +void UmosapiService::Api::retrieveAll( const std::shared_ptr< restbed::Session> session ){ + UmosapiService::uobject uobject; + auto jsonObjects = json_object_new_array(); + const auto& request = session->get_request( ); + auto json_string = uobject.retrieveAll(request->get_path_parameter( "mcollection" ), jsonObjects); + + session->close( restbed::OK, json_string, { + { "Content-Length", std::to_string(json_string.length()) }, + { "Content-Type", "application/json" } + }); + json_object_put(jsonObjects); + +} + +void UmosapiService::Api::addUObject( const std::shared_ptr< restbed::Session > session ){ + const auto request = session->get_request(); + + size_t content_length = request->get_header( "Content-Length", 0 ); + + session->fetch( content_length, [ request ]( const std::shared_ptr< restbed::Session > session, const restbed::Bytes & body ) + { + UmosapiService::uobject uobject; + 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( restbed::OK, json_string, { + { "Content-Length", std::to_string(json_string.length()) }, + { "Content-Type", "application/json" } + }); + json_object_put(jsonObject); + } ); +} + +void UmosapiService::Api::deleteUObject( const std::shared_ptr< restbed::Session > session ){ + UmosapiService::uobject uobject; 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( restbed::OK, json_string, { + { "Content-Length", std::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::Api::searchUObjectByKeyValue( const std::shared_ptr< restbed::Session > session ){ + UmosapiService::uobject uobject; 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( restbed::OK, json_string, { + { "Content-Length", std::to_string(json_string.length()) }, + { "Content-Type", "application/json" } + }); json_object_put(jsonObject); } + +void UmosapiService::Api::swaggerEndpoint( const std::shared_ptr< restbed::Session > session ){ + const auto request = session->get_request(); + + std::ifstream stream(config["swaggerui"] + "/index.html", std::ifstream::in ); + + if ( stream.is_open() ) { + const std::string body = std::string( std::istreambuf_iterator< char >(stream), std::istreambuf_iterator< char>()); + + const std::multimap< std::string, std::string> headers { + { "Content-Type", "text/html" }, + { "Content-Length", std::to_string( body.length() ) } + }; + session->close(restbed::OK, body, headers); + } else { + session->close( restbed::NOT_FOUND ); + } +} + +void UmosapiService::Api::swaggerEndpointResources( const std::shared_ptr< restbed::Session > session ) +{ + const auto request = session->get_request( ); + const std::string filename = request->get_path_parameter( "filename" ); + + std::ifstream stream( config["swaggerui"] + "/" + filename, std::ifstream::in ); + + if ( stream.is_open( ) ) + { + const std::string body = std::string( std::istreambuf_iterator< char >( stream ), std::istreambuf_iterator< char >( ) ); + + const std::multimap< std::string, std::string > headers + { + { "Content-Length", std::to_string( body.length( ) ) } + }; + + session->close( restbed::OK, body, headers ); + } + else + { + session->close( restbed::NOT_FOUND ); + } +} + +void UmosapiService::Api::swaggerEndpointApi( const std::shared_ptr< restbed::Session > session ) +{ + const auto request = session->get_request( ); + const std::string filename = request->get_path_parameter( "filename" ); + const std::string path = request->get_path(); + + std::ifstream stream( config["swaggerui"] + "/swagger.json", std::ifstream::in ); + + if ( stream.is_open( ) ) + { + const std::string body = std::string( std::istreambuf_iterator< char >( stream ), std::istreambuf_iterator< char >( ) ); + + const std::multimap< std::string, std::string > headers + { + { "Content-Type", "application/json" }, + { "Content-Length", std::to_string( body.length( ) ) } + }; + + session->close( restbed::OK, body, headers ); + } + else + { + session->close( restbed::NOT_FOUND ); + } +} |