1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::helper::get_current_da_time;
use crate::{Channel, Result, UrbitAPIError};
use json::{object, JsonValue};
use std::time::{SystemTime, UNIX_EPOCH};

/// A struct which exposes Graph Store functionality
pub struct GraphStore<'a> {
    pub channel: &'a mut Channel,
}

impl<'a> GraphStore<'a> {
    /// Issue a `post` to Graph Store.
    /// On success returns the index of the newly added node.
    pub fn post(
        &mut self,
        resource_ship: &str,
        resource_name: &str,
        contents: Vec<JsonValue>,
    ) -> Result<String> {
        // The index. For chat the default is current time in `@da` encoding with a `/` in front.
        let index = format!("/{}", get_current_da_time());

        self.post_custom_index(resource_ship, resource_name, contents, &index)
    }

    /// Issue a `post` to Graph Store using a custom index.
    /// On success returns the index of the newly added node.
    pub fn post_custom_index(
        &mut self,
        resource_ship: &str,
        resource_name: &str,
        contents: Vec<JsonValue>,
        index: &str,
    ) -> Result<String> {
        // Add the ~ to the ship name to be used within the post as author
        let ship = format!("~{}", self.channel.ship_interface.ship_name);

        // Get the current Unix Time
        let unix_time = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64;

        // Creating the json by adding the index dynamically for the key
        // for the inner part of the json
        let mut nodes_json = object!();
        nodes_json[index.clone()] = object! {
                        "post": {
                            "author": ship.clone(),
                            "index": index.clone(),
                            "time-sent": unix_time,
                            "contents": contents,
                            "hash": null,
                            "signatures": []
                        },
                        "children": null
        };

        // Using `?` to ensure adding the node was a success, else return error.
        self.add_nodes(resource_ship, resource_name, nodes_json)?;
        Ok(index.to_string())
    }

    /// Add nodes to Graph Store
    pub fn add_nodes(
        &mut self,
        resource_ship: &str,
        resource_name: &str,
        nodes_json: JsonValue,
    ) -> Result<()> {
        let prepped_json = object! {
            "add-nodes": {
                "resource": {
                    "ship": resource_ship,
                    "name": resource_name
                },
            "nodes": nodes_json
            }
        };

        let resp = (&mut self.channel).poke("graph-push-hook", "graph-update", &prepped_json)?;

        if resp.status().as_u16() == 204 {
            Ok(())
        } else {
            return Err(UrbitAPIError::FailedToAddNodesToGraphStore(
                resource_name.to_string(),
            ));
        }
    }

    /// Remove nodes from Graph Store using the provided list of indices
    pub fn remove_nodes(
        &mut self,
        resource_ship: &str,
        resource_name: &str,
        indices: Vec<&str>,
    ) -> Result<()> {
        let prepped_json = object! {
            "remove-nodes": {
                "resource": {
                    "ship": resource_ship,
                    "name": resource_name
                },
            "indices": indices
            }
        };
        println!("remove nodes: {}", prepped_json.dump());

        let resp = (&mut self.channel).poke("graph-push-hook", "graph-update", &prepped_json)?;

        if resp.status().as_u16() == 204 {
            Ok(())
        } else {
            return Err(UrbitAPIError::FailedToRemoveNodesFromGraphStore(
                resource_name.to_string(),
            ));
        }
    }
}