1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-20 12:02:22 +03:00

Split the storage trait from the implementation

This commit is contained in:
Quentin Gliech
2023-01-18 09:53:42 +01:00
parent b33a330b5f
commit 73a921cc30
95 changed files with 6294 additions and 5741 deletions

View File

@@ -14,10 +14,8 @@
//! Utilities to manage paginated queries.
use sqlx::{Database, QueryBuilder};
use thiserror::Error;
use ulid::Ulid;
use uuid::Uuid;
/// An error returned when invalid pagination parameters are provided
#[derive(Debug, Error)]
@@ -26,14 +24,14 @@ pub struct InvalidPagination;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Pagination {
before: Option<Ulid>,
after: Option<Ulid>,
count: usize,
direction: PaginationDirection,
pub before: Option<Ulid>,
pub after: Option<Ulid>,
pub count: usize,
pub direction: PaginationDirection,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PaginationDirection {
pub enum PaginationDirection {
Forward,
Backward,
}
@@ -101,60 +99,8 @@ impl Pagination {
self
}
/// Add cursor-based pagination to a query, as used in paginated GraphQL
/// connections
fn generate_pagination<'a, DB>(&self, query: &mut QueryBuilder<'a, DB>, id_field: &'static str)
where
DB: Database,
Uuid: sqlx::Type<DB> + sqlx::Encode<'a, DB>,
i64: sqlx::Type<DB> + sqlx::Encode<'a, DB>,
{
// ref: https://github.com/graphql/graphql-relay-js/issues/94#issuecomment-232410564
// 1. Start from the greedy query: SELECT * FROM table
// 2. If the after argument is provided, add `id > parsed_cursor` to the `WHERE`
// clause
if let Some(after) = self.after {
query
.push(" AND ")
.push(id_field)
.push(" > ")
.push_bind(Uuid::from(after));
}
// 3. If the before argument is provided, add `id < parsed_cursor` to the
// `WHERE` clause
if let Some(before) = self.before {
query
.push(" AND ")
.push(id_field)
.push(" < ")
.push_bind(Uuid::from(before));
}
match self.direction {
// 4. If the first argument is provided, add `ORDER BY id ASC LIMIT first+1` to the
// query
PaginationDirection::Forward => {
query
.push(" ORDER BY ")
.push(id_field)
.push(" ASC LIMIT ")
.push_bind((self.count + 1) as i64);
}
// 5. If the first argument is provided, add `ORDER BY id DESC LIMIT last+1` to the
// query
PaginationDirection::Backward => {
query
.push(" ORDER BY ")
.push(id_field)
.push(" DESC LIMIT ")
.push_bind((self.count + 1) as i64);
}
};
}
/// Process a page returned by a paginated query
#[must_use]
pub fn process<T>(&self, mut edges: Vec<T>) -> Page<T> {
let is_full = edges.len() == (self.count + 1);
if is_full {
@@ -198,7 +144,6 @@ impl<T> Page<T> {
}
}
#[must_use]
pub fn try_map<F, E, T2>(self, f: F) -> Result<Page<T2>, E>
where
F: FnMut(T) -> Result<T2, E>,
@@ -211,23 +156,3 @@ impl<T> Page<T> {
})
}
}
/// An extension trait to the `sqlx` [`QueryBuilder`], to help adding pagination
/// to a query
pub trait QueryBuilderExt {
/// Add cursor-based pagination to a query, as used in paginated GraphQL
/// connections
fn generate_pagination(&mut self, id_field: &'static str, pagination: Pagination) -> &mut Self;
}
impl<'a, DB> QueryBuilderExt for QueryBuilder<'a, DB>
where
DB: Database,
Uuid: sqlx::Type<DB> + sqlx::Encode<'a, DB>,
i64: sqlx::Type<DB> + sqlx::Encode<'a, DB>,
{
fn generate_pagination(&mut self, id_field: &'static str, pagination: Pagination) -> &mut Self {
pagination.generate_pagination(self, id_field);
self
}
}