From 4f24db9bdbb8f8d33a1b453541f2749d0f8668df Mon Sep 17 00:00:00 2001 From: gak Date: Wed, 25 Oct 2023 13:46:42 +1100 Subject: [PATCH] wip(tesla_api_coverage): rename keys based on similar urls --- tesla_api_coverage/src/fleet.rs | 14 +++- tesla_api_coverage/src/main.rs | 103 +++++++++++++++++++++++------ tesla_api_coverage/src/teslatte.rs | 19 +++++- tesla_api_coverage/src/timdorr.rs | 18 +++++ 4 files changed, 131 insertions(+), 23 deletions(-) diff --git a/tesla_api_coverage/src/fleet.rs b/tesla_api_coverage/src/fleet.rs index cbee496..80c8fda 100644 --- a/tesla_api_coverage/src/fleet.rs +++ b/tesla_api_coverage/src/fleet.rs @@ -1,4 +1,6 @@ +use crate::Restful; use heck::ToKebabCase; +use reqwest::Method; use scraper::{Element, ElementRef, Html, Selector}; use std::collections::HashMap; use std::str::FromStr; @@ -69,7 +71,7 @@ pub struct Parameter { #[derive(Debug, Clone)] pub struct FleetEndpoint { pub name: String, - pub method: reqwest::Method, + pub method: Method, pub uri: String, // description: String, // category: Category, @@ -79,6 +81,16 @@ pub struct FleetEndpoint { // response_example: String, } +impl Restful for FleetEndpoint { + fn method(&self) -> &Method { + &self.method + } + + fn uri(&self) -> &str { + &self.uri + } +} + pub fn parse(html: &str) -> HashMap { let document = Html::parse_document(html); let content_selector = selector(".content h1"); diff --git a/tesla_api_coverage/src/main.rs b/tesla_api_coverage/src/main.rs index 5e451ce..dfc37cc 100644 --- a/tesla_api_coverage/src/main.rs +++ b/tesla_api_coverage/src/main.rs @@ -10,6 +10,7 @@ use crate::teslatte::TeslatteEndpoint; use crate::timdorr::TimdorrEndpoint; use crate::vehicle_command::VehicleCommandEndpoint; use clap::Parser; +use reqwest::Method; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::str::FromStr; @@ -23,6 +24,15 @@ const VEHICLE_COMMAND_FILE: &str = "vehicle_command.go"; const FLEET_API_URL: &str = "https://developer.tesla.com/docs/fleet-api"; const FLEET_API_FILE: &str = "fleet.html"; +trait Restful { + fn method(&self) -> &Method; + fn uri(&self) -> &str; +} + +trait EndpointGeneral { + fn name(&self) -> &str; +} + #[derive(Parser, Debug)] #[clap(author, version)] struct Cli { @@ -45,20 +55,25 @@ async fn main() { let mut teslatte_project_path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap(); teslatte_project_path.push(".."); let teslatte_endpoints = teslatte::parse(&teslatte_project_path).unwrap(); - let teslatte_endpoints: HashMap = teslatte_endpoints - .into_iter() - .map(|e| (e.name.clone(), e)) - .collect(); - let fleet_endpoints = fleet::parse(&fleet_html); + let mut fleet_endpoints = fleet::parse(&fleet_html); let command_endpoints = vehicle_command::parse(&command_golang); - let timdorr_endpoints = timdorr::parse(&timdorr); + let mut timdorr_endpoints = timdorr::parse(&timdorr); + info!("TOTALS: (before filtering and merging)"); info!("{} endpoints in teslatte", teslatte_endpoints.len()); info!("{} endpoints in fleet", fleet_endpoints.len()); info!("{} endpoints in command", command_endpoints.len()); info!("{} endpoints in timdorr", timdorr_endpoints.len()); + // Before we merge let's mangle the names so the URI's match. + info!("-- rename timdorr based on teslatte"); + rename_keys_based_on_uri(&teslatte_endpoints, &mut timdorr_endpoints); + info!("-- rename fleet based on teslatte"); + rename_keys_based_on_uri(&teslatte_endpoints, &mut fleet_endpoints); + info!("-- rename timdorr based on fleet"); + rename_keys_based_on_uri(&fleet_endpoints, &mut timdorr_endpoints); + let mut merged = merge( teslatte_endpoints, fleet_endpoints, @@ -67,10 +82,56 @@ async fn main() { ) .unwrap(); + remove_unwanted_endpoints(&mut merged); + ensure_timdorr_matches_fleet(&merged); + + dbg!(&merged); + + todo!(); +} + +fn rename_keys_based_on_uri( + base: &HashMap, + mut to_rename: &mut HashMap, +) { + let mut renames = vec![]; + for (base_key, base_endpoint) in base.iter() { + let mut seen = false; + for (rename_key, rename_endpoint) in to_rename.iter() { + if base_endpoint.uri() != rename_endpoint.uri() { + continue; + } + if base_endpoint.method() != rename_endpoint.method() { + continue; + } + if base_key == rename_key { + continue; + } + + info!( + "Rename {rename_key} -> {base_key} for {}", + base_endpoint.uri() + ); + if seen { + panic!("Duplicate rename for {base_key} -> {rename_key}"); + } + + renames.push((base_key.to_string(), rename_key.to_string())); + seen = true; + } + } + + for (base_key, rename_key) in renames { + let endpoint = to_rename.remove(&rename_key).unwrap(); + to_rename.insert(base_key.to_string(), endpoint); + } +} + +fn ensure_timdorr_matches_fleet(merged: &HashMap) { // Let's do a check to see if the fleet api matches the timdorr api. // If it doesn't, let's panic! let mut perfect = true; - for (k, v) in &merged { + for (k, v) in merged { if let Some(fleet) = &v.fleet { if let Some(timdorr) = &v.timdorr { if fleet.uri != timdorr.uri { @@ -80,26 +141,26 @@ async fn main() { } } } - if !perfect { panic!("Fleet and Timdorr don't match. See errors above."); } - - // filter_interesting_endpoints(&mut merged); - todo!(); - - dbg!(&merged); } /// Remove endpoints that we're not interested in (yet) in place. -// pub fn filter_interesting_endpoints(mut endpoints: &mut HashMap) { -// endpoints.retain(|_, e| { -// !e. starts_with("/api/1/directives") -// && !e.starts_with("/api/1/subscriptions") -// && !e.starts_with("/api/1/dx/") -// && !e.starts_with("/bff/v2/mobile-app") -// }); -// } +/// +/// Base it on timdorr's list. +pub fn remove_unwanted_endpoints(mut endpoints: &mut HashMap) { + endpoints.retain(|_, e| { + let Some(timdorr) = &e.timdorr else { + return true; + }; + let uri = &timdorr.uri; + !uri.starts_with("/api/1/directives") + && !uri.starts_with("/api/1/subscriptions") + && !uri.starts_with("/api/1/dx/") + && !uri.starts_with("/bff/v2/mobile-app") + }); +} #[derive(Debug)] pub struct Endpoint { diff --git a/tesla_api_coverage/src/teslatte.rs b/tesla_api_coverage/src/teslatte.rs index 1d6ff64..e1cab82 100644 --- a/tesla_api_coverage/src/teslatte.rs +++ b/tesla_api_coverage/src/teslatte.rs @@ -1,12 +1,14 @@ //! Parse the whole teslatte project and find any get*! post*! macros. use crate::nom_help::{ignore_whitespace, quoted_string, short_trace}; +use crate::Restful; use heck::ToKebabCase; use nom::branch::alt; use nom::bytes::complete::{tag, take_while1}; use nom::combinator::opt; use nom::IResult; use reqwest::Method; +use std::collections::HashMap; use std::fs::read_to_string; use std::path::{Path, PathBuf}; use tracing::{debug, trace}; @@ -20,7 +22,17 @@ pub struct TeslatteEndpoint { // pub post_struct: Option, } -pub fn parse(path: &Path) -> anyhow::Result> { +impl Restful for TeslatteEndpoint { + fn method(&self) -> &Method { + &self.method + } + + fn uri(&self) -> &str { + &self.uri + } +} + +pub fn parse(path: &Path) -> anyhow::Result> { let mut path = PathBuf::from(path); path.push("src"); path.push("**"); @@ -40,8 +52,13 @@ pub fn parse(path: &Path) -> anyhow::Result> { // Change the name to be kebab-case. for endpoint in &mut endpoints { endpoint.name = endpoint.name.to_kebab_case(); + // Insert "/api/1" at the beginning of the URI. + endpoint.uri = format!("/api/1{}", endpoint.uri); } + let endpoints: HashMap = + endpoints.into_iter().map(|e| (e.name.clone(), e)).collect(); + Ok(endpoints) } diff --git a/tesla_api_coverage/src/timdorr.rs b/tesla_api_coverage/src/timdorr.rs index 697f1d4..5aff8c8 100644 --- a/tesla_api_coverage/src/timdorr.rs +++ b/tesla_api_coverage/src/timdorr.rs @@ -1,4 +1,6 @@ +use crate::Restful; use heck::ToKebabCase; +use reqwest::Method; use serde::Deserialize; use std::collections::HashMap; @@ -11,6 +13,22 @@ pub struct TimdorrEndpoint { pub auth: bool, } +impl Restful for TimdorrEndpoint { + fn method(&self) -> &Method { + match self.method.as_str() { + "GET" => &Method::GET, + "POST" => &Method::POST, + "PUT" => &Method::PUT, + "DELETE" => &Method::DELETE, + _ => panic!("Unknown method {}", self.method), + } + } + + fn uri(&self) -> &str { + &self.uri + } +} + pub fn parse(json_str: &str) -> HashMap { let map: HashMap = serde_json::from_str(json_str).unwrap();