parent
79e0206fc1
commit
9b8d2f566e
@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "CgiApplication.hpp"
|
||||
#include "MarkdownRouteHandler.hpp"
|
||||
#include "libmdml.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
@ -43,9 +44,17 @@ handle_error(
|
||||
int
|
||||
main(int argc, const char* argv[], const char* envp[])
|
||||
{
|
||||
auto app = mdml::CgiApplication(argc, argv, envp);
|
||||
|
||||
try {
|
||||
auto app = mdml::CgiApplication(argc, argv, envp);
|
||||
auto markdown_routes =
|
||||
mdml::MarkdownRouteHandler::GenerateRoutes(
|
||||
"/usr/local/www/content",
|
||||
"/usr/local/www/templates/main.thmtl"
|
||||
);
|
||||
|
||||
app.ImportRoutes(markdown_routes);
|
||||
|
||||
auto result = app.ProcessRequest();
|
||||
|
||||
if (result.IsError) {
|
||||
|
0
data/test.md → data/content/test.md
vendored
0
data/test.md → data/content/test.md
vendored
@ -23,20 +23,18 @@ class IRouteHandler;
|
||||
class CgiApplication : public Application {
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
using ptr = std::shared_ptr<T>;
|
||||
template<typename T>
|
||||
inline static ptr<T> make_ptr()
|
||||
{
|
||||
return std::make_shared<T>();
|
||||
}
|
||||
using RouteDictionary = std::map<std::string, ptr<IRouteHandler>>;
|
||||
using RouteDictionary = Dictionary<route::ptr<IRouteHandler>>;
|
||||
|
||||
CgiApplication(int argc, c::const_string argv[], c::const_string env[]);
|
||||
virtual ~CgiApplication();
|
||||
|
||||
Result<std::string> ProcessRequest();
|
||||
|
||||
inline void ImportRoutes(RouteDictionary& route_collection)
|
||||
{
|
||||
this->routes = std::move(route_collection);
|
||||
}
|
||||
|
||||
#ifdef TESTING
|
||||
/* Expose a public reference to the Routes map, but only for unit-testing
|
||||
* builds */
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "types.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace mdml {
|
||||
@ -23,21 +24,34 @@ namespace fs = std::filesystem;
|
||||
class MarkdownRouteHandler : public IRouteHandler {
|
||||
public:
|
||||
MarkdownRouteHandler();
|
||||
MarkdownRouteHandler(const MarkdownRouteHandler& other) = default;
|
||||
/*: IRouteHandler(other)
|
||||
, html_data(other.html_data)
|
||||
, markdown_data(other.markdown_data)
|
||||
, OutputStream()
|
||||
{
|
||||
}*/
|
||||
virtual ~MarkdownRouteHandler();
|
||||
|
||||
void LoadTemplate(const std::string& template_name);
|
||||
void LoadMarkdown(const std::string& markdown_page_name);
|
||||
void LoadTemplate(const fs::path& template_name);
|
||||
void LoadMarkdown(const fs::path& markdown_page_name);
|
||||
|
||||
virtual Result<std::string> Process(
|
||||
const std::string& name, const std::string& request_uri
|
||||
);
|
||||
|
||||
std::reference_wrapper<std::ostream> OutputStream;
|
||||
|
||||
#ifdef TESTING
|
||||
inline std::string& GetHtmlData() { return html_data; }
|
||||
inline std::string& GetMarkdownData() { return markdown_data; }
|
||||
#endif
|
||||
|
||||
static Dictionary<route::ptr<IRouteHandler>> GenerateRoutes(
|
||||
std::filesystem::path content_dir,
|
||||
std::filesystem::path main_template
|
||||
);
|
||||
|
||||
std::reference_wrapper<std::ostream> OutputStream;
|
||||
|
||||
protected:
|
||||
std::string render_document(
|
||||
const std::string& title, const std::string& request_uri
|
||||
@ -47,7 +61,7 @@ class MarkdownRouteHandler : public IRouteHandler {
|
||||
const fs::path& document_path, std::string& out_document
|
||||
);
|
||||
|
||||
fs::path work_dir;
|
||||
static fs::path work_dir;
|
||||
std::string html_data;
|
||||
std::string markdown_data;
|
||||
};
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@ -21,6 +22,17 @@ using string = char*;
|
||||
using const_string = const char*;
|
||||
}
|
||||
|
||||
namespace route {
|
||||
template<typename T>
|
||||
using ptr = std::shared_ptr<T>;
|
||||
template<typename T>
|
||||
inline static ptr<T>
|
||||
make_ptr()
|
||||
{
|
||||
return std::make_shared<T>();
|
||||
}
|
||||
}
|
||||
|
||||
enum error_t : bool { NO_ERROR = false, ERROR = true };
|
||||
using count_t = size_t;
|
||||
|
||||
|
@ -18,16 +18,19 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
using namespace mdml;
|
||||
|
||||
#if defined(TESTING)
|
||||
constexpr const char* DEFAULT_WORKDIR = ".";
|
||||
constexpr const char* DEFAULT_WORKDIR = "./data";
|
||||
#else
|
||||
constexpr const char* DEFAULT_WORKDIR = "/usr/local/www/templates";
|
||||
constexpr const char* DEFAULT_WORKDIR = "/usr/local/www";
|
||||
#endif
|
||||
|
||||
fs::path MarkdownRouteHandler::work_dir = fs::absolute(DEFAULT_WORKDIR);
|
||||
|
||||
const auto NOT_FOUND = std::string::npos;
|
||||
|
||||
void
|
||||
@ -48,7 +51,7 @@ string_replace(
|
||||
}
|
||||
|
||||
MarkdownRouteHandler::MarkdownRouteHandler()
|
||||
: IRouteHandler(), OutputStream(std::cout), work_dir(DEFAULT_WORKDIR)
|
||||
: IRouteHandler(), OutputStream(std::cout)
|
||||
{
|
||||
work_dir = fs::absolute(work_dir);
|
||||
}
|
||||
@ -56,17 +59,28 @@ MarkdownRouteHandler::MarkdownRouteHandler()
|
||||
MarkdownRouteHandler::~MarkdownRouteHandler() {}
|
||||
|
||||
void
|
||||
MarkdownRouteHandler::LoadTemplate(const std::string& template_filename)
|
||||
MarkdownRouteHandler::LoadTemplate(const fs::path& template_filename)
|
||||
{
|
||||
fs::path full_html_path = work_dir / template_filename;
|
||||
fs::path full_html_path;
|
||||
|
||||
if (template_filename.is_relative()) {
|
||||
full_html_path = work_dir / template_filename;
|
||||
} else {
|
||||
full_html_path = template_filename;
|
||||
}
|
||||
this->load_document(full_html_path, this->html_data);
|
||||
}
|
||||
|
||||
void
|
||||
MarkdownRouteHandler::LoadMarkdown(const std::string& markdown_filename)
|
||||
MarkdownRouteHandler::LoadMarkdown(const fs::path& markdown_filename)
|
||||
{
|
||||
fs::path full_md_path = work_dir / markdown_filename;
|
||||
fs::path full_md_path;
|
||||
|
||||
if (markdown_filename.is_relative()) {
|
||||
full_md_path = work_dir / markdown_filename;
|
||||
} else {
|
||||
full_md_path = markdown_filename;
|
||||
}
|
||||
|
||||
this->load_document(full_md_path, this->markdown_data);
|
||||
}
|
||||
@ -84,6 +98,44 @@ MarkdownRouteHandler::Process(
|
||||
return { NO_ERROR, document };
|
||||
}
|
||||
|
||||
Dictionary<route::ptr<IRouteHandler>>
|
||||
MarkdownRouteHandler::GenerateRoutes(
|
||||
std::filesystem::path content_dir, std::filesystem::path main_template
|
||||
)
|
||||
{
|
||||
if (content_dir.is_relative()) {
|
||||
content_dir = work_dir / content_dir;
|
||||
}
|
||||
Dictionary<route::ptr<IRouteHandler>> results;
|
||||
|
||||
for (const auto& entry :
|
||||
std::filesystem::directory_iterator(content_dir)) {
|
||||
if (entry.is_regular_file()) {
|
||||
auto filename = entry.path().filename().string();
|
||||
auto routename = entry.path().stem().string();
|
||||
auto extension = entry.path().extension().string();
|
||||
|
||||
// Check if the file has a .md extension and a specific
|
||||
// name pattern
|
||||
if (extension == ".md") {
|
||||
auto& routeHandler =
|
||||
*(new MarkdownRouteHandler());
|
||||
|
||||
// Load template and document
|
||||
routeHandler.LoadTemplate(
|
||||
(work_dir / main_template).string()
|
||||
);
|
||||
routeHandler.LoadMarkdown(entry.path().string());
|
||||
|
||||
// Move the routeHandler into the vector
|
||||
results.emplace(routename, &routeHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
void
|
||||
MarkdownRouteHandler::load_document(
|
||||
const fs::path& document_path, std::string& out_document
|
||||
|
@ -26,6 +26,21 @@ inline const char* simulated_launch::env[] = {
|
||||
"REQUEST_URI=markdown?msg=hello-world",
|
||||
nullptr
|
||||
};
|
||||
|
||||
// Define a test oute handler for the "/markdown" route
|
||||
class ExampleRouteHandler : public mdml::IRouteHandler {
|
||||
public:
|
||||
virtual mdml::Result<std::string> Process(
|
||||
const std::string& name, const std::string& request_uri
|
||||
) override
|
||||
{
|
||||
// Implement test logic for the route
|
||||
// handler
|
||||
return { mdml::NO_ERROR, "Processed" };
|
||||
}
|
||||
virtual ~ExampleRouteHandler() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
BEGIN_TEST_SUITE("CgiApplication")
|
||||
@ -49,6 +64,26 @@ BEGIN_TEST_SUITE("CgiApplication")
|
||||
);
|
||||
};
|
||||
|
||||
TEST("CgiApplication RegisterRoutes Test")
|
||||
{
|
||||
|
||||
CgiApplication test(
|
||||
1, simulated_launch::argv, simulated_launch::env
|
||||
);
|
||||
|
||||
mdml::CgiApplication::RouteDictionary routes;
|
||||
routes.emplace("test", new ExampleRouteHandler());
|
||||
routes.emplace("test2", new ExampleRouteHandler());
|
||||
routes.emplace("fasdfdshjk", new ExampleRouteHandler());
|
||||
|
||||
test.ImportRoutes(routes);
|
||||
|
||||
REQUIRE(routes.size() < 1);
|
||||
|
||||
REQUIRE(test.Routes.size() == 3);
|
||||
REQUIRE(test.Routes.at("test") != nullptr);
|
||||
};
|
||||
|
||||
TEST("CgiApplication::ProcessRequest no REQUEST_URI variable")
|
||||
{
|
||||
const char* no_request_env[] = { "PATH=/usr/bin",
|
||||
@ -62,23 +97,9 @@ BEGIN_TEST_SUITE("CgiApplication")
|
||||
|
||||
FIXTURE_TEST("CgiApplication ProcessRequest with valid route")
|
||||
{
|
||||
// Define a test oute handler for the "/markdown" route
|
||||
class ExampleRouteHandler : public IRouteHandler {
|
||||
public:
|
||||
virtual Result<std::string> Process(
|
||||
const std::string& name,
|
||||
const std::string& request_uri
|
||||
) override
|
||||
{
|
||||
// Implement test logic for the route
|
||||
// handler
|
||||
return { mdml::NO_ERROR, "Processed" };
|
||||
}
|
||||
virtual ~ExampleRouteHandler() = default;
|
||||
};
|
||||
|
||||
// Add the test route handler to the Routes dictionary
|
||||
auto handler = CgiApplication::make_ptr<ExampleRouteHandler>();
|
||||
auto handler = mdml::route::make_ptr<ExampleRouteHandler>();
|
||||
cgi_app.Routes["markdown"] = handler;
|
||||
|
||||
auto result = cgi_app.ProcessRequest();
|
||||
@ -123,7 +144,7 @@ BEGIN_TEST_SUITE("CgiApplication")
|
||||
}
|
||||
virtual ~BadRouteHandler() = default;
|
||||
};
|
||||
auto handler = CgiApplication::make_ptr<BadRouteHandler>();
|
||||
auto handler = mdml::route::make_ptr<BadRouteHandler>();
|
||||
cgi_app.Routes["markdown"] = handler;
|
||||
|
||||
REQUIRE_THROWS_AS(
|
||||
|
@ -40,6 +40,19 @@ BEGIN_TEST_SUITE("MarkdownRouteHandler Unit Tests")
|
||||
REQUIRE_NOTHROW([]() { MarkdownRouteHandler test; }());
|
||||
}
|
||||
|
||||
TEST("MarkdownRouteHandler Static Route Generator")
|
||||
{
|
||||
|
||||
REQUIRE_NOTHROW([]() {
|
||||
auto routes = MarkdownRouteHandler::GenerateRoutes(
|
||||
"content", "templates/main.thtml"
|
||||
);
|
||||
|
||||
REQUIRE(routes.size() > 0);
|
||||
REQUIRE(routes.begin()->first == "test");
|
||||
}());
|
||||
}
|
||||
|
||||
struct TestFixture {
|
||||
MarkdownRouteHandler test_obj;
|
||||
};
|
||||
@ -51,7 +64,7 @@ BEGIN_TEST_SUITE("MarkdownRouteHandler Unit Tests")
|
||||
SECTION("File exists")
|
||||
{
|
||||
REQUIRE_NOTHROW([&]() {
|
||||
test_obj.LoadTemplate("data/test.thtml");
|
||||
test_obj.LoadTemplate("templates/main.thtml");
|
||||
}());
|
||||
|
||||
REQUIRE(false == test_obj.GetHtmlData().empty());
|
||||
@ -59,7 +72,7 @@ BEGIN_TEST_SUITE("MarkdownRouteHandler Unit Tests")
|
||||
SECTION("File does not exists")
|
||||
{
|
||||
REQUIRE_THROWS([&]() {
|
||||
test_obj.LoadTemplate("not-found.txt");
|
||||
test_obj.LoadTemplate("templates/not-found.txt");
|
||||
}());
|
||||
}
|
||||
}
|
||||
@ -70,7 +83,7 @@ BEGIN_TEST_SUITE("MarkdownRouteHandler Unit Tests")
|
||||
SECTION("File exists")
|
||||
{
|
||||
REQUIRE_NOTHROW([&]() {
|
||||
test_obj.LoadMarkdown("data/test.md");
|
||||
test_obj.LoadMarkdown("content/test.md");
|
||||
}());
|
||||
|
||||
REQUIRE(false == test_obj.GetMarkdownData().empty());
|
||||
@ -78,7 +91,7 @@ BEGIN_TEST_SUITE("MarkdownRouteHandler Unit Tests")
|
||||
SECTION("File does not exists")
|
||||
{
|
||||
REQUIRE_THROWS([&]() {
|
||||
test_obj.LoadMarkdown("not-found.txt");
|
||||
test_obj.LoadMarkdown("content/not-found.txt");
|
||||
}());
|
||||
}
|
||||
}
|
||||
@ -88,8 +101,8 @@ BEGIN_TEST_SUITE("MarkdownRouteHandler Unit Tests")
|
||||
test_obj.OutputStream = buffer;
|
||||
|
||||
REQUIRE_NOTHROW([&]() {
|
||||
test_obj.LoadTemplate("data/test.thtml");
|
||||
test_obj.LoadMarkdown("data/test.md");
|
||||
test_obj.LoadTemplate("templates/main.thtml");
|
||||
test_obj.LoadMarkdown("content/test.md");
|
||||
|
||||
test_obj.Process("test", "test?param=1");
|
||||
}());
|
||||
|
Loading…
Reference in New Issue
Block a user