LCOV - code coverage report
Current view: top level - safekeeper/src - patch_control_file.rs (source / functions) Coverage Total Hit
Test: 32f4a56327bc9da697706839ed4836b2a00a408f.info Lines: 91.7 % 48 44
Test Date: 2024-02-07 07:37:29 Functions: 66.7 % 18 12

            Line data    Source code
       1              : use std::sync::Arc;
       2              : 
       3              : use serde::{Deserialize, Serialize};
       4              : use serde_json::Value;
       5              : use tracing::info;
       6              : 
       7              : use crate::{state::TimelinePersistentState, timeline::Timeline};
       8              : 
       9            5 : #[derive(Deserialize, Debug, Clone)]
      10              : pub struct Request {
      11              :     /// JSON object with fields to update
      12              :     pub updates: serde_json::Value,
      13              :     /// List of fields to apply
      14              :     pub apply_fields: Vec<String>,
      15              : }
      16              : 
      17            1 : #[derive(Serialize)]
      18              : pub struct Response {
      19              :     pub old_control_file: TimelinePersistentState,
      20              :     pub new_control_file: TimelinePersistentState,
      21              : }
      22              : 
      23              : /// Patch control file with given request. Will update the persistent state using
      24              : /// fields from the request and persist the new state on disk.
      25            1 : pub async fn handle_request(tli: Arc<Timeline>, request: Request) -> anyhow::Result<Response> {
      26            1 :     let response = tli
      27            1 :         .map_control_file(|state| {
      28            1 :             let old_control_file = state.clone();
      29            1 :             let new_control_file = state_apply_diff(&old_control_file, &request)?;
      30              : 
      31            1 :             info!(
      32            1 :                 "patching control file, old: {:?}, new: {:?}, patch: {:?}",
      33            1 :                 old_control_file, new_control_file, request
      34            1 :             );
      35            1 :             *state = new_control_file.clone();
      36            1 : 
      37            1 :             Ok(Response {
      38            1 :                 old_control_file,
      39            1 :                 new_control_file,
      40            1 :             })
      41            1 :         })
      42            3 :         .await?;
      43              : 
      44            1 :     Ok(response)
      45            1 : }
      46              : 
      47            1 : fn state_apply_diff(
      48            1 :     state: &TimelinePersistentState,
      49            1 :     request: &Request,
      50            1 : ) -> anyhow::Result<TimelinePersistentState> {
      51            1 :     let mut json_value = serde_json::to_value(state)?;
      52              : 
      53            1 :     if let Value::Object(a) = &mut json_value {
      54            1 :         if let Value::Object(b) = &request.updates {
      55            1 :             json_apply_diff(a, b, &request.apply_fields)?;
      56              :         } else {
      57            0 :             anyhow::bail!("request.updates is not a json object")
      58              :         }
      59              :     } else {
      60            0 :         anyhow::bail!("TimelinePersistentState is not a json object")
      61              :     }
      62              : 
      63            1 :     let new_state: TimelinePersistentState = serde_json::from_value(json_value)?;
      64            1 :     Ok(new_state)
      65            1 : }
      66              : 
      67            1 : fn json_apply_diff(
      68            1 :     object: &mut serde_json::Map<String, Value>,
      69            1 :     updates: &serde_json::Map<String, Value>,
      70            1 :     apply_keys: &Vec<String>,
      71            1 : ) -> anyhow::Result<()> {
      72            2 :     for key in apply_keys {
      73            1 :         if let Some(new_value) = updates.get(key) {
      74            1 :             if let Some(existing_value) = object.get_mut(key) {
      75            1 :                 *existing_value = new_value.clone();
      76            1 :             } else {
      77            0 :                 anyhow::bail!("key not found in original object: {}", key);
      78              :             }
      79              :         } else {
      80            0 :             anyhow::bail!("key not found in request.updates: {}", key);
      81              :         }
      82              :     }
      83              : 
      84            1 :     Ok(())
      85            1 : }
        

Generated by: LCOV version 2.1-beta