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

mendersoftware / mender / 1054626264

30 Oct 2023 10:27AM UTC coverage: 80.137% (-0.06%) from 80.194%
1054626264

push

gitlab-ci

kacf
chore: Add many missing error checks and exception harnesses.

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

100 of 100 new or added lines in 7 files covered. (100.0%)

6887 of 8594 relevant lines covered (80.14%)

9361.04 hits per line

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

77.78
/mender-update/inventory.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 <mender-update/inventory.hpp>
16

17
#include <functional>
18
#include <sstream>
19
#include <string>
20

21
#include <api/api.hpp>
22
#include <api/client.hpp>
23
#include <common/common.hpp>
24
#include <common/error.hpp>
25
#include <common/events.hpp>
26
#include <common/http.hpp>
27
#include <common/inventory_parser.hpp>
28
#include <common/io.hpp>
29
#include <common/json.hpp>
30
#include <common/log.hpp>
31
#include <common/path.hpp>
32

33
namespace mender {
34
namespace update {
35
namespace inventory {
36

37
using namespace std;
38

39
namespace api = mender::api;
40
namespace common = mender::common;
41
namespace error = mender::common::error;
42
namespace events = mender::common::events;
43
namespace expected = mender::common::expected;
44
namespace http = mender::http;
45
namespace inv_parser = mender::common::inventory_parser;
46
namespace io = mender::common::io;
47
namespace json = mender::common::json;
48
namespace log = mender::common::log;
49
namespace path = mender::common::path;
50

51
const InventoryErrorCategoryClass InventoryErrorCategory;
52

53
const char *InventoryErrorCategoryClass::name() const noexcept {
×
54
        return "InventoryErrorCategory";
×
55
}
56

57
string InventoryErrorCategoryClass::message(int code) const {
×
58
        switch (code) {
×
59
        case NoError:
60
                return "Success";
×
61
        case BadResponseError:
62
                return "Bad response error";
×
63
        }
64
        assert(false);
65
        return "Unknown";
×
66
}
67

68
error::Error MakeError(InventoryErrorCode code, const string &msg) {
×
69
        return error::Error(error_condition(code, InventoryErrorCategory), msg);
1✔
70
}
71

72
const string uri = "/api/devices/v1/inventory/device/attributes";
73

74
error::Error PushInventoryData(
4✔
75
        const string &inventory_generators_dir,
76
        events::EventLoop &loop,
77
        api::Client &client,
78
        size_t &last_data_hash,
79
        APIResponseHandler api_handler) {
80
        auto ex_inv_data = inv_parser::GetInventoryData(inventory_generators_dir);
4✔
81
        if (!ex_inv_data) {
4✔
82
                return ex_inv_data.error();
×
83
        }
84

85
        stringstream top_ss;
8✔
86
        top_ss << "[";
4✔
87
        auto inv_data = ex_inv_data.value();
4✔
88
        auto key_vector = common::GetMapKeyVector(inv_data);
8✔
89
        std::sort(key_vector.begin(), key_vector.end());
4✔
90
        for (const auto &key : key_vector) {
13✔
91
                top_ss << R"({"name":")";
9✔
92
                top_ss << json::EscapeString(key);
9✔
93
                top_ss << R"(","value":)";
9✔
94
                if (inv_data[key].size() == 1) {
9✔
95
                        top_ss << "\"" + json::EscapeString(inv_data[key][0]) + "\"";
12✔
96
                } else {
97
                        stringstream items_ss;
6✔
98
                        items_ss << "[";
3✔
99
                        for (const auto &str : inv_data[key]) {
9✔
100
                                items_ss << "\"" + json::EscapeString(str) + "\",";
12✔
101
                        }
102
                        auto items_str = items_ss.str();
103
                        // replace the trailing comma with the closing square bracket
104
                        items_str[items_str.size() - 1] = ']';
3✔
105
                        top_ss << items_str;
3✔
106
                }
107
                top_ss << R"(},)";
9✔
108
        }
109
        auto payload = top_ss.str();
110
        if (payload[payload.size() - 1] == ',') {
4✔
111
                // replace the trailing comma with the closing square bracket
112
                payload.pop_back();
3✔
113
        }
114
        payload.push_back(']');
4✔
115

116
        size_t payload_hash = std::hash<string> {}(payload);
4✔
117
        if (payload_hash == last_data_hash) {
4✔
118
                loop.Post([api_handler]() { api_handler(error::NoError); });
8✔
119
                return error::NoError;
1✔
120
        }
121

122
        http::BodyGenerator payload_gen = [payload]() {
24✔
123
                return make_shared<io::StringReader>(payload);
3✔
124
        };
3✔
125

126
        auto req = make_shared<api::APIRequest>();
3✔
127
        req->SetPath(uri);
128
        req->SetMethod(http::Method::PUT);
3✔
129
        req->SetHeader("Content-Type", "application/json");
6✔
130
        req->SetHeader("Content-Length", to_string(payload.size()));
6✔
131
        req->SetHeader("Accept", "application/json");
6✔
132
        req->SetBodyGenerator(payload_gen);
3✔
133

134
        auto received_body = make_shared<vector<uint8_t>>();
3✔
135
        return client.AsyncCall(
136
                req,
137
                [received_body, api_handler](http::ExpectedIncomingResponsePtr exp_resp) {
3✔
138
                        if (!exp_resp) {
3✔
139
                                log::Error("Request to push inventory data failed: " + exp_resp.error().message);
×
140
                                api_handler(exp_resp.error());
×
141
                                return;
×
142
                        }
143

144
                        auto body_writer = make_shared<io::ByteWriter>(received_body);
3✔
145
                        auto resp = exp_resp.value();
3✔
146
                        auto content_length = resp->GetHeader("Content-Length");
6✔
147
                        auto ex_len = common::StringToLongLong(content_length.value());
3✔
148
                        if (!ex_len) {
3✔
149
                                log::Error("Failed to get content length from the inventory API response headers");
×
150
                                body_writer->SetUnlimited(true);
×
151
                        } else {
152
                                received_body->resize(ex_len.value());
3✔
153
                        }
154
                        resp->SetBodyWriter(body_writer);
6✔
155
                },
156
                [received_body, api_handler, payload_hash, &last_data_hash](
3✔
157
                        http::ExpectedIncomingResponsePtr exp_resp) {
2✔
158
                        if (!exp_resp) {
3✔
159
                                log::Error("Request to push inventory data failed: " + exp_resp.error().message);
×
160
                                api_handler(exp_resp.error());
×
161
                                return;
×
162
                        }
163

164
                        auto resp = exp_resp.value();
3✔
165
                        auto status = resp->GetStatusCode();
3✔
166
                        if (status == http::StatusOK) {
3✔
167
                                last_data_hash = payload_hash;
2✔
168
                                api_handler(error::NoError);
4✔
169
                        } else {
170
                                auto ex_err_msg = api::ErrorMsgFromErrorResponse(*received_body);
1✔
171
                                string err_str;
172
                                if (ex_err_msg) {
1✔
173
                                        err_str = ex_err_msg.value();
1✔
174
                                } else {
175
                                        err_str = resp->GetStatusMessage();
×
176
                                }
177
                                api_handler(MakeError(
2✔
178
                                        BadResponseError,
179
                                        "Got unexpected response " + to_string(status)
2✔
180
                                                + " from inventory API: " + err_str));
2✔
181
                        }
182
                });
12✔
183
}
184

185
} // namespace inventory
186
} // namespace update
187
} // 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

© 2025 Coveralls, Inc