webcheck save
This commit is contained in:
parent
2a714d029a
commit
17d43f0fd0
2 changed files with 127 additions and 19 deletions
|
@ -21,9 +21,11 @@ use super::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod driver;
|
mod driver;
|
||||||
|
mod save;
|
||||||
mod utd_app;
|
mod utd_app;
|
||||||
|
|
||||||
pub struct WebcheckApp {
|
pub struct WebcheckApp {
|
||||||
|
reqwest_client: reqwest::Client,
|
||||||
state: Mutex<WebcheckAppState>,
|
state: Mutex<WebcheckAppState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +33,7 @@ struct WebcheckAppState {
|
||||||
config: Option<&'static Config>,
|
config: Option<&'static Config>,
|
||||||
global_app_state: Option<Arc<Mutex<AppState>>>,
|
global_app_state: Option<Arc<Mutex<AppState>>>,
|
||||||
last_response: HashMap<String, LastResponse>,
|
last_response: HashMap<String, LastResponse>,
|
||||||
checkers: HashMap<String, Box<dyn WebDriverChecker + Send + Sync>>,
|
checkers: HashMap<String, Checker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[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]
|
#[async_trait]
|
||||||
pub trait WebDriverChecker {
|
pub trait WebDriverChecker {
|
||||||
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()>;
|
fn init(&mut self, config: &HashMap<String, String>) -> anyhow::Result<()>;
|
||||||
|
@ -64,6 +78,7 @@ pub trait WebDriverChecker {
|
||||||
impl WebcheckApp {
|
impl WebcheckApp {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
reqwest_client: reqwest::Client::new(),
|
||||||
state: Mutex::new(WebcheckAppState {
|
state: Mutex::new(WebcheckAppState {
|
||||||
config: None,
|
config: None,
|
||||||
global_app_state: None,
|
global_app_state: None,
|
||||||
|
@ -76,25 +91,30 @@ impl WebcheckApp {
|
||||||
pub async fn run_single_check(self: &Self, key: &str) -> anyhow::Result<()> {
|
pub async fn run_single_check(self: &Self, key: &str) -> anyhow::Result<()> {
|
||||||
let mut state = self.state.lock().await;
|
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();
|
let mut caps = DesiredCapabilities::chrome();
|
||||||
driver.spawn(&["--enable-chrome-logs"])?;
|
caps.set_headless().unwrap();
|
||||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
caps.set_disable_gpu().unwrap();
|
||||||
|
|
||||||
let mut caps = DesiredCapabilities::chrome();
|
let driver = driver.connect(caps).await?;
|
||||||
caps.set_headless().unwrap();
|
let response = match checker.check(&driver).await {
|
||||||
caps.set_disable_gpu().unwrap();
|
Ok(response) => response,
|
||||||
|
Err(e) => {
|
||||||
let driver = driver.connect(caps).await?;
|
driver.quit().await?;
|
||||||
let response = match checker.check(&driver).await {
|
return Err(e);
|
||||||
Ok(response) => response,
|
}
|
||||||
Err(e) => {
|
};
|
||||||
driver.quit().await?;
|
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 {
|
let new_response = LastResponse {
|
||||||
response: response.clone(),
|
response: response.clone(),
|
||||||
|
@ -141,7 +161,10 @@ impl WebcheckApp {
|
||||||
let interval = {
|
let interval = {
|
||||||
let state = self_clone.state.lock().await;
|
let state = self_clone.state.lock().await;
|
||||||
let checker = state.checkers.get(key.as_str()).unwrap();
|
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));
|
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);
|
state.global_app_state = Some(app_state);
|
||||||
|
|
||||||
let Some(ref config) = config.webcheck else {
|
let Some(ref config) = config.webcheck else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
config.keys().for_each(|key| match key.as_str() {
|
config.keys().for_each(|key| match key.as_str() {
|
||||||
"utd_app" => {
|
"utd_app" => {
|
||||||
|
@ -180,7 +203,18 @@ impl App for WebcheckApp {
|
||||||
checker
|
checker
|
||||||
.init(config.get(key).unwrap())
|
.init(config.get(key).unwrap())
|
||||||
.expect("Failed to initialize UTDAppChecker");
|
.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),
|
_ => panic!("Invalid key in webcheck config: {}", key),
|
||||||
});
|
});
|
||||||
|
|
74
src/apps/webcheck/save.rs
Normal file
74
src/apps/webcheck/save.rs
Normal 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,))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue