use crate::dapr::proto::runtime::v1::app_callback_alpha_server::AppCallbackAlpha;
use crate::dapr::proto::runtime::v1::app_callback_server::AppCallback;
use crate::dapr::proto::{common, runtime};
use std::collections::HashMap;
use tonic::{Code, Request, Response, Status};
pub type InvokeRequest = common::v1::InvokeRequest;
pub type InvokeResponse = common::v1::InvokeResponse;
pub type ListTopicSubscriptionsResponse = runtime::v1::ListTopicSubscriptionsResponse;
pub type TopicSubscription = runtime::v1::TopicSubscription;
pub type TopicEventRequest = runtime::v1::TopicEventRequest;
pub type TopicEventResponse = runtime::v1::TopicEventResponse;
pub type ListInputBindingsResponse = runtime::v1::ListInputBindingsResponse;
pub type BindingEventRequest = runtime::v1::BindingEventRequest;
pub type BindingEventResponse = runtime::v1::BindingEventResponse;
pub type TopicEventBulkRequest = runtime::v1::TopicEventBulkRequest;
pub type TopicEventBulkResponse = runtime::v1::TopicEventBulkResponse;
pub type JobEventRequest = runtime::v1::JobEventRequest;
pub type JobEventResponse = runtime::v1::JobEventResponse;
impl ListTopicSubscriptionsResponse {
pub fn topic(pubsub_name: String, topic: String) -> Self {
let topic_subscription = TopicSubscription::new(pubsub_name, topic, None);
Self {
subscriptions: vec![topic_subscription],
}
}
}
impl TopicSubscription {
pub fn new(
pubsub_name: String,
topic: String,
metadata: Option<HashMap<String, String>>,
) -> Self {
let mut topic_subscription = TopicSubscription {
pubsub_name,
topic,
..Default::default()
};
if let Some(metadata) = metadata {
topic_subscription.metadata = metadata;
}
topic_subscription
}
}
impl ListInputBindingsResponse {
pub fn binding(binding_name: String) -> Self {
Self {
bindings: vec![binding_name],
}
}
}
pub struct AppCallbackService {
handlers: Vec<Handler>,
job_handlers: HashMap<String, Box<dyn JobHandlerMethod + Send + Sync + 'static>>,
}
pub struct Handler {
pub pub_sub_name: String,
pub topic: String,
pub handler: Box<dyn HandlerMethod>,
}
#[tonic::async_trait]
impl AppCallback for AppCallbackService {
async fn on_invoke(
&self,
_request: Request<common::v1::InvokeRequest>,
) -> Result<Response<common::v1::InvokeResponse>, Status> {
todo!("on_invoke is not implemented yet")
}
async fn list_topic_subscriptions(
&self,
_request: Request<()>,
) -> Result<Response<runtime::v1::ListTopicSubscriptionsResponse>, Status> {
let topics = self
.handlers
.iter()
.fold(Vec::new(), |mut topics, handler| {
topics.push(TopicSubscription::new(
handler.pub_sub_name.clone(),
handler.topic.clone(),
None,
));
topics
});
Ok(Response::new(ListTopicSubscriptionsResponse {
subscriptions: topics,
}))
}
async fn on_topic_event(
&self,
request: Request<runtime::v1::TopicEventRequest>,
) -> Result<Response<runtime::v1::TopicEventResponse>, Status> {
let request_inner = request.into_inner();
let pub_sub_name = request_inner.pubsub_name.clone();
let topic_name = request_inner.topic.clone();
let handler = self
.handlers
.iter()
.find(|x| x.pub_sub_name == pub_sub_name && x.topic == topic_name);
if let Some(handler) = handler {
return handler.handler.handler(request_inner).await;
}
Err(Status::new(Code::Internal, "Handler Not Found"))
}
async fn list_input_bindings(
&self,
_request: Request<()>,
) -> Result<Response<runtime::v1::ListInputBindingsResponse>, Status> {
todo!("list_input_bindings is not implemented yet")
}
async fn on_binding_event(
&self,
_request: Request<BindingEventRequest>,
) -> Result<Response<BindingEventResponse>, Status> {
todo!("on_binding_event is not implemented yet")
}
async fn on_bulk_topic_event(
&self,
_request: Request<TopicEventBulkRequest>,
) -> Result<Response<TopicEventBulkResponse>, Status> {
todo!("on_bulk_topic_event is not implemented yet")
}
async fn on_job_event(
&self,
request: Request<runtime::v1::JobEventRequest>,
) -> Result<Response<runtime::v1::JobEventResponse>, Status> {
let request_inner = request.into_inner();
let job_name = if !request_inner.name.is_empty() {
request_inner.name.clone()
} else if let Some(stripped) = request_inner.method.strip_prefix("job/") {
stripped.to_string()
} else {
return Err(Status::invalid_argument(format!(
"cannot determine job name from request (method={:?})",
request_inner.method,
)));
};
if let Some(handler) = self.job_handlers.get(&job_name) {
let handle_response = handler.handler(request_inner).await;
handle_response.map(Response::new)
} else {
Err(Status::not_found(format!(
"no handler registered for job {:?}",
job_name,
)))
}
}
}
#[tonic::async_trait]
impl AppCallbackAlpha for AppCallbackService {
async fn on_bulk_topic_event_alpha1(
&self,
request: Request<runtime::v1::TopicEventBulkRequest>,
) -> Result<Response<runtime::v1::TopicEventBulkResponse>, Status> {
self.on_bulk_topic_event(request).await
}
async fn on_job_event_alpha1(
&self,
request: Request<runtime::v1::JobEventRequest>,
) -> Result<Response<runtime::v1::JobEventResponse>, Status> {
self.on_job_event(request).await
}
}
impl Default for AppCallbackService {
fn default() -> Self {
Self::new()
}
}
impl AppCallbackService {
pub fn new() -> AppCallbackService {
AppCallbackService {
handlers: vec![],
job_handlers: HashMap::new(),
}
}
pub fn add_handler(&mut self, handler: Handler) {
self.handlers.push(handler)
}
pub fn add_job_handler(&mut self, job_name: String, handler: Box<dyn JobHandlerMethod>) {
self.job_handlers.insert(job_name, handler);
}
}
#[tonic::async_trait]
pub trait HandlerMethod: Send + Sync + 'static {
async fn handler(
&self,
request: runtime::v1::TopicEventRequest,
) -> Result<Response<runtime::v1::TopicEventResponse>, Status>;
}
#[tonic::async_trait]
pub trait JobHandlerMethod: Send + Sync + 'static {
async fn handler(
&self,
request: runtime::v1::JobEventRequest,
) -> Result<runtime::v1::JobEventResponse, Status>;
}
#[macro_export]
macro_rules! add_job_handler {
($app_callback_service:expr, $handler_name:ident, $handler_fn:expr) => {
pub struct $handler_name {}
#[$crate::reexport::async_trait]
impl $crate::appcallback::JobHandlerMethod for $handler_name {
async fn handler(
&self,
request: $crate::appcallback::JobEventRequest,
) -> ::std::result::Result<$crate::appcallback::JobEventResponse, ::tonic::Status>
{
$handler_fn(request).await
}
}
impl $handler_name {
pub fn new() -> Self {
$handler_name {}
}
}
let handler_name = $handler_name.to_string();
$app_callback_service.add_job_handler(handler_name, Box::new($handler_name::new()));
};
}