omry_integration/
bridge.rs1use anyhow::{Context, Result};
3use omry_search::wrappers::SearchParametersWrapper;
4use omry_service::archiving::{Record, RecordMeta, RecordParams};
5use omry_service::common::RecordId;
6use omry_service::search::SearchResponse;
7use omry_service::service::{MetaHit, SearchRespMeta};
8use omry_service::{OmryApi, PONG};
9use tokio::task::JoinSet;
10use tracing::instrument;
11
12#[allow(async_fn_in_trait)]
15pub trait ApiBridge {
16 fn api(&self) -> &OmryApi;
18
19 #[instrument(skip(self))]
23 async fn ping(&self) -> Result<String> {
24 let rx = self
25 .api()
26 .ping()
27 .await
28 .context("error sending ping request")?;
29 let resp = rx.await?;
30 if resp == PONG {
31 Ok(resp)
32 } else {
33 anyhow::bail!("Unexpected ping response: {resp}")
34 }
35 }
36
37 #[instrument(skip(self, record))]
39 async fn add_record(&self, record: RecordParams) -> Result<RecordId> {
40 let (tx, mut rx) = self.api().add().await.context("error calling add")?;
41 tx.send(record).await.context("Failed to send record")?;
42 drop(tx);
43
44 let id = rx
45 .recv()
46 .await?
47 .ok_or_else(|| anyhow::anyhow!("got empty response"))?;
48 Ok(id)
49 }
50
51 #[instrument(skip(self, records))]
53 async fn add_many_records(&self, records: Vec<RecordParams>) -> Result<Vec<RecordId>> {
54 let (tx, mut rx) = self.api().add().await.context("error calling add")?;
55 let n_recs = records.len();
56
57 let mut set = JoinSet::new();
58 for rec in records {
59 let tx = tx.clone();
60 set.spawn(async move { tx.send(rec).await.context("error sending record") });
61 }
62
63 set.join_all().await;
64 drop(tx);
65
66 let mut ids = Vec::with_capacity(n_recs);
67 while let Some(id) = rx.recv().await.context("error receiving response")? {
68 ids.push(id);
69 }
70
71 ids.sort_unstable();
72 Ok(ids)
73 }
74
75 #[instrument(skip(self))]
77 async fn count(&self) -> Result<i64> {
78 let rx = self.api().count().await.context("failed calling count")?;
79 Ok(rx.await?)
80 }
81
82 #[instrument(skip(self))]
84 async fn get_records(&self, record_ids: Vec<RecordId>) -> Result<Vec<Record>> {
85 let (tx, mut rx) = self
86 .api()
87 .record()
88 .await
89 .context("failed calling get record")?;
90 let mut set = JoinSet::new();
91
92 for id in &record_ids {
93 let tx = tx.clone();
94 let id = *id;
95 set.spawn(async move { tx.send(id).await });
96 }
97
98 drop(tx);
99
100 set.join_all().await;
101 let mut records = Vec::with_capacity(record_ids.len());
102 while let Some(record) = rx.recv().await? {
103 records.push(record);
104 }
105
106 #[allow(clippy::expect_used)]
108 records.sort_by_key(|rec| {
109 let this_id = rec.id().expect("returned record should have an ID");
110 record_ids
111 .iter()
112 .position(|id| id == &this_id)
113 .expect("returned ID should be present among original IDs")
114 });
115
116 Ok(records)
117 }
118
119 #[instrument(skip(self))]
121 async fn get_all_records(&self) -> Result<Vec<Record>> {
122 let mut rx = self
123 .api()
124 .all_records()
125 .await
126 .context("Failed to invoke 'all_records' RPC")?;
127
128 let mut recs = vec![];
129 while let Some(rec) = rx.recv().await? {
130 recs.push(rec);
131 }
132
133 Ok(recs)
134 }
135
136 #[instrument(skip(self))]
138 async fn get_all_records_meta(&self) -> Result<Vec<RecordMeta>> {
139 let mut rx = self
140 .api()
141 .all_meta()
142 .await
143 .context("Failed to invoke 'all_meta' RPC")?;
144
145 let mut rec_metas = vec![];
146 while let Some(rec) = rx.recv().await? {
147 rec_metas.push(rec);
148 }
149
150 Ok(rec_metas)
151 }
152
153 #[instrument(skip(self))]
155 async fn most_recent_meta(&self) -> Result<Vec<RecordMeta>> {
156 let mut rx = self
157 .api()
158 .most_recent_meta()
159 .await
160 .context("Error invoking 'most_recent_meta'")?;
161
162 let mut rec_metas = vec![];
163 while let Some(rec) = rx.recv().await? {
164 rec_metas.push(rec);
165 }
166
167 Ok(rec_metas)
168 }
169
170 #[instrument(skip(self))]
172 async fn search(&self, search_parameters: SearchParametersWrapper) -> Result<SearchResponse> {
173 let mut rx = self
174 .api()
175 .search(search_parameters)
176 .await
177 .context("failed while calling search")?;
178
179 let mut metahits = vec![];
180 while let Some(item) = rx.recv().await? {
181 metahits.push(item);
182 }
183
184 metahits.sort_by_key(|hit| hit.index);
185 let last_mh = metahits.pop().context("No search response")?;
186 let MetaHit {
187 meta,
188 index: _,
189 hit: last_hit,
190 } = last_mh;
191 let SearchRespMeta {
192 found,
193 out_of,
194 page,
195 request_params,
196 search_time,
197 } = meta;
198
199 let mut hits = Vec::with_capacity(found);
200 for mh in metahits {
201 let Some(hit) = mh.hit else {
202 break;
205 };
206 hits.push(hit);
207 }
208
209 if let Some(hit) = last_hit {
210 hits.push(hit);
211 }
212
213 let resp = SearchResponse {
214 found,
215 out_of,
216 page,
217 request_params,
218 search_time,
219 hits,
220 };
221 Ok(resp)
222 }
223
224 #[instrument(skip(self))]
227 async fn delete(&self, ids: Vec<RecordId>) -> Result<usize> {
228 tracing::debug!(
229 "Will make {} RPCs to delete the given IDs (one per each)",
230 ids.len()
231 );
232
233 let (tx, mut rx) = self.api().delete_record().await?;
234 let mut set = JoinSet::new();
235
236 for id in ids {
237 let tx = tx.clone();
238 set.spawn(async move { tx.send(id).await });
239 }
240
241 drop(tx);
242
243 set.join_all().await;
244
245 let mut n_deleted = 0usize;
246 let mut n_errors = 0usize;
247
248 loop {
249 match rx.recv().await {
250 Ok(Some(num_affected)) => n_deleted += num_affected,
251 Ok(None) => break,
252 Err(e) => {
253 tracing::error!("{e}");
254 n_errors += 1;
255 }
256 }
257 }
258
259 if n_errors != 0 {
260 anyhow::bail!("{n_errors} records couldn't be scheduled for deletion due to errors.");
261 }
262
263 Ok(n_deleted)
264 }
265
266 #[instrument(skip(self))]
268 async fn version(&self) -> Result<String> {
269 let rx = self
270 .api()
271 .version()
272 .await
273 .context("Failed to invoke 'version' RPC")?;
274
275 Ok(rx.await?)
276 }
277}