wrap payloads to json-object on the wire

This commit is contained in:
B. Petersen
2021-12-12 21:52:33 +01:00
committed by bjoern
parent de20e4c9dd
commit de7706f622
2 changed files with 69 additions and 24 deletions

View File

@@ -1163,7 +1163,9 @@ impl<'a> MimeFactory<'a> {
let json = self.msg.param.get(Param::Arg).unwrap_or_default(); let json = self.msg.param.get(Param::Arg).unwrap_or_default();
parts.push(context.build_status_update_part(json).await); parts.push(context.build_status_update_part(json).await);
} else if self.msg.viewtype == Viewtype::W30 { } else if self.msg.viewtype == Viewtype::W30 {
let json = context.get_w30_status_updates(self.msg.id, None).await?; let json = context
.get_w30_status_updates_with_format(self.msg.id, None, true)
.await?;
if json != "[]" { if json != "[]" {
parts.push(context.build_status_update_part(&json).await); parts.push(context.build_status_update_part(&json).await);
} }

View File

@@ -102,7 +102,7 @@ impl Context {
match instance.state { match instance.state {
MessageState::Undefined | MessageState::OutPreparing | MessageState::OutDraft => { MessageState::Undefined | MessageState::OutPreparing | MessageState::OutDraft => {
// send update once the instance is actually send; // send update once the instance is actually send;
// on sending, the updates are retrieved using get_w30_status_updates() then. // on sending, the updates are retrieved using get_w30_status_updates_with_format() then.
Ok(None) Ok(None)
} }
_ => { _ => {
@@ -118,8 +118,12 @@ impl Context {
status_update.param.set_cmd(SystemMessage::W30StatusUpdate); status_update.param.set_cmd(SystemMessage::W30StatusUpdate);
status_update.param.set( status_update.param.set(
Param::Arg, Param::Arg,
self.get_w30_status_updates(instance_msg_id, Some(status_update_id)) self.get_w30_status_updates_with_format(
.await?, instance_msg_id,
Some(status_update_id),
true,
)
.await?,
); );
status_update.set_quote(self, &instance).await?; status_update.set_quote(self, &instance).await?;
let status_update_msg_id = let status_update_msg_id =
@@ -145,13 +149,9 @@ impl Context {
/// `msg_id` may be an instance (in case there are initial status updates) /// `msg_id` may be an instance (in case there are initial status updates)
/// or a reply to an instance (for all other updates). /// or a reply to an instance (for all other updates).
/// ///
/// `json_array` is an array containing one or more payloads as created by send_w30_status_update(), /// `json` is an array containing one or more payloads as created by send_w30_status_update(),
/// the array is parsed using serde, the single payloads are used as is. /// the array is parsed using serde, the single payloads are used as is.
pub(crate) async fn receive_status_update( pub(crate) async fn receive_status_update(&self, msg_id: MsgId, json: &str) -> Result<()> {
&self,
msg_id: MsgId,
json_array: &str,
) -> Result<()> {
let msg = Message::load_from_db(self, msg_id).await?; let msg = Message::load_from_db(self, msg_id).await?;
let instance = if msg.viewtype == Viewtype::W30 { let instance = if msg.viewtype == Viewtype::W30 {
msg msg
@@ -165,10 +165,13 @@ impl Context {
bail!("receive_status_update: status message has no parent.") bail!("receive_status_update: status message has no parent.")
}; };
let payloads: Vec<Value> = serde_json::from_str(json_array)?; let update_items: Vec<StatusUpdateItem> = serde_json::from_str(json)?;
for payload in payloads { for update_item in update_items {
let status_update_id = self let status_update_id = self
.create_status_update_record(instance.id, &*serde_json::to_string(&payload)?) .create_status_update_record(
instance.id,
&*serde_json::to_string(&update_item.payload)?,
)
.await?; .await?;
self.emit_event(EventType::W30StatusUpdate { self.emit_event(EventType::W30StatusUpdate {
msg_id: instance.id, msg_id: instance.id,
@@ -188,7 +191,28 @@ impl Context {
instance_msg_id: MsgId, instance_msg_id: MsgId,
status_update_id: Option<StatusUpdateId>, status_update_id: Option<StatusUpdateId>,
) -> Result<String> { ) -> Result<String> {
let json_array = self self.get_w30_status_updates_with_format(instance_msg_id, status_update_id, false)
.await
}
/// Returns status updates as JSON-array.
///
/// If `for_wire` is `false`, the result is suitable to be passed to the app,
/// that get back exactly the payloads as sent:
/// `["any update data", "another update data"]`
/// get_w30_status_updates() returns this format.
///
/// If `for_wire` is `true`, the payload is wrapped to an object
/// and can be enhanced in the future:
/// `[{"payload":"any update data"},{"payload":"another update data"}]`
/// This is suitable for sending objects that are not visible to apps:
pub(crate) async fn get_w30_status_updates_with_format(
&self,
instance_msg_id: MsgId,
status_update_id: Option<StatusUpdateId>,
for_wire: bool,
) -> Result<String> {
let json = self
.sql .sql
.query_map( .query_map(
"SELECT payload FROM msgs_status_updates WHERE msg_id=? AND (1=? OR id=?)", "SELECT payload FROM msgs_status_updates WHERE msg_id=? AND (1=? OR id=?)",
@@ -199,19 +223,25 @@ impl Context {
], ],
|row| row.get::<_, String>(0), |row| row.get::<_, String>(0),
|rows| { |rows| {
let mut json_array = String::default(); let mut json = String::default();
for row in rows { for row in rows {
let json_entry = row?; let payload = row?;
if !json_array.is_empty() { if !json.is_empty() {
json_array.push_str(",\n"); json.push_str(",\n");
}
if for_wire {
json.push_str("{\"payload\":");
}
json.push_str(&payload);
if for_wire {
json.push('}');
} }
json_array.push_str(&json_entry);
} }
Ok(json_array) Ok(json)
}, },
) )
.await?; .await?;
Ok(format!("[{}]", json_array)) Ok(format!("[{}]", json))
} }
} }
@@ -405,14 +435,27 @@ true]"#
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_w30_instance(&t, chat_id).await?; let instance = send_w30_instance(&t, chat_id).await?;
t.receive_status_update(instance.id, r#"[{"foo":"bar"}]"#) assert!(t
.receive_status_update(instance.id, r#"foo: bar"#)
.await
.is_err()); // no json
assert!(t
.receive_status_update(instance.id, r#"[{"foo":"bar"}]"#)
.await
.is_err()); // "payload" field missing
assert!(t
.receive_status_update(instance.id, r#"{"payload":{"foo":"bar"}}"#)
.await
.is_err()); // not an array
t.receive_status_update(instance.id, r#"[{"payload":{"foo":"bar"}}]"#)
.await?; .await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_w30_status_updates(instance.id, None).await?,
r#"[{"foo":"bar"}]"# r#"[{"foo":"bar"}]"#
); );
t.receive_status_update(instance.id, r#" [ 42 , 23 ] "#) t.receive_status_update(instance.id, r#" [ {"payload" :42} , {"payload": 23} ] "#)
.await?; .await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_w30_status_updates(instance.id, None).await?,
@@ -570,7 +613,7 @@ true]"#
assert_eq!(bob_instance.get_filename(), Some("index.w30".to_string())); assert_eq!(bob_instance.get_filename(), Some("index.w30".to_string()));
assert!(sent1.payload().contains("Content-Type: application/json")); assert!(sent1.payload().contains("Content-Type: application/json"));
assert!(sent1.payload().contains("status-update.json")); assert!(sent1.payload().contains("status-update.json"));
assert!(sent1.payload().contains(r#"{"foo":"bar"}"#)); assert!(sent1.payload().contains(r#""payload":{"foo":"bar"}"#));
assert_eq!( assert_eq!(
bob.get_w30_status_updates(bob_instance.id, None).await?, bob.get_w30_status_updates(bob_instance.id, None).await?,
r#"[{"foo":"bar"}, r#"[{"foo":"bar"},