• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 2281564137

23 Jan 2026 10:59AM UTC coverage: 81.48% (+1.7%) from 79.764%
2281564137

push

gitlab-ci

michalkopczan
fix: Schedule next deployment poll if current one failed early causing no handler to be called

Ticket: MEN-9144
Changelog: Fix a hang when polling for deployment failed early causing no handler of API response
to be called. Added handler call for this case, causing the deployment polling
to continue.

Signed-off-by: Michal Kopczan <michal.kopczan@northern.tech>

1 of 1 new or added line in 1 file covered. (100.0%)

327 existing lines in 44 files now uncovered.

8839 of 10848 relevant lines covered (81.48%)

20226.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.56
/src/common/json.hpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#ifndef MENDER_COMMON_JSON_HPP
16
#define MENDER_COMMON_JSON_HPP
17

18
#include <common/config.h>
19

20
#include <string>
21
#include <map>
22
#include <unordered_map>
23

24
#include <common/common.hpp>
25
#include <common/error.hpp>
26
#include <common/expected.hpp>
27
#include <common/io.hpp>
28

29
#ifdef MENDER_USE_NLOHMANN_JSON
30
#include <nlohmann/json.hpp>
31
#endif
32

33
namespace mender {
34
namespace common {
35
namespace json {
36

37
using namespace std;
38

39
namespace error = mender::common::error;
40
namespace io = mender::common::io;
41
namespace common = mender::common;
42

43
enum JsonErrorCode {
44
        NoError = 0,
45
        ParseError,
46
        KeyError,
47
        IndexError,
48
        TypeError,
49
        EmptyError,
50
};
51

52
class CaseInsensitiveLess {
53
public:
54
        bool operator()(const string &lhs, const string &rhs) const {
36,191✔
55
                return common::StringToLower(lhs) < common::StringToLower(rhs);
36,191✔
56
        }
57
};
58

59
template <class Key, class T, class IgnoredLess, class Allocator = allocator<pair<const Key, T>>>
60
class CaseInsensitiveMap : public map<const Key, T, CaseInsensitiveLess, Allocator> {
20,790✔
61
public:
62
        using CaseInsensitiveMapType = map<const Key, T, CaseInsensitiveLess, Allocator>;
63

64
        CaseInsensitiveMap() :
65
                CaseInsensitiveMapType() {
66
        }
67

68
        template <class InputIterator>
69
        CaseInsensitiveMap(
9✔
70
                InputIterator first,
71
                InputIterator last,
72
                const CaseInsensitiveLess &comp = CaseInsensitiveLess(),
73
                const Allocator &alloc = Allocator()) :
74
                CaseInsensitiveMapType(first, last, comp, alloc) {
9✔
75
        }
76
};
77

78
#ifdef MENDER_USE_NLOHMANN_JSON
79
using insensitive_json = nlohmann::basic_json<CaseInsensitiveMap>;
80
#endif
81

82
class JsonErrorCategoryClass : public error_category {
83
public:
84
        const char *name() const noexcept override;
85
        string message(int code) const override;
86
};
87
extern const JsonErrorCategoryClass JsonErrorCategory;
88

89
error::Error MakeError(JsonErrorCode code, const string &msg);
90

91
using ExpectedString = mender::common::expected::ExpectedString;
92
using ExpectedInt64 = mender::common::expected::ExpectedInt64;
93
using ExpectedDouble = mender::common::expected::ExpectedDouble;
94
using ExpectedBool = mender::common::expected::ExpectedBool;
95
using ExpectedSize = mender::common::expected::ExpectedSize;
96

97
class Json {
16,526✔
98
public:
99
        using ExpectedJson = expected::expected<Json, error::Error>;
100
        using ChildrenMap = map<string, Json>;
101
        using ExpectedChildrenMap = expected::expected<ChildrenMap, error::Error>;
102

103
        Json() = default;
1,536✔
104

105
        string Dump(const int indent = 2) const;
106

107
        ExpectedJson Get(const char *child_key) const;
108
        ExpectedJson operator[](const char *child_key) const {
109
                return this->Get(child_key);
1✔
110
        }
111
        ExpectedJson Get(const string &child_key) const {
235✔
112
                return this->Get(child_key.data());
235✔
113
        }
114
        ExpectedJson operator[](const string &child_key) const {
115
                return this->Get(child_key.data());
116
        }
117
        ExpectedJson Get(const size_t idx) const;
118
        ExpectedJson operator[](const size_t idx) const {
119
                return this->Get(idx);
120
        }
121

122
        ExpectedChildrenMap GetChildren() const;
123

124
        bool IsObject() const;
125
        bool IsArray() const;
126
        bool IsString() const;
127
        bool IsInt64() const;
128
        bool IsNumber() const;
129
        bool IsDouble() const;
130
        bool IsBool() const;
131
        bool IsNull() const;
132

133
        ExpectedString GetString() const;
134
        ExpectedInt64 GetInt64() const;
135
        ExpectedDouble GetDouble() const;
136
        ExpectedBool GetBool() const;
137

138
        // Defined in cpp file as specialized templates.
139
        template <typename T>
140
        typename enable_if<
141
                not is_integral<T>::value or is_same<T, int64_t>::value,
142
                expected::expected<T, error::Error>>::type
143
        Get() const;
144

145
        // Use this as a catch-all for all integral types besides int64_t. It then automates the
146
        // process of checking whether it fits in the requested data type.
147
        template <typename T>
148
        typename enable_if<
149
                is_integral<T>::value and not is_same<T, int64_t>::value,
150
                expected::expected<T, error::Error>>::type
151
        Get() const {
130✔
152
                auto num = Get<int64_t>();
130✔
153
                if (!num) {
130✔
154
                        return expected::unexpected(num.error());
×
155
                }
156
                bool fits = true;
157
                if (is_signed<T>()) {
158
                        if (num.value() < numeric_limits<T>::lowest()
130✔
159
                                or num.value() > numeric_limits<T>::max()) {
130✔
160
                                fits = false;
161
                        }
162
                } else {
UNCOV
163
                        if (static_cast<unsigned long long>(num.value()) > numeric_limits<T>::max()) {
×
164
                                fits = false;
165
                        }
166
                }
167
                if (not fits) {
168
                        return expected::unexpected(error::Error(
×
169
                                make_error_condition(errc::result_out_of_range),
×
170
                                "Json::Get(): Number " + to_string(num.value())
×
171
                                        + " does not fit in requested data type"));
×
172
                }
173
                return static_cast<T>(num.value());
130✔
174
        }
175

176
        ExpectedSize GetArraySize() const;
177

178
        friend ExpectedJson LoadFromFile(string file_path);
179
        friend ExpectedJson Load(string json_str);
180
        friend ExpectedJson Load(istream &str);
181
        friend ExpectedJson Load(io::Reader &reader);
182

183
private:
184
#ifdef MENDER_USE_NLOHMANN_JSON
185
        insensitive_json n_json;
186
        Json(insensitive_json n_json) :
6,022✔
187
                n_json(n_json) {};
6,022✔
188
#endif
189
};
190

191
using ExpectedJson = expected::expected<Json, error::Error>;
192
using ChildrenMap = map<string, Json>;
193
using ExpectedChildrenMap = expected::expected<ChildrenMap, error::Error>;
194

195
ExpectedJson LoadFromFile(string file_path);
196
ExpectedJson Load(string json_str);
197
ExpectedJson Load(istream &str);
198
ExpectedJson Load(io::Reader &reader);
199

200
string EscapeString(const string &str);
201

202
using ExpectedStringVector = expected::ExpectedStringVector;
203
using KeyValueMap = unordered_map<string, string>;
204
using ExpectedKeyValueMap = expected::expected<KeyValueMap, error::Error>;
205

206
ExpectedStringVector ToStringVector(const json::Json &j);
207
ExpectedKeyValueMap ToKeyValueMap(const json::Json &j);
208
ExpectedString ToString(const json::Json &j);
209
ExpectedInt64 ToInt64(const json::Json &j);
210
ExpectedBool ToBool(const json::Json &j);
211

212
template <typename T>
213
expected::expected<T, error::Error> To(const json::Json &j) {
39✔
214
        return j.Get<T>();
39✔
215
}
216

217
// Template which we specialize for the given type in the platform dependent implementation
218
template <typename DataType>
219
ExpectedString Dump(DataType);
220

221
enum class MissingOk {
222
        No,
223
        Yes,
224
};
225

226
template <typename T>
227
expected::expected<T, error::Error> Get(
234✔
228
        const json::Json &json, const string &key, MissingOk missing_ok) {
229
        auto exp_value = json.Get(key);
234✔
230
        if (!exp_value) {
234✔
231
                if (missing_ok == MissingOk::Yes
2✔
232
                        && exp_value.error().code != json::MakeError(json::KeyError, "").code) {
2✔
UNCOV
233
                        return T();
×
234
                } else {
235
                        auto err = exp_value.error();
2✔
236
                        err.message += ": Could not get `" + key + "` from json data";
4✔
237
                        return expected::unexpected(err);
4✔
238
                }
239
        }
240
        return exp_value.value().Get<T>();
232✔
241
}
242

243
} // namespace json
244
} // namespace common
245
} // namespace mender
246

247
#endif // MENDER_COMMON_JSON_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc