Browse Source

Merge branch '80-us45-1-extended-list-command-server' into 'develop'

US45.1: Extended list command (Server)

Closes #80

See merge request tobias.wach/ccats!90
Sander, Paul 4 years ago
parent
commit
ace938e35f

+ 42 - 0
daemon/include/FileManager.h

@@ -2,6 +2,7 @@
 #define FILEMANAGER_H
 
 #include <fstream>
+#include <tuple>
 #include <vector>
 
 /**
@@ -59,6 +60,12 @@ private:
 	 */
 	std::vector<std::vector<std::string>> list;
 
+	/**
+	 * extendedlist vector for extendend list command
+	 * contains name head and size
+	 */
+	std::vector<std::vector<std::tuple<std::string, std::string, double>>> extendedlist;
+
 public:
 	enum { max_data_length = 4096 };
 
@@ -195,6 +202,41 @@ public:
 	 * @return return code 0: ok 1: disabled in config 2: file not found
 	 */
 	virtual Error deleteFile(const std::string &filename);
+
+	/**
+	 * Open extendedlist command. Set extendedlist vector and claculate chunks
+	 *
+	 * @return chunks of the resulting list | if a filename is too long it returns
+	 * -1
+	 */
+	virtual int openExtendedList();
+
+	/**
+	 * Open extendedlist command. Set extendedlist vector and claculate chunks
+	 *
+	 * @return chunks of the resulting extendedlist | if a filename is too long it returns
+	 * -1
+	 */
+	virtual int getRemainingExtendedListChunks();
+
+	/**
+	 * Return next chunk for extendedlistdata command.
+	 * You need to run openList before.
+	 *
+	 * @return next chnuk vector
+	 */
+	virtual std::vector<std::tuple<std::string, std::string, double>> getNextChunkFromExtendedList();
+
+	/**
+	 * @return size of the extendendlist vector
+	 */
+	virtual int getExtendedListSize();
+
+	/**
+	 * Cancel current extendendlist command.
+	 * Clear extendendlist vector and set remainingListChunks zero.
+	 */
+	virtual void cancelExtendedList();
 };
 
 #endif

+ 10 - 0
daemon/include/JsonCommander.h

@@ -167,6 +167,16 @@ private:
 	 */
 	Response executeNotifications(const Json::Value &message);
 
+	/**
+	 * Executes the extendedlist command
+	 */
+	Response executeExtendedList(const Json::Value &message);
+
+	/**
+	 * Executes the extendedlistdata command
+	 */
+	Response executeExtendedListData(const Json::Value &message);
+
 	/**
 	 * Executes the queue command
 	 */

+ 64 - 0
daemon/src/FileManager.cpp

@@ -2,6 +2,7 @@
 
 #include <boost/filesystem.hpp>
 #include <boost/range/iterator_range.hpp>
+#include <string>
 
 #include "../include/Config.h"
 
@@ -191,3 +192,66 @@ FileManager::Error FileManager::deleteFile(const std::string &filename) {
 
 	return no_error;
 }
+
+int FileManager::openExtendedList() {
+	this->extendedlist.push_back(std::vector<std::tuple<std::string, std::string, double>>());
+
+	int cursize = 0;
+
+	for (const auto &entry : boost::filesystem::directory_iterator(this->fileDirectory)) {
+		// getting filename from
+		if (boost::filesystem::is_directory(entry.path()))
+			continue;
+		const std::string name = entry.path().filename().string();
+
+		// calc head
+		std::string head;
+		std::pair<std::vector<char>, FileManager::Error> h = getBytesFromFile(name, 32);
+		if (h.second == FileManager::no_error) {
+			std::string s(h.first.begin(), h.first.end());
+			head = s;
+		} else
+			head = "";
+
+		// calc size in kbyte
+		std::string fname = this->fileDirectory;
+		fname.append(name);
+		std::ifstream file(fname, std::ios::ate | std::ios::binary);
+		double size = (double)((double)file.tellg() / (double)1000.0);
+
+		cursize += name.length() + head.length() + std::to_string(size).length() + 7;
+		if (cursize > max_data_length) {
+			this->extendedlist.push_back(std::vector<std::tuple<std::string, std::string, double>>());
+			cursize = 0;
+		}
+		std::tuple<std::string, std::string, double> p = std::tuple(name, head, size);
+		this->extendedlist.back().push_back(p);
+	}
+
+	if (this->extendedlist.size() == 1 && this->extendedlist.back().size() == 0) {
+		this->extendedlist.clear();
+	}
+
+	return this->extendedlist.size();
+}
+
+int FileManager::getRemainingExtendedListChunks() { return this->extendedlist.size(); }
+
+std::vector<std::tuple<std::string, std::string, double>> FileManager::getNextChunkFromExtendedList() {
+	if (getRemainingExtendedListChunks() == 0)
+		return std::vector<std::tuple<std::string, std::string, double>>();
+
+	std::vector<std::tuple<std::string, std::string, double>> ret = this->extendedlist.back();
+	this->extendedlist.pop_back();
+
+	return ret;
+}
+
+int FileManager::getExtendedListSize() {
+	int size = 0;
+	for (const std::vector<std::tuple<std::string, std::string, double>> v : this->extendedlist)
+		size += v.size();
+	return size;
+}
+
+void FileManager::cancelExtendedList() { this->extendedlist.clear(); }

