Skip to content

Commit

Permalink
feat: added the seasons handler
Browse files Browse the repository at this point in the history
  • Loading branch information
thibault-cne committed Feb 4, 2024
1 parent 494be7a commit f648fb3
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 13 deletions.
47 changes: 47 additions & 0 deletions api/src/seasons.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use diesel::Connection;
use rocket::serde::json::Json;
use rocket::{get, routes, Route, State};

use application;
use infrastructure::ConnectionPool;
use shared::prelude::*;

#[get("/<series>/seasons?<param..>")]
pub fn seasons(
db: &State<ConnectionPool>,
series: Series,
param: SeasonParameter,
) -> Result<Json<SeasonsResponse>> {
let (seasons, pagination) = season_inner_handler(db, series, param)?;

let response = SeasonsResponse {
pagination,
series,
seasons,
};

Ok(Json(response))
}

fn season_inner_handler(
db: &State<ConnectionPool>,
series: Series,
param: SeasonParameter,
) -> Result<(Vec<Season>, Pagination)> {
let filter: SeasonFilter = param.into();
filter.validate()?;

let pool = &mut db.from_series(series).get()?;
let res = pool.transaction(|conn| application::builders::SeasonBuilder::new(filter).load(conn));

Ok(res.map(|(seasons, pagination)| {
(
seasons.into_iter().map(Season::from).collect::<Vec<_>>(),
pagination,
)
})?)
}

pub fn handlers() -> Vec<Route> {
routes![seasons]
}
1 change: 1 addition & 0 deletions application/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod builders {
pub use super::constructors::ConstructorBuilder;
pub use super::driver_standing::DriverStandingBuilder;
pub use super::drivers::DriverBuilder;
pub use super::seasons::SeasonBuilder;
}

