Skip to content

Commit

Permalink
feat: Common JSON interface
Browse files Browse the repository at this point in the history
Ticket: MEN-6112
Changelog: None
Signed-off-by: Vratislav Podzimek <[email protected]>
  • Loading branch information
vpodzime committed Jan 13, 2023
1 parent ac97430 commit deecfb1
Show file tree
Hide file tree
Showing 9 changed files with 488 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
/mender
/mender-auth/mender-auth
/mender-update/mender-update
config.h

# Test reports
/reports
Expand Down
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@ set(PLATFORM linux_x86)


option(MENDER_LOG_BOOST "Use Boost as the underlying logging library provider (Default: ON)" ON)
configure_file(config.h.in config.h)

if (${PLATFORM} STREQUAL linux_x86)
set(MENDER_USE_NLOHMANN_JSON 1)
else()
set(MENDER_USE_NLOHMANN_JSON 0)
endif()

add_library(mender_compiler_flags INTERFACE)
target_compile_features(mender_compiler_flags INTERFACE cxx_std_11)
Expand Down Expand Up @@ -100,3 +105,5 @@ add_subdirectory(common)
add_subdirectory(mender-update)
add_subdirectory(mender-auth)
add_subdirectory(support)

configure_file(config.h.in config.h)
12 changes: 9 additions & 3 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
set(json_sources "$<$<STREQUAL:${PLATFORM},linux_x86>:nlohmann/nlohmann_json.cpp>")
set(json_sources "$<$<EQUAL:${MENDER_USE_NLOHMANN_JSON},1>:nlohmann/nlohmann_json.cpp>")
set(kv_db_sources "$<$<STREQUAL:${PLATFORM},linux_x86>:lmdb/lmdb_kv_db.cpp>")
set(events_sources "$<$<STREQUAL:${PLATFORM},linux_x86>:boost/events.cpp>")

add_library(common_json STATIC json/platform/${json_sources})
target_include_directories(common_json PRIVATE ../)
target_include_directories(common_json PUBLIC ../ ${CMAKE_BINARY_DIR})
if(${json_sources} MATCHES ".*nlohmann.*")
target_include_directories(common_json PRIVATE ../vendor/json/include/)
target_include_directories(common_json PUBLIC ../vendor/json/include/)
endif()

add_executable(json_test EXCLUDE_FROM_ALL json_test.cpp)
target_link_libraries(json_test PUBLIC common_json GTest::gtest_main gmock)
target_include_directories(json_test PRIVATE ../)
gtest_discover_tests(json_test)
add_dependencies(check json_test)

add_library(common_kv_db STATIC kv_db/platform/${kv_db_sources})
target_include_directories(common_kv_db PRIVATE ../)
if(${kv_db_sources} MATCHES ".*lmdb.*")
Expand Down
72 changes: 65 additions & 7 deletions common/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,87 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef JSON_HPP
#define JSON_HPP
#ifndef MENDER_COMMON_JSON_HPP
#define MENDER_COMMON_JSON_HPP

#include <config.h>
#include <common/error.hpp>
#include <common/expected.hpp>

namespace json {
#ifdef MENDER_USE_NLOHMANN_JSON
#include <nlohmann/json.hpp>
#endif

namespace mender::common::json {

using namespace std;

enum JsonErrorCode {
NoError = 0,
KeyNoExist,
ParseError,
KeyError,
IndexError,
TypeError,
};
using JsonError = mender::common::error::Error<JsonErrorCode>;

using ExpectedString = mender::common::expected::Expected<string, JsonError>;
using ExpectedInt = mender::common::expected::Expected<int, JsonError>;
using ExpectedBool = mender::common::expected::Expected<bool, JsonError>;
using ExpectedSize = mender::common::expected::Expected<size_t, JsonError>;

class Json {
public:
using ExpectedJson = mender::common::expected::Expected<Json, JsonError>;
void hello_world();

Json() = default;

string Dump(const int indent = 2) const;

ExpectedJson Get(const char *child_key) const;
ExpectedJson operator[](const char *child_key) const {
return this->Get(child_key);
}
ExpectedJson Get(const string &child_key) const {
return this->Get(child_key.data());
}
ExpectedJson operator[](const string &child_key) const {
return this->Get(child_key.data());
}
ExpectedJson Get(const size_t idx) const;
ExpectedJson operator[](const size_t idx) const {
return this->Get(idx);
}

bool IsObject() const;
bool IsArray() const;
bool IsString() const;
bool IsInt() const;
bool IsBool() const;
bool IsNull() const;

ExpectedString GetString() const;
ExpectedInt GetInt() const;
ExpectedBool GetBool() const;

ExpectedSize GetArraySize() const;

friend ExpectedJson LoadFromFile(string file_path);
friend ExpectedJson LoadFromString(string json_str);

private:
#ifdef MENDER_USE_NLOHMANN_JSON
nlohmann::json n_json;
Json(nlohmann::json n_json) :
n_json(n_json) {};
#endif
};

using ExpectedJson = mender::common::expected::Expected<Json, JsonError>;

} // namespace json
ExpectedJson LoadFromFile(string file_path);
ExpectedJson LoadFromString(string json_str);

} // namespace mender::common::json

