<commit_before>#include <algorithm>
#include <iostream>
#include <regex>
#include <stdexcept>
#include <stdlib.h>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>

#include "authentication_application.hpp"
#include "database_application.hpp"
#include "jwt.hpp"
#include "model_configformat.hpp"
#include "model_entry.hpp"
#include "model_importedconfig.hpp"
#include "model_pluginformat.hpp"
#include "model_user.hpp"
#include "root_application.hpp"
#include "service.hpp"

namespace kdbrest
{

DatabaseApp::DatabaseApp (cppcms::service & srv) : cppcms::application (srv)
{

	dispatcher ().assign ("/(([a-zA-Z0-9\\-\\.]+)(/([a-zA-Z0-9\\-\\.]+)){3})/{0,1}", &DatabaseApp::getUniqueEntry, this, 1);
	mapper ().assign ("entry", "/{1}");

	dispatcher ().assign ("/(([a-zA-Z0-9\\-\\.]+)(/([a-zA-Z0-9\\-\\.]+)){0,2})/{0,1}", &DatabaseApp::getEntriesByPrefix, this, 1);
	mapper ().assign ("search", "/{1}");

	dispatcher ().assign ("(/?)", &DatabaseApp::getAllEntries, this);
	mapper ().assign ("");
}

void DatabaseApp::getAllEntries ()
{
	if (request ().request_method () == "GET")
	{
		this->handleGet (request (), response ());
		return;
	}
	else if (request ().request_method () == "POST")
	{
		this->handleInsert (request (), response ());
		return;
	}
	else if (request ().request_method () == "OPTIONS")
	{
		RootApp::setCORSHeaders (response (), "GET,POST,OPTIONS");
		RootApp::setOk (response ());
		return;
	}
	else
	{
		RootApp::setMethodNotAllowed (response ()); // send HTTP 405
		return;
	}
}

void DatabaseApp::getEntriesByPrefix (std::string keyPart)
{
	if (request ().request_method () == "GET")
	{
		this->handleGet (request (), response (), keyPart);
		return;
	}
	else if (request ().request_method () == "OPTIONS")
	{
		RootApp::setCORSHeaders (response (), "GET,OPTIONS");
		RootApp::setOk (response ());
		return;
	}
	else
	{
		RootApp::setMethodNotAllowed (response ()); // send HTTP 405
		return;
	}
}

void DatabaseApp::getUniqueEntry (std::string key)
{
	if (request ().request_method () == "GET")
	{
		this->handleGetUnique (request (), response (), key);
		return;
	}
	else if (request ().request_method () == "PUT")
	{
		this->handleUpdate (request (), response (), key);
		return;
	}
	else if (request ().request_method () == "DELETE")
	{
		this->handleDelete (request (), response (), key);
		return;
	}
	else if (request ().request_method () == "OPTIONS")
	{
		RootApp::setCORSHeaders (response (), "GET,PUT,DELETE,OPTIONS");
		RootApp::setOk (response ());
		return;
	}
	else
	{
		RootApp::setMethodNotAllowed (response ()); // send HTTP 405
		return;
	}
}


inline void DatabaseApp::handleGet (cppcms::http::request & req, cppcms::http::response & resp, std::string keyPart)
{
	// first get the complete entry list
	std::vector<kdbrest::model::Entry> entries = service::StorageEngine::instance ().getAllEntries ();

	// if we are searching a sub-tree, filter first
	if (!keyPart.empty ())
	{
		service::SearchEngine::instance ().filterConfigurationsByName (entries, keyPart);
	}

	// then execute search based on parameters
	this->processFiltering (req, entries);
	// and sort the list
	this->processSorting (req, entries);

	// finally create the output
	this->produceOutput (req, resp, entries);
}

inline void DatabaseApp::handleGetUnique (cppcms::http::request & req, cppcms::http::response & resp, std::string key)
{
	try
	{
		model::Entry entry = service::StorageEngine::instance ().getEntry (key);

		std::string raw = req.get (PARAM_RAW);
		if (!raw.empty ())
		{
			std::vector<model::ConfigFormat> convertedFormats = service::ConvertEngine::instance ().exportToAll (entry);
			for (auto & elem : convertedFormats)
			{
				if (elem.getPluginformat ().getFileformat ().compare (raw) == 0)
				{
					RootApp::setOkRaw (resp, elem.getConfig (), "text/plain");
					return; // quit here
				}
			}
			RootApp::setBadRequest (resp, "Configuration format not supported", "ENTRY_UNSUPPORTED_FORMAT");
			return; // quit early
		}

		cppcms::json::value data;

		data["data"]["key"]["full"] = entry.getPublicName ();
		data["data"]["key"]["organization"] = entry.getOrganization ();
		data["data"]["key"]["application"] = entry.getApplication ();
		data["data"]["key"]["scope"] = entry.getScope ();
		data["data"]["key"]["slug"] = entry.getSlug ();

		int i = 0;
		for (auto & elem : entry.getTags ())
		{
			data["data"]["meta"]["tags"][i] = elem;
			i++;
		}
		data["data"]["meta"]["title"] = entry.getTitle ();
		data["data"]["meta"]["description"] = entry.getDescription ();
		data["data"]["meta"]["author"] = entry.getAuthor ();
		data["data"]["meta"]["created_at"] = entry.getCreatedAt ();

		std::vector<model::ConfigFormat> formats = service::ConvertEngine::instance ().exportToAll (entry);
		int j = 0;
		for (auto & elem : formats)
		{
			data["data"]["value"][j]["format"] = elem.getPluginformat ().getFileformat ();
			data["data"]["value"][j]["value"] = elem.getConfig ();
			j++;
		}

		RootApp::setOk (resp, data, "application/json");
	}
	catch (exception::EntryNotFoundException & e)
	{
		RootApp::setNotFound (resp, "The requested entry does not exist.", "ENTRY_DOES_NOT_EXIST");
		return;
	}
}

inline void DatabaseApp::handleInsert (cppcms::http::request & req, cppcms::http::response & resp)
{
	// first check if the currently authenticated user may create new database entries
	if (!AuthenticationApp::validateAuthentication (req, resp, ELEKTRA_REST_PERMISSIONS_CREATE_ENTRY))
	{
		return; // quit early, error message set already
	}

	model::User currentUser ("");
	try
	{
		currentUser = AuthenticationApp::getCurrentUser (req);
	}
	catch (exception::ElektraRestException & e)
	{<commit_msg>rest-backend: add plugin name to entry details conversion output<commit_after>#include <algorithm>
#include <iostream>
#include <regex>
#include <stdexcept>
#include <stdlib.h>
#include <vector>

#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>

#include "authentication_application.hpp"
#include "database_application.hpp"
#include "jwt.hpp"
#include "model_configformat.hpp"
#include "model_entry.hpp"
#include "model_importedconfig.hpp"
#include "model_pluginformat.hpp"
#include "model_user.hpp"
#include "root_application.hpp"
#include "service.hpp"

namespace kdbrest
{

DatabaseApp::DatabaseApp (cppcms::service & srv) : cppcms::application (srv)
{

	dispatcher ().assign ("/(([a-zA-Z0-9\\-\\.]+)(/([a-zA-Z0-9\\-\\.]+)){3})/{0,1}", &DatabaseApp::getUniqueEntry, this, 1);
	mapper ().assign ("entry", "/{1}");

	dispatcher ().assign ("/(([a-zA-Z0-9\\-\\.]+)(/([a-zA-Z0-9\\-\\.]+)){0,2})/{0,1}", &DatabaseApp::getEntriesByPrefix, this, 1);
	mapper ().assign ("search", "/{1}");

	dispatcher ().assign ("(/?)", &DatabaseApp::getAllEntries, this);
	mapper ().assign ("");
}

void DatabaseApp::getAllEntries ()
{
	if (request ().request_method () == "GET")
	{
		this->handleGet (request (), response ());
		return;
	}
	else if (request ().request_method () == "POST")
	{
		this->handleInsert (request (), response ());
		return;
	}
	else if (request ().request_method () == "OPTIONS")
	{
		RootApp::setCORSHeaders (response (), "GET,POST,OPTIONS");
		RootApp::setOk (response ());
		return;
	}
	else
	{
		RootApp::setMethodNotAllowed (response ()); // send HTTP 405
		return;
	}
}

void DatabaseApp::getEntriesByPrefix (std::string keyPart)
{
	if (request ().request_method () == "GET")
	{
		this->handleGet (request (), response (), keyPart);
		return;
	}
	else if (request ().request_method () == "OPTIONS")
	{
		RootApp::setCORSHeaders (response (), "GET,OPTIONS");
		RootApp::setOk (response ());
		return;
	}
	else
	{
		RootApp::setMethodNotAllowed (response ()); // send HTTP 405
		return;
	}
}

void DatabaseApp::getUniqueEntry (std::string key)
{
	if (request ().request_method () == "GET")
	{
		this->handleGetUnique (request (), response (), key);
		return;
	}
	else if (request ().request_method () == "PUT")
	{
		this->handleUpdate (request (), response (), key);
		return;
	}
	else if (request ().request_method () == "DELETE")
	{
		this->handleDelete (request (), response (), key);
		return;
	}
	else if (request ().request_method () == "OPTIONS")
	{
		RootApp::setCORSHeaders (response (), "GET,PUT,DELETE,OPTIONS");
		RootApp::setOk (response ());
		return;
	}
	else
	{
		RootApp::setMethodNotAllowed (response ()); // send HTTP 405
		return;
	}
}


inline void DatabaseApp::handleGet (cppcms::http::request & req, cppcms::http::response & resp, std::string keyPart)
{
	// first get the complete entry list
	std::vector<kdbrest::model::Entry> entries = service::StorageEngine::instance ().getAllEntries ();

	// if we are searching a sub-tree, filter first
	if (!keyPart.empty ())
	{
		service::SearchEngine::instance ().filterConfigurationsByName (entries, keyPart);
	}

	// then execute search based on parameters
	this->processFiltering (req, entries);
	// and sort the list
	this->processSorting (req, entries);

	// finally create the output
	this->produceOutput (req, resp, entries);
}

inline void DatabaseApp::handleGetUnique (cppcms::http::request & req, cppcms::http::response & resp, std::string key)
{
	try
	{
		model::Entry entry = service::StorageEngine::instance ().getEntry (key);

		std::string raw = req.get (PARAM_RAW);
		if (!raw.empty ())
		{
			std::vector<model::ConfigFormat> convertedFormats = service::ConvertEngine::instance ().exportToAll (entry);
			for (auto & elem : convertedFormats)
			{
				if (elem.getPluginformat ().getFileformat ().compare (raw) == 0)
				{
					RootApp::setOkRaw (resp, elem.getConfig (), "text/plain");
					return; // quit here
				}
			}
			RootApp::setBadRequest (resp, "Configuration format not supported", "ENTRY_UNSUPPORTED_FORMAT");
			return; // quit early
		}

		cppcms::json::value data;

		data["data"]["key"]["full"] = entry.getPublicName ();
		data["data"]["key"]["organization"] = entry.getOrganization ();
		data["data"]["key"]["application"] = entry.getApplication ();
		data["data"]["key"]["scope"] = entry.getScope ();
		data["data"]["key"]["slug"] = entry.getSlug ();

		int i = 0;
		for (auto & elem : entry.getTags ())
		{
			data["data"]["meta"]["tags"][i] = elem;
			i++;
		}
		data["data"]["meta"]["title"] = entry.getTitle ();
		data["data"]["meta"]["description"] = entry.getDescription ();
		data["data"]["meta"]["author"] = entry.getAuthor ();
		data["data"]["meta"]["created_at"] = entry.getCreatedAt ();

		std::vector<model::ConfigFormat> formats = service::ConvertEngine::instance ().exportToAll (entry);
		int j = 0;
		for (auto & elem : formats)
		{
			data["data"]["value"][j]["format"] = elem.getPluginformat ().getFileformat ();
			data["data"]["value"][j]["plugin"] = elem.getPluginformat ().getPluginname ();
			data["data"]["value"][j]["value"] = elem.getConfig ();
			j++;
		}

		RootApp::setOk (resp, data, "application/json");
	}
	catch (exception::EntryNotFoundException & e)
	{
		RootApp::setNotFound (resp, "The requested entry does not exist.", "ENTRY_DOES_NOT_EXIST");
		return;
	}
}

inline void DatabaseApp::handleInsert (cppcms::http::request & req, cppcms::http::response & resp)
{
	// first check if the currently authenticated user may create new database entries
	if (!AuthenticationApp::validateAuthentication (req, resp, ELEKTRA_REST_PERMISSIONS_CREATE_ENTRY))
	{
		return; // quit early, error message set already
	}

	model::User currentUser ("");
	try
	{
		currentUser = AuthenticationApp::getCurrentUser (req);
	}
	catch (exception::ElektraRestException & e)
	{
{'fraction_non_alphanumeric': 0.19386247065982787, 'fraction_numerical': 0.005389898287403286, 'mean_word_length': 4.869387755102041, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2643685', 'n_tokens_mistral': 4130, 'n_tokens_neox': 3862, 'n_words': 817}