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, SortByField, SortOrder};
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 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::new()
54 .add_field(SortByField::text_match(Some(5), SortOrder::Desc))?
55 .add_field(SortByField::regular(
56 "timestamp".to_string(),
57 SortOrder::Desc,
58 ))?;
59
60 let params = SearchParameters {
61 query,
62 query_by,
63 num_typos: Some(2),
64 filter_by: None,
65 sort_by: Some(sort_by),
66 };
67
68 ServiceBridge::search(self, params.into())
69 .await
70 .context("Couldn't call search")
71 }
72 }
73
74 impl ServiceBridge for App {
75 fn client(&self) -> &FloraServiceClient {
76 &self.client
77 }
78 }
79
80 #[instrument]
83 pub(super) async fn init_app(flora_stack: Arc<FloraStack>) -> Result<App> {
84 const MAX_CONN_ATTEMPTS: usize = 5;
85 const BACKOFF_MULTIPLIER: u32 = 2;
86
87 tokio::spawn(async move {
88 let mut attempts_so_far = 0;
89 let mut backoff_interval = Duration::from_secs(1);
90
91 loop {
100 match flora_stack::flora_client(flora_stack.flora_container()).await {
101 Ok(client) => match client.ping(context::current()).await {
102 Ok(_) => break Ok(App::new(client)),
103 Err(error) => {
104 tracing::warn!("error while initializing the tarpc client: {error}");
105 }
106 },
107 Err(error) => {
108 tracing::warn!("error while creating the flora client: {error}");
109 }
110 }
111
112 if attempts_so_far == MAX_CONN_ATTEMPTS {
113 anyhow::bail!("couldn't succeed after {MAX_CONN_ATTEMPTS} attempts");
114 }
115
116 time::sleep(backoff_interval).await;
117 backoff_interval *= BACKOFF_MULTIPLIER;
118 attempts_so_far += 1;
119 }
120 })
121 .await?
122 }
123}
124
125#[derive(Debug)]
127pub struct OmryFullStack {
128 backend: Arc<FloraStack>,
130
131 app: App,
133}
134
135impl OmryFullStack {
136 #[instrument]
145 pub async fn new() -> Result<Self> {
146 let stack_options = FloraStackOptions {
147 search_sync_interval: Duration::from_secs(5),
148 ..Default::default()
149 };
150
151 let backend = Arc::new(FloraStack::new(Some(stack_options)).await?);
152 let app = test_app::init_app(Arc::clone(&backend)).await?;
153
154 tracing::info!("successfully initialized the test bridge");
155 Ok(Self { backend, app })
156 }
157
158 #[must_use]
160 pub fn app(&self) -> &App {
161 &self.app
162 }
163
164 pub async fn kill_flora_rpc(&mut self) -> Result<()> {
170 self.backend.stop().await?;
171 Ok(())
172 }
173}