#endif // JSON_HPP
#endif // MENDER_COMMON_JSON_HPP
141 changes: 130 additions & 11 deletions common/json/platform/nlohmann/nlohmann_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,143 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <common/json.hpp>

#include <fstream>
#include <string>
#include <nlohmann/json.hpp>

using njson = nlohmann::json;
using namespace std;

namespace mender::common::json {

ExpectedJson LoadFromFile(string file_path) {
ifstream f(file_path);
try {
njson parsed = njson::parse(f);
Json j = Json(parsed);
return ExpectedJson(j);
} catch (njson::parse_error &e) {
JsonError err = {
JsonErrorCode::ParseError, "Failed to parse '" + file_path + "': " + e.what()};
return ExpectedJson(err);
}
}

ExpectedJson LoadFromString(string json_str) {
try {
njson parsed = njson::parse(json_str);
Json j = Json(parsed);
return ExpectedJson(j);
} catch (njson::parse_error &e) {
JsonError err = {
JsonErrorCode::ParseError, "Failed to parse '''" + json_str + "''': " + e.what()};
return ExpectedJson(err);
}
}

string Json::Dump(const int indent) const {
return this->n_json.dump(indent);
}

namespace json {
ExpectedJson Json::Get(const char *child_key) const {
if (!this->n_json.is_object()) {
JsonError err = {
JsonErrorCode::TypeError, "Invalid JSON type to get '" + string(child_key) + "' from"};
return ExpectedJson(err);
}

void Json::hello_world() {
njson data = njson::parse(R"(
{
"Hello": "World"
}
)");
bool contains = this->n_json.contains(child_key);
if (!contains) {
JsonError err = {JsonErrorCode::KeyError, "Key '" + string(child_key) + "' doesn't exist"};
return ExpectedJson(err);
}

njson n_json = this->n_json[child_key];
Json j = Json(n_json);
return ExpectedJson(j);
}

ExpectedJson Json::Get(const size_t idx) const {
if (!this->n_json.is_array()) {
JsonError err = {
JsonErrorCode::TypeError,
"Invalid JSON type to get item at index " + to_string(idx) + " from"};
return ExpectedJson(err);
}

if (this->n_json.size() <= idx) {
JsonError err = {JsonErrorCode::IndexError, "Index " + to_string(idx) + " out of range"};
return ExpectedJson(err);
}

njson n_json = this->n_json[idx];
Json j = Json(n_json);
return ExpectedJson(j);
}

bool Json::IsObject() const {
return this->n_json.is_object();
}

const int spaces_indent = 4;
bool Json::IsArray() const {
return this->n_json.is_array();
}

bool Json::IsString() const {
return this->n_json.is_string();
}

bool Json::IsInt() const {
return this->n_json.is_number_integer();
}

bool Json::IsBool() const {
return this->n_json.is_boolean();
}

bool Json::IsNull() const {
return this->n_json.is_null();
}

ExpectedString Json::GetString() const {
try {
string s = this->n_json.get<string>();
return ExpectedString(s);
} catch (njson::type_error &e) {
JsonError err = {JsonErrorCode::TypeError, "Type mismatch when getting string"};
return ExpectedString(err);
}
}

ExpectedInt Json::GetInt() const {
try {
int s = this->n_json.get<int>();
return ExpectedInt(s);
} catch (njson::type_error &e) {
JsonError err = {JsonErrorCode::TypeError, "Type mismatch when getting int"};
return ExpectedInt(err);
}
}

ExpectedBool Json::GetBool() const {
try {
bool s = this->n_json.get<bool>();
return ExpectedBool(s);
} catch (njson::type_error &e) {
JsonError err = {JsonErrorCode::TypeError, "Type mismatch when getting bool"};
return ExpectedBool(err);
}
}

std::cout << data.dump(spaces_indent) << std::endl;
ExpectedSize Json::GetArraySize() const {
if (!this->n_json.is_array()) {
JsonError err = {JsonErrorCode::TypeError, "Not a JSON array"};
return ExpectedSize(err);
} else {
return ExpectedSize(this->n_json.size());
}
}

} // namespace json
} // namespace mender::common::json
Loading

0 comments on commit deecfb1

Please sign in to comment.