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
use warp::Filter;

use super::handlers;
use super::models::{ListOptions, Todo};
use super::db::DbConn;

/// The 5 TODOs filters combined. 
pub fn todos(
    db_conn: DbConn,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    list_todos(db_conn.clone())
        .or(todos_create(db_conn.clone()))
        .or(todos_update(db_conn.clone()))
        .or(todos_delete(db_conn.clone()))
        .or(get_todo(db_conn.clone()))
}

/// GET /todos?offset=3&limit=5 
pub fn list_todos(
    db_conn: DbConn,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("todos")
        .and(warp::get())
        .and(warp::query::<ListOptions>())
        .and(with_db_conn(db_conn))
        .and_then(handlers::list_todos)
}

/// POST /todos with JSON body 
pub fn todos_create(
    db_conn: DbConn,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("todos")
        .and(warp::post())
        .and(json_body())
        .and(with_db_conn(db_conn))
        .and_then(handlers::create_todo)
}

/// PUT /todos/:id with JSON body 
pub fn todos_update(
    db_conn: DbConn,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("todos" / u32)
        .and(warp::put())
        .and(json_body())
        .and(with_db_conn(db_conn))
        .and_then(handlers::update_todo)
}

/// DELETE /todos/:id
pub fn todos_delete(
    db_conn: DbConn,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    // make one of the endpoints admin-only to show how authentication filters are used
    let admin_only = warp::header::exact("authorization", "Bearer Bearer admin");
    warp::path!("todos" / u32)
        // It is important to put the auth check _after_ the path filters. 
        // If we put the auth check before, the request `PUT /todos/invalid-string`
        // would try this filter and reject because the authorization header 
        // doesn't match, rather because the param is wrong for that other path. 
        .and(admin_only)
        .and(warp::delete())
        .and(with_db_conn(db_conn))
        .and_then(handlers::delete_todo)
}

/// GET /todos/:id 
pub fn get_todo(
    db_conn: DbConn
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path!("todos" / u32)
        .and(warp::get())
        .and(with_db_conn(db_conn))
        .and_then(handlers::get_todo)
}

fn with_db_conn(db_conn: DbConn) -> impl Filter<Extract = (DbConn,), Error = std::convert::Infallible> + Clone {
    warp::any().map(move || db_conn.clone())
}

fn json_body() -> impl Filter<Extract = (Todo,), Error = warp::Rejection> + Clone {
    // When accepting a body, we want a JSON body 
    // (and to reject huge payloads)... 
    warp::body::content_length_limit(1024 * 16).and(warp::body::json()) 
}