From 88b95a495520d98d9c0ba09681f925264f62b10b Mon Sep 17 00:00:00 2001 From: Glenn Chen Date: Sat, 28 Oct 2023 16:23:09 -0700 Subject: [PATCH 1/2] Update HTTP endpoints to use query parameters Fixes #10 --- service/http_server/crow_service.cpp | 96 +++++++++++++++++++++------- service/http_server/crow_service.h | 4 ++ 2 files changed, 77 insertions(+), 23 deletions(-) diff --git a/service/http_server/crow_service.cpp b/service/http_server/crow_service.cpp index 2357c2d..7007cd5 100644 --- a/service/http_server/crow_service.cpp +++ b/service/http_server/crow_service.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,7 @@ void CrowService::run() { std::mutex mtx; // Get all values - CROW_ROUTE(app, "/v1/transactions") + CROW_ROUTE(app, "/v1/transactions/getall") ([this](const crow::request& req, response& res) { auto values = kv_client_.GetValues(); if (values != nullptr) { @@ -81,15 +82,17 @@ void CrowService::run() { res.set_header("Content-Type", "application/json"); res.end(std::string(values->c_str())); } else { - res.code = 500; + res.code = internal_server_error_code; res.set_header("Content-Type", "text/plain"); res.end("getvalues fail"); } }); // Get value of specific id - CROW_ROUTE(app, "/v1/transactions/") - ([this](const crow::request& req, response& res, std::string id) { + // :18000/v1/transactions/get?id='sampleid' + CROW_ROUTE(app, "/v1/transactions/get") + ([this](const crow::request& req, response& res) { + const std::string id = req.url_params.get("id"); auto value = kv_client_.Get(id); if (value != nullptr) { LOG(INFO) << "client get value = " << value->c_str(); @@ -103,16 +106,32 @@ void CrowService::run() { res.set_header("Content-Type", "application/json"); res.end(std::string(value->c_str())); } else { - res.code = 500; + res.code = internal_server_error_code; res.set_header("Content-Type", "text/plain"); - res.end("get value fail"); + res.end("get value fail\n"); } }); // Get values based on key range - CROW_ROUTE(app, "/v1/transactions//") - ([this](const crow::request& req, response& res, std::string min_id, - std::string max_id) { + // :18000/v1/transactions/getrange?"min_id=&max_id=" + CROW_ROUTE(app, "/v1/transactions/getrange") + ([this](const crow::request& req, response& res) { + if (req.url_params.get("min_id") == nullptr) { + res.code = bad_request_code; + res.set_header("Content-Type", "text/plain"); + res.end("no min_id specified\nExample usage: :18000/v1/transactions/getrange?\"min_id=&max_id=\"\n"); + return; + } + if (req.url_params.get("max_id") == nullptr) { + res.code = bad_request_code; + res.set_header("Content-Type", "text/plain"); + res.end("no max_id specified\nExample usage: :18000/v1/transactions/getrange?\"min_id=&max_id=\"\n"); + return; + } + + const std::string min_id = req.url_params.get("min_id"); + const std::string max_id = req.url_params.get("max_id"); + auto value = kv_client_.GetRange(min_id, max_id); if (value != nullptr) { LOG(INFO) << "client getrange value = " << value->c_str(); @@ -126,9 +145,9 @@ void CrowService::run() { res.set_header("Content-Type", "application/json"); res.end(std::string(value->c_str())); } else { - res.code = 500; + res.code = internal_server_error_code; res.set_header("Content-Type", "text/plain"); - res.end("getrange fail"); + res.end("getrange fail\n"); } }); @@ -137,7 +156,6 @@ void CrowService::run() { CROW_ROUTE(app, "/v1/transactions/commit") .methods("POST"_method)([this](const request& req) { std::string body = req.body; - LOG(INFO) << "body: " << body; resdb::SDKTransaction transaction = resdb::ParseSDKTransaction(body); const std::string id = transaction.id; const std::string value = transaction.value; @@ -147,7 +165,7 @@ void CrowService::run() { if (retval != 0) { LOG(ERROR) << "Error when trying to commit id " << id; - response res(500, "id: " + id); + response res(internal_server_error_code, "id: " + id); res.set_header("Content-Type", "text/plain"); return res; } @@ -158,25 +176,34 @@ void CrowService::run() { for (auto u : users) u->send_text("Update blocks"); } - response res(201, "id: " + id); // Created status code + + response res(created_code, "id: " + id); // Created status code res.set_header("Content-Type", "text/plain"); return res; }); - CROW_ROUTE(app, "/v1/blocks")([this](const crow::request& req, response& res) { + CROW_ROUTE(app, "/v1/blocks/getall")([this](const crow::request& req, response& res) { auto values = GetAllBlocks(1); res.set_header("Content-Type", "application/json"); res.end(values); }); // Retrieve blocks in batches of size of the int parameter - CROW_ROUTE(app, "/v1/blocks/") - ([this](const crow::request& req, response& res, int batch_size) { + CROW_ROUTE(app, "/v1/blocks/get") + ([this](const crow::request& req, response& res) { + if (req.url_params.get("batch_size") == nullptr) { + res.code = bad_request_code; + res.set_header("Content-Type", "text/plain"); + res.end("no batch_size specified\nExample usage: :18000/v1/blocks/get?batch_size=1>\"\n"); + return; + } + // TODO: catch conversion error + int batch_size = atoi(req.url_params.get("batch_size")); auto values = GetAllBlocks(batch_size); if (values == "") { - res.code = 500; + res.code = internal_server_error_code; res.set_header("Content-Type", "text/plain"); - res.end("get replica state fail"); + res.end("get replica state fail\n"); exit(1); }; res.set_header("Content-Type", "application/json"); @@ -184,16 +211,39 @@ void CrowService::run() { }); // Retrieve blocks within a range - CROW_ROUTE(app, "/v1/blocks//") - ([this](const crow::request& req, response& res, int min_seq, int max_seq) { + CROW_ROUTE(app, "/v1/blocks/getrange") + ([this](const crow::request& req, response& res) { + if (req.url_params.get("min_seq") == nullptr) { + res.code = bad_request_code; + res.set_header("Content-Type", "text/plain"); + res.end("no min_seq specified\nExample usage: :18000/v1/blocks/getrange?\"min_seq=1&max_seq=3\">\n"); + return; + } + if (req.url_params.get("max_seq") == nullptr) { + res.code = bad_request_code; + res.set_header("Content-Type", "text/plain"); + res.end("no max_seq specified\nExample usage: :18000/v1/blocks/getrange?\"min_seq=1&max_seq=3\">\n"); + return; + } + + char* pEnd; // dummy pointer used to pass to strtol + const uint64_t min_seq = strtol(req.url_params.get("min_seq"), &pEnd, 10); + const uint64_t max_seq = strtol(req.url_params.get("max_seq"), &pEnd, 10); + + if (min_seq == 0 || max_seq == 0 || min_seq > max_seq) { + res.code = bad_request_code; + res.set_header("Content-Type", "text/plain"); + res.end("Invalid range query. Blocks are 1-indexed.\n"); + return; + } auto resp = txn_client_.GetTxn(min_seq, max_seq); absl::StatusOr>> GetTxn( uint64_t min_seq, uint64_t max_seq); if (!resp.ok()) { LOG(ERROR) << "get replica state fail"; - res.code = 500; + res.code = internal_server_error_code; res.set_header("Content-Type", "text/plain"); - res.end("get replica state fail"); + res.end("get replica state fail\n"); exit(1); } diff --git a/service/http_server/crow_service.h b/service/http_server/crow_service.h index 6f89e7c..57ddec6 100644 --- a/service/http_server/crow_service.h +++ b/service/http_server/crow_service.h @@ -53,6 +53,10 @@ class CrowService { ResDBKVClient kv_client_; resdb::ResDBTxnAccessor txn_client_; std::unordered_set users; + const int ok_code = 200; + const int created_code = 201; + const int bad_request_code = 400; + const int internal_server_error_code = 500; }; } // namespace resdb From 4e96710a367ea9850f292e1c7a8b0096c5bce0f0 Mon Sep 17 00:00:00 2001 From: Glenn Chen Date: Sat, 28 Oct 2023 16:36:03 -0700 Subject: [PATCH 2/2] Update HTTP endpoints README --- service/http_server/README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/service/http_server/README.md b/service/http_server/README.md index 66c8772..0f79631 100644 --- a/service/http_server/README.md +++ b/service/http_server/README.md @@ -3,13 +3,13 @@ ## Transactions Note that key-value pairs committed to ResilientDB through the API store the JSON object as the value. To get the intended value of the pair, simply access the value of the JSON object. -### GET /v1/transactions +### GET /v1/transactions/getall Get all values -### GET /v1/transactions/\ +### GET /v1/transactions/get?id=\ Get value of specific id -### GET /v1/transactions/\/\ +### GET /v1/transactions/getrange?min_id=\&max_id=\ Get values based on key range ### POST /v1/transactions/commit @@ -19,18 +19,15 @@ Ex: `curl -X POST -d '{"id":"samplekey","value":"samplevalue"}' localhost:18000 ## Blocks -### GET /v1/blocks +### GET /v1/blocks/getall Retrieve all blocks -### GET /v1/blocks/\ +### GET /v1/blocks/get?batch_size=\ Retrieve all blocks, grouped in batch sizes of the int parameter -### GET /v1/blocks/\/\ -Retrieve list of blocks within a range +### GET /v1/blocks/getrange?min_seq=\&max_seq=\ +Retrieve list of blocks within a range of sequence numbers ## Miscellaneous ### GET /populatetable -Used for the Explorer webpage - -## Todo -Update endpoints to use URL query parameters \ No newline at end of file +Used for the Explorer webpage \ No newline at end of file