Deliver enum channel as both numeric and stringified
This commit is contained in:
@@ -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,5 +1,4 @@
|
||||
use crate::err::Error;
|
||||
use crate::RetrievalError;
|
||||
use err::ToPublicError;
|
||||
use http::Response;
|
||||
use http::StatusCode;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user