webcheck save

This commit is contained in:
ゆめ 2023-10-01 00:21:56 -05:00
parent 2a714d029a
commit 17d43f0fd0
2 changed files with 127 additions and 19 deletions

View file

@ -21,9 +21,11 @@ use super::{
};
mod driver;
mod save;
mod utd_app;
pub struct WebcheckApp {
reqwest_client: reqwest::Client,
state: Mutex<WebcheckAppState>,
}
@ -31,7 +33,7 @@ struct WebcheckAppState {
config: Option<&'static Config>,
global_app_state: Option<Arc<Mutex<AppState>>>,
last_response: HashMap<String, LastResponse>,
checkers: HashMap<String, Box<dyn WebDriverChecker + Send + Sync>>,
checkers: HashMap<String, Checker>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
@ -54,6 +56,18 @@ async fn route_get_results(
))
}
pub enum Checker {
WebDriver(Box<dyn WebDriverChecker + Send + Sync>),
Reqwest(Box<dyn ReqwestChecker + Send + Sync>),
}
#[async_trait]
pub trait ReqwestChecker {
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()>;
fn interval(&self) -> u64;
async fn check(&self, client: &reqwest::Client) -> anyhow::Result<String>;
}
#[async_trait]
pub trait WebDriverChecker {
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()>;
@ -64,6 +78,7 @@ pub trait WebDriverChecker {
impl WebcheckApp {
pub fn new() -> Self {
Self {
reqwest_client: reqwest::Client::new(),
state: Mutex::new(WebcheckAppState {
config: None,
global_app_state: None,
@ -76,25 +91,30 @@ impl WebcheckApp {
pub async fn run_single_check(self: &Self, key: &str) -> anyhow::Result<()> {
let mut state = self.state.lock().await;
let checker = state.checkers.get_mut(key).unwrap();
let response = match state.checkers.get_mut(key).unwrap() {
Checker::WebDriver(checker) => {
let mut driver = driver::chrome::ChromeDriver::new();
driver.spawn(&["--enable-chrome-logs"])?;
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
let mut driver = driver::chrome::ChromeDriver::new();
driver.spawn(&["--enable-chrome-logs"])?;
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
let mut caps = DesiredCapabilities::chrome();
caps.set_headless().unwrap();
caps.set_disable_gpu().unwrap();
let mut caps = DesiredCapabilities::chrome();
caps.set_headless().unwrap();
caps.set_disable_gpu().unwrap();
let driver = driver.connect(caps).await?;
let response = match checker.check(&driver).await {
Ok(response) => response,
Err(e) => {
let driver = driver.connect(caps).await?;
let response = match checker.check(&driver).await {
Ok(response) => response,
Err(e) => {
driver.quit().await?;
return Err(e);
}
};
driver.quit().await?;
return Err(e);
response
}
Checker::Reqwest(checker) => checker.check(&self.reqwest_client).await?,
};
driver.quit().await?;
info!("Webcheck for {} returned {}", key, response);
let new_response = LastResponse {
response: response.clone(),
@ -141,7 +161,10 @@ impl WebcheckApp {
let interval = {
let state = self_clone.state.lock().await;
let checker = state.checkers.get(key.as_str()).unwrap();
checker.interval()
match checker {
Checker::WebDriver(checker) => checker.interval(),
Checker::Reqwest(checker) => checker.interval(),
}
};
let mut ticker = tokio::time::interval(std::time::Duration::from_secs(interval));
@ -171,8 +194,8 @@ impl App for WebcheckApp {
state.global_app_state = Some(app_state);
let Some(ref config) = config.webcheck else {
return;
};
return;
};
config.keys().for_each(|key| match key.as_str() {
"utd_app" => {
@ -180,7 +203,18 @@ impl App for WebcheckApp {
checker
.init(config.get(key).unwrap())
.expect("Failed to initialize UTDAppChecker");
state.checkers.insert(key.clone(), Box::new(checker));
state
.checkers
.insert(key.clone(), Checker::WebDriver(Box::new(checker)));
}
"save" => {
let mut checker = save::SaveChecker::new();
checker
.init(config.get(key).unwrap())
.expect("Failed to initialize SaveChecker");
state
.checkers
.insert(key.clone(), Checker::Reqwest(Box::new(checker)));
}
_ => panic!("Invalid key in webcheck config: {}", key),
});

74
src/apps/webcheck/save.rs Normal file
View file

@ -0,0 +1,74 @@
use std::collections::HashMap;
use super::ReqwestChecker;
use async_trait::async_trait;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct SaveCaseCheckResp {
#[serde(rename = "caseNumber")]
case_number: String,
#[serde(rename = "createdDate")]
created_date: String,
#[serde(rename = "agencyName")]
agency_name: String,
#[serde(rename = "caseStatus")]
case_status: String,
}
pub struct SaveChecker {
config: HashMap<String, String>,
}
impl SaveChecker {
pub fn new() -> Self {
Self {
config: HashMap::new(),
}
}
}
#[async_trait]
impl ReqwestChecker for SaveChecker {
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()> {
if config.get("case_id").is_none() {
return Err(anyhow::anyhow!("case_id not set"));
}
self.config = config.clone();
Ok(())
}
fn interval(&self) -> u64 {
let default_interval = "3600".to_string();
let interval = self.config.get("interval").unwrap_or(&default_interval);
interval.parse::<u64>().unwrap()
}
async fn check(&self, client: &reqwest::Client) -> anyhow::Result<String> {
let case_id = self.config.get("case_id").unwrap();
let req = client
.request(
reqwest::Method::GET,
format!(
"https://save.uscis.gov/api/save/read/cases/check/{}",
case_id
),
)
.header("Accept", "application/json")
.header("Content-Type", "application/json")
.header(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/117.0",
)
.header(
"Referer",
format!(
"https://save.uscis.gov/save/app/client/ui/case-check/detail/{}",
case_id
),
)
.build()?;
let resp = client.execute(req).await?;
let res = resp.json::<SaveCaseCheckResp>().await?;
Ok(format!("Case {} is {}", res.case_number, res.case_status,))
}
}