+ 76 - 0
daemon/src/JsonCommander.cpp

@@ -19,6 +19,8 @@ JsonCommander::JsonCommander(FileManager &fileManager) : fileManager(fileManager
 	commandsMap["deletefile"] = &JsonCommander::executeDeleteFile;
 	commandsMap["extendedstatus"] = &JsonCommander::executeExtendedStatus;
 	commandsMap["notifications"] = &JsonCommander::executeNotifications;
+	commandsMap["extendedlist"] = &JsonCommander::executeExtendedList;
+	commandsMap["extendedlistdata"] = &JsonCommander::executeExtendedListData;
 	commandsMap["queue"] = &JsonCommander::executeQueue;
 	commandsMap["dequeue"] = &JsonCommander::executeDequeue;
 }
@@ -607,6 +609,32 @@ JsonCommander::Response JsonCommander::executeNotifications(const Json::Value &m
 	return response;
 }
 
+JsonCommander::Response JsonCommander::executeExtendedList(const Json::Value &message) {
+	JsonCommander::Response response;
+	response.action = send;
+	response.json["command"] = "extendedlist";
+
+	int chunks;
+	if (fileManager.getRemainingExtendedListChunks() > 0) {
+		response.json["accept"] = false;
+		response.json["chunks"] = -1;
+		response.json["items"] = -1;
+		response.json["error"] = "there is already an open extendedlist command";
+	} else if ((chunks = fileManager.openExtendedList()) == -1) {
+		response.json["accept"] = false;
+		response.json["chunks"] = -1;
+		response.json["items"] = -1;
+		response.json["error"] = "there is a filename which is too long";
+	} else {
+		response.json["accept"] = true;
+		response.json["chunks"] = chunks;
+		response.json["items"] = fileManager.getExtendedListSize();
+		response.json["error"] = "";
+	}
+
+	return response;
+}
+
 JsonCommander::Response JsonCommander::executeQueue(const Json::Value &message) {
 	JsonCommander::Response response;
 	response.json["command"] = "queue";
@@ -634,6 +662,54 @@ JsonCommander::Response JsonCommander::executeQueue(const Json::Value &message)
 	return response;
 }
 
+JsonCommander::Response JsonCommander::executeExtendedListData(const Json::Value &message) {
+	JsonCommander::Response response;
+	response.action = send;
+	response.json["command"] = "extendedlistdata";
+	Json::Value array;
+
+	const int remainingListchunks = fileManager.getRemainingExtendedListChunks();
+	if (!message["chunk"].isInt() || !message["cancel"].isBool()) {
+		response.action = closeAndSend;
+		response.json["cancel"] = true;
+		response.json["remaining"] = -1;
+		response.json["files"] = Json::arrayValue;
+		response.json["error"] = "incorrect listdata command request";
+	} else if (remainingListchunks == 0) {
+		response.json["cancel"] = true;
+		response.json["remaining"] = -1;
+		response.json["files"] = Json::arrayValue;
+		response.json["error"] = "there are no chunks to send";
+	} else if (message["cancel"].asBool()) {
+		response.json["cancel"] = true;
+		response.json["remaining"] = message["chunk"].asInt();
+		response.json["files"] = Json::arrayValue;
+		response.json["error"] = "";
+		fileManager.cancelExtendedList();
+	} else if (remainingListchunks - 1 != message["chunk"].asInt()) {
+		response.action = closeAndSend;
+		response.json["cancel"] = true;
+		response.json["remaining"] = -1;
+		response.json["files"] = Json::arrayValue;
+		response.json["error"] = "wrong chunk number";
+	} else {
+		auto v = fileManager.getNextChunkFromExtendedList();
+		for (int i = 0; i < v.size(); i++) {
+			Json::Value obj;
+			obj["name"] = std::get<0>(v.at(i));
+			obj["head"] = std::get<1>(v.at(i)).compare("") == 0 ? "" : base64::encode(std::get<1>(v.at(i)));
+			obj["size"] = std::get<2>(v.at(i));
+			array.append(obj);
+		}
+		response.json["remaining"] = message["chunk"].asInt();
+		response.json["cancel"] = false;
+		response.json["files"] = array;
+		response.json["error"] = "";
+	}
+
+	return response;
+}
+
 JsonCommander::Response JsonCommander::executeDequeue(const Json::Value &message) {
 	JsonCommander::Response response;
 	response.json["command"] = "dequeue";

+ 6 - 0
daemon/test/FileManagerMock.h

@@ -32,6 +32,12 @@ public:
 
 	MOCK_METHOD(FileManager::Error, deleteFile, (const std::string &filename), (override));
 	MOCK_METHOD((std::pair<std::vector<char>, FileManager::Error>), getBytesFromFile, (const std::string &filename, int numOfBytes), (override));
+
+	MOCK_METHOD(int, openExtendedList, (), (override));
+	MOCK_METHOD(int, getRemainingExtendedListChunks, (), (override));
+	MOCK_METHOD(int, getExtendedListSize, (), (override));
+	MOCK_METHOD((std::vector<std::tuple<std::string, std::string, double>>), getNextChunkFromExtendedList, (), (override));
+	MOCK_METHOD(void, cancelExtendedList, (), (override));
 };
 
 #endif

