// A Session is where everything begins. My plan (for now) is that we go via the session
// for everything, but we will see how the API develops.
use crate::api::types::Config;
use crate::api::errors;
use reqwest;
use crate::api::json::{MelcloudLoginResponse};
use crate::api::types::devices::Devices;
#[derive(Debug)]
pub struct Session {
pub config: Config,
pub api_key: String
}
impl Session {
/// Starts a new session
pub fn start(config: Config) -> Result<Session, errors::ApiError> {
let request_url = format!("{api_base}/Login/ClientLogin", api_base = &config.api_url);
let post_body = format!("{{\"Email\":\"{email}\",\"Password\":\"{password}\",\"Language\":0,\"AppVersion\":\"1.18.5.1\",\"Persist\":true,\"CaptchaResponse\":null}}", email = config.api_username, password = config.api_password);
let result = reqwest::Client::new()
.post(&request_url)
.body(post_body)
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.send();
match result {
Ok(mut resp) => {
match resp.json() {
Ok(json) => Session::parse_new_session_response(config, json),
Err(err) => {
println!("Err is {:?}", err);
Err(errors::ApiError::InvalidLoginResponse)
}
}
},
Err(err) => Err(errors::ApiError::LoginFailure)
}
}
pub fn devices(&self) -> Devices {
Devices::new(self)
}
fn parse_new_session_response(config: Config, message: MelcloudLoginResponse) -> Result<Session, errors::ApiError> {
match message {
MelcloudLoginResponse::Success {login_data} => {
Ok(Session {
config: config,
api_key: login_data.context_key
})
},
MelcloudLoginResponse::AccessDenied {error_id} => {
Err(errors::ApiError::LoginFailure)
}
}
}
}
#[cfg(test)]
mod tests {
use mockito::{mock, Matcher};
use crate::api::types::Config;
use crate::api::types::Session;
use crate::api::errors;
#[test]
fn test_start_session() {
// Arrange - Create a config and setup a mock server
let config = Config::new(&mockito::server_url(), "testuser", "testpassword");
let m = mock("POST", "/Login/ClientLogin")
.with_status(200)
.with_body("{\"ErrorId\":null,\"ErrorMessage\":null,\"LoginStatus\":0,\"UserId\":0,\"RandomKey\":null,\"AppVersionAnnouncement\":null,\"LoginData\":{\"ContextKey\":\"C71933C57FB04358B6750ED77D79AA\",\"Client\":128538,\"Terms\":1199,\"AL\":1,\"ML\":0,\"CMI\":true,\"IsStaff\":false,\"CUTF\":false,\"CAA\":false,\"ReceiveCountryNotifications\":false,\"ReceiveAllNotifications\":false,\"CACA\":false,\"CAGA\":false,\"MaximumDevices\":10,\"ShowDiagnostics\":false,\"Language\":0,\"Country\":237,\"RealClient\":0,\"Name\":\"Gary Taylor\",\"UseFahrenheit\":false,\"Duration\":525600,\"Expiry\":\"2020-10-14T13:54:42.653\",\"CMSC\":false,\"PartnerApplicationVersion\":null,\"EmailSettingsReminderShown\":true,\"EmailUnitErrors\":1,\"EmailCommsErrors\":1,\"IsImpersonated\":false,\"LanguageCode\":\"en\",\"CountryName\":\"United Kingdom\",\"CurrencySymbol\":\"£\",\"SupportEmailAddress\":\"melcloud.support@meuk.mee.com \",\"DateSeperator\":\"/\",\"TimeSeperator\":\":\",\"AtwLogoFile\":\"ecodan_logo.png\",\"DECCReport\":true,\"CSVReport1min\":true,\"HidePresetPanel\":false,\"EmailSettingsReminderRequired\":false,\"TermsText\":null,\"MapView\":false,\"MapZoom\":0,\"MapLongitude\":-133.082129213169600,\"MapLatitude\":52.621242998717900},\"ListPendingInvite\":[],\"ListOwnershipChangeRequest\":[],\"ListPendingAnnouncement\":[],\"LoginMinutes\":0,\"LoginAttempts\":0}")
.create();
// Act - Start the session
let result = Session::start(config);
// Assert - Make sure the result contains the api key
m.assert();
match result {
Ok(session) => assert!(session.api_key == "C71933C57FB04358B6750ED77D79AA", "The api key was wrong"),
Err(err) => assert!(false, "Something went wrong"),
}
}
#[test]
fn test_start_session_wrong_credentials() {
// Arrange - Create a config and setup a mock server
let config = Config::new(&mockito::server_url(), "testuser", "testpassword");
let m = mock("POST", "/Login/ClientLogin")
.with_status(200)
.with_body("{\"ErrorId\":1,\"ErrorMessage\":null,\"LoginStatus\":0,\"UserId\":0,\"RandomKey\":null,\"AppVersionAnnouncement\":null,\"LoginData\":null,\"ListPendingInvite\":null,\"ListOwnershipChangeRequest\":null,\"ListPendingAnnouncement\":null,\"LoginMinutes\":0,\"LoginAttempts\":1}")
.create();
// Act - Start the session
let result = Session::start(config);
// Assert - Make sure the result contains the api key
m.assert();
match result {
Ok(session) => assert!(false, "Must return an error"),
Err(errors::ApiError::LoginFailure) => assert!(true),
Err(_) => assert!(false, "Must return a Login Failure")
}
}
}