Deliver enum channel as both numeric and stringified

This commit is contained in:
Dominik Werder
2024-09-03 16:33:40 +02:00
parent 4cc0f65a0b
commit 55b3bf4acd
21 changed files with 712 additions and 204 deletions
+40 -11
View File
@@ -19,9 +19,9 @@ use http::StatusCode;
use httpclient::body_empty;
use httpclient::body_stream;
use httpclient::error_response;
use httpclient::not_found_response;
use httpclient::IntoBody;
use httpclient::Requ;
use httpclient::StreamBody;
use httpclient::StreamResponse;
use httpclient::ToJsonBody;
use netpod::log::*;
@@ -50,6 +50,44 @@ pub enum Error {
EventsJson(#[from] streams::plaineventsjson::Error),
}
impl Error {
pub fn user_message(&self) -> String {
match self {
Error::ChannelNotFound => format!("channel not found"),
_ => self.to_string(),
}
}
pub fn status_code(&self) -> StatusCode {
match self {
Error::ChannelNotFound => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
pub fn response(&self, reqid: &str) -> http::Response<StreamBody> {
let js = serde_json::json!({
"message": self.user_message(),
"requestid": reqid,
});
if let Ok(body) = serde_json::to_string_pretty(&js) {
match http::Response::builder()
.status(self.status_code())
.header(http::header::CONTENT_TYPE, APP_JSON)
.body(httpclient::body_string(body))
{
Ok(res) => res,
Err(e) => {
error!("can not generate http error response {e}");
httpclient::internal_error()
}
}
} else {
httpclient::internal_error()
}
}
}
impl From<crate::channelconfig::Error> for Error {
fn from(value: crate::channelconfig::Error) -> Self {
use crate::channelconfig::Error::*;
@@ -100,16 +138,7 @@ impl EventsHandler {
.await
{
Ok(ret) => Ok(ret),
Err(e) => match e {
Error::ChannelNotFound => {
let res = not_found_response("channel not found".into(), ctx.reqid());
Ok(res)
}
_ => {
error!("EventsHandler sees: {e}");
Ok(error_response(e.public_message(), ctx.reqid()))
}
},
Err(e) => Ok(e.response(ctx.reqid())),
}
}
}
-1
View File
@@ -1,5 +1,4 @@
use crate::err::Error;
use crate::RetrievalError;
use err::ToPublicError;
use http::Response;
use http::StatusCode;
+29 -25
View File
@@ -1,5 +1,6 @@
use crate::bodystream::response;
use crate::err::Error;
use crate::requests::accepts_json_or_all;
use crate::ReqCtx;
use crate::ServiceSharedResources;
use futures_util::StreamExt;
@@ -7,6 +8,8 @@ use http::Method;
use http::StatusCode;
use httpclient::body_empty;
use httpclient::body_string;
use httpclient::error_response;
use httpclient::error_status_response;
use httpclient::IntoBody;
use httpclient::Requ;
use httpclient::StreamResponse;
@@ -113,35 +116,36 @@ impl ChannelStatusEventsHandler {
pub async fn handle(
&self,
req: Requ,
_ctx: &ReqCtx,
ctx: &ReqCtx,
shared_res: &ServiceSharedResources,
ncc: &NodeConfigCached,
) -> Result<StreamResponse, Error> {
if req.method() == Method::GET {
let accept_def = APP_JSON;
let accept = req
.headers()
.get(http::header::ACCEPT)
.map_or(accept_def, |k| k.to_str().unwrap_or(accept_def));
if accept.contains(APP_JSON) || accept.contains(ACCEPT_ALL) {
let url = req_uri_to_url(req.uri())?;
let q = ChannelStateEventsQuery::from_url(&url)?;
match self.fetch_data(&q, shared_res, ncc).await {
Ok(k) => {
let body = ToJsonBody::from(&k).into_body();
Ok(response(StatusCode::OK).body(body)?)
}
Err(e) => {
error!("{e}");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR)
.body(body_string(format!("{:?}", e.public_msg())))?)
}
}
} else {
Ok(response(StatusCode::BAD_REQUEST).body(body_empty())?)
}
if req.method() != Method::GET {
Ok(error_status_response(
StatusCode::METHOD_NOT_ALLOWED,
"expect a GET request".into(),
ctx.reqid(),
))
} else if !accepts_json_or_all(req.headers()) {
Ok(error_status_response(
StatusCode::NOT_ACCEPTABLE,
"server can only deliver json".into(),
ctx.reqid(),
))
} else {
Ok(response(StatusCode::METHOD_NOT_ALLOWED).body(body_empty())?)
let url = req_uri_to_url(req.uri())?;
let q = ChannelStateEventsQuery::from_url(&url)?;
match self.fetch_data(&q, shared_res, ncc).await {
Ok(k) => {
let body = ToJsonBody::from(&k).into_body();
Ok(response(StatusCode::OK).body(body)?)
}
Err(e) => {
error!("{e}");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR)
.body(body_string(format!("{:?}", e.public_msg())))?)
}
}
}
}
+125 -84
View File
@@ -55,6 +55,7 @@ use tokio::task::JoinHandle;
use tokio::time::error::Elapsed;
use url::Url;
static USE_CACHE: bool = false;
static CACHE: Cache<u64, u64> = Cache::new();
pub struct MapPulseHisto {
@@ -1274,51 +1275,69 @@ impl MapPulseHttpFunction {
}
trace!("MapPulseHttpFunction handle uri: {:?}", req.uri());
let pulse = extract_path_number_after_prefix(&req, Self::prefix())?;
match CACHE.portal(pulse) {
CachePortal::Fresh => {
let histo = MapPulseHistoHttpFunction::histo(pulse, node_config).await?;
let mut i1 = 0;
let mut max = 0;
for i2 in 0..histo.tss.len() {
if histo.counts[i2] > max {
max = histo.counts[i2];
i1 = i2;
if USE_CACHE {
match CACHE.portal(pulse) {
CachePortal::Fresh => {
let histo = MapPulseHistoHttpFunction::histo(pulse, node_config).await?;
let mut i1 = 0;
let mut max = 0;
for i2 in 0..histo.tss.len() {
if histo.counts[i2] > max {
max = histo.counts[i2];
i1 = i2;
}
}
if max > 0 {
let val = histo.tss[i1];
CACHE.set_value(pulse, val);
Ok(response(StatusCode::OK).body(body_string(serde_json::to_string(&val)?))?)
} else {
Ok(response(StatusCode::NO_CONTENT).body(body_empty())?)
}
}
if max > 0 {
let val = histo.tss[i1];
CACHE.set_value(pulse, val);
Ok(response(StatusCode::OK).body(body_string(serde_json::to_string(&val)?))?)
} else {
Ok(response(StatusCode::NO_CONTENT).body(body_empty())?)
}
}
CachePortal::Existing(rx) => {
trace!("waiting for already running pulse map pulse {pulse}");
match rx.recv().await {
Ok(_) => {
error!("should never recv from existing operation pulse {pulse}");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR).body(body_empty())?)
CachePortal::Existing(rx) => {
trace!("waiting for already running pulse map pulse {pulse}");
match rx.recv().await {
Ok(_) => {
error!("should never recv from existing operation pulse {pulse}");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR).body(body_empty())?)
}
Err(_e) => match CACHE.portal(pulse) {
CachePortal::Known(ts) => {
info!("pulse {pulse} known from cache ts {ts}");
Ok(response(StatusCode::OK).body(body_string(serde_json::to_string(&ts)?))?)
}
CachePortal::Fresh => {
error!("pulse {pulse} woken up, but fresh");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR).body(body_empty())?)
}
CachePortal::Existing(..) => {
error!("pulse {pulse} woken up, but existing");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR).body(body_empty())?)
}
},
}
Err(_e) => match CACHE.portal(pulse) {
CachePortal::Known(ts) => {
info!("pulse {pulse} known from cache ts {ts}");
Ok(response(StatusCode::OK).body(body_string(serde_json::to_string(&ts)?))?)
}
CachePortal::Fresh => {
error!("pulse {pulse} woken up, but fresh");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR).body(body_empty())?)
}
CachePortal::Existing(..) => {
error!("pulse {pulse} woken up, but existing");
Ok(response(StatusCode::INTERNAL_SERVER_ERROR).body(body_empty())?)
}
},
}
CachePortal::Known(ts) => {
info!("pulse {pulse} in cache ts {ts}");
Ok(response(StatusCode::OK).body(body_string(serde_json::to_string(&ts)?))?)
}
}
CachePortal::Known(ts) => {
info!("pulse {pulse} in cache ts {ts}");
Ok(response(StatusCode::OK).body(body_string(serde_json::to_string(&ts)?))?)
} else {
let histo = MapPulseHistoHttpFunction::histo(pulse, node_config).await?;
let mut i1 = 0;
let mut max = 0;
for i2 in 0..histo.tss.len() {
if histo.counts[i2] > max {
max = histo.counts[i2];
i1 = i2;
}
}
if max > 0 {
let val = histo.tss[i1];
Ok(response(StatusCode::OK).body(body_string(serde_json::to_string(&val)?))?)
} else {
Ok(response(StatusCode::NO_CONTENT).body(body_empty())?)
}
}
}
@@ -1342,58 +1361,80 @@ impl Api4MapPulseHttpFunction {
pub async fn find_timestamp(q: MapPulseQuery, ncc: &NodeConfigCached) -> Result<Option<u64>, Error> {
use crate::cache::CachePortal;
let pulse = q.pulse;
let res = match CACHE.portal(pulse) {
CachePortal::Fresh => {
trace!("value not yet in cache pulse {pulse}");
let histo = MapPulseHistoHttpFunction::histo(pulse, ncc).await?;
let mut i1 = 0;
let mut max = 0;
for i2 in 0..histo.tss.len() {
if histo.counts[i2] > max {
max = histo.counts[i2];
i1 = i2;
let res = if USE_CACHE {
match CACHE.portal(pulse) {
CachePortal::Fresh => {
trace!("value not yet in cache pulse {pulse}");
let histo = MapPulseHistoHttpFunction::histo(pulse, ncc).await?;
let mut i1 = 0;
let mut max = 0;
for i2 in 0..histo.tss.len() {
if histo.counts[i2] > max {
max = histo.counts[i2];
i1 = i2;
}
}
if histo.tss.len() > 1 {
warn!("Ambigious pulse map pulse {} histo {:?}", pulse, histo);
}
if max > 0 {
let val = histo.tss[i1];
CACHE.set_value(pulse, val);
Ok(Some(val))
} else {
Ok(None)
}
}
if histo.tss.len() > 1 {
warn!("Ambigious pulse map pulse {} histo {:?}", pulse, histo);
}
if max > 0 {
let val = histo.tss[i1];
CACHE.set_value(pulse, val);
Ok(Some(val))
} else {
Ok(None)
}
}
CachePortal::Existing(rx) => {
trace!("waiting for already running pulse map pulse {pulse}");
match rx.recv().await {
Ok(_) => {
error!("should never recv from existing operation pulse {pulse}");
Err(Error::with_msg_no_trace("map pulse error"))
}
Err(_e) => {
trace!("woken up while value wait pulse {pulse}");
match CACHE.portal(pulse) {
CachePortal::Known(val) => {
trace!("good, value after wakeup pulse {pulse}");
Ok(Some(val))
}
CachePortal::Fresh => {
error!("woken up, but portal fresh pulse {pulse}");
Err(Error::with_msg_no_trace("map pulse error"))
}
CachePortal::Existing(..) => {
error!("woken up, but portal existing pulse {pulse}");
Err(Error::with_msg_no_trace("map pulse error"))
CachePortal::Existing(rx) => {
trace!("waiting for already running pulse map pulse {pulse}");
match rx.recv().await {
Ok(_) => {
error!("should never recv from existing operation pulse {pulse}");
Err(Error::with_msg_no_trace("map pulse error"))
}
Err(_e) => {
trace!("woken up while value wait pulse {pulse}");
match CACHE.portal(pulse) {
CachePortal::Known(val) => {
trace!("good, value after wakeup pulse {pulse}");
Ok(Some(val))
}
CachePortal::Fresh => {
error!("woken up, but portal fresh pulse {pulse}");
Err(Error::with_msg_no_trace("map pulse error"))
}
CachePortal::Existing(..) => {
error!("woken up, but portal existing pulse {pulse}");
Err(Error::with_msg_no_trace("map pulse error"))
}
}
}
}
}
CachePortal::Known(val) => {
trace!("value already in cache pulse {pulse} ts {val}");
Ok(Some(val))
}
}
CachePortal::Known(val) => {
trace!("value already in cache pulse {pulse} ts {val}");
} else {
trace!("value not yet in cache pulse {pulse}");
let histo = MapPulseHistoHttpFunction::histo(pulse, ncc).await?;
let mut i1 = 0;
let mut max = 0;
for i2 in 0..histo.tss.len() {
if histo.counts[i2] > max {
max = histo.counts[i2];
i1 = i2;
}
}
if histo.tss.len() > 1 {
warn!("Ambigious pulse map pulse {} histo {:?}", pulse, histo);
}
if max > 0 {
let val = histo.tss[i1];
Ok(Some(val))
} else {
Ok(None)
}
};
res