+ 186 - 0
daemon/test/JsonCommanderTest.cpp

@@ -1538,4 +1538,190 @@ TEST(Notifications, TwoMessages) {
 	Notifications::userTimeStamps.clear();
 }
 
+TEST(ExtendedList, positive) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlist";
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(0));
+	EXPECT_CALL(fileManager, openExtendedList()).WillOnce(testing::Return(1));
+	EXPECT_CALL(fileManager, getExtendedListSize()).WillOnce(testing::Return(5));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["chunks"].asInt(), 1);
+	EXPECT_EQ(response.json["items"].asInt(), 5);
+	EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(ExtendedList, negative) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlist";
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(0));
+	EXPECT_CALL(fileManager, openExtendedList()).WillOnce(testing::Return(-1));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_FALSE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["chunks"].asInt(), -1);
+	EXPECT_EQ(response.json["items"].asInt(), -1);
+	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(ExtendedList, EmptyList) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlist";
+	Json::Value message;
+	message["command"] = command;
+
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(0));
+	EXPECT_CALL(fileManager, openExtendedList()).WillOnce(testing::Return(0));
+	EXPECT_CALL(fileManager, getExtendedListSize()).WillOnce(testing::Return(0));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["accept"].asBool());
+	EXPECT_EQ(response.json["chunks"].asInt(), 0);
+	EXPECT_EQ(response.json["items"].asInt(), 0);
+	EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(ExtendedListData, Positive) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlistdata";
+	Json::Value message;
+	message["command"] = command;
+	message["chunk"] = 0;
+	message["cancel"] = false;
+
+	std::vector<std::tuple<std::string, std::string, double>> chunks;
+	std::tuple<std::string, std::string, double> chunk = std::tuple("asdf", "asdfghjkasdfghjkasdfghjkasdfghjk", 41.2);
+	chunks.push_back(chunk);
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(1));
+	EXPECT_CALL(fileManager, getNextChunkFromExtendedList()).WillOnce(testing::Return(chunks));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_FALSE(response.json["cancel"].asBool());
+	EXPECT_EQ(response.json["remaining"].asInt(), 0);
+	EXPECT_TRUE(response.json["files"].isArray());
+	EXPECT_EQ(response.json["files"].size(), 1);
+	EXPECT_EQ(response.json["files"][0]["name"].asString(), "asdf");
+	EXPECT_NE(response.json["files"][0]["head"].asString(), "");
+	EXPECT_EQ(response.json["files"][0]["size"].asDouble(), 41.2);
+	EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(ExtendedListData, Cancel) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlistdata";
+	Json::Value message;
+	message["command"] = command;
+	message["chunk"] = 0;
+	message["cancel"] = true;
+
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(1));
+	EXPECT_CALL(fileManager, cancelExtendedList());
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["cancel"].asBool());
+	EXPECT_EQ(response.json["remaining"].asInt(), 0);
+	EXPECT_TRUE(response.json["files"].isArray());
+	EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(ExtendedListData, WrongChunkNumber) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlistdata";
+	Json::Value message;
+	message["command"] = command;
+	message["chunk"] = 1;
+	message["cancel"] = false;
+
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(1));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["cancel"].asBool());
+	EXPECT_EQ(response.json["remaining"].asInt(), -1);
+	EXPECT_TRUE(response.json["files"].isArray());
+	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(ExtendedListData, NoChunksToBeSend) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlistdata";
+	Json::Value message;
+	message["command"] = command;
+	message["chunk"] = 1;
+	message["cancel"] = false;
+
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(0));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::send);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["cancel"].asBool());
+	EXPECT_EQ(response.json["remaining"].asInt(), -1);
+	EXPECT_TRUE(response.json["files"].isArray());
+	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(ExtendedListData, InvalidRequest) {
+	FileManagerMock fileManager;
+
+	JsonCommander jsonCommander(fileManager);
+
+	const std::string command = "extendedlistdata";
+	Json::Value message;
+	message["command"] = command;
+	message["chunk"] = 5;
+
+	EXPECT_CALL(fileManager, getRemainingExtendedListChunks()).WillOnce(testing::Return(0));
+
+	JsonCommander::Response response = jsonCommander.execute(message);
+	EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
+	EXPECT_EQ(response.json["command"].asString(), command);
+	EXPECT_TRUE(response.json["cancel"].asBool());
+	EXPECT_EQ(response.json["remaining"].asInt(), -1);
+	EXPECT_TRUE(response.json["files"].isArray());
+	EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
 } // namespace