namespace samples::http_cache {
struct KeyLang {
std::string key;
std::string language;
};
struct KeyLangEq {
bool operator()(const KeyLang& x, const KeyLang& y) const noexcept;
};
struct KeyLangHash {
bool operator()(const KeyLang& x) const noexcept;
};
using KeyLangToTranslation = std::unordered_map<KeyLang, std::string, KeyLangHash, KeyLangEq>;
}
namespace samples::http_cache {
bool KeyLangEq::operator()(const KeyLang& x, const KeyLang& y) const noexcept {
return x.key == y.key && x.language == y.language;
}
bool KeyLangHash::operator()(const KeyLang& x) const noexcept {
std::string data;
data.append(x.key);
data.append(x.language);
return std::hash<std::string>{}(data);
}
}
namespace samples::http_cache {
public:
static constexpr std::string_view kName = "cache-http-translations";
~HttpCachedTranslations() override;
void Update(
[[maybe_unused]] const std::chrono::system_clock::time_point& last_update,
[[maybe_unused]] const std::chrono::system_clock::time_point& now,
) override;
private:
const std::string translations_url_;
std::string last_update_remote_;
void MergeAndSetData(
KeyLangToTranslation&& content,
);
};
}
namespace samples::http_cache {
HttpCachedTranslations::HttpCachedTranslations(
)
: CachingComponentBase(config, context),
http_client_(context.FindComponent<
components::HttpClient>().GetHttpClient()),
translations_url_(config[
"translations-url"].As<
std::string>()) {
CacheUpdateTrait::StartPeriodicUpdates();
}
HttpCachedTranslations::~HttpCachedTranslations() { CacheUpdateTrait::StopPeriodicUpdates(); }
void HttpCachedTranslations::Update(
[[maybe_unused]] const std::chrono::system_clock::time_point& last_update,
[[maybe_unused]] const std::chrono::system_clock::time_point& now,
) {
switch (type) {
json = GetAllData();
break;
json = GetUpdatedData();
break;
default:
}
if (json.IsEmpty()) {
return;
}
KeyLangToTranslation content;
const auto snapshot = Get();
content = *snapshot;
}
MergeAndSetData(std::move(content), json, stats_scope);
}
auto response = http_client_.CreateRequest()
.get(translations_url_)
.retry(2)
.timeout(std::chrono::milliseconds{500})
.perform();
response->raise_for_status();
}
const auto url =
http::MakeUrl(translations_url_, {{
"last_update", last_update_remote_}});
auto response = http_client_.CreateRequest().get(url).retry(2).timeout(std::chrono::milliseconds{500}).perform();
response->raise_for_status();
}
void HttpCachedTranslations::MergeAndSetData(
KeyLangToTranslation&& content,
) {
for (const auto& [key, value] : Items(json["content"])) {
for (const auto& [lang, text] : Items(value)) {
content.insert_or_assign(KeyLang{key, lang}, text.As<std::string>());
}
}
auto update_time = json["update_time"].As<std::string>();
const auto size = content.size();
Set(std::move(content));
last_update_remote_ = std::move(update_time);
}
public:
static constexpr std::string_view kName = "handler-greet-user";
: HttpHandlerBase(config, context), cache_(context.FindComponent<HttpCachedTranslations>()) {}
const override {
const auto cache_snapshot = cache_.Get();
using samples::http_cache::KeyLang;
const auto& hello = cache_snapshot->at(KeyLang{"hello", "ru"});
const auto& welcome = cache_snapshot->at(KeyLang{"welcome", "ru"});
return fmt::format(
"{}, {}! {}", hello, request.
GetArg(
"username"), welcome);
}
private:
samples::http_cache::HttpCachedTranslations& cache_;
};
return yaml_config::MergeSchemas<components::CachingComponentBase<KeyLangToTranslation>>(R"(
type: object
description: HTTP caching sample component
additionalProperties: false
properties:
translations-url:
type: string
description: some other microservice listens on this URL
)");
}
}
int main(int argc, char* argv[]) {
.
Append<samples::http_cache::HttpCachedTranslations>()
.Append<samples::http_cache::GreetUser>()
.Append<server::handlers::TestsControl>()
.Append<components::HttpClient>();
}