Download and parse untyped XML data

This commit is contained in:
2022-10-20 10:38:55 -03:00
commit 9545bb1b3d
6 changed files with 1413 additions and 0 deletions

1210
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

5
Cargo.toml Normal file
View File

@@ -0,0 +1,5 @@
[workspace]
members = [
"crates/runner",
"crates/bidgely_adapter",
]

View File

@@ -0,0 +1,9 @@
[package]
name = "bidgely_adapter"
version = "0.1.0"
authors = ["Steve Sampson <mail@stephensampson.dev>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive"] }

View File

@@ -0,0 +1,114 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct UserAuthResponse {
pub request_id: String,
pub payload: String,
pub error: Option<String>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct SessionResponse {
pub request_id: String,
pub payload: SessionPayload,
pub error: Option<String>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct SessionPayload {
pub pilot_id: u64,
pub client_id: String,
pub token_details: TokenDetails,
pub user_profile_details: UserProfileDetails,
pub user_type_details: UserTypeDetails,
pub premises_details: PremisesDetails,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct TokenDetails {
pub access_token: String,
pub expiry_time_in_millis: u64,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct UserProfileDetails {
pub user_id: String,
pub partner_user_id: String,
pub meter_id: Option<String>,
pub first_name: String,
pub last_name: String,
pub fuel_type: String, // todo: type this as enum FuelType
pub email: String,
pub utility_tags: UtilityTags,
pub home_accounts: HomeAccounts,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "snake_case"))]
pub struct UtilityTags {
pub account_number: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct HomeAccounts {
pub address: String,
pub has_solar: bool,
pub postal_code: String,
pub rate: RatePlanInfo,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct RatePlanInfo {
pub rate_plan_id: String,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct UserTypeDetails {
pub user_segment: String, // todo: type this as enum UserSegment
pub measurement_to_user_type_mappings: Vec<MeasurementToUserType>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct MeasurementToUserType {
pub measurement_type: String,
pub user_type: String,
pub max_contract_end: u32,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct PremisesDetails {
pub partner_user_id: String,
pub premises: Vec<Premise>,
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct Premise {
pub uuid: String,
pub premise_id: Option<String>,
pub address: PremiseAddress,
pub supported_measurement_types: Vec<String>, // toto: type this as enum MeasurementType
pub dashboard_last_visited: u32,
pub status: String, // todo: type this as enum PremiseStatus
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all(deserialize = "camelCase"))]
pub struct PremiseAddress {
pub address_line_1: String,
pub address_line_2: Option<String>,
pub city: String,
pub state: String,
pub zipcode: String,
pub context: Option<String>,
}

15
crates/runner/Cargo.toml Normal file
View File

@@ -0,0 +1,15 @@
[package]
name = "nspdata"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bidgely_adapter = { path = "../bidgely_adapter" }
clap = { version = "4.0.17", features = ["derive"] }
quick-xml = "0.25.0"
reqwest = "0.11.12"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
tokio = { version = "1.21.2", features = ["full"] }

60
crates/runner/src/main.rs Normal file
View File

@@ -0,0 +1,60 @@
use bidgely_adapter::{SessionResponse, UserAuthResponse};
use clap::Parser;
const BIDGELY_BASE_URL: &'static str = "https://caapi.bidgely.com/v2.0";
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long)]
uid: String,
}
#[tokio::main]
async fn main() {
let args: Args = Args::parse();
let user_id = args.uid.as_str();
let user_auth_response: UserAuthResponse = serde_json::from_str(
&reqwest::get(format!(
"{BIDGELY_BASE_URL}/user-auth/cipher?user-id={user_id}&pilot-id=40003"
))
.await
.unwrap()
.text()
.await
.unwrap(),
)
.unwrap();
let session = user_auth_response.payload;
let session_response: SessionResponse = serde_json::from_str(
&reqwest::get(format!(
"{BIDGELY_BASE_URL}/web/web-session/{session}?pilotId=40003&clientId=nsp-dashboard"
))
.await
.unwrap()
.text()
.await
.unwrap(),
)
.unwrap();
let token = session_response.payload.token_details.access_token;
let client = reqwest::Client::new();
let xml_data = client.get(format!(
"{BIDGELY_BASE_URL}/dashboard/users/{user_id}/gb-download?start=1660694400&end=1665964800&measurement-type=ELECTRIC"
))
.header(reqwest::header::CONTENT_TYPE, "application/json;charset=UTF-8")
.header(reqwest::header::AUTHORIZATION, format!("Bearer {token}"))
.send()
.await
.unwrap()
.text()
.await
.unwrap();
println!("{:?}", xml_data);
}