#![allow(dead_code)]
use std::sync::Arc;
use sqlx::PgPool;
use tonic::{Request, Response, Status};
use crate::metrics::{MetricsRecorder, NoopMetrics};
use crate::proto::udb::core::webhook::services::v1 as webhook_pb;
use crate::proto::udb::core::webhook::services::v1::webhook_service_server::WebhookService;
use crate::runtime::channels::ChannelManager;
pub use crate::proto::udb::core::webhook::services::v1::webhook_service_server::WebhookServiceServer;
use super::DataBrokerService;
mod config;
mod errors;
mod events;
mod handlers;
mod model;
mod security;
#[cfg(test)]
mod tests;
mod worker;
pub(crate) use config::{WEBHOOK_DELIVERY_BATCH, webhook_delivery_interval};
pub(crate) use security::resolve_and_validate_target;
#[cfg(test)]
pub(crate) use security::validate_webhook_target_url;
#[cfg(feature = "http-client")]
pub(crate) use worker::run_webhook_delivery_worker_once;
pub struct WebhookServiceImpl {
pub(crate) pg_pool: Option<PgPool>,
pub(crate) outbox_relation: Option<String>,
pub(crate) channels: Option<ChannelManager>,
pub(crate) metrics: Arc<dyn MetricsRecorder>,
}
impl WebhookServiceImpl {
pub fn new() -> Self {
Self {
pg_pool: None,
outbox_relation: None,
channels: None,
metrics: Arc::new(NoopMetrics),
}
}
pub fn with_postgres(mut self, pool: Option<PgPool>) -> Self {
self.pg_pool = pool;
self
}
pub(crate) fn with_outbox(mut self, relation: Option<String>) -> Self {
self.outbox_relation = relation;
self
}
pub(crate) fn with_channels(mut self, channels: Option<ChannelManager>) -> Self {
self.channels = channels;
self
}
pub(crate) fn with_metrics(mut self, metrics: Arc<dyn MetricsRecorder>) -> Self {
self.metrics = metrics;
self
}
pub(crate) fn require_pool(&self) -> Result<&PgPool, Status> {
self.pg_pool.as_ref().ok_or_else(|| {
errors::webhook_capability_status(
"postgres_store",
"postgres_store",
"webhook service requires a Postgres-backed store (no PG pool configured)",
)
})
}
}
impl Default for WebhookServiceImpl {
fn default() -> Self {
Self::new()
}
}
#[tonic::async_trait]
impl WebhookService for WebhookServiceImpl {
async fn create_endpoint(
&self,
request: Request<webhook_pb::CreateEndpointRequest>,
) -> Result<Response<webhook_pb::CreateEndpointResponse>, Status> {
handlers::create_endpoint(self, request).await
}
async fn get_endpoint(
&self,
request: Request<webhook_pb::GetEndpointRequest>,
) -> Result<Response<webhook_pb::GetEndpointResponse>, Status> {
handlers::get_endpoint(self, request).await
}
async fn list_endpoints(
&self,
request: Request<webhook_pb::ListEndpointsRequest>,
) -> Result<Response<webhook_pb::ListEndpointsResponse>, Status> {
handlers::list_endpoints(self, request).await
}
async fn update_endpoint(
&self,
request: Request<webhook_pb::UpdateEndpointRequest>,
) -> Result<Response<webhook_pb::UpdateEndpointResponse>, Status> {
handlers::update_endpoint(self, request).await
}
async fn delete_endpoint(
&self,
request: Request<webhook_pb::DeleteEndpointRequest>,
) -> Result<Response<webhook_pb::DeleteEndpointResponse>, Status> {
handlers::delete_endpoint(self, request).await
}
async fn list_deliveries(
&self,
request: Request<webhook_pb::ListDeliveriesRequest>,
) -> Result<Response<webhook_pb::ListDeliveriesResponse>, Status> {
handlers::list_deliveries(self, request).await
}
}
impl DataBrokerService {
pub(crate) fn build_webhook_service(&self) -> WebhookServiceImpl {
let runtime = self.runtime.load_full();
let pg_pool = runtime
.native_store_pool_for_service("webhook", true, "")
.ok();
let outbox = runtime.config().cdc.outbox_relation();
let channels = Some(runtime.channels().clone());
WebhookServiceImpl::new()
.with_postgres(pg_pool)
.with_outbox(Some(outbox))
.with_channels(channels)
.with_metrics(self.metrics.clone())
}
}