Admin API: parameter to include total number of items
This allows removing the count calculation when not needed, or to skip the list of items entirely.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
// Generated code from schemars violates this rule
|
||||
#![allow(clippy::str_to_string)]
|
||||
|
||||
use std::num::NonZeroUsize;
|
||||
use std::{borrow::Cow, num::NonZeroUsize};
|
||||
|
||||
use aide::OperationIo;
|
||||
use axum::{
|
||||
@@ -64,6 +64,34 @@ impl std::ops::Deref for UlidPathParam {
|
||||
/// The default page size if not specified
|
||||
const DEFAULT_PAGE_SIZE: usize = 10;
|
||||
|
||||
#[derive(Deserialize, JsonSchema, Clone, Copy, Default, Debug)]
|
||||
pub enum IncludeCount {
|
||||
/// Include the total number of items (default)
|
||||
#[default]
|
||||
#[serde(rename = "true")]
|
||||
True,
|
||||
|
||||
/// Do not include the total number of items
|
||||
#[serde(rename = "false")]
|
||||
False,
|
||||
|
||||
/// Only include the total number of items, skip the items themselves
|
||||
#[serde(rename = "only")]
|
||||
Only,
|
||||
}
|
||||
|
||||
impl IncludeCount {
|
||||
pub(crate) fn add_to_base(self, base: &str) -> Cow<'_, str> {
|
||||
let separator = if base.contains('?') { '&' } else { '?' };
|
||||
match self {
|
||||
// This is the default, don't add anything
|
||||
Self::True => Cow::Borrowed(base),
|
||||
Self::False => format!("{base}{separator}count=false").into(),
|
||||
Self::Only => format!("{base}{separator}count=only").into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, JsonSchema, Clone, Copy)]
|
||||
struct PaginationParams {
|
||||
/// Retrieve the items before the given ID
|
||||
@@ -83,6 +111,10 @@ struct PaginationParams {
|
||||
/// Retrieve the last N items
|
||||
#[serde(rename = "page[last]")]
|
||||
last: Option<NonZeroUsize>,
|
||||
|
||||
/// Include the total number of items. Defaults to `true`.
|
||||
#[serde(rename = "count")]
|
||||
include_count: Option<IncludeCount>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -107,7 +139,7 @@ impl IntoResponse for PaginationRejection {
|
||||
/// An extractor for pagination parameters in the query string
|
||||
#[derive(OperationIo, Debug, Clone, Copy)]
|
||||
#[aide(input_with = "Query<PaginationParams>")]
|
||||
pub struct Pagination(pub mas_storage::Pagination);
|
||||
pub struct Pagination(pub mas_storage::Pagination, pub IncludeCount);
|
||||
|
||||
impl<S: Send + Sync> FromRequestParts<S> for Pagination {
|
||||
type Rejection = PaginationRejection;
|
||||
@@ -130,11 +162,14 @@ impl<S: Send + Sync> FromRequestParts<S> for Pagination {
|
||||
(None, Some(last)) => (PaginationDirection::Backward, last.into()),
|
||||
};
|
||||
|
||||
Ok(Self(mas_storage::Pagination {
|
||||
before: params.before,
|
||||
after: params.after,
|
||||
direction,
|
||||
count,
|
||||
}))
|
||||
Ok(Self(
|
||||
mas_storage::Pagination {
|
||||
before: params.before,
|
||||
after: params.after,
|
||||
direction,
|
||||
count,
|
||||
},
|
||||
params.include_count.unwrap_or_default(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,12 @@ struct PaginationLinks {
|
||||
self_: String,
|
||||
|
||||
/// The link to the first page of results
|
||||
first: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
first: Option<String>,
|
||||
|
||||
/// The link to the last page of results
|
||||
last: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
last: Option<String>,
|
||||
|
||||
/// The link to the next page of results
|
||||
///
|
||||
@@ -42,17 +44,26 @@ struct PaginationLinks {
|
||||
#[derive(Serialize, JsonSchema)]
|
||||
struct PaginationMeta {
|
||||
/// The total number of results
|
||||
count: usize,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
count: Option<usize>,
|
||||
}
|
||||
|
||||
impl PaginationMeta {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.count.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
/// A top-level response with a page of resources
|
||||
#[derive(Serialize, JsonSchema)]
|
||||
pub struct PaginatedResponse<T> {
|
||||
/// Response metadata
|
||||
#[serde(skip_serializing_if = "PaginationMeta::is_empty")]
|
||||
meta: PaginationMeta,
|
||||
|
||||
/// The list of resources
|
||||
data: Vec<SingleResource<T>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
data: Option<Vec<SingleResource<T>>>,
|
||||
|
||||
/// Related links
|
||||
links: PaginationLinks,
|
||||
@@ -87,16 +98,22 @@ fn url_with_pagination(base: &str, pagination: Pagination) -> String {
|
||||
}
|
||||
|
||||
impl<T: Resource> PaginatedResponse<T> {
|
||||
pub fn new(
|
||||
pub fn for_page(
|
||||
page: mas_storage::Page<T>,
|
||||
current_pagination: Pagination,
|
||||
count: usize,
|
||||
count: Option<usize>,
|
||||
base: &str,
|
||||
) -> Self {
|
||||
let links = PaginationLinks {
|
||||
self_: url_with_pagination(base, current_pagination),
|
||||
first: url_with_pagination(base, Pagination::first(current_pagination.count)),
|
||||
last: url_with_pagination(base, Pagination::last(current_pagination.count)),
|
||||
first: Some(url_with_pagination(
|
||||
base,
|
||||
Pagination::first(current_pagination.count),
|
||||
)),
|
||||
last: Some(url_with_pagination(
|
||||
base,
|
||||
Pagination::last(current_pagination.count),
|
||||
)),
|
||||
next: page.has_next_page.then(|| {
|
||||
url_with_pagination(
|
||||
base,
|
||||
@@ -121,7 +138,23 @@ impl<T: Resource> PaginatedResponse<T> {
|
||||
|
||||
Self {
|
||||
meta: PaginationMeta { count },
|
||||
data,
|
||||
data: Some(data),
|
||||
links,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_count_only(count: usize, base: &str) -> Self {
|
||||
let links = PaginationLinks {
|
||||
self_: base.to_owned(),
|
||||
first: None,
|
||||
last: None,
|
||||
next: None,
|
||||
prev: None,
|
||||
};
|
||||
|
||||
Self {
|
||||
meta: PaginationMeta { count: Some(count) },
|
||||
data: None,
|
||||
links,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{CompatSession, Resource},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -143,10 +143,10 @@ Use the `filter[status]` parameter to filter the sessions by their status and `p
|
||||
};
|
||||
|
||||
t.description("Paginated response of compatibility sessions")
|
||||
.example(PaginatedResponse::new(
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
42,
|
||||
Some(42),
|
||||
CompatSession::PATH,
|
||||
))
|
||||
})
|
||||
@@ -159,10 +159,11 @@ Use the `filter[status]` parameter to filter the sessions by their status and `p
|
||||
#[tracing::instrument(name = "handler.admin.v1.compat_sessions.list", skip_all)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<CompatSession>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = CompatSession::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let filter = CompatSessionFilter::default();
|
||||
|
||||
// Load the user from the filter
|
||||
@@ -206,15 +207,31 @@ pub async fn handler(
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let page = repo.compat_session().list(filter, pagination).await?;
|
||||
let count = repo.compat_session().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo
|
||||
.compat_session()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(CompatSession::from);
|
||||
let count = repo.compat_session().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo
|
||||
.compat_session()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(CompatSession::from);
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.compat_session().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(CompatSession::from),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -25,7 +25,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{OAuth2Session, Resource},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -198,10 +198,10 @@ Use the `filter[status]` parameter to filter the sessions by their status and `p
|
||||
};
|
||||
|
||||
t.description("Paginated response of OAuth 2.0 sessions")
|
||||
.example(PaginatedResponse::new(
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
42,
|
||||
Some(42),
|
||||
OAuth2Session::PATH,
|
||||
))
|
||||
})
|
||||
@@ -218,10 +218,11 @@ Use the `filter[status]` parameter to filter the sessions by their status and `p
|
||||
#[tracing::instrument(name = "handler.admin.v1.oauth2_sessions.list", skip_all)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<OAuth2Session>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = OAuth2Session::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let filter = OAuth2SessionFilter::default();
|
||||
|
||||
// Load the user from the filter
|
||||
@@ -300,15 +301,31 @@ pub async fn handler(
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let page = repo.oauth2_session().list(filter, pagination).await?;
|
||||
let count = repo.oauth2_session().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo
|
||||
.oauth2_session()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(OAuth2Session::from);
|
||||
let count = repo.oauth2_session().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo
|
||||
.oauth2_session()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(OAuth2Session::from);
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.oauth2_session().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(OAuth2Session::from),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{Resource, UpstreamOAuthLink},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -118,10 +118,10 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
};
|
||||
|
||||
t.description("Paginated response of upstream OAuth 2.0 links")
|
||||
.example(PaginatedResponse::new(
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
42,
|
||||
Some(42),
|
||||
UpstreamOAuthLink::PATH,
|
||||
))
|
||||
})
|
||||
@@ -135,10 +135,11 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
#[tracing::instrument(name = "handler.admin.v1.upstream_oauth_links.list", skip_all)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<UpstreamOAuthLink>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = UpstreamOAuthLink::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let filter = UpstreamOAuthLinkFilter::default();
|
||||
|
||||
// Load the user from the filter
|
||||
@@ -183,15 +184,31 @@ pub async fn handler(
|
||||
filter
|
||||
};
|
||||
|
||||
let page = repo.upstream_oauth_link().list(filter, pagination).await?;
|
||||
let count = repo.upstream_oauth_link().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo
|
||||
.upstream_oauth_link()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UpstreamOAuthLink::from);
|
||||
let count = repo.upstream_oauth_link().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo
|
||||
.upstream_oauth_link()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UpstreamOAuthLink::from);
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.upstream_oauth_link().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(UpstreamOAuthLink::from),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{Resource, UpstreamOAuthProvider},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -90,10 +90,10 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
};
|
||||
|
||||
t.description("Paginated response of upstream OAuth 2.0 providers")
|
||||
.example(PaginatedResponse::new(
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
42,
|
||||
Some(42),
|
||||
UpstreamOAuthProvider::PATH,
|
||||
))
|
||||
})
|
||||
@@ -102,10 +102,11 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
#[tracing::instrument(name = "handler.admin.v1.upstream_oauth_providers.list", skip_all)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<UpstreamOAuthProvider>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = UpstreamOAuthProvider::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let filter = UpstreamOAuthProviderFilter::new();
|
||||
|
||||
let filter = match params.enabled {
|
||||
@@ -114,18 +115,31 @@ pub async fn handler(
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let page = repo
|
||||
.upstream_oauth_provider()
|
||||
.list(filter, pagination)
|
||||
.await?;
|
||||
let count = repo.upstream_oauth_provider().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo
|
||||
.upstream_oauth_provider()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UpstreamOAuthProvider::from);
|
||||
let count = repo.upstream_oauth_provider().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo
|
||||
.upstream_oauth_provider()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UpstreamOAuthProvider::from);
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.upstream_oauth_provider().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(UpstreamOAuthProvider::from),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{Resource, UserEmail},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -105,10 +105,10 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
};
|
||||
|
||||
t.description("Paginated response of user emails")
|
||||
.example(PaginatedResponse::new(
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
42,
|
||||
Some(42),
|
||||
UserEmail::PATH,
|
||||
))
|
||||
})
|
||||
@@ -121,10 +121,11 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
#[tracing::instrument(name = "handler.admin.v1.user_emails.list", skip_all)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<UserEmail>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = UserEmail::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let filter = UserEmailFilter::default();
|
||||
|
||||
// Load the user from the filter
|
||||
@@ -150,15 +151,31 @@ pub async fn handler(
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let page = repo.user_email().list(filter, pagination).await?;
|
||||
let count = repo.user_email().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo
|
||||
.user_email()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UserEmail::from);
|
||||
let count = repo.user_email().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo
|
||||
.user_email()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UserEmail::from);
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.user_email().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(UserEmail::from),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{Resource, UserRegistrationToken},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -118,10 +118,10 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
};
|
||||
|
||||
t.description("Paginated response of registration tokens")
|
||||
.example(PaginatedResponse::new(
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
42,
|
||||
Some(42),
|
||||
UserRegistrationToken::PATH,
|
||||
))
|
||||
})
|
||||
@@ -132,10 +132,11 @@ pub async fn handler(
|
||||
CallContext {
|
||||
mut repo, clock, ..
|
||||
}: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<UserRegistrationToken>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = UserRegistrationToken::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let now = clock.now();
|
||||
let mut filter = UserRegistrationTokenFilter::new(now);
|
||||
|
||||
@@ -155,18 +156,31 @@ pub async fn handler(
|
||||
filter = filter.with_valid(valid);
|
||||
}
|
||||
|
||||
let page = repo
|
||||
.user_registration_token()
|
||||
.list(filter, pagination)
|
||||
.await?;
|
||||
let count = repo.user_registration_token().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo
|
||||
.user_registration_token()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(|token| UserRegistrationToken::new(token, now));
|
||||
let count = repo.user_registration_token().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo
|
||||
.user_registration_token()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(|token| UserRegistrationToken::new(token, now));
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.user_registration_token().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(|token| UserRegistrationToken::new(token, now)),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{Resource, UserSession},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -129,10 +129,10 @@ Use the `filter[status]` parameter to filter the sessions by their status and `p
|
||||
};
|
||||
|
||||
t.description("Paginated response of user sessions")
|
||||
.example(PaginatedResponse::new(
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
42,
|
||||
Some(42),
|
||||
UserSession::PATH,
|
||||
))
|
||||
})
|
||||
@@ -145,10 +145,11 @@ Use the `filter[status]` parameter to filter the sessions by their status and `p
|
||||
#[tracing::instrument(name = "handler.admin.v1.user_sessions.list", skip_all)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<UserSession>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = UserSession::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let filter = BrowserSessionFilter::default();
|
||||
|
||||
// Load the user from the filter
|
||||
@@ -175,15 +176,31 @@ pub async fn handler(
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let page = repo.browser_session().list(filter, pagination).await?;
|
||||
let count = repo.browser_session().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo
|
||||
.browser_session()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UserSession::from);
|
||||
let count = repo.browser_session().count(filter).await?;
|
||||
PaginatedResponse::for_page(page, pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo
|
||||
.browser_session()
|
||||
.list(filter, pagination)
|
||||
.await?
|
||||
.map(UserSession::from);
|
||||
PaginatedResponse::for_page(page, pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.browser_session().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(UserSession::from),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -21,7 +21,7 @@ use crate::{
|
||||
admin::{
|
||||
call_context::CallContext,
|
||||
model::{Resource, User},
|
||||
params::Pagination,
|
||||
params::{IncludeCount, Pagination},
|
||||
response::{ErrorResponse, PaginatedResponse},
|
||||
},
|
||||
impl_from_error_for_route,
|
||||
@@ -143,17 +143,23 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
|
||||
};
|
||||
|
||||
t.description("Paginated response of users")
|
||||
.example(PaginatedResponse::new(page, pagination, 42, User::PATH))
|
||||
.example(PaginatedResponse::for_page(
|
||||
page,
|
||||
pagination,
|
||||
Some(42),
|
||||
User::PATH,
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "handler.admin.v1.users.list", skip_all)]
|
||||
pub async fn handler(
|
||||
CallContext { mut repo, .. }: CallContext,
|
||||
Pagination(pagination): Pagination,
|
||||
Pagination(pagination, include_count): Pagination,
|
||||
params: FilterParams,
|
||||
) -> Result<Json<PaginatedResponse<User>>, RouteError> {
|
||||
let base = format!("{path}{params}", path = User::PATH);
|
||||
let base = include_count.add_to_base(&base);
|
||||
let filter = UserFilter::default();
|
||||
|
||||
let filter = match params.admin {
|
||||
@@ -180,13 +186,21 @@ pub async fn handler(
|
||||
None => filter,
|
||||
};
|
||||
|
||||
let page = repo.user().list(filter, pagination).await?;
|
||||
let count = repo.user().count(filter).await?;
|
||||
let response = match include_count {
|
||||
IncludeCount::True => {
|
||||
let page = repo.user().list(filter, pagination).await?;
|
||||
let count = repo.user().count(filter).await?;
|
||||
PaginatedResponse::for_page(page.map(User::from), pagination, Some(count), &base)
|
||||
}
|
||||
IncludeCount::False => {
|
||||
let page = repo.user().list(filter, pagination).await?;
|
||||
PaginatedResponse::for_page(page.map(User::from), pagination, None, &base)
|
||||
}
|
||||
IncludeCount::Only => {
|
||||
let count = repo.user().count(filter).await?;
|
||||
PaginatedResponse::for_count_only(count, &base)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Json(PaginatedResponse::new(
|
||||
page.map(User::from),
|
||||
pagination,
|
||||
count,
|
||||
&base,
|
||||
)))
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
@@ -107,6 +107,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[user]",
|
||||
@@ -373,6 +384,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[user]",
|
||||
@@ -893,6 +915,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[admin]",
|
||||
@@ -1752,6 +1785,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[user]",
|
||||
@@ -2097,6 +2141,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[user]",
|
||||
@@ -2335,6 +2390,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[used]",
|
||||
@@ -2882,6 +2948,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[user]",
|
||||
@@ -3279,6 +3356,17 @@
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "count",
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"schema": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
},
|
||||
"style": "form"
|
||||
},
|
||||
{
|
||||
"in": "query",
|
||||
"name": "filter[enabled]",
|
||||
@@ -3481,6 +3569,11 @@
|
||||
"format": "uint",
|
||||
"minimum": 1.0,
|
||||
"nullable": true
|
||||
},
|
||||
"count": {
|
||||
"description": "Include the total number of items. Defaults to `true`.",
|
||||
"$ref": "#/components/schemas/IncludeCount",
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3494,6 +3587,31 @@
|
||||
"type": "string",
|
||||
"pattern": "^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$"
|
||||
},
|
||||
"IncludeCount": {
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Include the total number of items (default)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"true"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Do not include the total number of items",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"false"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Only include the total number of items, skip the items themselves",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"only"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"CompatSessionFilter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -3525,7 +3643,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -3539,7 +3656,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_CompatSession"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
@@ -3549,15 +3667,13 @@
|
||||
},
|
||||
"PaginationMeta": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"count"
|
||||
],
|
||||
"properties": {
|
||||
"count": {
|
||||
"description": "The total number of results",
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
"minimum": 0.0,
|
||||
"nullable": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3678,8 +3794,6 @@
|
||||
"description": "Related links",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"first",
|
||||
"last",
|
||||
"self"
|
||||
],
|
||||
"properties": {
|
||||
@@ -3689,11 +3803,13 @@
|
||||
},
|
||||
"first": {
|
||||
"description": "The link to the first page of results",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"last": {
|
||||
"description": "The link to the last page of results",
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
},
|
||||
"next": {
|
||||
"description": "The link to the next page of results\n\nOnly present if there is a next page",
|
||||
@@ -3820,7 +3936,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -3834,7 +3949,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_OAuth2Session"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
@@ -4065,7 +4181,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -4079,7 +4194,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_User"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
@@ -4266,7 +4382,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -4280,7 +4395,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_UserEmail"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
@@ -4401,7 +4517,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -4415,7 +4530,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_UserSession"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
@@ -4538,7 +4654,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -4552,7 +4667,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_UserRegistrationToken"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
@@ -4727,7 +4843,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -4741,7 +4856,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_UpstreamOAuthLink"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
@@ -4869,7 +4985,6 @@
|
||||
"description": "A top-level response with a page of resources",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"links",
|
||||
"meta"
|
||||
],
|
||||
@@ -4883,7 +4998,8 @@
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SingleResource_for_UpstreamOAuthProvider"
|
||||
}
|
||||
},
|
||||
"nullable": true
|
||||
},
|
||||
"links": {
|
||||
"description": "Related links",
|
||||
|
||||
Reference in New Issue
Block a user