From 63ed16fbe6e80464b07cd8104856aec4755e0832 Mon Sep 17 00:00:00 2001 From: hunjixin <1084400399@qq.com> Date: Thu, 24 Mar 2022 13:25:29 +0800 Subject: [PATCH 1/2] add example for integrate with jsonrpsee --- examples/jsonrpsee_example/.env | 4 + examples/jsonrpsee_example/Cargo.toml | 21 +++ examples/jsonrpsee_example/README.md | 64 ++++++++ examples/jsonrpsee_example/entity/Cargo.toml | 25 +++ examples/jsonrpsee_example/entity/src/lib.rs | 3 + examples/jsonrpsee_example/entity/src/post.rs | 17 ++ .../jsonrpsee_example/migration/Cargo.toml | 13 ++ .../jsonrpsee_example/migration/README.md | 37 +++++ .../jsonrpsee_example/migration/src/lib.rs | 12 ++ .../src/m20220120_000001_create_post_table.rs | 39 +++++ .../jsonrpsee_example/migration/src/main.rs | 7 + examples/jsonrpsee_example/src/main.rs | 149 ++++++++++++++++++ 12 files changed, 391 insertions(+) create mode 100644 examples/jsonrpsee_example/.env create mode 100644 examples/jsonrpsee_example/Cargo.toml create mode 100644 examples/jsonrpsee_example/README.md create mode 100644 examples/jsonrpsee_example/entity/Cargo.toml create mode 100644 examples/jsonrpsee_example/entity/src/lib.rs create mode 100644 examples/jsonrpsee_example/entity/src/post.rs create mode 100644 examples/jsonrpsee_example/migration/Cargo.toml create mode 100644 examples/jsonrpsee_example/migration/README.md create mode 100644 examples/jsonrpsee_example/migration/src/lib.rs create mode 100644 examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs create mode 100644 examples/jsonrpsee_example/migration/src/main.rs create mode 100644 examples/jsonrpsee_example/src/main.rs diff --git a/examples/jsonrpsee_example/.env b/examples/jsonrpsee_example/.env new file mode 100644 index 000000000..acd83c0e1 --- /dev/null +++ b/examples/jsonrpsee_example/.env @@ -0,0 +1,4 @@ +HOST=127.0.0.1 +PORT=8000 +#DATABASE_URL="mysql://root:@localhost/poem_example" +DATABASE_URL="sqlite::memory:" diff --git a/examples/jsonrpsee_example/Cargo.toml b/examples/jsonrpsee_example/Cargo.toml new file mode 100644 index 000000000..3a0253a79 --- /dev/null +++ b/examples/jsonrpsee_example/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "sea-orm-jsonrpsee-example" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[workspace] +members = [".", "entity", "migration"] + +[dependencies] +jsonrpsee = { version = "^0.8.0", features = ["full"] } +jsonrpsee-core = "0.9.0" +tokio = { version = "1.8.0", features = ["full"] } +serde = { version = "1", features = ["derive"] } +dotenv = "0.15" +entity = { path = "entity" } +migration = { path = "migration" } +anyhow = "1.0.52" +async-trait = "0.1.52" +log = { version = "0.4", features = ["std"] } +simplelog = "*" \ No newline at end of file diff --git a/examples/jsonrpsee_example/README.md b/examples/jsonrpsee_example/README.md new file mode 100644 index 000000000..dbb739a0e --- /dev/null +++ b/examples/jsonrpsee_example/README.md @@ -0,0 +1,64 @@ +# Poem with SeaORM example app + +1. Modify the `DATABASE_URL` var in `.env` to point to your chosen database + +1. Turn on the appropriate database feature for your chosen db in `entity/Cargo.toml` (the `"sqlx-sqlite",` line) + +1. Execute `cargo run` to start the server + +2. Send jsonrpc request to server + +```shell +#insert +curl --location --request POST 'http://127.0.0.1:8000' \ +--header 'Content-Type: application/json' \ +--data-raw '{"jsonrpc": "2.0", "method": "Post.Insert", "params": [ + { + "id":0, + "title":"aaaaaaa", + "text":"aaaaaaa" + } +], "id": 2}' + +#list +curl --location --request POST 'http://127.0.0.1:8000' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "jsonrpc": "2.0", + "method": "Post.List", + "params": [ + 1, + 100 + ], + "id": 2 +}' + +#delete +curl --location --request POST 'http://127.0.0.1:8000' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "jsonrpc": "2.0", + "method": "Post.Delete", + "params": [ + 10 + ], + "id": 2 +}' + +#update +curl --location --request POST 'http://127.0.0.1:8000' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "jsonrpc": "2.0", + "method": "Post.Update", + "params": [ + { + "id": 1, + "title": "1111", + "text": "11111" + } + ], + "id": 2 +}' + +``` \ No newline at end of file diff --git a/examples/jsonrpsee_example/entity/Cargo.toml b/examples/jsonrpsee_example/entity/Cargo.toml new file mode 100644 index 000000000..7a6ac369e --- /dev/null +++ b/examples/jsonrpsee_example/entity/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "entity" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "entity" +path = "src/lib.rs" + +[dependencies] +serde = { version = "1", features = ["derive"] } + +[dependencies.sea-orm] +# path = "../../../" # remove this line in your own project +version = "^0.6.0" +features = [ + "macros", + "debug-print", + "runtime-tokio-native-tls", + "sqlx-sqlite", + # "sqlx-postgres", + # "sqlx-mysql", +] +default-features = false \ No newline at end of file diff --git a/examples/jsonrpsee_example/entity/src/lib.rs b/examples/jsonrpsee_example/entity/src/lib.rs new file mode 100644 index 000000000..263f05b42 --- /dev/null +++ b/examples/jsonrpsee_example/entity/src/lib.rs @@ -0,0 +1,3 @@ +pub mod post; + +pub use sea_orm; diff --git a/examples/jsonrpsee_example/entity/src/post.rs b/examples/jsonrpsee_example/entity/src/post.rs new file mode 100644 index 000000000..719fa118c --- /dev/null +++ b/examples/jsonrpsee_example/entity/src/post.rs @@ -0,0 +1,17 @@ +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] +#[sea_orm(table_name = "posts")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub title: String, + #[sea_orm(column_type = "Text")] + pub text: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/examples/jsonrpsee_example/migration/Cargo.toml b/examples/jsonrpsee_example/migration/Cargo.toml new file mode 100644 index 000000000..9a7c338f6 --- /dev/null +++ b/examples/jsonrpsee_example/migration/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +sea-schema = { version = "^0.5.0", default-features = false, features = [ "migration", "debug-print" ] } +entity = { path = "../entity" } diff --git a/examples/jsonrpsee_example/migration/README.md b/examples/jsonrpsee_example/migration/README.md new file mode 100644 index 000000000..963caaeb6 --- /dev/null +++ b/examples/jsonrpsee_example/migration/README.md @@ -0,0 +1,37 @@ +# Running Migrator CLI + +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/examples/jsonrpsee_example/migration/src/lib.rs b/examples/jsonrpsee_example/migration/src/lib.rs new file mode 100644 index 000000000..3679d81f5 --- /dev/null +++ b/examples/jsonrpsee_example/migration/src/lib.rs @@ -0,0 +1,12 @@ +pub use sea_schema::migration::prelude::*; + +mod m20220120_000001_create_post_table; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![Box::new(m20220120_000001_create_post_table::Migration)] + } +} diff --git a/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs b/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs new file mode 100644 index 000000000..0fe872c4c --- /dev/null +++ b/examples/jsonrpsee_example/migration/src/m20220120_000001_create_post_table.rs @@ -0,0 +1,39 @@ +use entity::post::*; +use sea_schema::migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20220120_000001_create_post_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Entity) + .if_not_exists() + .col( + ColumnDef::new(Column::Id) + .integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Column::Title).string().not_null()) + .col(ColumnDef::new(Column::Text).string().not_null()) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Entity).to_owned()) + .await + } +} diff --git a/examples/jsonrpsee_example/migration/src/main.rs b/examples/jsonrpsee_example/migration/src/main.rs new file mode 100644 index 000000000..7e5e996df --- /dev/null +++ b/examples/jsonrpsee_example/migration/src/main.rs @@ -0,0 +1,7 @@ +use migration::Migrator; +use sea_schema::migration::*; + +#[async_std::main] +async fn main() { + cli::run_cli(Migrator).await; +} diff --git a/examples/jsonrpsee_example/src/main.rs b/examples/jsonrpsee_example/src/main.rs new file mode 100644 index 000000000..93474cbcc --- /dev/null +++ b/examples/jsonrpsee_example/src/main.rs @@ -0,0 +1,149 @@ +use std::env; + +use anyhow::anyhow; +use entity::post; +use entity::sea_orm; +use jsonrpsee::core::{async_trait, RpcResult}; +use jsonrpsee::http_server::HttpServerBuilder; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::types::error::CallError; +use log::info; +use migration::{Migrator, MigratorTrait}; +use sea_orm::NotSet; +use sea_orm::{entity::*, query::*, DatabaseConnection}; +use simplelog::*; +use std::fmt::Display; +use std::net::SocketAddr; +use tokio::signal::ctrl_c; +use tokio::signal::unix::{signal, SignalKind}; + +const DEFAULT_POSTS_PER_PAGE: usize = 5; + +#[rpc(server, client)] +pub trait PostRpc { + #[method(name = "Post.List")] + async fn list( + &self, + page: Option, + posts_per_page: Option, + ) -> RpcResult>; + + #[method(name = "Post.Insert")] + async fn insert(&self, p: post::Model) -> RpcResult; + + #[method(name = "Post.Update")] + async fn update(&self, p: post::Model) -> RpcResult; + + #[method(name = "Post.Delete")] + async fn delete(&self, id: i32) -> RpcResult; +} + +pub struct PpcImpl { + conn: DatabaseConnection, +} + +#[async_trait] +impl PostRpcServer for PpcImpl { + async fn list( + &self, + page: Option, + posts_per_page: Option, + ) -> RpcResult> { + let page = page.unwrap_or(1); + let posts_per_page = posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE); + let paginator = post::Entity::find() + .order_by_asc(post::Column::Id) + .paginate(&self.conn, posts_per_page); + paginator.fetch_page(page - 1).await.internal_call_error() + } + + async fn insert(&self, p: post::Model) -> RpcResult { + let active_post = post::ActiveModel { + id: NotSet, + title: Set(p.title), + text: Set(p.text), + }; + let new_post = active_post.insert(&self.conn).await.internal_call_error()?; + Ok(new_post.id) + } + + async fn update(&self, p: post::Model) -> RpcResult { + let update_post = post::ActiveModel { + id: Set(p.id), + title: Set(p.title), + text: Set(p.text), + }; + update_post + .update(&self.conn) + .await + .map(|_| true) + .internal_call_error() + } + async fn delete(&self, id: i32) -> RpcResult { + let post = post::Entity::find_by_id(id) + .one(&self.conn) + .await + .internal_call_error()?; + + post.unwrap() + .delete(&self.conn) + .await + .map(|res| res.rows_affected == 1) + .internal_call_error() + } +} + +pub trait IntoJsonRpcResult { + fn internal_call_error(self) -> RpcResult; +} + +impl IntoJsonRpcResult for Result +where + E: Display, +{ + fn internal_call_error(self) -> RpcResult { + self.map_err(|e| jsonrpsee::core::Error::Call(CallError::Failed(anyhow!("{}", e)))) + } +} + +#[tokio::main] +async fn main() -> std::io::Result<()> { + let _ = TermLogger::init( + LevelFilter::Trace, + Config::default(), + TerminalMode::Mixed, + ColorChoice::Auto, + ); + + // get env vars + dotenv::dotenv().ok(); + let db_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file"); + let host = env::var("HOST").expect("HOST is not set in .env file"); + let port = env::var("PORT").expect("PORT is not set in .env file"); + let server_url = format!("{}:{}", host, port); + + // create post table if not exists + let conn = sea_orm::Database::connect(&db_url).await.unwrap(); + Migrator::up(&conn, None).await.unwrap(); + + let server = HttpServerBuilder::default() + .build(server_url.parse::().unwrap()) + .unwrap(); + + let rpc_impl = PpcImpl { conn }; + let server_addr = server.local_addr().unwrap(); + let handle = server.start(rpc_impl.into_rpc()).unwrap(); + + info!("starting listening {}", server_addr); + let mut sig_int = signal(SignalKind::interrupt()).unwrap(); + let mut sig_term = signal(SignalKind::terminate()).unwrap(); + + tokio::select! { + _ = sig_int.recv() => info!("receive SIGINT"), + _ = sig_term.recv() => info!("receive SIGTERM"), + _ = ctrl_c() => info!("receive Ctrl C"), + } + handle.stop().unwrap(); + info!("Shutdown program"); + Ok(()) +} From 9366f9f4b3e4a2ee13d07a6a8139010910f4811f Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Thu, 24 Mar 2022 22:53:54 +0800 Subject: [PATCH 2/2] Update CI --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d6d5f7d05..e504d5e67 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -293,7 +293,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - path: [basic, actix_example, actix4_example, axum_example, axum-graphql_example, rocket_example, poem_example] + path: [basic, actix_example, actix4_example, axum_example, axum-graphql_example, rocket_example, poem_example, jsonrpsee_example] steps: - uses: actions/checkout@v2