mirror of
https://github.com/n08i40k/schedule-parser-rusted.git
synced 2025-12-06 09:47:50 +03:00
feat(downloader): implement etag-based difference check for schedule
This commit is contained in:
@@ -51,9 +51,9 @@ pub mod error {
|
|||||||
/// Errors that may occur during the creation of a schedule snapshot.
|
/// Errors that may occur during the creation of a schedule snapshot.
|
||||||
#[derive(Debug, Display, Error)]
|
#[derive(Debug, Display, Error)]
|
||||||
pub enum SnapshotCreationError {
|
pub enum SnapshotCreationError {
|
||||||
/// The URL is the same as the one already being used (no update needed).
|
/// The ETag is the same (no update needed).
|
||||||
#[display("The URL is the same as the one already being used.")]
|
#[display("The ETag is the same.")]
|
||||||
SameUrl,
|
Same,
|
||||||
|
|
||||||
/// The URL query for the XLS file failed to execute, either due to network issues or invalid API parameters.
|
/// The URL query for the XLS file failed to execute, either due to network issues or invalid API parameters.
|
||||||
#[display("Failed to fetch URL: {_0}")]
|
#[display("Failed to fetch URL: {_0}")]
|
||||||
@@ -86,10 +86,6 @@ impl Updater {
|
|||||||
downloader: &mut XlsDownloader,
|
downloader: &mut XlsDownloader,
|
||||||
url: String,
|
url: String,
|
||||||
) -> Result<ScheduleSnapshot, SnapshotCreationError> {
|
) -> Result<ScheduleSnapshot, SnapshotCreationError> {
|
||||||
if downloader.url.as_ref().is_some_and(|_url| _url.eq(&url)) {
|
|
||||||
return Err(SnapshotCreationError::SameUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
let head_result = downloader.set_url(&url).await.map_err(|error| {
|
let head_result = downloader.set_url(&url).await.map_err(|error| {
|
||||||
if let FetchError::Unknown(error) = &error {
|
if let FetchError::Unknown(error) = &error {
|
||||||
sentry::capture_error(&error);
|
sentry::capture_error(&error);
|
||||||
@@ -98,6 +94,10 @@ impl Updater {
|
|||||||
SnapshotCreationError::FetchFailed(error)
|
SnapshotCreationError::FetchFailed(error)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
if downloader.etag == Some(head_result.etag) {
|
||||||
|
return Err(SnapshotCreationError::Same);
|
||||||
|
}
|
||||||
|
|
||||||
let xls_data = downloader
|
let xls_data = downloader
|
||||||
.fetch(false)
|
.fetch(false)
|
||||||
.await
|
.await
|
||||||
@@ -249,7 +249,7 @@ impl Updater {
|
|||||||
|
|
||||||
let snapshot = match Self::new_snapshot(&mut self.downloader, url).await {
|
let snapshot = match Self::new_snapshot(&mut self.downloader, url).await {
|
||||||
Ok(snapshot) => snapshot,
|
Ok(snapshot) => snapshot,
|
||||||
Err(SnapshotCreationError::SameUrl) => {
|
Err(SnapshotCreationError::Same) => {
|
||||||
let mut clone = current_snapshot.clone();
|
let mut clone = current_snapshot.clone();
|
||||||
clone.update();
|
clone.update();
|
||||||
|
|
||||||
|
|||||||
@@ -66,25 +66,30 @@ pub struct FetchOk {
|
|||||||
/// Date data received.
|
/// Date data received.
|
||||||
pub requested_at: DateTime<Utc>,
|
pub requested_at: DateTime<Utc>,
|
||||||
|
|
||||||
|
/// Etag.
|
||||||
|
pub etag: String,
|
||||||
|
|
||||||
/// File data.
|
/// File data.
|
||||||
pub data: Option<Vec<u8>>,
|
pub data: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FetchOk {
|
impl FetchOk {
|
||||||
/// Result without file content.
|
/// Result without file content.
|
||||||
pub fn head(uploaded_at: DateTime<Utc>) -> Self {
|
pub fn head(uploaded_at: DateTime<Utc>, etag: String) -> Self {
|
||||||
FetchOk {
|
FetchOk {
|
||||||
uploaded_at,
|
uploaded_at,
|
||||||
requested_at: Utc::now(),
|
requested_at: Utc::now(),
|
||||||
|
etag,
|
||||||
data: None,
|
data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Full result.
|
/// Full result.
|
||||||
pub fn get(uploaded_at: DateTime<Utc>, data: Vec<u8>) -> Self {
|
pub fn get(uploaded_at: DateTime<Utc>, etag: String, data: Vec<u8>) -> Self {
|
||||||
FetchOk {
|
FetchOk {
|
||||||
uploaded_at,
|
uploaded_at,
|
||||||
requested_at: Utc::now(),
|
requested_at: Utc::now(),
|
||||||
|
etag,
|
||||||
data: Some(data),
|
data: Some(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,11 +99,15 @@ pub type FetchResult = Result<FetchOk, FetchError>;
|
|||||||
|
|
||||||
pub struct XlsDownloader {
|
pub struct XlsDownloader {
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
|
pub etag: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XlsDownloader {
|
impl XlsDownloader {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
XlsDownloader { url: None }
|
XlsDownloader {
|
||||||
|
url: None,
|
||||||
|
etag: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_specified(url: &str, head: bool) -> FetchResult {
|
async fn fetch_specified(url: &str, head: bool) -> FetchResult {
|
||||||
@@ -124,9 +133,12 @@ impl XlsDownloader {
|
|||||||
.get("Content-Type")
|
.get("Content-Type")
|
||||||
.ok_or(FetchError::bad_headers("Content-Type"))?;
|
.ok_or(FetchError::bad_headers("Content-Type"))?;
|
||||||
|
|
||||||
if !headers.contains_key("etag") {
|
let etag = headers
|
||||||
return Err(FetchError::bad_headers("etag"));
|
.get("etag")
|
||||||
}
|
.ok_or(FetchError::bad_headers("etag"))?
|
||||||
|
.to_str()
|
||||||
|
.or(Err(FetchError::bad_headers("etag")))?
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let last_modified = headers
|
let last_modified = headers
|
||||||
.get("last-modified")
|
.get("last-modified")
|
||||||
@@ -141,9 +153,13 @@ impl XlsDownloader {
|
|||||||
.with_timezone(&Utc);
|
.with_timezone(&Utc);
|
||||||
|
|
||||||
Ok(if head {
|
Ok(if head {
|
||||||
FetchOk::head(last_modified)
|
FetchOk::head(last_modified, etag)
|
||||||
} else {
|
} else {
|
||||||
FetchOk::get(last_modified, response.bytes().await.unwrap().to_vec())
|
FetchOk::get(
|
||||||
|
last_modified,
|
||||||
|
etag,
|
||||||
|
response.bytes().await.unwrap().to_vec(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user