add example for integrate with jsonrpsee (#632)

* add example for integrate with jsonrpsee

* Update CI
This commit is contained in:
Mike 2022-03-26 17:40:06 +08:00 committed by GitHub
parent 983a16fc41
commit 290d2c5172
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 392 additions and 1 deletions

View File

@ -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

View File

@ -0,0 +1,4 @@
HOST=127.0.0.1
PORT=8000
#DATABASE_URL="mysql://root:@localhost/poem_example"
DATABASE_URL="sqlite::memory:"

View File

@ -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 = "*"

View File

@ -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
}'
```

View File

@ -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

View File

@ -0,0 +1,3 @@
pub mod post;
pub use sea_orm;

View File

@ -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 {}

View File

@ -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" }

View File

@ -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
```

View File

@ -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<Box<dyn MigrationTrait>> {
vec![Box::new(m20220120_000001_create_post_table::Migration)]
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,7 @@
use migration::Migrator;
use sea_schema::migration::*;
#[async_std::main]
async fn main() {
cli::run_cli(Migrator).await;
}

View File

@ -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<usize>,
posts_per_page: Option<usize>,
) -> RpcResult<Vec<post::Model>>;
#[method(name = "Post.Insert")]
async fn insert(&self, p: post::Model) -> RpcResult<i32>;
#[method(name = "Post.Update")]
async fn update(&self, p: post::Model) -> RpcResult<bool>;
#[method(name = "Post.Delete")]
async fn delete(&self, id: i32) -> RpcResult<bool>;
}
pub struct PpcImpl {
conn: DatabaseConnection,
}
#[async_trait]
impl PostRpcServer for PpcImpl {
async fn list(
&self,
page: Option<usize>,
posts_per_page: Option<usize>,
) -> RpcResult<Vec<post::Model>> {
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<i32> {
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<bool> {
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<bool> {
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<T> {
fn internal_call_error(self) -> RpcResult<T>;
}
impl<T, E> IntoJsonRpcResult<T> for Result<T, E>
where
E: Display,
{
fn internal_call_error(self) -> RpcResult<T> {
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::<SocketAddr>().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(())
}