mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 21:40:05 +00:00
Provide interface to request multiple text product lists in parallel
This commit is contained in:
parent
33c73ef0e2
commit
7a8a0302e0
2 changed files with 118 additions and 68 deletions
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/outcome/result.hpp>
|
#include <boost/outcome/result.hpp>
|
||||||
|
|
||||||
|
|
@ -29,6 +30,10 @@ public:
|
||||||
ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
||||||
std::optional<std::string_view> cccc = {},
|
std::optional<std::string_view> cccc = {},
|
||||||
std::optional<std::string_view> pil = {});
|
std::optional<std::string_view> pil = {});
|
||||||
|
static boost::outcome_v2::result<std::vector<std::string>>
|
||||||
|
ListTextProducts(std::vector<std::chrono::sys_time<std::chrono::days>> dates,
|
||||||
|
std::vector<std::string_view> ccccs = {},
|
||||||
|
std::vector<std::string_view> pils = {});
|
||||||
|
|
||||||
static std::vector<std::shared_ptr<awips::TextProductFile>>
|
static std::vector<std::shared_ptr<awips::TextProductFile>>
|
||||||
LoadTextProducts(const std::vector<std::string>& textProducts);
|
LoadTextProducts(const std::vector<std::string>& textProducts);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <boost/json.hpp>
|
#include <boost/json.hpp>
|
||||||
#include <cpr/cpr.h>
|
#include <cpr/cpr.h>
|
||||||
|
#include <range/v3/view/cartesian_product.hpp>
|
||||||
|
|
||||||
#if (__cpp_lib_chrono < 201907L)
|
#if (__cpp_lib_chrono < 201907L)
|
||||||
# include <date/date.h>
|
# include <date/date.h>
|
||||||
|
|
@ -41,8 +42,25 @@ IemApiProvider& IemApiProvider::operator=(IemApiProvider&&) noexcept = default;
|
||||||
|
|
||||||
boost::outcome_v2::result<std::vector<std::string>>
|
boost::outcome_v2::result<std::vector<std::string>>
|
||||||
IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
||||||
std::optional<std::string_view> cccc,
|
std::optional<std::string_view> optionalCccc,
|
||||||
std::optional<std::string_view> pil)
|
std::optional<std::string_view> optionalPil)
|
||||||
|
{
|
||||||
|
std::string_view cccc =
|
||||||
|
optionalCccc.has_value() ? optionalCccc.value() : std::string_view {};
|
||||||
|
std::string_view pil =
|
||||||
|
optionalPil.has_value() ? optionalPil.value() : std::string_view {};
|
||||||
|
|
||||||
|
return ListTextProducts(
|
||||||
|
std::vector<std::chrono::sys_time<std::chrono::days>> {date},
|
||||||
|
{cccc},
|
||||||
|
{pil});
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::outcome_v2::result<std::vector<std::string>>
|
||||||
|
IemApiProvider::ListTextProducts(
|
||||||
|
std::vector<std::chrono::sys_time<std::chrono::days>> dates,
|
||||||
|
std::vector<std::string_view> ccccs,
|
||||||
|
std::vector<std::string_view> pils)
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
|
@ -57,93 +75,120 @@ IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
||||||
# define kDateFormat "%Y-%m-%d"
|
# define kDateFormat "%Y-%m-%d"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto parameters = cpr::Parameters {{"date", df::format(kDateFormat, date)}};
|
if (ccccs.empty())
|
||||||
|
|
||||||
// WMO Source Code
|
|
||||||
if (cccc.has_value())
|
|
||||||
{
|
{
|
||||||
parameters.Add({"cccc", std::string {cccc.value()}});
|
ccccs.push_back({});
|
||||||
}
|
}
|
||||||
|
|
||||||
// AFOS / AWIPS ID / 3-6 length identifier
|
if (pils.empty())
|
||||||
if (pil.has_value())
|
|
||||||
{
|
{
|
||||||
parameters.Add({"pil", std::string {pil.value()}});
|
pils.push_back({});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto response =
|
std::vector<cpr::AsyncResponse> responses {};
|
||||||
cpr::Get(cpr::Url {kBaseUrl_ + kListNwsTextProductsEndpoint_},
|
|
||||||
network::cpr::GetHeader(),
|
for (const auto& [date, cccc, pil] :
|
||||||
parameters);
|
ranges::views::cartesian_product(dates, ccccs, pils))
|
||||||
boost::json::value json = util::json::ReadJsonString(response.text);
|
{
|
||||||
|
auto parameters =
|
||||||
|
cpr::Parameters {{"date", df::format(kDateFormat, date)}};
|
||||||
|
|
||||||
|
// WMO Source Code
|
||||||
|
if (!cccc.empty())
|
||||||
|
{
|
||||||
|
parameters.Add({"cccc", std::string {cccc}});
|
||||||
|
}
|
||||||
|
|
||||||
|
// AFOS / AWIPS ID / 3-6 length identifier
|
||||||
|
if (!pil.empty())
|
||||||
|
{
|
||||||
|
parameters.Add({"pil", std::string {pil}});
|
||||||
|
}
|
||||||
|
|
||||||
|
responses.emplace_back(
|
||||||
|
cpr::GetAsync(cpr::Url {kBaseUrl_ + kListNwsTextProductsEndpoint_},
|
||||||
|
network::cpr::GetHeader(),
|
||||||
|
parameters));
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> textProducts {};
|
std::vector<std::string> textProducts {};
|
||||||
|
|
||||||
if (response.status_code == cpr::status::HTTP_OK)
|
for (auto& asyncResponse : responses)
|
||||||
{
|
{
|
||||||
try
|
auto response = asyncResponse.get();
|
||||||
{
|
|
||||||
// Get AFOS list from response
|
|
||||||
auto entries = boost::json::value_to<types::iem::AfosList>(json);
|
|
||||||
|
|
||||||
for (auto& entry : entries.data_)
|
boost::json::value json = util::json::ReadJsonString(response.text);
|
||||||
|
|
||||||
|
if (response.status_code == cpr::status::HTTP_OK)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
textProducts.push_back(entry.productId_);
|
// Get AFOS list from response
|
||||||
|
auto entries = boost::json::value_to<types::iem::AfosList>(json);
|
||||||
|
|
||||||
|
for (auto& entry : entries.data_)
|
||||||
|
{
|
||||||
|
textProducts.push_back(entry.productId_);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger_->trace("Found {} products", entries.data_.size());
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
// Unexpected bad response
|
||||||
|
logger_->warn("Error parsing JSON: {}", ex.what());
|
||||||
|
return boost::system::errc::make_error_code(
|
||||||
|
boost::system::errc::bad_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (response.status_code == cpr::status::HTTP_BAD_REQUEST &&
|
||||||
|
json != nullptr)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Log bad request details
|
||||||
|
auto badRequest =
|
||||||
|
boost::json::value_to<types::iem::BadRequest>(json);
|
||||||
|
logger_->warn("ListTextProducts bad request: {}",
|
||||||
|
badRequest.detail_);
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
// Unexpected bad response
|
||||||
|
logger_->warn("Error parsing bad response: {}", ex.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
logger_->trace("Found {} products", entries.data_.size());
|
|
||||||
}
|
|
||||||
catch (const std::exception& ex)
|
|
||||||
{
|
|
||||||
// Unexpected bad response
|
|
||||||
logger_->warn("Error parsing JSON: {}", ex.what());
|
|
||||||
return boost::system::errc::make_error_code(
|
return boost::system::errc::make_error_code(
|
||||||
boost::system::errc::bad_message);
|
boost::system::errc::invalid_argument);
|
||||||
}
|
}
|
||||||
}
|
else if (response.status_code == cpr::status::HTTP_UNPROCESSABLE_ENTITY &&
|
||||||
else if (response.status_code == cpr::status::HTTP_BAD_REQUEST &&
|
json != nullptr)
|
||||||
json != nullptr)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
// Log bad request details
|
try
|
||||||
auto badRequest = boost::json::value_to<types::iem::BadRequest>(json);
|
{
|
||||||
logger_->warn("ListTextProducts bad request: {}", badRequest.detail_);
|
// Log validation error details
|
||||||
}
|
auto error =
|
||||||
catch (const std::exception& ex)
|
boost::json::value_to<types::iem::ValidationError>(json);
|
||||||
{
|
logger_->warn("ListTextProducts validation error: {}",
|
||||||
// Unexpected bad response
|
error.detail_.at(0).msg_);
|
||||||
logger_->warn("Error parsing bad response: {}", ex.what());
|
}
|
||||||
}
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
// Unexpected bad response
|
||||||
|
logger_->warn("Error parsing validation error: {}", ex.what());
|
||||||
|
}
|
||||||
|
|
||||||
return boost::system::errc::make_error_code(
|
return boost::system::errc::make_error_code(
|
||||||
boost::system::errc::invalid_argument);
|
boost::system::errc::no_message_available);
|
||||||
}
|
|
||||||
else if (response.status_code == cpr::status::HTTP_UNPROCESSABLE_ENTITY &&
|
|
||||||
json != nullptr)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Log validation error details
|
|
||||||
auto error = boost::json::value_to<types::iem::ValidationError>(json);
|
|
||||||
logger_->warn("ListTextProducts validation error: {}",
|
|
||||||
error.detail_.at(0).msg_);
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
else
|
||||||
{
|
{
|
||||||
// Unexpected bad response
|
logger_->warn("Could not list text products: {}",
|
||||||
logger_->warn("Error parsing validation error: {}", ex.what());
|
response.status_line);
|
||||||
|
|
||||||
|
return boost::system::errc::make_error_code(
|
||||||
|
boost::system::errc::no_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return boost::system::errc::make_error_code(
|
|
||||||
boost::system::errc::no_message_available);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger_->warn("Could not list text products: {}", response.status_line);
|
|
||||||
|
|
||||||
return boost::system::errc::make_error_code(
|
|
||||||
boost::system::errc::no_message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return textProducts;
|
return textProducts;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue