omry_integration/
full_stack.rs1use crate::flora_stack::{FloraStack, FloraStackOptions};
4use anyhow::Result;
5use std::sync::Arc;
6use std::time::Duration;
7pub use test_app::App;
8use tracing::instrument;
9
10mod test_app {
12 use crate::bridge::ServiceBridge;
13 use crate::flora_stack::{self, FloraStack};
14 use anyhow::{Context, Result};
15 use omry_service::FloraServiceClient;
16 use omry_service::search::{SearchParameters, SearchResponse, SortBy};
17 use omry_service::tarpc::context;
18 use std::sync::Arc;
19 use tokio::time::{self, Duration};
20 use tracing::instrument;
21
22 #[derive(Debug, Clone)]
24 pub struct App {
25 client: FloraServiceClient,
27 }
28
29 impl App {
30 #[must_use]
32 pub fn new(client: FloraServiceClient) -> Self {
33 Self { client }
34 }
35
36 #[instrument]
44 pub async fn search(&self, query: String, page: Option<usize>) -> Result<SearchResponse> {
45 const BUCKETS: u32 = 5;
46 const QUERY_BY: &[&str] = &["title", "text-body"];
47
48 let query_by = QUERY_BY
49 .iter()
50 .map(std::string::ToString::to_string)
51 .collect::<Vec<_>>();
52
53 let sort_by = SortBy::relevance_and_recency(BUCKETS);
54 let page = page.unwrap_or(1);
55 let params = SearchParameters::new_query(query, query_by)
56 .num_typos(2)
57 .sort_by(sort_by)
58 .page(page);
59
60 ServiceBridge::search(self, params.into())
61 .await
62 .context("Couldn't call search")
63 }
64 }
65
66 impl ServiceBridge for App {
67 fn client(&self) -> &FloraServiceClient {
68 &self.client
69 }
70 }
71
72 #[instrument]
75 pub(super) async fn init_app(flora_stack: Arc<FloraStack>) -> Result<App> {
76 const MAX_CONN_ATTEMPTS: usize = 5;
77 const BACKOFF_MULTIPLIER: u32 = 2;
78
79 tokio::spawn(async move {
80 let mut attempts_so_far = 0;
81 let mut backoff_interval = Duration::from_secs(1);
82
83 loop {
92 match flora_stack::flora_client(flora_stack.flora_container()).await {
93 Ok(client) => match client.ping(context::current()).await {
94 Ok(_) => break Ok(App::new(client)),
95 Err(error) => {
96 tracing::warn!("error while initializing the tarpc client: {error}");
97 }
98 },
99 Err(error) => {
100 tracing::warn!("error while creating the flora client: {error}");
101 }
102 }
103
104 if attempts_so_far == MAX_CONN_ATTEMPTS {
105 anyhow::bail!("couldn't succeed after {MAX_CONN_ATTEMPTS} attempts");
106 }
107
108 time::sleep(backoff_interval).await;
109 backoff_interval *= BACKOFF_MULTIPLIER;
110 attempts_so_far += 1;
111 }
112 })
113 .await?
114 }
115}
116
117#[derive(Debug)]
119pub struct OmryFullStack {
120 backend: Arc<FloraStack>,
122
123 app: App,
125}
126
127impl OmryFullStack {
128 #[instrument]
137 pub async fn new() -> Result<Self> {
138 let stack_options = FloraStackOptions {
139 search_sync_interval: Duration::from_secs(5),
140 ..Default::default()
141 };
142
143 let backend = Arc::new(FloraStack::new(Some(stack_options)).await?);
144 let app = test_app::init_app(Arc::clone(&backend)).await?;
145
146 tracing::info!("successfully initialized the test bridge");
147 Ok(Self { backend, app })
148 }
149
150 #[must_use]
152 pub fn app(&self) -> &App {
153 &self.app
154 }
155
156 pub async fn kill_flora_rpc(&mut self) -> Result<()> {
162 self.backend.stop().await?;
163 Ok(())
164 }
165}