Improve gateway reliability and dashboard docs
This commit is contained in:
@@ -5,7 +5,7 @@ use tonic::transport::{Certificate, Channel, ClientTlsConfig};
|
||||
use tonic::Request;
|
||||
|
||||
use crate::auth::AuthInterceptor;
|
||||
use crate::error::{ensure_command_success, Error};
|
||||
use crate::error::{ensure_command_success, ensure_protocol_success, Error};
|
||||
use crate::generated::mxaccess_gateway::v1::mx_access_gateway_client::MxAccessGatewayClient;
|
||||
use crate::generated::mxaccess_gateway::v1::{
|
||||
CloseSessionReply, CloseSessionRequest, MxCommandReply, MxCommandRequest, MxEvent,
|
||||
@@ -23,6 +23,7 @@ pub type EventStream =
|
||||
pub struct GatewayClient {
|
||||
inner: RawGatewayClient,
|
||||
call_timeout: std::time::Duration,
|
||||
stream_timeout: Option<std::time::Duration>,
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
@@ -57,6 +58,7 @@ impl GatewayClient {
|
||||
Ok(Self {
|
||||
inner: MxAccessGatewayClient::with_interceptor(channel, interceptor),
|
||||
call_timeout: options.call_timeout(),
|
||||
stream_timeout: options.stream_timeout(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,6 +85,7 @@ impl GatewayClient {
|
||||
|
||||
pub async fn open_session(&self, request: OpenSessionRequest) -> Result<Session, Error> {
|
||||
let reply = self.open_session_raw(request).await?;
|
||||
ensure_protocol_success("open session", reply.protocol_status.as_ref())?;
|
||||
Ok(Session::new(reply.session_id, self.clone()))
|
||||
}
|
||||
|
||||
@@ -107,7 +110,7 @@ impl GatewayClient {
|
||||
|
||||
pub async fn stream_events(&self, request: StreamEventsRequest) -> Result<EventStream, Error> {
|
||||
let mut client = self.inner.clone();
|
||||
let response = client.stream_events(self.unary_request(request)).await?;
|
||||
let response = client.stream_events(self.stream_request(request)).await?;
|
||||
let stream = futures_util::StreamExt::map(response.into_inner(), |result| {
|
||||
result.map_err(Error::from)
|
||||
});
|
||||
@@ -120,4 +123,13 @@ impl GatewayClient {
|
||||
request.set_timeout(self.call_timeout);
|
||||
request
|
||||
}
|
||||
|
||||
fn stream_request<T>(&self, message: T) -> Request<T> {
|
||||
let mut request = Request::new(message);
|
||||
if let Some(timeout) = self.stream_timeout {
|
||||
request.set_timeout(timeout);
|
||||
}
|
||||
|
||||
request
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use thiserror::Error as ThisError;
|
||||
use tonic::Code;
|
||||
|
||||
use crate::generated::mxaccess_gateway::v1::{MxCommandReply, ProtocolStatusCode};
|
||||
use crate::generated::mxaccess_gateway::v1::{MxCommandReply, ProtocolStatus, ProtocolStatusCode};
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
pub enum Error {
|
||||
@@ -47,6 +47,13 @@ pub enum Error {
|
||||
|
||||
#[error("gateway command failed: {0}")]
|
||||
Command(#[from] Box<CommandError>),
|
||||
|
||||
#[error("gateway {operation} failed: {code:?}: {message}")]
|
||||
ProtocolStatus {
|
||||
operation: &'static str,
|
||||
code: ProtocolStatusCode,
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -125,6 +132,27 @@ pub fn ensure_command_success(reply: MxCommandReply) -> Result<MxCommandReply, E
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_protocol_success(
|
||||
operation: &'static str,
|
||||
status: Option<&ProtocolStatus>,
|
||||
) -> Result<(), Error> {
|
||||
let code = status
|
||||
.and_then(|status| ProtocolStatusCode::try_from(status.code).ok())
|
||||
.unwrap_or(ProtocolStatusCode::Unspecified);
|
||||
|
||||
if code == ProtocolStatusCode::Ok {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ProtocolStatus {
|
||||
operation,
|
||||
code,
|
||||
message: status
|
||||
.map(|status| status.message.clone())
|
||||
.unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_credentials(message: &str) -> String {
|
||||
message
|
||||
.split_whitespace()
|
||||
|
||||
@@ -13,6 +13,7 @@ pub struct ClientOptions {
|
||||
server_name_override: Option<String>,
|
||||
connect_timeout: Duration,
|
||||
call_timeout: Duration,
|
||||
stream_timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl ClientOptions {
|
||||
@@ -25,6 +26,7 @@ impl ClientOptions {
|
||||
server_name_override: None,
|
||||
connect_timeout: Duration::from_secs(10),
|
||||
call_timeout: Duration::from_secs(30),
|
||||
stream_timeout: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +60,11 @@ impl ClientOptions {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_stream_timeout(mut self, stream_timeout: Duration) -> Self {
|
||||
self.stream_timeout = Some(stream_timeout);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn endpoint(&self) -> &str {
|
||||
&self.endpoint
|
||||
}
|
||||
@@ -85,6 +92,10 @@ impl ClientOptions {
|
||||
pub fn call_timeout(&self) -> Duration {
|
||||
self.call_timeout
|
||||
}
|
||||
|
||||
pub fn stream_timeout(&self) -> Option<Duration> {
|
||||
self.stream_timeout
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ClientOptions {
|
||||
@@ -104,6 +115,7 @@ impl fmt::Debug for ClientOptions {
|
||||
.field("server_name_override", &self.server_name_override)
|
||||
.field("connect_timeout", &self.connect_timeout)
|
||||
.field("call_timeout", &self.call_timeout)
|
||||
.field("stream_timeout", &self.stream_timeout)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::client::{EventStream, GatewayClient};
|
||||
use crate::error::Error;
|
||||
use crate::error::{ensure_protocol_success, Error};
|
||||
use crate::generated::mxaccess_gateway::v1::mx_command::Payload;
|
||||
use crate::generated::mxaccess_gateway::v1::mx_command_reply;
|
||||
use crate::generated::mxaccess_gateway::v1::{
|
||||
@@ -11,6 +11,8 @@ use crate::generated::mxaccess_gateway::v1::{
|
||||
};
|
||||
use crate::value::MxValue;
|
||||
|
||||
const MAX_BULK_ITEMS: usize = 1_000;
|
||||
|
||||
/// Session identifier returned by the gateway.
|
||||
#[derive(Clone)]
|
||||
pub struct Session {
|
||||
@@ -40,12 +42,14 @@ impl Session {
|
||||
}
|
||||
|
||||
pub async fn close(&self) -> Result<(), Error> {
|
||||
self.client
|
||||
let reply = self
|
||||
.client
|
||||
.close_session_raw(CloseSessionRequest {
|
||||
session_id: self.id.clone(),
|
||||
client_correlation_id: "rust-client-close-session".to_owned(),
|
||||
})
|
||||
.await?;
|
||||
ensure_protocol_success("close session", reply.protocol_status.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -137,6 +141,7 @@ impl Session {
|
||||
server_handle: i32,
|
||||
tag_addresses: Vec<String>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
ensure_bulk_size("tag_addresses", tag_addresses.len())?;
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::AddItemBulk,
|
||||
@@ -155,6 +160,7 @@ impl Session {
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
ensure_bulk_size("item_handles", item_handles.len())?;
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::AdviseItemBulk,
|
||||
@@ -173,6 +179,7 @@ impl Session {
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
ensure_bulk_size("item_handles", item_handles.len())?;
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::RemoveItemBulk,
|
||||
@@ -191,6 +198,7 @@ impl Session {
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
ensure_bulk_size("item_handles", item_handles.len())?;
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::UnAdviseItemBulk,
|
||||
@@ -209,6 +217,7 @@ impl Session {
|
||||
server_handle: i32,
|
||||
tag_addresses: Vec<String>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
ensure_bulk_size("tag_addresses", tag_addresses.len())?;
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::SubscribeBulk,
|
||||
@@ -227,6 +236,7 @@ impl Session {
|
||||
server_handle: i32,
|
||||
item_handles: Vec<i32>,
|
||||
) -> Result<Vec<SubscribeResult>, Error> {
|
||||
ensure_bulk_size("item_handles", item_handles.len())?;
|
||||
let reply = self
|
||||
.invoke(
|
||||
MxCommandKind::UnsubscribeBulk,
|
||||
@@ -327,6 +337,17 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_bulk_size(name: &'static str, len: usize) -> Result<(), Error> {
|
||||
if len > MAX_BULK_ITEMS {
|
||||
Err(Error::InvalidArgument {
|
||||
name: name.to_owned(),
|
||||
detail: format!("bulk commands are limited to {MAX_BULK_ITEMS} item(s)"),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn register_server_handle(reply: &MxCommandReply) -> i32 {
|
||||
match reply.payload.as_ref() {
|
||||
Some(mx_command_reply::Payload::Register(register)) => register.server_handle,
|
||||
|
||||
Reference in New Issue
Block a user