xmtp_mls/groups/
metadata_fields.rs1use std::collections::BTreeMap;
13
14use openmls::group::MlsGroup as OpenMlsGroup;
15use xmtp_db::{
16 Fetch,
17 group_intent::{ID, IntentState, StoredGroupIntent},
18};
19use xmtp_mls_common::{
20 app_data::fields::{
21 ComponentMutation, FieldError, FieldKey, FieldSnapshot, FieldValue, FieldWrite,
22 MetadataFieldDescriptor, MetadataFieldRef, MetadataFieldValue, MetadataValue,
23 UserFieldUpdate, UserFieldValue,
24 },
25 inbox_id::InboxId,
26};
27
28use super::{
29 GroupError, MlsGroup,
30 app_data::sender_intents::field_writes_commit,
31 intents::{AppDataUpdateIntentData, QueueIntent},
32};
33use crate::context::XmtpSharedContext;
34
35impl<Context> MlsGroup<Context>
36where
37 Context: XmtpSharedContext,
38{
39 pub fn metadata_fields(&self) -> Result<Vec<MetadataFieldDescriptor>, GroupError> {
42 self.read_fields(|fields| Ok(fields.fields().to_vec()))
43 }
44
45 pub fn metadata_field(
49 &self,
50 name: &str,
51 ) -> Result<Option<MetadataFieldDescriptor>, GroupError> {
52 self.read_fields(|fields| Ok(fields.field(name).cloned()))
53 }
54
55 pub fn metadata_value(
58 &self,
59 field: &MetadataFieldRef,
60 ) -> Result<Option<MetadataValue>, GroupError> {
61 self.read_fields(|fields| fields.value(field))
62 }
63
64 pub fn metadata_values(
67 &self,
68 fields: &[MetadataFieldRef],
69 ) -> Result<Vec<MetadataFieldValue>, GroupError> {
70 self.read_fields(|snapshot| snapshot.values(fields))
71 }
72
73 pub fn map_value(
76 &self,
77 field: &MetadataFieldRef,
78 key: &FieldKey,
79 ) -> Result<Option<FieldValue>, GroupError> {
80 self.read_fields(|fields| fields.map_value(field, key))
81 }
82
83 pub fn user_data(
87 &self,
88 fields: Option<&[MetadataFieldRef]>,
89 inbox_ids: Option<&[InboxId]>,
90 ) -> Result<BTreeMap<InboxId, Vec<UserFieldValue>>, GroupError> {
91 self.read_fields(|snapshot| snapshot.user_data(fields, inbox_ids))
92 }
93
94 pub async fn update_metadata_field(
98 &self,
99 field: &MetadataFieldRef,
100 mutation: &ComponentMutation,
101 ) -> Result<(), GroupError> {
102 self.write_fields(|fields| Ok(vec![fields.field_write(field, mutation)?]))
103 .await
104 }
105
106 pub async fn update_user_data(&self, values: &[UserFieldUpdate]) -> Result<(), GroupError> {
111 self.write_fields(|fields| fields.user_data_writes(values))
112 .await
113 }
114
115 fn read_fields<T>(
116 &self,
117 read: impl FnOnce(&FieldSnapshot<'_>) -> Result<T, FieldError>,
118 ) -> Result<T, GroupError> {
119 let context = self.load_group_context()?;
120 let dictionary = context
121 .extensions()
122 .app_data_dictionary()
123 .map(|extension| extension.dictionary());
124 let catalogue = &self
125 .context
126 .server_configuration()
127 .configuration()
128 .application_components;
129 Ok(read(&FieldSnapshot::new(dictionary, catalogue)?)?)
130 }
131
132 async fn write_fields(
147 &self,
148 plan: impl FnOnce(&FieldSnapshot<'_>) -> Result<Vec<FieldWrite>, FieldError>,
149 ) -> Result<(), GroupError> {
150 self.ensure_not_paused().await?;
151 self.sync().await?;
152 let own = InboxId::from_hex(self.context.inbox_id())
153 .map_err(|e| GroupError::ComponentSource(e.into()))?;
154 let writes = self.with_group_snapshot(|group: &OpenMlsGroup| {
155 let committed = group
156 .extensions()
157 .app_data_dictionary()
158 .map(|extension| extension.dictionary());
159 let writes = plan(&FieldSnapshot::new(committed, &[])?)?;
160 Ok(field_writes_commit(group, own, &writes)?.map(|_| writes))
161 })?;
162 let Some(writes) = writes else {
163 return Ok(());
164 };
165 let intent = QueueIntent::app_data_update()
166 .data(Vec::<u8>::from(AppDataUpdateIntentData::Fields(
167 writes.clone(),
168 )))
169 .queue(self)?;
170 self.publish_field_writes(intent.id, own, &writes).await
171 }
172
173 pub(super) async fn publish_field_writes(
182 &self,
183 intent_id: ID,
184 own: InboxId,
185 writes: &[FieldWrite],
186 ) -> Result<(), GroupError> {
187 let Err(error) = self.sync_until_intent_resolved(intent_id).await else {
188 return Ok(());
189 };
190 let failed = matches!(
191 self.context.db().fetch(&intent_id),
192 Ok(Some(StoredGroupIntent {
193 state: IntentState::Error,
194 ..
195 }))
196 );
197 if failed
198 && let Err(field @ GroupError::MetadataField(_)) =
199 self.with_group_snapshot(|group| field_writes_commit(group, own, writes))
200 {
201 return Err(field);
202 }
203 Err(error)
204 }
205}