#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #include "Batch.h" #include "esp_event.h" #include "esp_http_client.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_ota_ops.h" #include "esp_tls.h" #include "nvs_flash.h" #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE #include "esp_crt_bundle.h" #endif #include "esp_system.h" #include "http_handlers.h" #include "nvs.h" #include "nvs_flash.h" #include "nvs_utilities.h" #include "tools.h" #include #include #include #include #include #if CONFIG_WITH_METRICS static const char* const TAG = "MetricsBatch"; static const char* const feature_evt_name = "$feature_flag_called"; static const char* const feature_flag_name = "$feature_flag"; static const char* const feature_flag_response_name = "$feature_flag_response"; namespace Metrics { Event& Batch::add_feature_event() { return add_event(feature_evt_name); } void Batch::add_remove_feature_event(const char* name, bool active) { if (!active) { remove_feature_event(name); } else { add_event(feature_evt_name).add_property(feature_flag_name, name); } } Event& Batch::add_feature_variant_event(const char* const name, const char* const value) { return add_event(feature_evt_name) .add_property(feature_flag_name, name) .add_property(feature_flag_response_name, value); } void Batch::remove_feature_event(const char* name) { for (Metrics::Event& e : _events) { if (strcmp(e.get_name(), feature_evt_name) == 0) { e.remove_property(feature_flag_name, name); return; } } } cJSON* Batch::to_json() { cJSON* batch_json = cJSON_CreateArray(); for (Metrics::Event& e : _events) { cJSON_AddItemToArray(batch_json, e.to_json(_metrics_uid.c_str())); } cJSON* message = cJSON_CreateObject(); cJSON_AddItemToObject(message, "batch", batch_json); cJSON_AddStringToObject(message, "api_key", _api_key); return batch_json; } char* Batch::to_json_str() { cJSON* json = to_json(); char* json_str = cJSON_PrintUnformatted(json); cJSON_Delete(json); return json_str; } void Batch::push() { int status_code = 0; if (_metrics_uid.empty() && !_warned) { ESP_LOGW(TAG, "Metrics disabled; no CID found"); _warned = true; return; } char* json_str = to_json_str(); ESP_LOGV(TAG, "Metrics payload: %s", json_str); uint32_t start_time = gettime_ms(); status_code = metrics_http_post_request(json_str, _url); if (status_code == 200 || status_code == 204) { _events.clear(); } FREE_AND_NULL(json_str) ESP_LOGD(TAG, "Total duration for metrics call: %lu. ", gettime_ms() - start_time); } void Batch::build_guid() { uint8_t raw[16]; std::ostringstream oss; esp_fill_random(raw, 16); std::for_each(std::begin(raw), std::end(raw), [&oss](const uint8_t& byte) { oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); }); _metrics_uid = oss.str(); } void Batch::assign_id() { size_t size = 0; esp_err_t esp_err = ESP_OK; _metrics_uid = std::string((char*)get_nvs_value_alloc_for_partition( NVS_DEFAULT_PART_NAME, TAG, NVS_TYPE_BLOB, "cid", &size)); if (_metrics_uid[0] == 'G') { ESP_LOGW(TAG, "Invalid ID. %s", _metrics_uid.c_str()); _metrics_uid.clear(); } if (_metrics_uid.empty()) { build_guid(); if (_metrics_uid.empty()) { ESP_LOGE(TAG, "ID Failed"); return; } ESP_LOGW(TAG, "Metrics ID: %s", _metrics_uid.c_str()); esp_err = store_nvs_value_len_for_partition(NVS_DEFAULT_PART_NAME, TAG, NVS_TYPE_BLOB, "cid", _metrics_uid.c_str(), _metrics_uid.length() + 1); if (esp_err != ESP_OK) { ESP_LOGE(TAG, "Store ID failed: %s", esp_err_to_name(esp_err)); } } } } // namespace Metrics #endif