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) -> 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 params = SearchParameters {
55 query,
56 query_by,
57 num_typos: Some(2),
58 filter_by: None,
59 sort_by: Some(sort_by),
60 };
61
62 ServiceBridge::search(self, params.into())
63 .await
64 .context("Couldn't call search")
65 }
66 }
67
68 impl ServiceBridge for App {
69 fn client(&self) -> &FloraServiceClient {
70 &self.client
71 }
72 }
73
74 #[instrument]
77 pub(super) async fn init_app(flora_stack: Arc<FloraStack>) -> Result<App> {
78 const MAX_CONN_ATTEMPTS: usize = 5;
79 const BACKOFF_MULTIPLIER: u32 = 2;
80
81 tokio::spawn(async move {
82 let mut attempts_so_far = 0;
83 let mut backoff_interval = Duration::from_secs(1);
84
85 loop {
94 match flora_stack::flora_client(flora_stack.flora_container()).await {
95 Ok(client) => match client.ping(context::current()).await {
96 Ok(_) => break Ok(App::new(client)),
97 Err(error) => {
98 tracing::warn!("error while initializing the tarpc client: {error}");
99 }
100 },
101 Err(error) => {
102 tracing::warn!("error while creating the flora client: {error}");
103 }
104 }
105
106 if attempts_so_far == MAX_CONN_ATTEMPTS {
107 anyhow::bail!("couldn't succeed after {MAX_CONN_ATTEMPTS} attempts");
108 }
109
110 time::sleep(backoff_interval).await;
111 backoff_interval *= BACKOFF_MULTIPLIER;
112 attempts_so_far += 1;
113 }
114 })
115 .await?
116 }
117}
118
119#[derive(Debug)]
121pub struct OmryFullStack {
122 backend: Arc<FloraStack>,
124
125 app: App,
127}
128
129impl OmryFullStack {
130 #[instrument]
139 pub async fn new() -> Result<Self> {
140 let stack_options = FloraStackOptions {
141 search_sync_interval: Duration::from_secs(5),
142 ..Default::default()
143 };
144
145 let backend = Arc::new(FloraStack::new(Some(stack_options)).await?);
146 let app = test_app::init_app(Arc::clone(&backend)).await?;
147
148 tracing::info!("successfully initialized the test bridge");
149 Ok(Self { backend, app })
150 }
151
152 #[must_use]
154 pub fn app(&self) -> &App {
155 &self.app
156 }
157
158 pub async fn kill_flora_rpc(&mut self) -> Result<()> {
164 self.backend.stop().await?;
165 Ok(())
166 }
167}