use super::*;
use saddle_admission::{AdmissionError, InputDocument, InputKind, InputValue};
#[derive(Debug)]
pub enum ManagedIngressError { Codec(CodecError), Storage(AdmissionError) }
impl ManagedIngressError {
pub fn http_status(&self) -> u16 { match self { Self::Codec(e) => e.http_status, Self::Storage(_) => 503 } }
}
impl std::fmt::Display for ManagedIngressError {
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result { write!(f,"{self:?}") }
}
impl std::error::Error for ManagedIngressError {
fn source(&self)->Option<&(dyn std::error::Error+'static)> { match self { Self::Storage(e)=>Some(e), _=>None } }
}
impl From<CodecError> for ManagedIngressError { fn from(e:CodecError)->Self { Self::Codec(e) } }
impl From<AdmissionError> for ManagedIngressError { fn from(e:AdmissionError)->Self { Self::Storage(e) } }
fn shape(value:InputValue<'_>, keys:&[&str], optional:Option<&str>)->Result<(),CodecError> {
let mut seen=0u64;
for (key,_) in value.fields().ok_or(error(400,"INVALID_JSON_ENVELOPE"))? {
let i=keys.iter().position(|k|*k==key).ok_or(error(400,"INVALID_JSON_ENVELOPE"))?;
if seen&(1<<i)!=0 { return Err(error(400,"INVALID_JSON_ENVELOPE")); }
seen|=1<<i;
}
for (i,key) in keys.iter().enumerate() { if seen&(1<<i)==0 && Some(*key)!=optional { return Err(error(400,"INVALID_JSON_ENVELOPE")); } }
Ok(())
}
fn string<'a>(v:InputValue<'a>,key:&str)->Result<&'a str,CodecError> {
let value=v.field(key).ok_or(error(400,"INVALID_JSON_ENVELOPE"))?;
if value.kind()!=InputKind::String { return Err(error(400,"INVALID_JSON_ENVELOPE")); }
Ok(value.text().unwrap())
}
impl ProfuseGwListenerAdapter {
#[doc(hidden)]
pub fn validate_managed_transport(&self,method:&str,path:&str,content_type:&str,identity:&IngressIdentity,body:&[u8])->Result<(),ManagedIngressError> {
validate_identity(identity)?; validate_transport(method,path,content_type,body)?; Ok(())
}
#[doc(hidden)]
pub fn accept_managed(&self,method:&str,path:&str,content_type:&str,identity:&IngressIdentity,body:&[u8],document:InputDocument)->Result<AcceptedIngress,ManagedIngressError> {
validate_identity(identity)?;
validate_transport(method,path,content_type,body)?;
let root=document.root();
shape(root,&["target","profuseGwContext","requestData"],None)?;
let target=root.field("target").unwrap();
shape(target,&["app","interfaceId"],None)?;
let app=string(target,"app")?;
let interface=string(target,"interfaceId")?;
let context=root.field("profuseGwContext").unwrap();
shape(context,&["userInfo","traceInfo","ldcInfo"],None)?;
let user=context.field("userInfo").unwrap(); shape(user,&["userId"],None)?;
let user=string(user,"userId")?;
let trace=context.field("traceInfo").unwrap(); shape(trace,&["traceId","rpcId"],Some("traceId"))?;
let trace_id=if trace.field("traceId").is_some() { string(trace,"traceId")? } else { "" };
let rpc=string(trace,"rpcId")?;
let ldc=context.field("ldcInfo").unwrap(); shape(ldc,&["zone","idc","env"],None)?;
let zone=string(ldc,"zone")?; let idc=string(ldc,"idc")?; let env=string(ldc,"env")?;
for value in [app,interface,user,rpc,zone,idc,env] { validate_id(value)?; }
if !trace_id.is_empty() { validate_trace_id(trace_id)?; }
if root.field("requestData").unwrap().kind()!=InputKind::Object { return Err(error(400,"INVALID_REQUEST_DATA").into()); }
if app!=self.application.as_str() { return Err(error(404,"APPLICATION_NOT_FOUND").into()); }
let metadata=root.framework_input(|b| Ok(IngressMetadata {
identity: IngressIdentity { request_id:b.copy_text(&[&identity.request_id])?, call_id:b.copy_text(&[&identity.call_id])?, deadline_unix_ms:identity.deadline_unix_ms },
interface_id:b.copy_text(&[interface])?, user_id:b.copy_text(&[user])?,
trace_id:if trace_id.is_empty() { b.copy_text(&["saddle-",&identity.request_id])? } else { b.copy_text(&[trace_id])? },
rpc_id:b.copy_text(&[rpc])?, zone:b.copy_text(&[zone])?, idc:b.copy_text(&[idc])?, env:b.copy_text(&[env])?,
}))?;
Ok(AcceptedIngress { metadata:AcceptedMetadata::Managed(metadata),request_data:AcceptedRequestData::Managed(document) })
}
}