use helix_core::effect::{Correlation, Effect, GetSpec, SqlValue, StorageOp};
use crate::error::ImError;
use crate::query::DialogListScope;
use crate::state::ChannelId;
pub const QUERY_CHANNEL_VIEW_SNAPSHOT: &str = "im_query_channel_view_snapshot";
pub fn parse_channel_id(payload: &[u8]) -> Result<ChannelId, ImError> {
let value = serde_json::from_slice::<serde_json::Value>(payload).map_err(|error| {
ImError::Parse(format!("{QUERY_CHANNEL_VIEW_SNAPSHOT} payload: {error}"))
})?;
let object = value.as_object().ok_or_else(|| {
ImError::Parse(format!(
"{QUERY_CHANNEL_VIEW_SNAPSHOT} payload must be an object"
))
})?;
for key in object.keys() {
if !matches!(key.as_str(), "channelId" | "channel_id" | "req_id") {
return Err(ImError::Parse(format!(
"{QUERY_CHANNEL_VIEW_SNAPSHOT} field is not caller-owned: {key}"
)));
}
}
object
.get("channelId")
.or_else(|| object.get("channel_id"))
.and_then(serde_json::Value::as_str)
.and_then(ChannelId::from_str)
.ok_or_else(|| ImError::Parse(format!("{QUERY_CHANNEL_VIEW_SNAPSHOT} requires channelId")))
}
pub fn query_effect(channel_id: ChannelId, corr: Correlation) -> Effect {
Effect::Persist {
corr,
ops: vec![StorageOp::Get(GetSpec {
table: "channel",
key_col: "id",
key_val: SqlValue::Text(channel_id.as_str().to_string()),
})],
}
}
pub fn result_body(
reply_bytes: &[u8],
scope: &DialogListScope,
channel_id: ChannelId,
) -> serde_json::Value {
serde_json::json!({
"snapshot": crate::query::project_channel_view_snapshot(reply_bytes, scope, channel_id)
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snapshot_is_runtime_scoped_and_render_ready() {
let channel_id = ChannelId::from_str("chfixx0000000000000000002a").unwrap();
let rows = serde_json::to_vec(&serde_json::json!([{
"id": channel_id.as_str(),
"team_id": "team-a",
"members": "[{\"userId\":\"user-a\",\"role\":\"MEMBER\"}]",
"setting_version": 3,
"permission_revision": 4
}]))
.unwrap();
let body = result_body(&rows, &DialogListScope::new("user-a", "team-a"), channel_id);
assert_eq!(body["snapshot"]["id"], channel_id.as_str());
assert_eq!(body["snapshot"]["settingVersion"], 3);
}
#[test]
fn snapshot_rejects_cross_tenant_row() {
let channel_id = ChannelId::from_str("chfixx0000000000000000002a").unwrap();
let rows = serde_json::to_vec(&serde_json::json!([{
"id": channel_id.as_str(),
"team_id": "team-b",
"user_id": "user-a"
}]))
.unwrap();
let body = result_body(&rows, &DialogListScope::new("user-a", "team-a"), channel_id);
assert!(body["snapshot"].is_null());
}
}