use crate::flora_stack::{FloraStack, FloraStackOptions};
use anyhow::Result;
use std::sync::Arc;
use std::time::Duration;
pub use test_app::App;
use tracing::instrument;
mod test_app {
use crate::bridge::ServiceBridge;
use crate::flora_stack::{self, FloraStack};
use anyhow::{Context, Result};
use omry_service::FloraServiceClient;
use omry_service::search::{SearchParameters, SearchResponse};
use omry_service::tarpc::context;
use std::sync::Arc;
use tokio::time::{self, Duration};
use tracing::instrument;
#[derive(Debug, Clone)]
pub struct App {
client: FloraServiceClient,
}
impl App {
#[must_use]
pub fn new(client: FloraServiceClient) -> Self {
Self { client }
}
#[instrument]
pub async fn search(&self, query: String) -> Result<SearchResponse> {
const QUERY_BY: &[&str] = &["title", "text-body"];
let query_by = QUERY_BY
.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<_>>();
let params = SearchParameters {
query,
query_by,
num_typos: Some(2),
filter_by: None,
};
ServiceBridge::search(self, params.into())
.await
.context("Couldn't call search")
}
}
impl ServiceBridge for App {
fn client(&self) -> &FloraServiceClient {
&self.client
}
}
#[instrument]
pub(super) async fn init_app(flora_stack: Arc<FloraStack>) -> Result<App> {
const MAX_CONN_ATTEMPTS: usize = 5;
const BACKOFF_MULTIPLIER: u32 = 2;
tokio::spawn(async move {
let mut attempts_so_far = 0;
let mut backoff_interval = Duration::from_secs(1);
loop {
match flora_stack::flora_client(flora_stack.flora_container()).await {
Ok(client) => match client.ping(context::current()).await {
Ok(_) => break Ok(App::new(client)),
Err(error) => {
tracing::warn!("error while initializing the tarpc client: {error}");
}
},
Err(error) => {
tracing::warn!("error while creating the flora client: {error}");
}
}
if attempts_so_far == MAX_CONN_ATTEMPTS {
anyhow::bail!("couldn't succeed after {MAX_CONN_ATTEMPTS} attempts");
}
time::sleep(backoff_interval).await;
backoff_interval *= BACKOFF_MULTIPLIER;
attempts_so_far += 1;
}
})
.await?
}
}
#[derive(Debug)]
pub struct OmryFullStack {
backend: Arc<FloraStack>,
app: App,
}
impl OmryFullStack {
#[instrument]
pub async fn new() -> Result<Self> {
let stack_options = FloraStackOptions {
search_sync_interval: Duration::from_secs(5),
..Default::default()
};
let backend = Arc::new(FloraStack::new(Some(stack_options)).await?);
let app = test_app::init_app(Arc::clone(&backend)).await?;
tracing::info!("successfully initialized the test bridge");
Ok(Self { backend, app })
}
#[must_use]
pub fn app(&self) -> &App {
&self.app
}
pub async fn kill_flora_rpc(&mut self) -> Result<()> {
self.backend.stop().await?;
Ok(())
}
}