• 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.15
/src/common/json/platform/nlohmann/nlohmann_json.cpp
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
#include <common/json.hpp>
16

17
#include <fstream>
18
#include <map>
19
#include <string>
20

21
#include <nlohmann/json.hpp>
22

23
#include <common/io.hpp>
24

25
using namespace std;
26
namespace expected = mender::common::expected;
27
namespace error = mender::common::error;
28
namespace io = mender::common::io;
29

30
namespace mender {
31
namespace common {
32
namespace json {
33

34
const string empty_input_error_message = "attempting to parse an empty input";
35

36
static bool IsEmptyError(insensitive_json::parse_error &e) {
85✔
37
        auto is_empty_error = string(e.what()).find(empty_input_error_message);
85✔
38
        return is_empty_error != string::npos;
85✔
39
}
40

41
static error::Error GetErrorFromException(exception &e, const string &context_message) {
88✔
42
        try {
43
                // Trick to delegate the exception into the handlers below: Rethrow the exception
44
                // (Lippincott Function). The `e` argument is not actually needed, but included for
45
                // clarity.
46
                throw;
88✔
47
        } catch (insensitive_json::parse_error &e) {
88✔
48
                if (IsEmptyError(e)) {
85✔
49
                        return MakeError(
50
                                JsonErrorCode::EmptyError, context_message + ": " + "Empty input encountered");
148✔
51
                }
52
                return MakeError(JsonErrorCode::ParseError, context_message + ": " + e.what());
22✔
53
        } catch (insensitive_json::type_error &e) {
88✔
54
                return MakeError(JsonErrorCode::TypeError, context_message + ": " + e.what());
3✔
55
        } catch (system_error &e) {
3✔
56
                return error::Error(e.code().default_error_condition(), context_message + ": " + e.what());
×
57
        } catch (exception &e) {
×
58
                return error::MakeError(error::GenericError, context_message + ": " + e.what());
×
UNCOV
59
        }
×
60
}
61

62
ExpectedJson LoadFromFile(string file_path) {
331✔
63
        ifstream f;
331✔
64
        errno = 0;
331✔
65
        f.open(file_path);
331✔
66
        if (!f) {
331✔
67
                int io_errno = errno;
302✔
68
                auto err = error::Error(
69
                        std::generic_category().default_error_condition(io_errno),
604✔
70
                        "Failed to open '" + file_path + "': " + strerror(io_errno));
604✔
71
                return expected::unexpected(err);
604✔
72
        }
73

74
        try {
75
                insensitive_json parsed = insensitive_json::parse(f);
29✔
76
                Json j = Json(parsed);
27✔
77
                return ExpectedJson(j);
27✔
78
        } catch (exception &e) {
2✔
79
                return expected::unexpected(
2✔
80
                        GetErrorFromException(e, "Failed to parse '" + file_path + "'"));
4✔
81
        }
2✔
82
}
331✔
83

84
ExpectedJson Load(string json_str) {
279✔
85
        try {
86
                insensitive_json parsed = insensitive_json::parse(json_str);
279✔
87
                Json j = Json(parsed);
273✔
88
                return ExpectedJson(j);
273✔
89
        } catch (exception &e) {
6✔
90
                return expected::unexpected(GetErrorFromException(e, "Failed to parse '" + json_str + "'"));
12✔
91
        }
6✔
92
}
93

94
ExpectedJson Load(istream &str) {
641✔
95
        try {
96
                insensitive_json parsed = insensitive_json::parse(str);
641✔
97
                Json j = Json(parsed);
564✔
98
                return ExpectedJson(j);
564✔
99
        } catch (exception &e) {
77✔
100
                return expected::unexpected(GetErrorFromException(e, "Failed to parse JSON from stream"));
154✔
101
        }
77✔
102
}
103

104
ExpectedJson Load(io::Reader &reader) {
637✔
105
        auto str_ptr = reader.GetStream();
637✔
106
        try {
107
                return Load(*str_ptr);
637✔
108
        } catch (exception &e) {
×
109
                return expected::unexpected(GetErrorFromException(e, "Failed to parse JSON from stream"));
×
UNCOV
110
        }
×
111
}
637✔
112

113
string Json::Dump(const int indent) const {
330✔
114
        return this->n_json.dump(indent);
330✔
115
}
116

117
ExpectedJson Json::Get(const char *child_key) const {
4,756✔
118
        if (!this->n_json.is_object()) {
4,756✔
119
                auto err = MakeError(
120
                        JsonErrorCode::TypeError, "Invalid JSON type to get '" + string(child_key) + "' from");
×
121
                return expected::unexpected(err);
×
122
        }
123

124
        bool contains = this->n_json.contains(child_key);
4,756✔
125
        if (!contains) {
4,756✔
126
                auto err =
127
                        MakeError(JsonErrorCode::KeyError, "Key '" + string(child_key) + "' doesn't exist");
2,258✔
128
                return expected::unexpected(err);
2,258✔
129
        }
130

131
        insensitive_json n_json = this->n_json[child_key];
3,627✔
132
        Json j = Json(n_json);
7,254✔
133
        return j;
134
}
135

136
ExpectedJson Json::Get(const size_t idx) const {
975✔
137
        if (!this->n_json.is_array()) {
975✔
138
                auto err = MakeError(
139
                        JsonErrorCode::TypeError,
140
                        "Invalid JSON type to get item at index " + to_string(idx) + " from");
×
141
                return expected::unexpected(err);
×
142
        }
143

144
        if (this->n_json.size() <= idx) {
975✔
145
                auto err =
146
                        MakeError(JsonErrorCode::IndexError, "Index " + to_string(idx) + " out of range");
2✔
147
                return expected::unexpected(err);
2✔
148
        }
149

150
        insensitive_json n_json = this->n_json[idx];
974✔
151
        return Json(n_json);
1,948✔
152
}
153

154
ExpectedChildrenMap Json::GetChildren() const {
359✔
155
        if (!this->IsObject()) {
359✔
156
                auto err = MakeError(JsonErrorCode::TypeError, "Invalid JSON type to get children from");
×
157
                return expected::unexpected(err);
×
158
        }
159

160
        ChildrenMap ret {};
161
        for (const auto &item : this->n_json.items()) {
1,275✔
162
                ret[item.key()] = Json(item.value());
1,671✔
163
        }
359✔
164
        return ExpectedChildrenMap(ret);
359✔
165
}
166

167
bool Json::IsObject() const {
259✔
168
        return this->n_json.is_object();
259✔
169
}
170

171
bool Json::IsArray() const {
748✔
172
        return this->n_json.is_array();
748✔
173
}
174

175
bool Json::IsString() const {
585✔
176
        return this->n_json.is_string();
585✔
177
}
178

179
bool Json::IsInt64() const {
2✔
180
        return this->n_json.is_number_integer();
2✔
181
}
182

183
bool Json::IsNumber() const {
×
184
        return this->n_json.is_number();
×
185
}
186

187
bool Json::IsDouble() const {
×
188
        return this->n_json.is_number_float();
×
189
}
190

191
bool Json::IsBool() const {
2✔
192
        return this->n_json.is_boolean();
2✔
193
}
194

195
bool Json::IsNull() const {
193✔
196
        return this->n_json.is_null();
193✔
197
}
198

199
ExpectedString Json::GetString() const {
2,688✔
200
        try {
201
                string s = this->n_json.get<string>();
2,688✔
202
                return s;
2,688✔
203
        } catch (exception &e) {
×
204
                return expected::unexpected(GetErrorFromException(e, "Type mismatch when getting string"));
×
UNCOV
205
        }
×
206
}
207

208
ExpectedInt64 Json::GetInt64() const {
355✔
209
        try {
210
                int64_t s {this->n_json.get<int64_t>()};
355✔
211
                return s;
212
        } catch (exception &e) {
1✔
213
                return expected::unexpected(GetErrorFromException(e, "Type mismatch when getting int"));
2✔
214
        }
1✔
215
}
216

217
ExpectedDouble Json::GetDouble() const {
4✔
218
        try {
219
                return this->n_json.get<double>();
4✔
220
        } catch (exception &e) {
×
221
                return expected::unexpected(GetErrorFromException(e, "Type mismatch when getting double"));
×
UNCOV
222
        }
×
223
}
224

225
ExpectedBool Json::GetBool() const {
135✔
226
        try {
227
                bool s = this->n_json.get<bool>();
135✔
228
                return s;
229
        } catch (exception &e) {
2✔
230
                return expected::unexpected(GetErrorFromException(e, "Type mismatch when getting bool"));
4✔
231
        }
2✔
232
}
233

234
ExpectedSize Json::GetArraySize() const {
769✔
235
        if (!this->n_json.is_array()) {
769✔
236
                auto err = MakeError(JsonErrorCode::TypeError, "Not a JSON array");
4✔
237
                return expected::unexpected(err);
4✔
238
        } else {
239
                return this->n_json.size();
767✔
240
        }
241
}
242

243

244
template <>
245
ExpectedString Dump(unordered_map<string, vector<string>> std_map) {
×
246
        insensitive_json map_json(std_map);
×
247
        return map_json.dump();
×
248
}
249

250
template <>
251
ExpectedString Dump(unordered_map<string, string> std_map) {
9✔
252
        insensitive_json map_json(std_map);
9✔
253
        return map_json.dump();
18✔
254
}
255

256

257
} // namespace json
258
} // namespace common
259
} // namespace mender
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