Refactor RestService interfaces.

This commit is contained in:
Adam Gu
2018-08-24 16:51:08 +08:00
parent e9096d4e53
commit a863b379d0
10 changed files with 227 additions and 265 deletions
+2 -20
View File
@@ -5,26 +5,8 @@
#include "webcc/logger.h"
#include "webcc/rest_async_client.h"
// -----------------------------------------------------------------------------
// Write a JSON object to string.
static std::string JsonToString(const Json::Value& json) {
Json::StreamWriterBuilder builder;
return Json::writeString(builder, json);
}
static Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::string errs;
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
std::cerr << errs << std::endl;
}
return json;
}
#include "example/common/book.h"
#include "example/common/book_json.h"
// -----------------------------------------------------------------------------
+56 -61
View File
@@ -23,26 +23,6 @@
// -----------------------------------------------------------------------------
static std::string JsonToString(const Json::Value& json) {
Json::StreamWriterBuilder builder;
return Json::writeString(builder, json);
}
static Json::Value StringToJson(const std::string& str) {
Json::Value json;
Json::CharReaderBuilder builder;
std::stringstream stream(str);
std::string errs;
if (!Json::parseFromStream(builder, stream, &json, &errs)) {
std::cerr << errs << std::endl;
}
return json;
}
// -----------------------------------------------------------------------------
class BookClientBase {
public:
BookClientBase(const std::string& host, const std::string& port,
@@ -54,18 +34,18 @@ class BookClientBase {
virtual ~BookClientBase() = default;
protected:
void PrintSeparateLine() {
std::cout << "--------------------------------";
std::cout << "--------------------------------";
std::cout << std::endl;
}
void PrintError() {
std::cout << webcc::DescribeError(rest_client_.error());
void LogError() {
if (rest_client_.timed_out()) {
std::cout << " (timed out)";
LOG_ERRO("%s (timed out)", webcc::DescribeError(rest_client_.error()));
} else {
LOG_ERRO(webcc::DescribeError(rest_client_.error()));
}
std::cout << std::endl;
//std::cout << webcc::DescribeError(rest_client_.error());
//if (rest_client_.timed_out()) {
// std::cout << " (timed out)";
//}
//std::cout << std::endl;
}
webcc::RestClient rest_client_;
@@ -81,11 +61,8 @@ public:
}
bool ListBooks() {
PrintSeparateLine();
std::cout << "ListBooks" << std::endl;
if (!rest_client_.Get("/books")) {
PrintError();
LogError();
return false;
}
@@ -94,20 +71,15 @@ public:
}
bool CreateBook(const std::string& title, double price, std::string* id) {
PrintSeparateLine();
std::cout << "CreateBook: " << title << ", " << price << std::endl;
Json::Value req_json(Json::objectValue);
Json::Value req_json;
req_json["title"] = title;
req_json["price"] = price;
if (!rest_client_.Post("/books", JsonToString(req_json))) {
PrintError();
LogError();
return false;
}
std::cout << rest_client_.response_status() << std::endl;
Json::Value rsp_json = StringToJson(rest_client_.response_content());
*id = rsp_json["id"].asString();
@@ -125,11 +97,8 @@ public:
}
bool GetBook(const std::string& id, Book* book) {
PrintSeparateLine();
std::cout << "GetBook: " << id << std::endl;
if (!rest_client_.Get("/books/" + id)) {
PrintError();
LogError();
return false;
}
@@ -138,40 +107,47 @@ public:
bool UpdateBook(const std::string& id, const std::string& title,
double price) {
PrintSeparateLine();
std::cout << "UpdateBook: " << id << ", " << title << ", " << price
<< std::endl;
// NOTE: ID is already in the URL.
Json::Value json(Json::objectValue);
Json::Value json;
json["title"] = title;
json["price"] = price;
if (!rest_client_.Put("/books/" + id, JsonToString(json))) {
PrintError();
LogError();
return false;
}
int status = rest_client_.response_status();
if (status != webcc::HttpStatus::kOK) {
LOG_ERRO("Failed to update book (status: %d).", status);
return false;
}
std::cout << rest_client_.response_status() << std::endl;
return true;
}
bool DeleteBook(const std::string& id) {
PrintSeparateLine();
std::cout << "DeleteBook: " << id << std::endl;
if (!rest_client_.Delete("/books/" + id)) {
PrintError();
if (!rest_client_.Delete("/books/0" /*+ id*/)) {
LogError();
return false;
}
int status = rest_client_.response_status();
if (status != webcc::HttpStatus::kOK) {
LOG_ERRO("Failed to delete book (status: %d).", status);
return false;
}
std::cout << rest_client_.response_status() << std::endl;
return true;
}
};
// -----------------------------------------------------------------------------
void PrintSeparator() {
std::cout << std::string(80, '-') << std::endl;
}
void Help(const char* argv0) {
std::cout << "Usage: " << argv0 << " <host> <port> [timeout]" << std::endl;
std::cout << " E.g.," << std::endl;
@@ -198,21 +174,40 @@ int main(int argc, char* argv[]) {
BookListClient list_client(host, port, timeout_seconds);
BookDetailClient detail_client(host, port, timeout_seconds);
PrintSeparator();
list_client.ListBooks();
PrintSeparator();
std::string id;
list_client.CreateBook("1984", 12.3, &id);
if (!list_client.CreateBook("1984", 12.3, &id)) {
return 1;
}
PrintSeparator();
Book book;
if (detail_client.GetBook(id, &book)) {
std::cout << "Book " << id << ": " << book << std::endl;
std::cout << "Book: " << book << std::endl;
}
PrintSeparator();
detail_client.UpdateBook(id, "1Q84", 32.1);
detail_client.GetBook(id, &book);
PrintSeparator();
if (detail_client.GetBook(id, &book)) {
std::cout << "Book " << book << std::endl;
}
PrintSeparator();
detail_client.DeleteBook(id);
PrintSeparator();
list_client.ListBooks();
return 0;
+45 -31
View File
@@ -18,8 +18,8 @@ static BookStore g_book_store;
// Return all books as a JSON array.
// TODO: Support query parameters.
bool BookListService::Get(const webcc::UrlQuery& /*query*/,
std::string* response_content) {
void BookListService::Get(const webcc::UrlQuery& /*query*/,
webcc::RestResponse* response) {
if (sleep_seconds_ > 0) {
LOG_INFO("Sleep %d seconds...", sleep_seconds_);
std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds_));
@@ -30,14 +30,12 @@ bool BookListService::Get(const webcc::UrlQuery& /*query*/,
json.append(BookToJson(book));
}
*response_content = JsonToString(json);
return true;
response->content = JsonToString(json);
response->status = webcc::HttpStatus::kOK;
}
// Add a new book.
bool BookListService::Post(const std::string& request_content,
std::string* response_content) {
void BookListService::Post(const std::string& request_content,
webcc::RestResponse* response) {
if (sleep_seconds_ > 0) {
LOG_INFO("Sleep %d seconds...", sleep_seconds_);
std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds_));
@@ -50,75 +48,91 @@ bool BookListService::Post(const std::string& request_content,
Json::Value json;
json["id"] = id;
*response_content = JsonToString(json);
return true;
response->content = JsonToString(json);
response->status = webcc::HttpStatus::kCreated;
} else {
// Invalid JSON
response->status = webcc::HttpStatus::kBadRequest;
}
return false;
}
// -----------------------------------------------------------------------------
bool BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
void BookDetailService::Get(const std::vector<std::string>& url_sub_matches,
const webcc::UrlQuery& query,
std::string* response_content) {
webcc::RestResponse* response) {
if (sleep_seconds_ > 0) {
LOG_INFO("Sleep %d seconds...", sleep_seconds_);
std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds_));
}
if (url_sub_matches.size() != 1) {
return false;
// TODO: kNotFound?
response->status = webcc::HttpStatus::kBadRequest;
return;
}
const std::string& book_id = url_sub_matches[0];
const Book& book = g_book_store.GetBook(book_id);
if (!book.IsNull()) {
*response_content = BookToJsonString(book);
return true;
if (book.IsNull()) {
response->status = webcc::HttpStatus::kNotFound;
return;
}
return false;
response->content = BookToJsonString(book);
response->status = webcc::HttpStatus::kOK;
}
// Update a book.
bool BookDetailService::Put(const std::vector<std::string>& url_sub_matches,
void BookDetailService::Put(const std::vector<std::string>& url_sub_matches,
const std::string& request_content,
std::string* response_content) {
webcc::RestResponse* response) {
if (sleep_seconds_ > 0) {
LOG_INFO("Sleep %d seconds...", sleep_seconds_);
std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds_));
}
if (url_sub_matches.size() != 1) {
return false;
// TODO: kNotFound?
response->status = webcc::HttpStatus::kBadRequest;
return;
}
const std::string& book_id = url_sub_matches[0];
Book book;
if (JsonStringToBook(request_content, &book)) {
book.id = book_id;
return g_book_store.UpdateBook(book);
if (!JsonStringToBook(request_content, &book)) {
response->status = webcc::HttpStatus::kBadRequest;
return;
}
return false;
book.id = book_id;
g_book_store.UpdateBook(book);
response->status = webcc::HttpStatus::kOK;
}
bool BookDetailService::Delete(
const std::vector<std::string>& url_sub_matches) {
void BookDetailService::Delete(
const std::vector<std::string>& url_sub_matches,
webcc::RestResponse* response) {
if (sleep_seconds_ > 0) {
LOG_INFO("Sleep %d seconds...", sleep_seconds_);
std::this_thread::sleep_for(std::chrono::seconds(sleep_seconds_));
}
if (url_sub_matches.size() != 1) {
return false;
// TODO: kNotFound?
response->status = webcc::HttpStatus::kBadRequest;
return;
}
const std::string& book_id = url_sub_matches[0];
return g_book_store.DeleteBook(book_id);
if (!g_book_store.DeleteBook(book_id)) {
response->status = webcc::HttpStatus::kNotFound;
return;
}
response->status = webcc::HttpStatus::kOK;
}
+15 -13
View File
@@ -16,24 +16,25 @@
// The query parameters could be regular expressions.
class BookListService : public webcc::RestListService {
public:
explicit BookListService(int sleep_seconds) : sleep_seconds_(sleep_seconds) {
explicit BookListService(int sleep_seconds)
: sleep_seconds_(sleep_seconds) {
}
protected:
// Return a list of books based on query parameters.
// Get a list of books based on query parameters.
// URL examples:
// - /books
// - /books?name={BookName}
bool Get(const webcc::UrlQuery& query,
std::string* response_content) override;
void Get(const webcc::UrlQuery& query,
webcc::RestResponse* response) final;
// Create a new book.
bool Post(const std::string& request_content,
std::string* response_content) override;
void Post(const std::string& request_content,
webcc::RestResponse* response) final;
private:
// Sleep for the client to test timeout control.
int sleep_seconds_ = 0;
int sleep_seconds_;
};
// -----------------------------------------------------------------------------
@@ -47,19 +48,20 @@ class BookDetailService : public webcc::RestDetailService {
}
protected:
bool Get(const std::vector<std::string>& url_sub_matches,
void Get(const std::vector<std::string>& url_sub_matches,
const webcc::UrlQuery& query,
std::string* response_content) override;
webcc::RestResponse* response) final;
bool Put(const std::vector<std::string>& url_sub_matches,
void Put(const std::vector<std::string>& url_sub_matches,
const std::string& request_content,
std::string* response_content) override;
webcc::RestResponse* response) final;
bool Delete(const std::vector<std::string>& url_sub_matches) override;
void Delete(const std::vector<std::string>& url_sub_matches,
webcc::RestResponse* response) final;
private:
// Sleep for the client to test timeout control.
int sleep_seconds_ = 0;
int sleep_seconds_;
};
#endif // EXAMPLE_REST_BOOK_SERVER_SERVICES_H_