Skip to content

Commit

Permalink
Revamp doc module
Browse files Browse the repository at this point in the history
  • Loading branch information
kigawas committed Jul 20, 2024
1 parent a7c8345 commit 529f621
Show file tree
Hide file tree
Showing 14 changed files with 130 additions and 65 deletions.
20 changes: 16 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ license = "MIT"
readme = "README.md"

[workspace]
members = ["api", "app", "models", "migration", "utils"]
members = ["api", "app", "doc", "models", "migration", "utils"]

[workspace.dependencies]
axum = { version = "0.7.5", default-features = false }
tower = { version = "0.4.13", default-features = false }
sea-orm = { version = "1.0.0-rc.7", default-features = false }
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", default-features = false }
tracing = "0.1.40"
utoipa = { version = "5.0.0-alpha.0", default-features = false }
validator = { version = "0.18", default-features = false }

[dependencies]
api = { path = "api" }
utils = { path = "utils" }
doc = { path = "doc" }

sea-orm = { workspace = true }

Expand All @@ -48,6 +50,7 @@ app = { path = "app" }
models = { path = "models" }

http-body-util = "0.1.2"
serde_json = { workspace = true }

[features]
default = []
Expand Down
56 changes: 42 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,51 @@ You probably don't need [Rust on Rails](https://github.com/loco-rs/loco).

## Module hierarchy

- `api`: Axum logic
- `api::routers`: Axum endpoints
- `api::doc`: Utoipa doc declaration
- `api::error`: Error handling
- `api::extractor` and `api::validation`: Axum extractor and JSON validation
### API logic

- `api::routers`: Axum endpoints
- `api::error`: Models and traits for error handling
- `api::extractor` Custom Axum extractors
- `api::extractor::json`: `Json` for bodies and responses
- `api::extractor::valid`: `Valid` for JSON body validation
- `api::validation`: JSON validation model based on `validator`
- `api::models`: Non domain model API models
- `api::models::response`: JSON error response
- `app`: DB/API-agnostic logic
- `app::services`: DB manipulation (CRUD) functions
- `app::config`: DB or API configuration
- `app::state`: APP state, e.g. DB connection
- `models`: DB/API-agnostic models
- `models::domains`: SeaORM domain models
- `models::params`: Serde input parameters for creating/updating domain models in DB
- `models::schemas`: Serde output schemas for combining different domain models
- `models::queries`: Serde queries for filtering domain models

### OpenAPI documentation

- `doc`: Utoipa doc declaration

### API-agonistic DB logic

Main concept: Web framework is replaceable.

All modules here should not include any specific API web framework logic.

- `app::services`: DB manipulation (CRUD) functions
- `app::config`: DB or API server configuration
- `app::state`: APP state, e.g. DB connection
- `app::error`: APP errors used by `api::error`. e.g. "User not found"

### DB/API-agnostic domain models

Main concept: Database (Sqlite/MySQL/PostgreSQL) is replaceable.

Except `models::domains` and `migration`, all modules are ORM library agnostic.

- `models::domains`: SeaORM domain models
- `models::params`: Serde input parameters for creating/updating domain models in DB
- `models::schemas`: Serde output schemas for combining different domain models
- `models::queries`: Serde queries for filtering domain models
- `migration`: SeaORM migration files

### Unit and integration tests

- `tests::api`: API integration tests. Hierarchy is the same as `api::routers`
- `tests::app`: DB/ORM-related unit tests. Hierarchy is the same as `app::services`

### Others

- `utils`: Utility functions
- `main`: Tokio and Shuttle conditional entry point

Expand Down
9 changes: 1 addition & 8 deletions api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,7 @@ dotenvy = "0.15.7"
sea-orm = { workspace = true }

# doc
utoipa = { workspace = true, features = ["axum_extras"] }
utoipa-swagger-ui = { version = "7.1.1-alpha.0", features = [
"axum",
"vendored",
], default-features = false }
utoipa-scalar = { version = "0.2.0-alpha.0", features = [
"axum",
], default-features = false }
utoipa = { workspace = true }

# local dependencies
app = { path = "../app" }
Expand Down
18 changes: 0 additions & 18 deletions api/src/doc/mod.rs

This file was deleted.

7 changes: 1 addition & 6 deletions api/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
use axum::Router;
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
use utoipa::OpenApi;
use utoipa_scalar::{Scalar, Servable as ScalarServable};
use utoipa_swagger_ui::SwaggerUi;

use app::config::Config;
use app::state::AppState;

use crate::doc::ApiDoc;
use crate::routers::create_router;