pub mod filters {
Expand Down
1 change: 1 addition & 0 deletions application/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ joinable!(results -> drivers (driver_id));
joinable!(results -> constructors (constructor_id));
joinable!(results -> races (race_id));
joinable!(races -> circuits (circuit_id));
joinable!(races -> seasons (year));
joinable!(driverStandings -> drivers (driver_id));
joinable!(driverStandings -> races (race_id));
joinable!(constructorStandings -> constructors (constructor_id));
Expand Down
135 changes: 123 additions & 12 deletions application/src/seasons.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,133 @@
use diesel::{
helper_types::{AsSelect, Eq, Filter, Select},
prelude::*,
};
use diesel::prelude::*;

use shared::parameters::Year;
use shared::filters::SeasonFilter;

use shared::models::Pagination;
use types::*;

use crate::models::Season;
use crate::prelude::*;

type All = Select<seasons::table, AsSelect<Season, super::Backend>>;
type ByYear = Filter<All, Eq<seasons::year, i32>>;
pub struct SeasonBuilder(SeasonFilter);

impl SeasonBuilder {
pub fn new(filter: SeasonFilter) -> Self {
Self(filter)
}

pub fn load(
self,
conn: &mut MysqlConnection,
) -> Result<(Vec<Season>, Pagination), diesel::result::Error> {
Self::filter(self.0).load_and_count_pages(conn)
}

fn boxed() -> BoxedQuery {
seasons::table
.inner_join(races::table)
.inner_join(results::table.on(races::race_id.eq(results::race_id)))
.inner_join(drivers::table.on(results::driver_id.eq(drivers::driver_id)))
.inner_join(
constructors::table.on(results::constructor_id.eq(constructors::constructor_id)),
)
.inner_join(circuits::table.on(races::circuit_id.eq(circuits::circuit_id)))
.select(Season::as_select())
.distinct()
.into_boxed()
}

fn filter(filter: SeasonFilter) -> Paginated<BoxedQuery> {
let limit = filter.limit.unwrap_or_default().0 as i64;
let page = filter.page.unwrap_or_default().0 as i64;

let conditions = fields_to_filter!(
filter,
driver_ref => (DriverRef, StringFilter::Equal),
constructor_ref => (ConstructorRef, StringFilter::Equal),
circuit_ref => (CircuitRef, StringFilter::Equal),
grid => (Grid, NumberFilter::Equal)
);

impl Season {
fn all() -> All {
seasons::table.select(Season::as_select())
let filter = match create_filter!(conditions, AndOr::And) {
Some(boxed_condition) => boxed_condition,
None => return Self::boxed().paginate(page).per_page(limit),
};

Self::boxed().filter(filter).paginate(page).per_page(limit)
}
}

enum Condition {
DriverRef(StringFilter),
ConstructorRef(StringFilter),
CircuitRef(StringFilter),
Grid(NumberFilter<i32>),
}

impl Condition {
fn into_boxed_condition(self) -> Option<BoxedCondition> {
use Condition::*;

pub fn by_year(year: Year) -> ByYear {
Self::all().filter(seasons::year.eq(year.0))
Some(match self {
ConstructorRef(f) => string_filter!(f, constructors::constructor_ref),
CircuitRef(f) => string_filter!(f, circuits::circuit_ref),
DriverRef(f) => string_filter!(f, drivers::driver_ref),
Grid(f) => number_filter!(f, results::grid),
})
}
}

mod types {
use super::Season;
use crate::prelude::*;
use diesel::{
helper_types::{
AsSelect, Eq, InnerJoin, InnerJoinOn, InnerJoinQuerySource, IntoBoxed, Select,
},
prelude::*,
sql_types::{Bool, Nullable},
};

pub type BoxedConditionSource = InnerJoinQuerySource<
InnerJoinQuerySource<
InnerJoinQuerySource<
InnerJoinQuerySource<
InnerJoinQuerySource<seasons::table, races::table>,
results::table,
Eq<races::race_id, results::race_id>,
>,
drivers::table,
Eq<results::driver_id, drivers::driver_id>,
>,
constructors::table,
Eq<results::constructor_id, constructors::constructor_id>,
>,
circuits::table,
Eq<races::circuit_id, circuits::circuit_id>,
>;
pub type BoxedCondition =
Box<dyn BoxableExpression<BoxedConditionSource, crate::Backend, SqlType = Nullable<Bool>>>;

pub type BoxedQuerySource = InnerJoinOn<
InnerJoinOn<
InnerJoinOn<
InnerJoinOn<
InnerJoin<seasons::table, races::table>,
results::table,
Eq<races::race_id, results::race_id>,
>,
drivers::table,
Eq<results::driver_id, drivers::driver_id>,
>,
constructors::table,
Eq<results::constructor_id, constructors::constructor_id>,
>,
circuits::table,
Eq<races::circuit_id, circuits::circuit_id>,
>;
pub type BoxedQuery = IntoBoxed<
'static,
Select<BoxedQuerySource, AsSelect<Season, crate::Backend>>,
crate::Backend,
>;
}
1 change: 1 addition & 0 deletions shared/src/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ pub struct SeasonFilter {
pub circuit_ref: Option<Circuit>,
pub constructor_ref: Option<ConstructorRef>,
pub driver_ref: Option<DriverRef>,
pub grid: Option<Grid>,
}
3 changes: 2 additions & 1 deletion shared/src/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ macros::struct_parameters!(
page: Page,
driver_ref: DriverRef,
constructor_ref: ConstructorRef,
circuit_ref: Circuit
circuit_ref: Circuit,
grid: Grid
} => crate::filters::SeasonFilter;
);

Expand Down
9 changes: 9 additions & 0 deletions shared/src/responses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,12 @@ pub struct StandingsResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub round: Option<i32>,
}

#[derive(Debug, Serialize)]
pub struct SeasonsResponse {
pub seasons: Vec<Season>,

#[serde(flatten)]
pub pagination: Pagination,
pub series: Series,
}

0 comments on commit f648fb3

Please sign in to comment.