Skip to content

Commit

Permalink
Merge pull request #382 from KisaragiEffective/feat/listing-modificat…
Browse files Browse the repository at this point in the history
…ion-dates
  • Loading branch information
KisaragiEffective committed May 7, 2024
2 parents 1a95d10 + c8fa66b commit 2c163c2
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 37 deletions.
11 changes: 9 additions & 2 deletions packages/toy-blog-endpoint-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,20 @@ pub enum Visibility {
}

#[derive(Serialize, Clone, Eq, PartialEq, Debug)]
pub struct ArticleIdSet(pub HashSet<ArticleId>);
pub struct ArticleListingResponseRepresentation(pub Vec<ArticleListResponseEntry>);

pub struct ArticleIdSetMetadata {
pub struct ArticleListingResponseMetadata {
pub oldest_created_at: Option<DateTime<Local>>,
pub newest_updated_at: Option<DateTime<Local>>,
}

#[derive(Serialize, Clone, Eq, PartialEq, Debug)]
pub struct ArticleListResponseEntry {
pub id: ArticleId,
pub created_at: DateTime<Local>,
pub updated_at: DateTime<Local>,
}

#[derive(Deserialize, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
pub struct AnnoDominiYear(NonZeroU32);

Expand Down
46 changes: 25 additions & 21 deletions packages/toy-blog/src/service/rest/api/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use actix_web::{get, Responder};
use actix_web::web::Path;
use chrono::Datelike;

use toy_blog_endpoint_model::{AnnoDominiYear, Article, ArticleId, ArticleIdSet, ArticleIdSetMetadata, OneOriginTwoDigitsMonth, OwnedMetadata, Visibility};
use toy_blog_endpoint_model::{AnnoDominiYear, Article, ArticleId, ArticleListingResponseRepresentation, ArticleListingResponseMetadata, ArticleListResponseEntry, OneOriginTwoDigitsMonth, OwnedMetadata, Visibility};

use crate::service::persistence::ArticleRepository;
use crate::service::rest::exposed_representation_format::{ArticleIdCollectionResponseRepr, EndpointRepresentationCompiler, MaybeNotModified, ReportLastModofied};
Expand Down Expand Up @@ -32,8 +32,12 @@ fn compute_and_filter_out(
ret_304 = false;
}

let id = ArticleIdSet(x.iter().filter(only_public).filter(additional_filter.clone())
.map(|x| &x.0).cloned().collect());
let entries = ArticleListingResponseRepresentation(x.iter().filter(only_public).filter(additional_filter.clone())
.map(|(id, a)| ArticleListResponseEntry {
id: id.clone(),
created_at: a.created_at,
updated_at: a.updated_at,
}).collect());
let old_cre = x.iter().filter(only_public).filter(additional_filter.clone())
.min_by_key(|x| x.1.created_at).map(|x| x.1.created_at);
let new_upd = x.iter().filter(only_public).filter(additional_filter)
Expand All @@ -43,11 +47,11 @@ fn compute_and_filter_out(
MaybeNotModified {
inner: ReportLastModofied {
inner: OwnedMetadata {
metadata: ArticleIdSetMetadata {
metadata: ArticleListingResponseMetadata {
oldest_created_at: old_cre,
newest_updated_at: new_upd,
},
data: id
data: entries
},
latest_updated: latest_updated.map(|x| x.try_into().unwrap())
},
Expand Down Expand Up @@ -124,7 +128,7 @@ mod tests {
use crate::service::rest::api::list::{article_id_list0, article_id_list_by_year0, article_id_list_by_year_and_month0};

#[test]
fn do_not_leak() {
fn do_not_include_non_public_article() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
Expand All @@ -134,24 +138,24 @@ mod tests {
ArticleRepository::init(m.path());
let a = ArticleRepository::new(m.path()).await;
{
let aa = ArticleId::new("123".to_string());
let aa = ArticleId::new("12345".to_string());
a.create_entry(&aa, "12345".to_string(), Visibility::Private).unwrap();
let ac = article_id_list0(&a, None);
let m = ac.0.inner.inner.data.0.get(&aa);
let m = ac.0.inner.inner.data.0.iter().find(|x| x.id == aa);
assert!(m.is_none());
}
{
let aa = ArticleId::new("1234".to_string());
let aa = ArticleId::new("123456".to_string());
a.create_entry(&aa, "123456".to_string(), Visibility::Restricted).unwrap();
let ac = article_id_list0(&a, None);
let m = ac.0.inner.inner.data.0.get(&aa);
let m = ac.0.inner.inner.data.0.iter().find(|x| x.id == aa);
assert!(m.is_none());
}
});
}

#[test]
fn do_not_leak_by_year() {
fn do_not_include_non_public_article_by_year_filter() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
Expand All @@ -161,24 +165,24 @@ mod tests {
ArticleRepository::init(m.path());
let a = ArticleRepository::new(m.path()).await;
{
let aa = ArticleId::new("123".to_string());
let aa = ArticleId::new("12345".to_string());
a.create_entry(&aa, "12345".to_string(), Visibility::Private).unwrap();
let ac = article_id_list_by_year0(&a, AnnoDominiYear::try_from(Local::now().year() as u32).unwrap(), None);
let m = ac.0.inner.inner.data.0.get(&aa);
let m = ac.0.inner.inner.data.0.iter().find(|x| x.id == aa);
assert!(m.is_none());
}
{
let aa = ArticleId::new("1234".to_string());
let aa = ArticleId::new("123456".to_string());
a.create_entry(&aa, "123456".to_string(), Visibility::Restricted).unwrap();
let ac = article_id_list_by_year0(&a, AnnoDominiYear::try_from(Local::now().year() as u32).unwrap(), None);
let m = ac.0.inner.inner.data.0.get(&aa);
let m = ac.0.inner.inner.data.0.iter().find(|x| x.id == aa);
assert!(m.is_none());
}
});
}

#[test]
fn do_not_leak_by_year_and_month() {
fn do_not_include_non_public_article_by_year_and_month_filter() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
Expand All @@ -188,7 +192,7 @@ mod tests {
ArticleRepository::init(m.path());
let a = ArticleRepository::new(m.path()).await;
{
let aa = ArticleId::new("123".to_string());
let aa = ArticleId::new("12345".to_string());
a.create_entry(&aa, "12345".to_string(), Visibility::Private).unwrap();
let now = Local::now();
let ac = article_id_list_by_year_and_month0(
Expand All @@ -197,11 +201,11 @@ mod tests {
OneOriginTwoDigitsMonth::try_from(now.month() as u8).unwrap()
), None
);
let a = ac.0.inner.inner.data.0.get(&aa);
let a = ac.0.inner.inner.data.0.iter().find(|x| x.id == aa);
assert!(a.is_none());
}
{
let aa = ArticleId::new("1235".to_string());
let aa = ArticleId::new("123456".to_string());
a.create_entry(&aa, "123456".to_string(), Visibility::Restricted).unwrap();
let now = Local::now();
let ac = article_id_list_by_year_and_month0(
Expand All @@ -210,9 +214,9 @@ mod tests {
OneOriginTwoDigitsMonth::try_from(now.month() as u8).unwrap()
), None
);
let a = ac.0.inner.inner.data.0.get(&aa);
let a = ac.0.inner.inner.data.0.iter().find(|x| x.id == aa);
assert!(a.is_none());
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use actix_web::HttpResponse;
use chrono::{FixedOffset, Utc};
use serde::{Serialize, Serializer};

use toy_blog_endpoint_model::{ArticleCreatedNotice, ArticleIdSet, ArticleIdSetMetadata, ChangeArticleIdError, ChangeArticleIdRequestResult, CreateArticleError, CreateArticleResult, DeleteArticleError, DeleteArticleResult, GetArticleError, GetArticleResult, ListArticleResponse, ListArticleResult, OwnedMetadata, UpdateArticleError, UpdateArticleResult};
use toy_blog_endpoint_model::{ArticleCreatedNotice, ArticleListingResponseRepresentation, ArticleListingResponseMetadata, ChangeArticleIdError, ChangeArticleIdRequestResult, CreateArticleError, CreateArticleResult, DeleteArticleError, DeleteArticleResult, GetArticleError, GetArticleResult, ListArticleResponse, ListArticleResult, OwnedMetadata, UpdateArticleError, UpdateArticleResult};

use crate::service::rest::header::HttpDate;
use crate::service::rest::inner_no_leak::{ComposeInternalError, UnhandledError};
Expand Down Expand Up @@ -515,7 +515,9 @@ impl<Repr: Serialize> Serialize for ReportLastModofied<Repr> {
}


pub(super) struct ArticleIdCollectionResponseRepr(pub(super) MaybeNotModified<ReportLastModofied<OwnedMetadata<ArticleIdSetMetadata, ArticleIdSet>>>);
pub(super) struct ArticleIdCollectionResponseRepr(
pub(super) MaybeNotModified<ReportLastModofied<OwnedMetadata<ArticleListingResponseMetadata, ArticleListingResponseRepresentation>>>
);

impl HttpStatusCode for ArticleIdCollectionResponseRepr {
fn call_status_code(&self) -> StatusCode {
Expand Down
18 changes: 6 additions & 12 deletions packages/toy-blog/src/service/rest/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,14 @@ impl FromRequest for LastModified {
type Future = Ready<Result<Self, Self::Error>>;

fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
let w = req.headers().get("Last-Modified")
.ok_or(HttpDateExtractionError::NotFound);
let w = match w {
Ok(t) => t,
Err(e) => return std::future::ready(Err(e)),
let inner = || {
let w = req.headers().get("Last-Modified")
.ok_or(HttpDateExtractionError::NotFound)?;
let r = Self::try_from(w)?;
Ok(r)
};

let r = Self::try_from(w);
let r = match r {
Ok(t) => t,
Err(e) => return std::future::ready(Err(HttpDateExtractionError::from(e))),
};

std::future::ready(Ok(r))
std::future::ready(inner())
}
}

Expand Down

0 comments on commit 2c163c2

Please sign in to comment.