diff --git a/README.md b/README.md index 879eba7..c8b8178 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,4 @@ Both clients can: ### Dependencies: -+ `libpqxx` for the API to the PostgreSQL database management system (official link: https://github.com/jtv/libpqxx) \ No newline at end of file ++ `libcurl` for accessing the webserver \ No newline at end of file diff --git a/cli/Makefile b/cli/Makefile new file mode 100644 index 0000000..19d3805 --- /dev/null +++ b/cli/Makefile @@ -0,0 +1,10 @@ +CC := g++ +SRC := dao.cpp handler.cpp main.cpp parser.cpp printer.cpp json_serializer.cpp +FLAGS := -lcurl -g -Wall -Wextra +OUTDIR := ./build +OUT := tickets + +build: + mkdir -p $(OUTDIR) + $(CC) -o $(OUTDIR)/$(OUT) $(SRC) $(FLAGS) + \ No newline at end of file diff --git a/cli/build/tickets b/cli/build/tickets new file mode 100755 index 0000000..89f9aab Binary files /dev/null and b/cli/build/tickets differ diff --git a/cli/dao.cpp b/cli/dao.cpp index ece3f82..6efb5b2 100644 --- a/cli/dao.cpp +++ b/cli/dao.cpp @@ -1,19 +1,70 @@ -#include -#include - #include "include/dao.h" using namespace std; -void Dao::populate_env() { +Dao::Dao() { this->api_key = getenv("FRONTEND_API_KEY"); - this->db_url = getenv("DB_URL"); + if (this->api_key.empty()) { + cout << "FRONTEND_API_KEY environment variable is not set" << endl; + exit(1); + } + this->base_url = getenv("BASE_API_URL"); + if (this->base_url.empty()) { + cout << "BASE_API_URL environment variable is not set" << endl; + exit(1); + } } -ticket_t Dao::fetch_ticket_create(string title, string description) { - +static size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) { + size_t total = size * nmemb; + std::string* s = static_cast(userp); + s->append(static_cast(contents), total); + return total; } +ticket_t Dao::fetch_ticket_create(std::string title, std::string description) { + string response; + string url = this->base_url + "/tickets"; + + CURL *handle = curl_easy_init(); + + struct curl_slist* headers = nullptr; + headers = curl_slist_append(headers, "Content-Type: application/json"); + headers = curl_slist_append(headers, ("Authorization: Bearer " + this->api_key).c_str()); + + string body = nlohmann::json{ + {"title", title}, + {"description", description} + }.dump(); + + curl_easy_setopt(handle, CURLOPT_URL, url.c_str()); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); + + curl_easy_setopt(handle, CURLOPT_POST, 1L); + curl_easy_setopt(handle, CURLOPT_POSTFIELDS, body.c_str()); + curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, static_cast(body.size())); + + curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_callback); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, &response); + + CURLcode rc = curl_easy_perform(handle); + + curl_slist_free_all(headers); + curl_easy_cleanup(handle); + + if (rc != CURLE_OK) { + throw std::runtime_error(std::string("curl_easy_perform failed: ") + curl_easy_strerror(rc)); + } + + nlohmann::json j = nlohmann::json::parse(response); + + ticket_t t = j.get(); + + return t; +} + + vector Dao::fetch_ticket_get_all() { } diff --git a/cli/include/dao.h b/cli/include/dao.h index 0cc8373..07c042d 100644 --- a/cli/include/dao.h +++ b/cli/include/dao.h @@ -3,20 +3,22 @@ #include #include +#include +#include +#include #include "ticket.h" - -using namespace std; +#include "json_serializer.h" class Dao { public: - string api_key; - string db_url; - void populate_env(); + std::string api_key; + std::string base_url; - ticket_t fetch_ticket_create(string title, string description); - vector fetch_ticket_get_all(); - vector fetch_ticket_get_by_status(string status); + Dao(); + ticket_t fetch_ticket_create(std::string title, std::string description); + std::vector fetch_ticket_get_all(); + std::vector fetch_ticket_get_by_status(std::string status); ticket_t fetch_ticket_get_by_id(unsigned long id); }; diff --git a/cli/include/handler.h b/cli/include/handler.h index 3845761..176f35e 100644 --- a/cli/include/handler.h +++ b/cli/include/handler.h @@ -5,11 +5,9 @@ #include "dao.h" -using namespace std; - -int handle_create(Dao *dao, string title, string description); +int handle_create(Dao *dao, std::string title, std::string description); int handle_list(Dao *dao); -int handle_list(Dao *dao, string status); +int handle_list(Dao *dao, std::string status); int handle_detail(Dao *dao, unsigned long id); #endif // HANDLER_H diff --git a/cli/include/json_serializer.h b/cli/include/json_serializer.h new file mode 100644 index 0000000..1f8d83d --- /dev/null +++ b/cli/include/json_serializer.h @@ -0,0 +1,13 @@ +#include +#include +#include + +#include "ticket.h" + +using json = nlohmann::json; + +// Deserialize JSON -> class +void from_json(const json& j, ticket_t& t); + +// Serialize class +void to_json(json& j, const ticket_t& t); \ No newline at end of file diff --git a/cli/include/printer.h b/cli/include/printer.h index bff8913..06f4290 100644 --- a/cli/include/printer.h +++ b/cli/include/printer.h @@ -5,8 +5,6 @@ #include "ticket.h" -using namespace std; - -void print_tickets(vector tickets); +void print_tickets(std::vector tickets); #endif // PRINTER_H \ No newline at end of file diff --git a/cli/include/ticket.h b/cli/include/ticket.h index 1111dd7..8e065a9 100644 --- a/cli/include/ticket.h +++ b/cli/include/ticket.h @@ -2,17 +2,14 @@ #define TICKET_H #include -#include - -using namespace std; typedef struct { unsigned long id; - string title; - string description; - string status; - string created_at; - string updated_at; + std::string title; + std::string description; + std::string status; + std::string created_at; + std::string updated_at; } ticket_t; #endif // TICKET_H \ No newline at end of file diff --git a/cli/json_serializer.cpp b/cli/json_serializer.cpp new file mode 100644 index 0000000..276a893 --- /dev/null +++ b/cli/json_serializer.cpp @@ -0,0 +1,23 @@ +#include "include/json_serializer.h" + +// Deserialize JSON -> class +void from_json(const json& j, ticket_t& t) { + t.id = j.at("id").get(); + t.title = j.at("title").get(); + t.description = j.at("description").get(); + t.status = j.at("status").get(); + t.created_at = j.at("created_at").get(); + t.updated_at = j.at("updated_at").get(); +} + +// Serialize class -> JSON +void to_json(json& j, const ticket_t& t) { + j = json{ + {"id", t.id}, + {"title", t.title}, + {"description", t.description}, + {"status", t.status}, + {"created_at", t.created_at}, + {"updated_at", t.updated_at} + }; +} diff --git a/cli/main.cpp b/cli/main.cpp index 2f5ab00..7103c38 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -3,7 +3,6 @@ int main(int argc, char *argv[], char *envp[]) { Dao *dao = new Dao(); - dao->populate_env(); int retval = parse_args(dao, argc, argv); delete dao; } diff --git a/cli/printer.cpp b/cli/printer.cpp index bbfa058..720ae03 100644 --- a/cli/printer.cpp +++ b/cli/printer.cpp @@ -2,6 +2,8 @@ #include "include/printer.h" +using namespace std; + void print_tickets(vector tickets) { for (ticket_t t : tickets) { cout << "/------+-------\n" diff --git a/webserver/handlers.go b/webserver/handlers.go index f99f2b0..788e117 100644 --- a/webserver/handlers.go +++ b/webserver/handlers.go @@ -171,9 +171,9 @@ func createTicket(w http.ResponseWriter, r *http.Request) { err = db.QueryRow( context.Background(), - "INSERT INTO tickets(title, description) VALUES($1, $2) RETURNING id;", + "INSERT INTO tickets(title, description) VALUES($1, $2) RETURNING id, status, created_at, updated_at;", t.Title, t.Description, - ).Scan(&t.Id) + ).Scan(&t.Id, &t.Status, &t.CreatedAt, &t.UpdatedAt) if err != nil { http.Error(w, err.Error(), 500)