WIP
This commit is contained in:
parent
73b72e3a31
commit
c6480635c0
@ -5,15 +5,17 @@ edition = "2018"
|
|||||||
publish = false
|
publish = false
|
||||||
[workspace]
|
[workspace]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# rocket = { path = "../../../Rocket/core/lib", features = ["json"] }
|
rocket = { path = "../../../Rocket/core/lib", features = ["json"] }
|
||||||
rocket = { git = "https://github.com/SergioBenitez/Rocket.git", branch = "master", features = [
|
# rocket = { git = "https://github.com/SergioBenitez/Rocket.git", branch = "master", features = [
|
||||||
"json",
|
# "json",
|
||||||
] }
|
# ] }
|
||||||
# async-std = { version = "^1.9", features = ["attributes"] }
|
# async-std = { version = "^1.9", features = ["attributes"] }
|
||||||
sea-orm = { path = "../../", features = [
|
sea-orm = { path = "../../", features = [
|
||||||
"sqlx-all",
|
"sqlx-all",
|
||||||
# "runtime-async-std-native-tls",
|
# "runtime-async-std-native-tls",
|
||||||
] }
|
] }
|
||||||
|
sea-query = { version = "^0.12.8" }
|
||||||
|
|
||||||
serde_json = { version = "^1" }
|
serde_json = { version = "^1" }
|
||||||
futures = { version = "^0.3" }
|
futures = { version = "^0.3" }
|
||||||
async-stream = { version = "^0.3" }
|
async-stream = { version = "^0.3" }
|
||||||
@ -24,7 +26,10 @@ version = "0.5.1"
|
|||||||
default-features = false
|
default-features = false
|
||||||
features = ["macros", "offline", "migrate"]
|
features = ["macros", "offline", "migrate"]
|
||||||
|
|
||||||
|
# [dependencies.rocket_db_pools]
|
||||||
|
# git = "https://github.com/SergioBenitez/Rocket"
|
||||||
|
# branch = "master"
|
||||||
|
# features = ["sea_orm"]
|
||||||
[dependencies.rocket_db_pools]
|
[dependencies.rocket_db_pools]
|
||||||
git = "https://github.com/SergioBenitez/Rocket"
|
path = "../../../Rocket/contrib/db_pools/lib"
|
||||||
branch = "master"
|
|
||||||
features = ["sqlx_sqlite"]
|
features = ["sqlx_sqlite"]
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
[default.databases.sqlx]
|
# [default.databases.sqlx]
|
||||||
url = "db/sqlx/db.sqlite"
|
# url = "sqlite::memory:"
|
||||||
|
|
||||||
|
[global.databases]
|
||||||
|
blog = { url = "sqlite::memory:" }
|
||||||
|
@ -6,17 +6,21 @@ use rocket::{futures, Build, Rocket};
|
|||||||
use rocket_db_pools::{sqlx, Connection, Database};
|
use rocket_db_pools::{sqlx, Connection, Database};
|
||||||
|
|
||||||
use futures::{future::TryFutureExt, stream::TryStreamExt};
|
use futures::{future::TryFutureExt, stream::TryStreamExt};
|
||||||
|
use sea_orm::entity::*;
|
||||||
|
use sea_orm::QueryFilter;
|
||||||
|
|
||||||
|
mod setup;
|
||||||
|
|
||||||
|
#[derive(Database, Debug)]
|
||||||
|
#[database("blog")]
|
||||||
|
struct Db(rocket_db_pools::sqlx::SqlitePool);
|
||||||
|
|
||||||
|
type Result<T, E = rocket::response::Debug<sqlx::Error>> = std::result::Result<T, E>;
|
||||||
|
|
||||||
// use post::*;
|
// use post::*;
|
||||||
mod post;
|
mod post;
|
||||||
pub use post::Entity as Post;
|
pub use post::Entity as Post;
|
||||||
|
use sea_orm::DatabaseConnection;
|
||||||
#[derive(Database)]
|
|
||||||
#[database("sqlx")]
|
|
||||||
struct Db(sqlx::SqlitePool);
|
|
||||||
|
|
||||||
type Result<T, E = rocket::response::Debug<sqlx::Error>> = std::result::Result<T, E>;
|
|
||||||
|
|
||||||
// #[derive(Debug, Clone, Deserialize, Serialize)]
|
// #[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
// #[serde(crate = "rocket::serde")]
|
// #[serde(crate = "rocket::serde")]
|
||||||
// struct Post {
|
// struct Post {
|
||||||
@ -47,17 +51,58 @@ async fn list(mut db: Connection<Db>) -> Result<Json<Vec<i64>>> {
|
|||||||
// .map_ok(|record| record.id)
|
// .map_ok(|record| record.id)
|
||||||
// .try_collect::<Vec<_>>()
|
// .try_collect::<Vec<_>>()
|
||||||
// .await?;
|
// .await?;
|
||||||
let ids = vec![];
|
// // let ids: Vec<i64> = vec![];
|
||||||
|
|
||||||
|
// let ids = sqlx::query(
|
||||||
|
// r#"
|
||||||
|
// SELECT id FROM posts
|
||||||
|
// "#,
|
||||||
|
// )
|
||||||
|
// .execute(&mut *db)
|
||||||
|
// .await?;
|
||||||
|
// // .map_ok(|record| record.id);
|
||||||
|
// // .try_collect::<Vec<_>>();
|
||||||
|
// println!("ids: {:#?}", ids);
|
||||||
|
|
||||||
|
// let ids: Vec<i64> = vec![];
|
||||||
|
// Ok(Json(ids))
|
||||||
|
|
||||||
|
// let mut conn = db.acquire().await?;
|
||||||
|
// println!("conn: {:#?}", conn);
|
||||||
|
|
||||||
|
// let ids = sqlx::query("SELECT id FROM posts")
|
||||||
|
// .fetch(&mut *db)
|
||||||
|
// .map_ok(|record| record.id)
|
||||||
|
// .try_collect::<Vec<_>>()
|
||||||
|
// .await?;
|
||||||
|
|
||||||
|
// Ok(Json(ids))
|
||||||
|
|
||||||
|
// let recs = sqlx::query(
|
||||||
|
// r#"
|
||||||
|
// SELECT id FROM posts
|
||||||
|
// "#,
|
||||||
|
// )
|
||||||
|
// .fetch_all(&mut *db)
|
||||||
|
// .await?;
|
||||||
|
// let ids: Vec<i64> = recs.into();
|
||||||
|
|
||||||
|
// println!("recs: {:#?}", ids);
|
||||||
|
|
||||||
|
let posts = Post::find().all(&mut *db).await.unwrap();
|
||||||
|
assert_eq!(posts.len(), 0);
|
||||||
|
|
||||||
|
let ids: Vec<i64> = vec![];
|
||||||
Ok(Json(ids))
|
Ok(Json(ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/<id>")]
|
#[get("/<id>")]
|
||||||
async fn read(mut db: Connection<Db>, id: i64) -> Option<Json<Post>> {
|
async fn read(mut db: Connection<Db>, id: i64) -> Option<Json<Post>> {
|
||||||
let post: Option<post::Model> = Post::find_by_id(id)
|
// let post: Option<post::Model> = Post::find_by_id(id)
|
||||||
.one(db)
|
// .one(db)
|
||||||
.await
|
// .await
|
||||||
.expect("could not find baker");
|
// .expect("could not find baker");
|
||||||
println!("post: {:#?}", post);
|
// println!("post: {:#?}", post);
|
||||||
|
|
||||||
// sqlx::query!("SELECT id, title, text FROM posts WHERE id = ?", id)
|
// sqlx::query!("SELECT id, title, text FROM posts WHERE id = ?", id)
|
||||||
// .fetch_one(&mut *db)
|
// .fetch_one(&mut *db)
|
||||||
@ -90,24 +135,105 @@ async fn read(mut db: Connection<Db>, id: i64) -> Option<Json<Post>> {
|
|||||||
// Ok(())
|
// Ok(())
|
||||||
// }
|
// }
|
||||||
|
|
||||||
async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
|
// async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
|
||||||
match Db::fetch(&rocket) {
|
// use crate::rocket_db_pools::Pool;
|
||||||
Some(db) => match sqlx::migrate!("db/sqlx/migrations").run(&**db).await {
|
// // match Db::fetch(&rocket) {
|
||||||
Ok(_) => Ok(rocket),
|
// // Some(db) => match sqlx::migrate!("db/sqlx/migrations").run(&**db).await {
|
||||||
Err(e) => {
|
// // Ok(_) => Ok(rocket),
|
||||||
error!("Failed to initialize SQLx database: {}", e);
|
// // Err(e) => {
|
||||||
Err(rocket)
|
// // error!("Failed to initialize SQLx database: {}", e);
|
||||||
}
|
// // Err(rocket)
|
||||||
},
|
// // }
|
||||||
None => Err(rocket),
|
// // },
|
||||||
}
|
// // None => Err(rocket),
|
||||||
}
|
// // }
|
||||||
|
// // let conn = Db::get(&rocket).await.expect("database connection");
|
||||||
|
|
||||||
|
// match Db::fetch(&rocket) {
|
||||||
|
// Some(db) => match setup::create_post_table(db.get().await().expect("database connection")).await {
|
||||||
|
// Ok(_) => {
|
||||||
|
// println!("rocket: {:#?}", rocket);
|
||||||
|
|
||||||
|
// Ok(rocket)
|
||||||
|
// }
|
||||||
|
// Err(e) => {
|
||||||
|
// error!("Failed to initialize SQLx database: {}", e);
|
||||||
|
// Err(rocket)
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// None => Err(rocket),
|
||||||
|
// }
|
||||||
|
// // Ok(rocket)
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn stage() -> AdHoc {
|
pub fn stage() -> AdHoc {
|
||||||
AdHoc::on_ignite("SQLx Stage", |rocket| async {
|
AdHoc::on_ignite("SQLx Stage", |rocket| async {
|
||||||
rocket
|
rocket
|
||||||
.attach(Db::init())
|
.attach(Db::init())
|
||||||
.attach(AdHoc::try_on_ignite("SQLx Migrations", run_migrations))
|
.attach(AdHoc::try_on_ignite("Create init post", |rocket| async {
|
||||||
|
let db = Db::fetch(&rocket).expect("database mounted");
|
||||||
|
let res = sqlx::query(
|
||||||
|
r#"
|
||||||
|
CREATE TABLE posts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title VARCHAR NOT NULL,
|
||||||
|
text VARCHAR NOT NULL,
|
||||||
|
published BOOLEAN NOT NULL DEFAULT 0
|
||||||
|
)"#,
|
||||||
|
)
|
||||||
|
.execute(&**db)
|
||||||
|
.await;
|
||||||
|
println!("res: {:#?}", res);
|
||||||
|
|
||||||
|
let res2 = sqlx::query(
|
||||||
|
r#"
|
||||||
|
INSERT INTO posts (title, text) VALUES ('a post', 'content of a post')
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.execute(&**db)
|
||||||
|
.await;
|
||||||
|
println!("res2: {:#?}", res2);
|
||||||
|
|
||||||
|
// Db::fetch(&rocket)
|
||||||
|
// .run(|db| {
|
||||||
|
// sqlx::query("DELETE FROM table").execute(&pool).await;
|
||||||
|
|
||||||
|
// // conn.execute(
|
||||||
|
// // r#"
|
||||||
|
// // CREATE TABLE posts (
|
||||||
|
// // id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
// // title VARCHAR NOT NULL,
|
||||||
|
// // text VARCHAR NOT NULL,
|
||||||
|
// // published BOOLEAN NOT NULL DEFAULT 0
|
||||||
|
// // )"#,
|
||||||
|
// // params![],
|
||||||
|
// // )
|
||||||
|
// })
|
||||||
|
// .await
|
||||||
|
// .expect("can init rusqlite DB");
|
||||||
|
Ok(rocket)
|
||||||
|
|
||||||
|
// match Db::fetch(&rocket) {
|
||||||
|
// Some(db) => {
|
||||||
|
// println!("db: {:#?}", db);
|
||||||
|
// println!("&**db: {:#?}", &**db);
|
||||||
|
|
||||||
|
// Ok(rocket)
|
||||||
|
// }
|
||||||
|
// None => Err(rocket),
|
||||||
|
// }
|
||||||
|
}))
|
||||||
.mount("/sqlx", routes![list, read])
|
.mount("/sqlx", routes![list, read])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub async fn create_post(db: &DbConn) {
|
||||||
|
// let post = post::ActiveModel {
|
||||||
|
// title: Set("Post One".to_owned()),
|
||||||
|
// text: Set("post content 1".to_owned()),
|
||||||
|
// ..Default::default()
|
||||||
|
// }
|
||||||
|
// .save(db)
|
||||||
|
// .await
|
||||||
|
// .expect("could not insert post");
|
||||||
|
// }
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use rocket::serde::{json::Json, Deserialize, Serialize};
|
use rocket::serde::{json::Json, Deserialize, Serialize};
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
#[derive(Copy, Clone, Default, Debug, DeriveEntity, Deserialize, Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct Entity;
|
pub struct Entity;
|
||||||
|
|
||||||
impl EntityName for Entity {
|
impl EntityName for Entity {
|
||||||
@ -13,6 +14,7 @@ impl EntityName for Entity {
|
|||||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Deserialize, Serialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
|
#[serde(skip_deserializing, skip_serializing_if = "Option::is_none")]
|
||||||
pub id: Option<i64>,
|
pub id: Option<i64>,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
@ -36,6 +38,9 @@ impl PrimaryKeyTrait for PrimaryKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
|
pub enum Relation {}
|
||||||
|
|
||||||
impl ColumnTrait for Column {
|
impl ColumnTrait for Column {
|
||||||
type EntityName = Entity;
|
type EntityName = Entity;
|
||||||
|
|
||||||
@ -48,4 +53,9 @@ impl ColumnTrait for Column {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RelationTrait for Relation {
|
||||||
|
fn def(&self) -> RelationDef {
|
||||||
|
panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
37
examples/rocket_example/src/sqlx/setup.rs
Normal file
37
examples/rocket_example/src/sqlx/setup.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
use sea_orm::{error::*, sea_query, DbConn, ExecResult};
|
||||||
|
|
||||||
|
use sea_query::{ColumnDef, ForeignKey, ForeignKeyAction, Index, TableCreateStatement};
|
||||||
|
|
||||||
|
// mod post;
|
||||||
|
pub use super::post::*;
|
||||||
|
|
||||||
|
async fn create_table(db: &DbConn, stmt: &TableCreateStatement) -> Result<ExecResult, DbErr> {
|
||||||
|
let builder = db.get_database_backend();
|
||||||
|
db.execute(builder.build(stmt)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_post_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||||
|
let stmt = sea_query::Table::create()
|
||||||
|
.table(super::post::Entity)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(super::post::Column::Id)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
.auto_increment()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(super::post::Column::Title)
|
||||||
|
.string()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(super::post::Column::Text)
|
||||||
|
.string()
|
||||||
|
.not_null(),
|
||||||
|
)
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
create_table(db, &stmt).await
|
||||||
|
}
|
@ -1,74 +0,0 @@
|
|||||||
use rocket::fairing::AdHoc;
|
|
||||||
use rocket::http::Status;
|
|
||||||
use rocket::local::blocking::Client;
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
struct Post {
|
|
||||||
title: String,
|
|
||||||
text: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(base: &str, stage: AdHoc) {
|
|
||||||
// Number of posts we're going to create/read/delete.
|
|
||||||
const N: usize = 20;
|
|
||||||
|
|
||||||
// NOTE: If we had more than one test running concurently that dispatches
|
|
||||||
// DB-accessing requests, we'd need transactions or to serialize all tests.
|
|
||||||
let client = Client::tracked(rocket::build().attach(stage)).unwrap();
|
|
||||||
|
|
||||||
// Clear everything from the database.
|
|
||||||
assert_eq!(client.delete(base).dispatch().status(), Status::Ok);
|
|
||||||
assert_eq!(
|
|
||||||
client.get(base).dispatch().into_json::<Vec<i64>>(),
|
|
||||||
Some(vec![])
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add some random posts, ensure they're listable and readable.
|
|
||||||
for i in 1..=N {
|
|
||||||
let title = format!("My Post - {}", i);
|
|
||||||
let text = format!("Once upon a time, at {}'o clock...", i);
|
|
||||||
let post = Post {
|
|
||||||
title: title.clone(),
|
|
||||||
text: text.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create a new post.
|
|
||||||
let response = client.post(base).json(&post).dispatch().into_json::<Post>();
|
|
||||||
assert_eq!(response.unwrap(), post);
|
|
||||||
|
|
||||||
// Ensure the index shows one more post.
|
|
||||||
let list = client.get(base).dispatch().into_json::<Vec<i64>>().unwrap();
|
|
||||||
assert_eq!(list.len(), i);
|
|
||||||
|
|
||||||
// The last in the index is the new one; ensure contents match.
|
|
||||||
let last = list.last().unwrap();
|
|
||||||
let response = client.get(format!("{}/{}", base, last)).dispatch();
|
|
||||||
assert_eq!(response.into_json::<Post>().unwrap(), post);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now delete all of the posts.
|
|
||||||
for _ in 1..=N {
|
|
||||||
// Get a valid ID from the index.
|
|
||||||
let list = client.get(base).dispatch().into_json::<Vec<i64>>().unwrap();
|
|
||||||
let id = list.get(0).expect("have post");
|
|
||||||
|
|
||||||
// Delete that post.
|
|
||||||
let response = client.delete(format!("{}/{}", base, id)).dispatch();
|
|
||||||
assert_eq!(response.status(), Status::Ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure they're all gone.
|
|
||||||
let list = client.get(base).dispatch().into_json::<Vec<i64>>().unwrap();
|
|
||||||
assert!(list.is_empty());
|
|
||||||
|
|
||||||
// Trying to delete should now 404.
|
|
||||||
let response = client.delete(format!("{}/{}", base, 1)).dispatch();
|
|
||||||
assert_eq!(response.status(), Status::NotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sqlx() {
|
|
||||||
test("/sqlx", crate::sqlx::stage())
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user