diff --git a/.gitignore b/.gitignore index f65519e..5bbcd3f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ build/** +config.toml +.cache/** diff --git a/.gitmodules b/.gitmodules index c0983b3..885e4d3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "external/tomlplusplus"] path = external/tomlplusplus url = https://github.com/marzer/tomlplusplus +[submodule "external/json"] + path = external/json + url = https://github.com/nlohmann/json diff --git a/CMakeLists.txt b/CMakeLists.txt index b0f43ef..8ffa7f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ endif() # ----------------------------- # Project setup # ----------------------------- -project(mainframe LANGUAGES CXX) +project(skyward LANGUAGES CXX) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -27,6 +27,7 @@ add_subdirectory(external/spdlog) add_subdirectory(external/cpr) add_subdirectory(external/clickhouse-cpp) add_subdirectory(external/tomlplusplus) +add_subdirectory(external/json) # ----------------------------- # Executable # ----------------------------- @@ -63,6 +64,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE cpr::cpr clickhouse-cpp-lib tomlplusplus::tomlplusplus + nlohmann_json::nlohmann_json ) # ----------------------------- diff --git a/SkywardGradeChecker b/SkywardGradeChecker index 8f01eaa..dbba005 160000 --- a/SkywardGradeChecker +++ b/SkywardGradeChecker @@ -1 +1 @@ -Subproject commit 8f01eaacb572bb76dac3830594f839eae858d800 +Subproject commit dbba00532754cf3d22ef70fbda5b62af8f7aa3d7 diff --git a/external/json b/external/json new file mode 160000 index 0000000..9f35919 --- /dev/null +++ b/external/json @@ -0,0 +1 @@ +Subproject commit 9f359191102920312d472c8b9543118839344398 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..a9b892f --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,17 @@ +#include "skwyward-api-utils.hpp" +#include "spdlog/spdlog.h" + +#include +#include +int main (int argc, char *argv[]) { + auto config = toml::parse_file("config.toml"); + std::string base_uri = config["host"].value_or(""); + std::string test_username = config["test_username"].value_or(""); + std::string test_password = config["test_password"].value_or(""); + api_utils::StatusResponse health_check = api_methods::get_status(base_uri); + spdlog::info("Status response output: {}", health_check.status); + + api_utils::ErrorResponse test_login = api_methods::get_auth_status(base_uri, test_username, test_password); + spdlog::info("Auth attempt response : {}", test_login.success); + return 0; +} diff --git a/src/skwyward-api-utils.cpp b/src/skwyward-api-utils.cpp new file mode 100644 index 0000000..b90deb3 --- /dev/null +++ b/src/skwyward-api-utils.cpp @@ -0,0 +1,165 @@ +#include "skwyward-api-utils.hpp" + +#include +#include +#include +namespace api_utils { + +using nlohmann::json; + +// ---------- Helpers ---------- + +static double parse_percent_score(const json& j) { + if (j.is_number()) { + return j.get(); + } + + if (j.is_string()) { + std::string s = j.get(); + s.erase(std::remove(s.begin(), s.end(), '%'), s.end()); + return std::stod(s); + } + + return 0.0; +} + +// ---------- Login ---------- + +void to_json(json& j, const Login& r) { + j = json{ + {"username", r.username}, + {"password", r.password} + }; +} + +void from_json(const json& j, Login& r) { + j.at("username").get_to(r.username); + j.at("password").get_to(r.password); +} + +// ---------- ErrorResponse ---------- + +void from_json(const json& j, ErrorResponse& r) { + j.at("success").get_to(r.success); + + if (j.contains("error") && !j.at("error").is_null()) { + r.error = j.at("error").get(); + } else { + r.error.reset(); + } +} + +void to_json(json& j, const ErrorResponse& r) { + j = json{{"success", r.success}}; + if (r.error) { + j["error"] = *r.error; + } +} + +// ---------- StatusResponse ---------- + +void to_json(json& j, const StatusResponse& r) { + j = json{ + {"status", r.status}, + {"message", r.message} + }; +} + +void from_json(const json& j, StatusResponse& r) { + j.at("status").get_to(r.status); + j.at("message").get_to(r.message); +} + +// ---------- AssignmentGrade ---------- + +void to_json(json& j, const AssignmentGrade& g) { + j = json{ + {"name", g.name}, + {"dueDate", g.dueDate}, + {"score", g.score}, + {"attempts", g.attempts}, + {"isMajorGrade", g.isMajorGrade} + }; +} + +void from_json(const json& j, AssignmentGrade& g) { + j.at("name").get_to(g.name); + j.at("dueDate").get_to(g.dueDate); + + g.score = parse_percent_score(j.at("score")); + + j.at("attempts").get_to(g.attempts); + j.at("isMajorGrade").get_to(g.isMajorGrade); +} + +// ---------- ClassGrades ---------- + +void to_json(json& j, const ClassGrades& c) { + j = json{ + {"className", c.className}, + {"teacher", c.teacher}, + {"period", c.period}, + {"category", c.category}, + {"grades", c.grades} + }; +} + +void from_json(const json& j, ClassGrades& c) { + j.at("className").get_to(c.className); + j.at("teacher").get_to(c.teacher); + j.at("period").get_to(c.period); + j.at("category").get_to(c.category); + j.at("grades").get_to(c.grades); +} + +// ---------- GradesResponse ---------- + +void to_json(json& j, const GradesResponse& r) { + j = json{ + {"success", r.success}, + {"totalClasses", r.totalClasses}, + {"grades", r.grades} + }; +} + +void from_json(const json& j, GradesResponse& r) { + j.at("success").get_to(r.success); + j.at("totalClasses").get_to(r.totalClasses); + j.at("grades").get_to(r.grades); +} + +} // namespace api_utils + + +// ================= API METHODS ================= + +namespace api_methods { + +api_utils::StatusResponse get_status(std::string url) { + cpr::Response r = cpr::Get( + cpr::Url{url + "/health"} + ); + + return nlohmann::json::parse(r.text) + .get(); +} + +api_utils::ErrorResponse get_auth_status( + std::string url, + std::string username, + std::string password +) { + api_utils::Login login{username, password}; + + cpr::Response r = cpr::Post( + cpr::Url{url + "/check-auth"}, + cpr::Body{nlohmann::json(login).dump()}, + cpr::Header{{"Content-Type", "application/json"}} + ); + + return nlohmann::json::parse(r.text) + .get(); +} + +} // namespace api_methods + diff --git a/src/skwyward-api-utils.hpp b/src/skwyward-api-utils.hpp new file mode 100644 index 0000000..6d0ea5a --- /dev/null +++ b/src/skwyward-api-utils.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include +#include + +namespace api_utils { + +// ---------- Auth ---------- + +struct Login { + std::string username; + std::string password; +}; + +// ---------- Simple responses ---------- + +struct ErrorResponse { + bool success{}; + std::optional error; +}; + +struct StatusResponse { + std::string status; + std::string message; +}; + +// ---------- Grades ---------- + +struct AssignmentGrade { + std::string name; + std::string dueDate; + double score; // <-- parsed from "72.00%" + std::string attempts; + bool isMajorGrade; +}; + +struct ClassGrades { + std::string className; + std::string teacher; + std::string period; + std::string category; + std::vector grades; +}; + +struct GradesResponse { + bool success; + int totalClasses; + std::vector grades; +}; + +// ---------- nlohmann::json hooks ---------- + +void to_json(nlohmann::json& j, const Login& r); +void from_json(const nlohmann::json& j, Login& r); + +void to_json(nlohmann::json& j, const ErrorResponse& r); +void from_json(const nlohmann::json& j, ErrorResponse& r); + +void to_json(nlohmann::json& j, const StatusResponse& r); +void from_json(const nlohmann::json& j, StatusResponse& r); + +void to_json(nlohmann::json& j, const AssignmentGrade& g); +void from_json(const nlohmann::json& j, AssignmentGrade& g); + +void to_json(nlohmann::json& j, const ClassGrades& c); +void from_json(const nlohmann::json& j, ClassGrades& c); + +void to_json(nlohmann::json& j, const GradesResponse& r); +void from_json(const nlohmann::json& j, GradesResponse& r); + +} // namespace api_utils + + +namespace api_methods { + api_utils::StatusResponse get_status(std::string url); + api_utils::ErrorResponse get_auth_status( + std::string url, + std::string username, + std::string password + ); +} + diff --git a/src/types.cpp b/src/types.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/types.hpp b/src/types.hpp new file mode 100644 index 0000000..e69de29