omry_integration/
bridge.rs1use anyhow::{Context, Result};
3use omry_search::wrappers::SearchParametersWrapper;
4use omry_service::archiving::{RawRecord, 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
103 while let Some(Some(record)) = rx.recv().await? {
106 records.push(record);
107 }
108
109 #[allow(clippy::expect_used)]
111 records.sort_by_key(|rec| {
112 let this_id = rec.id().expect("returned record should have an ID");
113 record_ids
114 .iter()
115 .position(|id| id == &this_id)
116 .expect("returned ID should be present among original IDs")
117 });
118
119 Ok(records)
120 }
121
122 #[instrument(skip(self))]
124 async fn get_all_records(&self) -> Result<Vec<Record>> {
125 let mut start_row = None;
126
127 let mut recs = vec![];
128 'paginate: loop {
129 let mut rx = self
130 .api()
131 .all_records(start_row)
132 .await
133 .context("Failed to invoke 'all_records' RPC")?;
134
135 while let Some(maybe_rec) = rx.recv().await? {
136 let Some(rec) = maybe_rec else {
137 break 'paginate;
138 };
139
140 recs.push(rec);
141 }
142
143 start_row = Some(recs.len());
144 }
145
146 Ok(recs)
147 }
148
149 #[instrument(skip(self))]
151 async fn get_all_records_meta(&self) -> Result<Vec<RecordMeta>> {
152 let mut rec_metas = vec![];
153 let mut start_row = None;
154
155 'paginate: loop {
156 let mut rx = self
157 .api()
158 .all_meta(start_row)
159 .await
160 .context("Failed to invoke 'all_meta' RPC")?;
161
162 while let Some(maybe_rec) = rx.recv().await? {
163 let Some(rec) = maybe_rec else {
164 break 'paginate;
165 };
166
167 rec_metas.push(rec);
168 }
169
170 start_row = Some(rec_metas.len());
171 }
172
173 Ok(rec_metas)
174 }
175
176 #[instrument(skip(self))]
178 async fn get_all_raw(&self) -> Result<Vec<RawRecord>> {
179 let mut raw_recs = vec![];
180 let mut start_row = None;
181
182 'paginate: loop {
183 let mut rx = self
184 .api()
185 .all_raw(start_row)
186 .await
187 .context("error invoking 'all_raw'")?;
188
189 while let Some(maybe_rec) = rx.recv().await? {
190 let Some(rec) = maybe_rec else {
191 break 'paginate;
192 };
193
194 raw_recs.push(rec);
195 }
196
197 start_row = Some(raw_recs.len());
198 }
199
200 Ok(raw_recs)
201 }
202
203 #[instrument(skip(self))]
205 async fn most_recent_meta(&self) -> Result<Vec<RecordMeta>> {
206 let mut rec_metas = vec![];
207 let mut start_idx = None;
208
209 'paginate: loop {
210 let mut rx = self
211 .api()
212 .most_recent_meta(start_idx)
213 .await
214 .context("Error invoking 'most_recent_meta'")?;
215
216 while let Some(maybe_rec) = rx.recv().await? {
217 let Some(rec) = maybe_rec else {
218 break 'paginate;
219 };
220
221 rec_metas.push(rec);
222 }
223
224 start_idx = Some(rec_metas.len());
225 }
226
227 Ok(rec_metas)
228 }
229
230 #[instrument(skip(self))]
232 async fn search(&self, search_parameters: SearchParametersWrapper) -> Result<SearchResponse> {
233 let mut rx = self
234 .api()
235 .search(search_parameters)
236 .await
237 .context("failed while calling search")?;
238
239 let mut metahits = vec![];
240 while let Some(item) = rx.recv().await? {
241 metahits.push(item);
242 }
243
244 metahits.sort_by_key(|hit| hit.index);
245 let last_mh = metahits.pop().context("No search response")?;
246 let MetaHit {
247 meta,
248 index: _,
249 hit: last_hit,
250 } = last_mh;
251 let SearchRespMeta {
252 found,
253 out_of,
254 page,
255 request_params,
256 search_time,
257 } = meta;
258
259 let mut hits = Vec::with_capacity(found);
260 for mh in metahits {
261 let Some(hit) = mh.hit else {
262 break;
265 };
266 hits.push(hit);
267 }
268
269 if let Some(hit) = last_hit {
270 hits.push(hit);
271 }
272
273 let resp = SearchResponse {
274 found,
275 out_of,
276 page,
277 request_params,
278 search_time,
279 hits,
280 };
281 Ok(resp)
282 }
283
284 #[instrument(skip(self))]
287 async fn delete(&self, ids: Vec<RecordId>) -> Result<usize> {
288 tracing::debug!(
289 "Will make {} RPCs to delete the given IDs (one per each)",
290 ids.len()
291 );
292
293 let (tx, mut rx) = self.api().delete_record().await?;
294 let mut set = JoinSet::new();
295
296 for id in ids {
297 let tx = tx.clone();
298 set.spawn(async move { tx.send(id).await });
299 }
300
301 drop(tx);
302
303 set.join_all().await;
304
305 let mut n_deleted = 0usize;
306 let mut n_errors = 0usize;
307
308 loop {
309 match rx.recv().await {
310 Ok(Some(num_affected)) => n_deleted += num_affected,
311 Ok(None) => break,
312 Err(e) => {
313 tracing::error!("{e}");
314 n_errors += 1;
315 }
316 }
317 }
318
319 if n_errors != 0 {
320 anyhow::bail!("{n_errors} records couldn't be scheduled for deletion due to errors.");
321 }
322
323 Ok(n_deleted)
324 }
325
326 #[instrument(skip_all)]
329 async fn update(&self, records: Vec<RecordParams>) -> Result<Vec<RecordId>> {
330 let (tx, mut rx) = self
331 .api()
332 .update_record()
333 .await
334 .context("error invoking 'update_record' RPC")?;
335
336 let mut set = JoinSet::new();
337 for rec in records {
338 let tx = tx.clone();
339 set.spawn(async move { tx.send(rec).await });
340 }
341
342 drop(tx);
343 set.join_all().await;
344
345 let mut ids = vec![];
346 while let Some(id) = rx.recv().await? {
347 ids.push(id);
348 }
349
350 Ok(ids)
351 }
352
353 #[instrument(skip(self))]
355 async fn version(&self) -> Result<String> {
356 let rx = self
357 .api()
358 .version()
359 .await
360 .context("Failed to invoke 'version' RPC")?;
361
362 Ok(rx.await?)
363 }
364}