From 1baaa2b344ff99da27012621f71dd5f37a6eebe2 Mon Sep 17 00:00:00 2001 From: Kishore Nallan Date: Sat, 29 Apr 2017 17:39:42 +0530 Subject: [PATCH] API - creating a new collection. --- include/api.h | 2 + include/field.h | 2 +- include/http_server.h | 34 +++++++++----- src/api.cpp | 85 +++++++++++++++++++++++++++++++++++ src/http_server.cpp | 8 +++- src/main/typesense_server.cpp | 11 ++--- 6 files changed, 119 insertions(+), 23 deletions(-) diff --git a/include/api.h b/include/api.h index 1f1692c7..7d18bd45 100644 --- a/include/api.h +++ b/include/api.h @@ -2,6 +2,8 @@ #include "http_server.h" +void post_create_collection(http_req & req, http_res & res); + void get_search(http_req & req, http_res & res); void post_add_document(http_req & req, http_res & res); diff --git a/include/field.h b/include/field.h index a3e8ed3c..90ee5b28 100644 --- a/include/field.h +++ b/include/field.h @@ -23,7 +23,7 @@ struct field { std::string name; std::string type; - field(std::string name, std::string type): name(name), type(type) { + field(const std::string & name, const std::string & type): name(name), type(type) { } diff --git a/include/http_server.h b/include/http_server.h index e5a5ce57..79e3514c 100644 --- a/include/http_server.h +++ b/include/http_server.h @@ -18,23 +18,33 @@ struct http_res { uint32_t status_code; std::string body; + void send_200(const std::string & res_body) { + status_code = 200; + body = res_body; + } + + void send_201(const std::string & res_body) { + status_code = 201; + body = res_body; + } + + void send_400(const std::string & message) { + status_code = 400; + body = "{\"message\": \"" + message + "\"}"; + } + void send_404() { status_code = 404; body = "{\"message\": \"Not Found\"}"; } + void send_409(const std::string & message) { + status_code = 400; + body = "{\"message\": \"" + message + "\"}"; + } + void send_500(const std::string & res_body) { - status_code = 404; - body = res_body; - } - - void send_200(const std::string & res_body) { - status_code = 404; - body = res_body; - } - - void send_201(const std::string & res_body) { - status_code = 404; + status_code = 500; body = res_body; } }; @@ -85,7 +95,7 @@ public: void post(const std::string & path, void (*handler)(http_req &, http_res &)); - void put(); + void put(const std::string & path, void (*handler)(http_req &, http_res &)); void del(); diff --git a/src/api.cpp b/src/api.cpp index 4461cbfb..ed73b969 100644 --- a/src/api.cpp +++ b/src/api.cpp @@ -3,6 +3,91 @@ #include "collection.h" #include "collection_manager.h" +void post_create_collection(http_req & req, http_res & res) { + nlohmann::json req_json = nlohmann::json::parse(req.body); + + CollectionManager & collectionManager = CollectionManager::get_instance(); + + // validate presence of mandatory fields + + if(req_json.count("name") == 0) { + return res.send_400("Parameter `name` is required."); + } + + if(req_json.count("search_fields") == 0) { + return res.send_400("Parameter `search_fields` is required."); + } + + if(req_json.count("rank_fields") == 0) { + return res.send_400("Parameter `rank_fields` is required."); + } + + if(collectionManager.get_collection(req_json["name"]) != nullptr) { + return res.send_409("Collection with name `" + req_json["name"].get() + "` already exists."); + } + + // field specific validation + + std::vector search_fields; + + if(!req_json["search_fields"].is_array() || req_json["search_fields"].size() == 0) { + return res.send_400("Wrong format for `search_fields`. It should be an array like: " + "[{\"name\": \"\", \"type\": \"\"}]"); + } + + for(const nlohmann::json & search_field_json: req_json["search_fields"]) { + if(!search_field_json.is_object() || + search_field_json.count(fields::name) == 0 || search_field_json.count(fields::type) == 0 || + !search_field_json.at(fields::name).is_string() || !search_field_json.at(fields::type).is_string()) { + + return res.send_400("Wrong format for `search_fields`. It should be an array like: " + "[{\"name\": \"\", \"type\": \"\"}]"); + } + + search_fields.push_back(field(search_field_json["name"], search_field_json["type"])); + } + + std::vector facet_fields; + + if(req_json.count("facet_fields") != 0) { + if(!req_json["facet_fields"].is_array()) { + return res.send_400("Wrong format for `facet_fields`. It should be an array like: " + "[{\"name\": \"\", \"type\": \"\"}]"); + } + + for(const nlohmann::json & facet_field_json: req_json["facet_fields"]) { + if(!facet_field_json.is_object() || + facet_field_json.count(fields::name) == 0 || facet_field_json.count(fields::type) == 0 || + !facet_field_json.at(fields::name).is_string() || !facet_field_json.at(fields::type).is_string()) { + + return res.send_400("Wrong format for `facet_fields`. It should be an array like: " + "[{\"name\": \"\", \"type\": \"\"}]"); + } + + facet_fields.push_back(field(facet_field_json["name"], facet_field_json["type"])); + } + } + + std::vector rank_fields; + + if(!req_json["rank_fields"].is_array() || req_json["rank_fields"].size() == 0) { + return res.send_400("Wrong format for `rank_fields`. It should be an array like: " + "[\", \"]"); + } + + for(const nlohmann::json & rank_field: req_json["rank_fields"]) { + if(!rank_field.is_string()) { + return res.send_400("Wrong format for `rank_fields`. It should be an array like: " + "[\", \"]"); + } + + rank_fields.push_back(rank_field.get()); + } + + collectionManager.create_collection(req_json["name"], search_fields, facet_fields, rank_fields); + res.send_201(req.body); +} + void get_search(http_req & req, http_res & res) { const char *NUM_TYPOS = "num_typos"; const char *PREFIX = "prefix"; diff --git a/src/http_server.cpp b/src/http_server.cpp index 1ed57a09..bdb28b60 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -82,6 +82,7 @@ const char* HttpServer::get_status_reason(uint32_t status_code) { case 201: return "Created"; case 400: return "Bad Request"; case 404: return "Not Found"; + case 409: return "Conflict"; case 500: return "Internal Server Error"; default: return ""; } @@ -195,8 +196,11 @@ void HttpServer::post(const std::string & path, void (*handler)(http_req &, http routes.push_back(rpath); } -void HttpServer::put() { - +void HttpServer::put(const std::string & path, void (*handler)(http_req &, http_res &)) { + std::vector path_parts; + StringUtils::split(path, path_parts, "/"); + route_path rpath = {"PUT", path_parts, handler}; + routes.push_back(rpath); } void HttpServer::del() { diff --git a/src/main/typesense_server.cpp b/src/main/typesense_server.cpp index dfff6842..d04e66b2 100644 --- a/src/main/typesense_server.cpp +++ b/src/main/typesense_server.cpp @@ -15,14 +15,9 @@ int main(int argc, char **argv) { HttpServer server; - server.get("/search/:collection", get_search); - server.post("/search/:collection", post_add_document); - - /*server.get("/search/:collection", [](http_req & req, http_res & res) -> int { - res.status_code = 200; - res.body = "{\"collection\": \"" + req.params["collection"] + "\"}"; - return 0; - });*/ + server.post("/collection", post_create_collection); + server.post("/collection/:collection", post_add_document); + server.get("/collection/:collection/search", get_search); server.run(); return 0;