1use crate::{ApiClientWrapper, ApiError, PublishUnit, Result, dyn_err};
2use std::collections::HashMap;
3use xmtp_api_backend::envelope::*;
4use xmtp_proto::{
5 api_client::{XmtpBackendClient, XmtpMlsStreams},
6 backend_v1 as wire,
7 types::{
8 Cursor, GroupId, GroupMessage, GroupMessageMetadata, IncomingBatchLimits, InstallationId,
9 Topic, TopicCursor, WelcomeMessage,
10 },
11};
12
13#[derive(Clone, Debug)]
14pub struct GroupFilter {
15 pub group_id: GroupId,
16 pub id_cursor: Option<u64>,
17}
18impl GroupFilter {
19 pub fn new(group_id: GroupId, id_cursor: Option<u64>) -> Self {
20 Self {
21 group_id,
22 id_cursor,
23 }
24 }
25}
26
27pub type KeyPackageMap = HashMap<InstallationId, Option<wire::KeyPackage>>;
28type MessageMetadataMap = HashMap<GroupId, GroupMessageMetadata>;
29
30impl<C: XmtpBackendClient> ApiClientWrapper<C> {
31 #[xmtp_common::rpc_span]
32 pub async fn query_group_messages(&self, group_id: GroupId) -> Result<Vec<GroupMessage>> {
33 self.query_group_messages_with_cursors(HashMap::from([(
34 Topic::new_group_message(group_id),
35 Cursor(0),
36 )]))
37 .await
38 }
39 pub async fn query_group_messages_with_cursors(
40 &self,
41 cursors: TopicCursor,
42 ) -> Result<Vec<GroupMessage>> {
43 self.query_all(cursors, self.limits().max_query_limit as u32)
44 .await?
45 .into_iter()
46 .map(|envelope| decode_group_message(envelope).map_err(Into::into))
47 .collect()
48 }
49 #[xmtp_common::rpc_span]
50 pub async fn query_latest_group_message(
51 &self,
52 group_id: GroupId,
53 ) -> Result<Option<GroupMessage>> {
54 let result = self
55 .newest(vec![Topic::new_group_message(group_id)], true)
56 .await?
57 .into_iter()
58 .next();
59 result
60 .map(|result| {
61 decode_group_message(wire::ServerEnvelope {
62 meta: result.meta,
63 envelope: result.envelope,
64 })
65 .map_err(Into::into)
66 })
67 .transpose()
68 }
69 #[xmtp_common::rpc_span]
70 pub async fn query_welcome_messages<Id: AsRef<[u8]> + Copy>(
71 &self,
72 installation_id: Id,
73 ) -> Result<Vec<WelcomeMessage>> {
74 self.query_welcome_messages_with_cursors(HashMap::from([(
75 Topic::new_welcome_message(installation_id.as_ref().try_into()?),
76 Cursor(0),
77 )]))
78 .await
79 }
80 #[xmtp_common::rpc_span]
85 pub async fn query_first_welcome(
86 &self,
87 destination: InstallationId,
88 max_bytes: usize,
89 ) -> Result<Option<wire::ServerEnvelope>> {
90 let page = self
91 .query_ordered_page(
92 HashMap::from([(Topic::new_welcome_message(destination), Cursor(0))]),
93 1,
94 IncomingBatchLimits {
95 max_rows: 1,
96 max_bytes,
97 },
98 )
99 .await?;
100 Ok(page
101 .batches
102 .into_iter()
103 .flat_map(|batch| batch.envelopes)
104 .next()
105 .map(|envelope| envelope.decode())
106 .transpose()?)
107 }
108 pub async fn query_welcome_messages_with_cursors(
109 &self,
110 cursors: TopicCursor,
111 ) -> Result<Vec<WelcomeMessage>> {
112 self.query_all(cursors, self.limits().max_query_limit as u32)
113 .await?
114 .into_iter()
115 .map(|envelope| decode_welcome_message(envelope).map_err(Into::into))
116 .collect()
117 }
118 #[xmtp_common::rpc_span]
119 pub async fn upload_key_package(&self, key_package: Vec<u8>) -> Result<wire::EnvelopeMeta> {
120 self.publish_units(vec![PublishUnit::single_within(
121 wire::ClientEnvelope {
122 payload: Some(wire::client_envelope::Payload::KeyPackage(
123 wire::KeyPackage {
124 key_package_tls_serialized: key_package,
125 },
126 )),
127 },
128 self.limits(),
129 )?])
130 .await?
131 .into_iter()
132 .next()
133 .ok_or(ApiError::InvalidResponse("key package metadata"))
134 }
135 #[xmtp_common::rpc_span]
137 pub async fn fetch_key_packages(&self, keys: &[InstallationId]) -> Result<KeyPackageMap> {
138 let mut found: KeyPackageMap = keys.iter().cloned().map(|key| (key, None)).collect();
139 let topics = found.keys().map(Topic::new_key_package).collect();
140 for result in self.newest(topics, true).await? {
141 let topic = Topic::parse(
142 &result
143 .topic
144 .ok_or(ApiError::InvalidResponse("key package topic"))?
145 .topic,
146 )?;
147 let key: InstallationId = topic.identifier().try_into()?;
148 let slot = found
149 .get_mut(&key)
150 .ok_or(ApiError::InvalidResponse("unrequested key package"))?;
151 if slot.is_some() {
152 return Err(ApiError::InvalidResponse("duplicate key package"));
153 }
154 *slot = Some(decode_key_package(wire::ServerEnvelope {
155 meta: result.meta,
156 envelope: result.envelope,
157 })?);
158 }
159 Ok(found)
160 }
161 #[xmtp_common::rpc_span]
164 pub async fn newest_key_package(
165 &self,
166 id: &InstallationId,
167 ) -> Result<Option<(wire::KeyPackage, Cursor)>> {
168 let newest = self.newest(vec![Topic::new_key_package(id)], true).await?;
169 let Some(result) = newest.into_iter().next() else {
170 return Ok(None);
171 };
172 let cursor = result.meta.as_ref().and_then(|meta| meta.cursor.as_ref());
173 let cursor = Cursor(cursor.map_or(0, |cursor| cursor.sequence_id));
174 let package = decode_key_package(wire::ServerEnvelope {
175 meta: result.meta,
176 envelope: result.envelope,
177 })?;
178 Ok(Some((package, cursor)))
179 }
180 #[xmtp_common::rpc_span]
181 pub async fn send_welcome_messages(
182 &self,
183 messages: &[wire::WelcomeMessage],
184 ) -> Result<Vec<wire::EnvelopeMeta>> {
185 let units = messages
186 .iter()
187 .cloned()
188 .map(|message| {
189 PublishUnit::single_within(
190 wire::ClientEnvelope {
191 payload: Some(wire::client_envelope::Payload::WelcomeMessage(message)),
192 },
193 self.limits(),
194 )
195 })
196 .collect::<Result<Vec<_>>>()?;
197 self.publish_units(units).await
198 }
199 #[xmtp_common::rpc_span]
200 pub async fn send_group_messages(
201 &self,
202 units: Vec<PublishUnit>,
203 ) -> Result<Vec<wire::EnvelopeMeta>> {
204 self.publish_units(units).await
205 }
206 #[xmtp_common::rpc_span]
212 pub async fn publish_commit_log(
213 &self,
214 entries: Vec<wire::SealedCommitLogEntry>,
215 stored: &mut Vec<wire::EnvelopeMeta>,
216 ) -> Result<()> {
217 let units = entries
218 .into_iter()
219 .map(|entry| {
220 PublishUnit::single_within(
221 wire::ClientEnvelope {
222 payload: Some(wire::client_envelope::Payload::SealedCommitLogEntry(entry)),
223 },
224 self.limits(),
225 )
226 })
227 .collect::<Result<Vec<_>>>()?;
228 self.publish_units_in_order(units, stored).await
229 }
230 #[xmtp_common::rpc_span]
234 pub async fn query_commit_log(
235 &self,
236 cursors: TopicCursor,
237 ) -> Result<Vec<xmtp_proto::types::CommitLogEntry>> {
238 Ok(self
239 .query_all(cursors, self.limits().max_query_limit as u32)
240 .await?
241 .into_iter()
242 .filter_map(|envelope| {
243 decode_commit_log_entry(envelope)
244 .inspect_err(|error| {
245 tracing::warn!(%error, "discarding a commit-log envelope");
246 })
247 .ok()
248 })
249 .collect())
250 }
251 #[xmtp_common::rpc_span]
252 pub async fn get_newest_message_metadata(
253 &self,
254 group_ids: &[GroupId],
255 ) -> Result<MessageMetadataMap> {
256 self.newest(
257 group_ids.iter().map(Topic::new_group_message).collect(),
258 false,
259 )
260 .await?
261 .into_iter()
262 .map(|result| {
263 let meta = decode_group_message_metadata(
264 result
265 .meta
266 .ok_or(ApiError::InvalidResponse("group metadata"))?,
267 )?;
268 Ok((meta.group_id, meta))
269 })
270 .collect()
271 }
272}
273
274impl<C: XmtpMlsStreams + XmtpBackendClient> ApiClientWrapper<C>
275where
276 C::GroupMessageStream: 'static,
277 C::WelcomeMessageStream: 'static,
278{
279 #[xmtp_common::rpc_span]
280 pub async fn subscribe_group_messages(
281 &self,
282 groups: &[&GroupId],
283 ) -> Result<<crate::preflight::GuardedApi<C> as XmtpMlsStreams>::GroupMessageStream> {
284 self.retry_call(|| self.api_client.subscribe_group_messages(groups), false)
285 .await
286 .map_err(dyn_err)
287 }
288 #[xmtp_common::rpc_span]
289 pub async fn subscribe_group_messages_with_cursors(
290 &self,
291 cursors: &TopicCursor,
292 ) -> Result<<crate::preflight::GuardedApi<C> as XmtpMlsStreams>::GroupMessageStream> {
293 self.retry_call(
294 || {
295 self.api_client
296 .subscribe_group_messages_with_cursors(cursors)
297 },
298 false,
299 )
300 .await
301 .map_err(dyn_err)
302 }
303 #[xmtp_common::rpc_span]
304 pub async fn subscribe_welcome_messages(
305 &self,
306 installations: &[&InstallationId],
307 ) -> Result<<crate::preflight::GuardedApi<C> as XmtpMlsStreams>::WelcomeMessageStream> {
308 self.retry_call(
309 || self.api_client.subscribe_welcome_messages(installations),
310 false,
311 )
312 .await
313 .map_err(dyn_err)
314 }
315 #[xmtp_common::rpc_span]
316 pub async fn subscribe_welcome_messages_with_cursors(
317 &self,
318 cursors: &TopicCursor,
319 ) -> Result<<crate::preflight::GuardedApi<C> as XmtpMlsStreams>::WelcomeMessageStream> {
320 self.retry_call(
321 || {
322 self.api_client
323 .subscribe_welcome_messages_with_cursors(cursors)
324 },
325 false,
326 )
327 .await
328 .map_err(dyn_err)
329 }
330}