// TODO: middleware, logging, authentication
pub fn setup_router(conn: DatabaseConnection) -> Router {
create_router(AppState { conn })
.merge(SwaggerUi::new("/docs").url("/openapi.json", ApiDoc::openapi()))
.merge(Scalar::with_url("/scalar", ApiDoc::openapi()))
}

pub fn setup_config() -> Config {
Expand Down
6 changes: 3 additions & 3 deletions api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
mod doc;
mod error;
mod extractor;
mod init;
mod models;
mod routers;
mod validation;

pub mod models;
pub mod routers;

pub use init::{setup_config, setup_db, setup_router};
1 change: 0 additions & 1 deletion api/src/routers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use app::state::AppState;
use root::create_root_router;
use user::create_user_router;

// TODO: middleware, testing, logging
pub fn create_router(state: AppState) -> Router {
Router::new()
.nest("/users", create_user_router(state.clone()))
Expand Down
19 changes: 19 additions & 0 deletions doc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "doc"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
axum = { workspace = true }
utoipa = { workspace = true, features = ["axum_extras"] }
utoipa-swagger-ui = { version = "7.1.1-alpha.0", features = [
"axum",
"vendored",
], default-features = false }
utoipa-scalar = { version = "0.2.0-alpha.0", features = [
"axum",
], default-features = false }

api = { path = "../api" }
models = { path = "../models" }
32 changes: 32 additions & 0 deletions doc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use axum::Router;
use utoipa::OpenApi;
use utoipa_scalar::{Scalar, Servable as ScalarServable};
use utoipa_swagger_ui::SwaggerUi;

mod root;
mod user;

#[derive(OpenApi)]
#[openapi(
nest(
(path = "/", api = root::RootApi),
(path = "/users", api = user::UserApi),
),
tags(
(name = "root", description = "Root API"),
(name = "user", description = "User API")
)
)]
struct _ApiDoc;

pub trait ApiDoc {
fn attach_doc(self) -> Self;
}

impl ApiDoc for Router {
fn attach_doc(self) -> Self {
self.merge(SwaggerUi::new("/docs").url("/openapi.json", _ApiDoc::openapi()))
.merge(Scalar::with_url("/scalar", _ApiDoc::openapi()))
}
}
4 changes: 2 additions & 2 deletions api/src/doc/root.rs → doc/src/root.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use utoipa::OpenApi;

use crate::routers::root::*;
use api::routers::root::*;

#[derive(OpenApi)]
#[openapi(paths(root))]
pub struct RootApi;
pub(super) struct RootApi;
6 changes: 3 additions & 3 deletions api/src/doc/user.rs → doc/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use utoipa::OpenApi;
use models::params::user::CreateUserParams;
use models::schemas::user::{UserListSchema, UserSchema};

use crate::models::{ApiErrorResponse, ParamsErrorResponse};
use crate::routers::user::*;
use api::models::{ApiErrorResponse, ParamsErrorResponse};
use api::routers::user::*;

#[derive(OpenApi)]
#[openapi(
Expand All @@ -17,4 +17,4 @@ use crate::routers::user::*;
ParamsErrorResponse,
))
)]
pub struct UserApi;
pub(super) struct UserApi;
2 changes: 1 addition & 1 deletion models/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ publish = false

[dependencies]
serde = { workspace = true }
serde_json = "1"
serde_json = { workspace = true }
sea-orm = { workspace = true, features = [
"sqlx-postgres",
"sqlx-sqlite",
Expand Down
10 changes: 6 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use api::{setup_db, setup_router};
use doc::ApiDoc;
use utils::migrate;

#[cfg(not(feature = "shuttle"))]
Expand All @@ -19,10 +21,10 @@ async fn main() {
let config = api::setup_config();
create_dev_db(&config.db_url);

let conn = api::setup_db(&config.db_url).await;
let conn = setup_db(&config.db_url).await;
migrate(&conn).await.expect("Migration failed!");

let router = api::setup_router(conn);
let router = setup_router(conn).attach_doc();
let listener = tokio::net::TcpListener::bind(&config.get_server_url())
.await
.unwrap();
Expand All @@ -36,9 +38,9 @@ async fn main() {
async fn main(#[shuttle_shared_db::Postgres] db_url: String) -> shuttle_axum::ShuttleAxum {
tracing::info!("Starting with shuttle");

let conn = api::setup_db(&db_url).await;
let conn = setup_db(&db_url).await;
migrate(&conn).await.expect("Migration failed!");

let router = api::setup_router(conn);
let router = setup_router(conn).attach_doc();
Ok(router.into())
}

0 comments on commit 529f621

Please sign in to comment.