Skip to content

Commit

Permalink
feat: sea-query
Browse files Browse the repository at this point in the history
  • Loading branch information
jayden-dang committed Jul 19, 2024
1 parent 6e1d8dc commit 9b195ea
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 31 deletions.
74 changes: 74 additions & 0 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions crates/jd_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use axum::Router;
use sqlx::PgPool;
use user::{create_user_route, delete_user_route, get_user_route, get_users_route, update_user_route};
use user::{delete_user_route, get_user_route, get_users_route, update_user_route, CourseDmc, UserDmc};

pub mod user;

Expand All @@ -12,7 +12,8 @@ pub fn user_routes() -> Router<PgPool> {
.merge(get_user_route())
.merge(get_users_route())
.merge(update_user_route())
.merge(create_user_route())
.merge(UserDmc::create_user_route())
.merge(CourseDmc::create_course_route())
.merge(delete_user_route()),
)
}
40 changes: 35 additions & 5 deletions crates/jd_api/src/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use axum::{
use jd_core::AppResult;
use jd_domain::user::{
request::{RequestCreateUser, RequestGetUser, RequestUpdateUser},
User,
Course, RequestCreateCourse, ResponseCreateCourse, User,
};
use jd_infra::user::DMC;
use sqlx::PgPool;

pub fn get_user_route() -> Router<PgPool> {
Expand All @@ -33,12 +34,41 @@ pub fn update_user_route() -> Router<PgPool> {
Router::new().route("/user", routing::patch(update_user))
}

pub fn create_user_route() -> Router<PgPool> {
pub async fn create_user(State(db): State<PgPool>, Json(req): Json<RequestCreateUser>) -> AppResult<Json<i64>> {
jd_infra::user::create(db, req).await
pub struct UserDmc;

impl DMC for UserDmc {
const SCHEMA: &'static str = "user";
const TABLE: &'static str = "tbl_users";
}

impl UserDmc {
pub fn create_user_route() -> Router<PgPool> {
pub async fn create_user(State(db): State<PgPool>, Json(req): Json<RequestCreateUser>) -> AppResult<Json<User>> {
jd_infra::user::create::<UserDmc, _, _>(db, req).await
}

Router::new().route("/user", routing::post(create_user))
}
}

Router::new().route("/user", routing::post(create_user))
pub struct CourseDmc;

impl DMC for CourseDmc {
const SCHEMA: &'static str = "course";
const TABLE: &'static str = "tbl_courses";
}

impl CourseDmc {
pub fn create_course_route() -> Router<PgPool> {
pub async fn create_course(
State(db): State<PgPool>,
Json(req): Json<RequestCreateCourse>,
) -> AppResult<Json<ResponseCreateCourse>> {
jd_infra::user::create::<CourseDmc, _, _>(db, req).await
}

Router::new().route("/course", routing::post(create_course))
}
}

pub fn delete_user_route() -> Router<PgPool> {
Expand Down
1 change: 1 addition & 0 deletions crates/jd_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"
axum = { version = "0.7.5", features = ["tracing"] }
config = "0.14.0"
dotenv = "0.15.0"
sea-query = "0.30.7"
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120"
sqlx = { version = "0.7.4", features = ["postgres"] }
Expand Down
6 changes: 6 additions & 0 deletions crates/jd_core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub enum AppError {

#[error("Sqxl Error")]
Sqlx(#[from] sqlx::Error),

#[error("SeaQuery Error")]
SeaQuery(#[from] sea_query::error::Error),
}

pub struct WebResponse {
Expand All @@ -32,6 +35,9 @@ impl IntoResponse for AppError {
AppError::NotFound => (StatusCode::NOT_FOUND, Json(json!({"error": "Not Found"}))).into_response(),
AppError::EnvError(e) => (StatusCode::FORBIDDEN, Json(json!({"error": e.to_string()}))).into_response(),
AppError::Sqlx(e) => (StatusCode::BAD_REQUEST, Json(json!({"error": e.to_string()}))).into_response(),
AppError::SeaQuery(e) => {
(StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": e.to_string()}))).into_response()
},
}
}
}
2 changes: 2 additions & 0 deletions crates/jd_domain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
modql = { version = "0.3.10", features = ["with-sea-query"] }
sea-query = "0.30.7"
serde = { version = "1.0.204", features = ["derive"] }
sqlx = { version = "0.7.4", features = ["postgres"] }
25 changes: 23 additions & 2 deletions crates/jd_domain/src/user/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
use serde::Serialize;
use modql::field::Fields;
use serde::{Deserialize, Serialize};
use sqlx::prelude::FromRow;

pub mod request;
pub mod response;

#[derive(Serialize, FromRow)]
#[derive(Serialize, FromRow, Fields)]
pub struct User {
pub pk_user_id: i64,
pub username: String,
}

#[derive(Serialize, FromRow, Fields)]
pub struct Course {
pub pk_course_id: i64,
pub title: String,
pub description: String,
}

#[derive(Deserialize, FromRow, Fields)]
pub struct RequestCreateCourse {
pub pk_course_id: i64,
pub title: String,
pub description: String,
}

#[derive(Serialize, FromRow, Fields)]
pub struct ResponseCreateCourse {
pub pk_course_id: i64,
pub title: String,
}
8 changes: 5 additions & 3 deletions crates/jd_domain/src/user/request.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use modql::field::Fields;
use serde::Deserialize;

#[derive(Deserialize)]
#[derive(Deserialize, Fields)]
pub struct RequestGetUser {
pub id: i64,
}

#[derive(Deserialize)]
#[derive(Deserialize, Fields)]
pub struct RequestCreateUser {
pub pk_user_id: i64,
pub username: String,
}

#[derive(Deserialize)]
#[derive(Deserialize, Fields)]
pub struct RequestUpdateUser {
pub id: i64,
pub username: String,
Expand Down
3 changes: 3 additions & 0 deletions crates/jd_infra/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ uuid = { version = "1.10.0", features = ["v4", "fast-rng"] }
# Members
jd_core = { path = "../jd_core"}
jd_domain = { path = "../jd_domain"}
sea-query = "0.30.7"
modql = { version = "0.3.10", features = ["with-sea-query"] }
sea-query-binder = { version = "0.5.0", features = ["sqlx-postgres"] }
9 changes: 8 additions & 1 deletion crates/jd_infra/migrations/00001_initialized.sql
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
CREATE SCHEMA IF NOT EXISTS "user";
CREATE SCHEMA IF NOT EXISTS "course";

CREATE TABLE IF NOT EXISTS "user"."tbl_users" (
pk_user_id SERIAL PRIMARY KEY NOT NULL,
pk_user_id BIGINT PRIMARY KEY NOT NULL,
username VARCHAR(150) NOT NULL
);

CREATE TABLE IF NOT EXISTS "course"."tbl_courses" (
pk_course_id BIGINT PRIMARY KEY NOT NULL,
title VARCHAR(150) NOT NULL,
description TEXT
)
4 changes: 1 addition & 3 deletions crates/jd_infra/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ use sqlx::{postgres::PgPoolOptions, PgPool};
pub async fn initialed_db(dsn: &str, max_conns: u32) -> PgPool {
let db = PgPoolOptions::new().max_connections(max_conns).connect(dsn).await.expect("Cannot connect database");

if !sqlx::migrate!().version_exists(1) {
sqlx::migrate!().run(&db).await.expect("Cannot migrate database");
}
sqlx::migrate!().run(&db).await.expect("Cannot migrate database");

db
}
50 changes: 40 additions & 10 deletions crates/jd_infra/src/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,46 @@ use jd_domain::user::{
request::{RequestCreateUser, RequestGetUser, RequestUpdateUser},
User,
};
use sqlx::PgPool;

pub async fn create(db: PgPool, id: RequestCreateUser) -> AppResult<Json<i64>> {
let (id,) =
sqlx::query_as::<_, (i64,)>(r#"INSERT INTO "user"."tbl_users" (username) VALUES ($1) returning pk_user_id"#)
.bind(id.username)
.fetch_optional(&db)
.await?
.ok_or(AppError::NotFound)?;
Ok(Json(id))
use modql::{field::HasFields, SIden};
use sea_query::{Alias, Asterisk, Expr, IntoIden, PostgresQueryBuilder, Query, TableRef};
use sea_query_binder::SqlxBinder;
use sqlx::{postgres::PgRow, FromRow, PgPool};
use tracing::info;

pub trait DMC {
const SCHEMA: &'static str;
const TABLE: &'static str;

fn table_ref() -> TableRef {
TableRef::SchemaTable(SIden(Self::SCHEMA).into_iden(), SIden(Self::TABLE).into_iden())
}
}

// DMC -> Database Model Control
pub async fn create<MC, I, O>(db: PgPool, input: I) -> AppResult<Json<O>>
where
MC: DMC,
I: HasFields,
O: HasFields + for<'a> FromRow<'a, PgRow> + Send + Unpin,
{
// Setup Data
let fields = input.not_none_fields();
let (columns, sea_values) = fields.for_sea_insert();

// Preparing Query
let mut query = Query::insert();
query.into_table(MC::table_ref()).columns(columns).values(sea_values)?;

// Returning
let o_fields = O::field_names();
query.returning(Query::returning().columns(o_fields.iter().map(|&f| Alias::new(f)).collect::<Vec<_>>()));

// Execute
let (sql, values) = query.build_sqlx(PostgresQueryBuilder);

let entity = sqlx::query_as_with::<_, O, _>(&sql, values).fetch_one(&db).await?;

Ok(Json(entity))
}

pub async fn get_user(db: PgPool, id: RequestGetUser) -> AppResult<Json<User>> {
Expand Down
Loading

0 comments on commit 9b195ea

Please sign in to comment.