Transforms the body of a webhook request into a `Vec `.
(
body: &[u8],
format: &WebhookBodyFormat,
)
| 201 | |
| 202 | /// Transforms the body of a webhook request into a `Vec<BodyRow>`. |
| 203 | fn transform_body( |
| 204 | body: &[u8], |
| 205 | format: &WebhookBodyFormat, |
| 206 | ) -> Result<Vec<BodyRow>, AppendWebhookError> { |
| 207 | let rows = match format { |
| 208 | WebhookBodyFormat::Bytes => { |
| 209 | vec![Row::pack_slice(&[Datum::Bytes(body)])] |
| 210 | } |
| 211 | WebhookBodyFormat::Text => { |
| 212 | let s = std::str::from_utf8(body) |
| 213 | .map_err(|m| AppendWebhookError::InvalidUtf8Body { msg: m.to_string() })?; |
| 214 | vec![Row::pack_slice(&[Datum::String(s)])] |
| 215 | } |
| 216 | WebhookBodyFormat::Json { array } => { |
| 217 | let objects = serde_json::Deserializer::from_slice(body) |
| 218 | // Automatically expand multiple JSON objects delimited by whitespace, e.g. |
| 219 | // newlines, into a single batch. |
| 220 | .into_iter::<serde_json::Value>() |
| 221 | // Optionally expand a JSON array into separate rows, if requested. |
| 222 | .flat_map(|value| match value { |
| 223 | Ok(serde_json::Value::Array(inners)) if *array => { |
| 224 | itertools::Either::Left(inners.into_iter().map(Result::Ok)) |
| 225 | } |
| 226 | value => itertools::Either::Right(std::iter::once(value)), |
| 227 | }) |
| 228 | .collect::<Result<Vec<_>, _>>() |
| 229 | .map_err(|m| AppendWebhookError::InvalidJsonBody { msg: m.to_string() })?; |
| 230 | |
| 231 | // Note: `into_iter()` should be re-using the underlying allocation of the `objects` |
| 232 | // vector, and it's more readable to split these into separate iterators. |
| 233 | let rows = objects |
| 234 | .into_iter() |
| 235 | // Map a JSON object into a Row. |
| 236 | .map(|o| { |
| 237 | let row = Jsonb::from_serde_json(o) |
| 238 | .map_err(|m| AppendWebhookError::InvalidJsonBody { msg: m.to_string() })? |
| 239 | .into_row(); |
| 240 | Ok::<_, AppendWebhookError>(row) |
| 241 | }) |
| 242 | .collect::<Result<_, _>>()?; |
| 243 | |
| 244 | rows |
| 245 | } |
| 246 | }; |
| 247 | |
| 248 | // A `Row` cannot describe its schema without unpacking it. To add some safety we wrap the |
| 249 | // returned `Row`s in a newtype to signify they already have the "body" column packed. |
| 250 | let body_rows = rows.into_iter().map(BodyRow).collect(); |
| 251 | |
| 252 | Ok(body_rows) |
| 253 | } |
| 254 | |
| 255 | /// Pack the headers of a request into a [`Row`]. |
| 256 | fn pack_header( |