1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use failure::Error;
#[allow(unused_imports)]
use log::{debug, error, info, trace, warn};
use rocket::request::{FromQuery, Query};
use df_rocket_okapi::{
gen::OpenApiGenerator, request::get_nested_query_parameters, request::OpenApiFromQuery,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::Debug;
/// A Query Guard for pagination of lists of items.
#[derive(Serialize, Deserialize, Clone, Debug, Default, JsonSchema)]
pub struct ApiPagination {
/// Number of the page you want to request. (starting from page 0)
pub page: Option<u32>,
/// Number of maximum items per page. (at least 1)
pub per_page: Option<u32>,
/// Tag to identify the version of the page requested.
/// [More info here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag).
pub etag: Option<String>,
/// Force the page to be ordered. "asc" = A-Z, "desc" = Z-A.
pub order: Option<OrderTypes>,
/// What field should the result be ordered by. Example: "id" or "name".
/// If the field does not exist it will order by the default field. (usually "id")
pub order_by: Option<String>,
/// Allows filtering over a property.
/// The property has to be of type `String` or `i32`, so it will not work for all properties.
/// Example: "type" or "year".
pub filter_by: Option<String>,
/// The values that is used as the filter. Example: "hf_died" or "1000".
/// If value is wrong type a `400 Bad Request` will be returned.
pub filter_value: Option<String>,
/// Don't load nested items. This will speed up page loads but will not fill in all the data.
/// Fields that can be used for ordering are usually the only field that are returned.
/// - `Some(false)` If value is not set, or set to "false" or "0" (same result default)
/// - `Some(true)` If value is set to ""(empty), "true" or "1"
/// - `None` If any other value this will add all the data to the response (default)
pub minimal_data: Option<bool>,
}
/// Possible ways of sorting data
#[derive(Serialize, Deserialize, Clone, Debug, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum OrderTypes {
Asc,
Desc,
}
/// From DB order to Api order.
impl From<df_st_db::OrderTypes> for OrderTypes {
fn from(order: df_st_db::OrderTypes) -> Self {
match order {
df_st_db::OrderTypes::Asc => OrderTypes::Asc,
df_st_db::OrderTypes::Desc => OrderTypes::Desc,
}
}
}
/// From Api order to DB order.
impl From<OrderTypes> for df_st_db::OrderTypes {
fn from(order: OrderTypes) -> Self {
match order {
OrderTypes::Asc => df_st_db::OrderTypes::Asc,
OrderTypes::Desc => df_st_db::OrderTypes::Desc,
}
}
}
impl fmt::Display for OrderTypes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
OrderTypes::Asc => write!(f, "asc"),
OrderTypes::Desc => write!(f, "desc"),
}
}
}
/// Parse incoming query data and store in `ApiPagination`
impl<'q> FromQuery<'q> for ApiPagination {
type Error = Error; //TODO Custom error
fn from_query(query: Query<'q>) -> Result<Self, Self::Error> {
let mut api_page_request = ApiPagination::default();
for key in query {
match key.key.url_decode()?.as_ref() {
"page" => api_page_request.page = Some(key.value.parse::<u32>()?),
"per_page" => api_page_request.per_page = Some(key.value.parse::<u32>()?),
"etag" => api_page_request.etag = Some(key.value.url_decode_lossy()),
"order" => {
api_page_request.order = Some(match key.value.url_decode_lossy().as_ref() {
"desc" => OrderTypes::Desc,
_ => OrderTypes::Asc,
})
}
"order_by" => api_page_request.order_by = Some(key.value.url_decode_lossy()),
"filter_by" => api_page_request.filter_by = Some(key.value.url_decode_lossy()),
"filter_value" => {
api_page_request.filter_value = Some(key.value.url_decode_lossy())
}
"minimal_data" => {
api_page_request.minimal_data = match key.value.url_decode_lossy().as_ref() {
"true" | "1" | "" => Some(true),
"false" | "0" => Some(false),
x => {
error!(
"Field `minimal_data` uses unknown value `{}`. \
Please use `true`, `1`, `false` or `0`.",
x
);
None
}
}
}
_ => {}
}
}
Ok(api_page_request)
}
}
/// Add support for OpenApi generator
impl<'r> OpenApiFromQuery<'r> for ApiPagination {
fn query_multi_parameter(
gen: &mut OpenApiGenerator,
name: String,
required: bool,
) -> df_rocket_okapi::Result<Vec<okapi::openapi3::Parameter>> {
Ok(get_nested_query_parameters::<ApiPagination>(
gen, name, required,
))
}
}