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
#![crate_name = "ampapi"]

use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};

use serde::de;

use serde_json::Value;

use reqwest::blocking;

mod types;
use crate::types::LoginResult;

mod modules;


/// AMPAPI - Struct for interacting with the AMP API
#[derive(Debug, Clone)]
pub struct AMPAPI {
    /// base_uri - The base URI of the AMP instance
    pub base_uri: String,
    /// data_source - The data source URI of the AMP instance
    pub data_source: String,
    /// username - The username to use for authentication
    pub username: String,
    /// password - The password to use for authentication
    pub password: String,
    /// remember_me_token - The remember me token to use for authentication
    pub remember_me_token: String,
    /// session_id - The session ID to use for authentication
    pub session_id: String,
    /// last_api_call - Time of the last API call
    last_api_call: i64,
    /// relog_interval - The interval to relog at
    relog_interval: i64,
}

/// AMPAPI methods
impl AMPAPI {
    /// AMPAPI.new - Create a new AMPAPI struct
    /// * `base_uri` - The base URI of the AMP instance
    /// * `username` - The username to use for authentication
    /// * `password` - The password to use for authentication
    ///   * Optional if using a remember me token
    /// * `remember_me_token` - The remember me token to use for authentication
    ///   * Optional
    /// * `session_id` - The session ID to use for authentication
    ///   * Optional, will be instantiated on login
    /// Returns AMPAPI
    pub fn new(base_uri: String, username: String, password: String, remember_me_token: String, session_id: String) -> AMPAPI {
        let mut new_base_uri = base_uri.clone();
        if (new_base_uri.chars().nth(base_uri.len()-1).unwrap()) != '/' {
            new_base_uri.push('/');
        }

        AMPAPI {
            base_uri: new_base_uri.clone(),
            data_source: new_base_uri + "API/",
            username,
            password,
            remember_me_token,
            session_id,
            last_api_call: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64,
            relog_interval: 1000*60*5,
        }
    }

    /// AMPAPI.api_call - This function takes an endpoint and a map of arguments and returns the response
    /// * `endpoint` - The endpoint to call
    /// * `args` - A map of arguments to pass to the endpoint
    /// Returns Result<T, reqwest::Error>
    pub fn api_call<T: de::DeserializeOwned>(&mut self, endpoint: String, args: HashMap<String, Value>) -> Result<T, reqwest::Error> {
        // Check the last API call time, and if it's been more than the relog interval, relog.
        let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as i64;
        if now - self.last_api_call > self.relog_interval {
            self.last_api_call = now;
            let _ = self.login();
        } else {
            self.last_api_call = now;
        }
        let mut map: HashMap<String, Value> = HashMap::new();
        map.insert("SESSIONID".to_string(), Value::String(self.session_id.to_string()));
        for (key, value) in args.iter() {
            map.insert(key.to_string(), value.clone());
        }

        let client = blocking::Client::new();
        let url = format!("{}{}", self.data_source, endpoint);
        let response = client.post(url)
            .json(&map)
            .header("Accept", "application/json")
            .header("Content-Type", "application/json")
            .header("User-Agent", "ampapi-rust/0.1.1")
            .send()?
            .json::<T>()?;
        Ok(response)
    }

    /// AMPAPI.login - Simplified login function
    /// Returns Result<LoginResult, reqwest::Error>
    pub fn login(&mut self) -> Result<LoginResult, reqwest::Error> {
        let mut args = HashMap::new();
        args.insert("username".to_string(), Value::String(self.username.clone()));
        args.insert("password".to_string(), Value::String("".to_string()));
        args.insert("token".to_string(), Value::String(self.remember_me_token.clone()));
        args.insert("rememberMe".to_string(), Value::Bool(true));

        // If remember me token is empty, use the password.
        if self.remember_me_token == "" {
            args.insert("password".to_string(), Value::String(self.password.clone()));
        }

        self.api_call::<LoginResult>("Core/Login".to_string(), args)
    }
}