feat: support to Rocket-Okapi (#1071)
* feat: support to okapi * fix: fmt checks * chore: rocket-okapi-example: add required schemas * chore: rocket-okapi-example: add dto * chore: rocket-okapi-example: add custom error * chore: rocket-okapi-example: add api controllers * chore: rocket-okapi-example: add notes in Readme * chore: make rocket_okapi optional * refactor: delete rocket example from rocket_example * chore: rocket-okapi-example: add base files for okapi example * chore: rocket-okapi-example: add controllers and dto * chore: rocket-okapi-example: add docs
This commit is contained in:
parent
0ca62ba145
commit
2cdc065b21
12
examples/rocket_okapi_example/Cargo.toml
Normal file
12
examples/rocket_okapi_example/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "sea-orm-rocket-okapi-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sam Samai <sam@studio2pi.com.au>", "Erick Pacheco <eum602@gmail.com"]
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = [".", "api", "core", "entity", "migration", "dto"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket-example-api = { path = "api" }
|
12
examples/rocket_okapi_example/README.md
Normal file
12
examples/rocket_okapi_example/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Rocket and Rocket-API with SeaORM example app
|
||||||
|
|
||||||
|
1. Modify the `url` var in `api/Rocket.toml` to point to your chosen database
|
||||||
|
|
||||||
|
1. Turn on the appropriate database feature for your chosen db in `core/Cargo.toml` (the `"sqlx-postgres",` line)
|
||||||
|
|
||||||
|
1. Execute `cargo run` to start the server
|
||||||
|
|
||||||
|
1. You can go to ```http://localhost:8000/swagger-ui/index.html``` to see the api documentation about this demo project.
|
||||||
|

|
||||||
|
1. Additionally, you can navigate to ```http://localhost:8000/rapidoc/index.html``` to see the rapidoc format for api documentation
|
||||||
|

|
11
examples/rocket_okapi_example/Rocket.toml
Normal file
11
examples/rocket_okapi_example/Rocket.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[default]
|
||||||
|
template_dir = "api/templates/"
|
||||||
|
|
||||||
|
[default.databases.sea_orm]
|
||||||
|
# Mysql
|
||||||
|
# make sure to enable "sqlx-mysql" feature in Cargo.toml, i.e default = ["sqlx-mysql"]
|
||||||
|
# url = "mysql://root:@localhost/rocket_example"
|
||||||
|
|
||||||
|
# Postgres
|
||||||
|
# make sure to enable "sqlx-postgres" feature in Cargo.toml, i.e default = ["sqlx-postgres"]
|
||||||
|
url = "postgres://user:pass@localhost:5432/rocket"
|
39
examples/rocket_okapi_example/api/Cargo.toml
Normal file
39
examples/rocket_okapi_example/api/Cargo.toml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[package]
|
||||||
|
name = "rocket-example-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Sam Samai <sam@studio2pi.com.au>"]
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-stream = { version = "^0.3" }
|
||||||
|
async-trait = { version = "0.1" }
|
||||||
|
rocket-example-core = { path = "../core" }
|
||||||
|
futures = { version = "^0.3" }
|
||||||
|
futures-util = { version = "^0.3" }
|
||||||
|
rocket = { version = "0.5.0-rc.1", features = [
|
||||||
|
"json",
|
||||||
|
] }
|
||||||
|
rocket_dyn_templates = { version = "0.1.0-rc.1", features = [
|
||||||
|
"tera",
|
||||||
|
] }
|
||||||
|
serde_json = { version = "^1" }
|
||||||
|
entity = { path = "../entity" }
|
||||||
|
migration = { path = "../migration" }
|
||||||
|
tokio = "1.20.0"
|
||||||
|
serde = "1.0"
|
||||||
|
dto = { path = "../dto" }
|
||||||
|
|
||||||
|
[dependencies.sea-orm-rocket]
|
||||||
|
path = "../../../sea-orm-rocket/lib" # remove this line in your own project and use the git line
|
||||||
|
features = ["rocket_okapi"] #enables rocket_okapi so to have open api features enabled
|
||||||
|
# git = "https://github.com/SeaQL/sea-orm"
|
||||||
|
|
||||||
|
[dependencies.rocket_okapi]
|
||||||
|
version = "0.8.0-rc.2"
|
||||||
|
features = ["swagger", "rapidoc","rocket_db_pools"]
|
||||||
|
|
||||||
|
[dependencies.rocket_cors]
|
||||||
|
git = "https://github.com/lawliet89/rocket_cors.git"
|
||||||
|
rev = "54fae070"
|
||||||
|
default-features = false
|
117
examples/rocket_okapi_example/api/src/error.rs
Normal file
117
examples/rocket_okapi_example/api/src/error.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use rocket::{
|
||||||
|
http::{ContentType, Status},
|
||||||
|
request::Request,
|
||||||
|
response::{self, Responder, Response},
|
||||||
|
};
|
||||||
|
use rocket_okapi::okapi::openapi3::Responses;
|
||||||
|
use rocket_okapi::okapi::schemars::{self, Map};
|
||||||
|
use rocket_okapi::{gen::OpenApiGenerator, response::OpenApiResponderInner, OpenApiError};
|
||||||
|
|
||||||
|
/// Error messages returned to user
|
||||||
|
#[derive(Debug, serde::Serialize, schemars::JsonSchema)]
|
||||||
|
pub struct Error {
|
||||||
|
/// The title of the error message
|
||||||
|
pub err: String,
|
||||||
|
/// The description of the error
|
||||||
|
pub msg: Option<String>,
|
||||||
|
// HTTP Status Code returned
|
||||||
|
#[serde(skip)]
|
||||||
|
pub http_status_code: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenApiResponderInner for Error {
|
||||||
|
fn responses(_generator: &mut OpenApiGenerator) -> Result<Responses, OpenApiError> {
|
||||||
|
use rocket_okapi::okapi::openapi3::{RefOr, Response as OpenApiReponse};
|
||||||
|
|
||||||
|
let mut responses = Map::new();
|
||||||
|
responses.insert(
|
||||||
|
"400".to_string(),
|
||||||
|
RefOr::Object(OpenApiReponse {
|
||||||
|
description: "\
|
||||||
|
# [400 Bad Request](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400)\n\
|
||||||
|
The request given is wrongly formatted or data asked could not be fulfilled. \
|
||||||
|
"
|
||||||
|
.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
responses.insert(
|
||||||
|
"404".to_string(),
|
||||||
|
RefOr::Object(OpenApiReponse {
|
||||||
|
description: "\
|
||||||
|
# [404 Not Found](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404)\n\
|
||||||
|
This response is given when you request a page that does not exists.\
|
||||||
|
"
|
||||||
|
.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
responses.insert(
|
||||||
|
"422".to_string(),
|
||||||
|
RefOr::Object(OpenApiReponse {
|
||||||
|
description: "\
|
||||||
|
# [422 Unprocessable Entity](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422)\n\
|
||||||
|
This response is given when you request body is not correctly formatted. \
|
||||||
|
".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
responses.insert(
|
||||||
|
"500".to_string(),
|
||||||
|
RefOr::Object(OpenApiReponse {
|
||||||
|
description: "\
|
||||||
|
# [500 Internal Server Error](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500)\n\
|
||||||
|
This response is given when something wend wrong on the server. \
|
||||||
|
".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
Ok(Responses {
|
||||||
|
responses,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
formatter,
|
||||||
|
"Error `{}`: {}",
|
||||||
|
self.err,
|
||||||
|
self.msg.as_deref().unwrap_or("<no message>")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
impl<'r> Responder<'r, 'static> for Error {
|
||||||
|
fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
|
||||||
|
// Convert object to json
|
||||||
|
let body = serde_json::to_string(&self).unwrap();
|
||||||
|
Response::build()
|
||||||
|
.sized_body(body.len(), std::io::Cursor::new(body))
|
||||||
|
.header(ContentType::JSON)
|
||||||
|
.status(Status::new(self.http_status_code))
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<rocket::serde::json::Error<'_>> for Error {
|
||||||
|
fn from(err: rocket::serde::json::Error) -> Self {
|
||||||
|
use rocket::serde::json::Error::*;
|
||||||
|
match err {
|
||||||
|
Io(io_error) => Error {
|
||||||
|
err: "IO Error".to_owned(),
|
||||||
|
msg: Some(io_error.to_string()),
|
||||||
|
http_status_code: 422,
|
||||||
|
},
|
||||||
|
Parse(_raw_data, parse_error) => Error {
|
||||||
|
err: "Parse Error".to_owned(),
|
||||||
|
msg: Some(parse_error.to_string()),
|
||||||
|
http_status_code: 422,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
139
examples/rocket_okapi_example/api/src/lib.rs
Normal file
139
examples/rocket_okapi_example/api/src/lib.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
use rocket::fairing::{self, AdHoc};
|
||||||
|
use rocket::{Build, Rocket};
|
||||||
|
|
||||||
|
use migration::MigratorTrait;
|
||||||
|
use sea_orm_rocket::Database;
|
||||||
|
|
||||||
|
use rocket_okapi::mount_endpoints_and_merged_docs;
|
||||||
|
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||||
|
use rocket_okapi::rapidoc::{make_rapidoc, GeneralConfig, HideShowConfig, RapiDocConfig};
|
||||||
|
use rocket_okapi::settings::UrlObject;
|
||||||
|
use rocket_okapi::swagger_ui::{make_swagger_ui, SwaggerUIConfig};
|
||||||
|
|
||||||
|
use rocket::http::Method;
|
||||||
|
use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors};
|
||||||
|
|
||||||
|
mod pool;
|
||||||
|
use pool::Db;
|
||||||
|
mod error;
|
||||||
|
mod okapi_example;
|
||||||
|
|
||||||
|
pub use entity::post;
|
||||||
|
pub use entity::post::Entity as Post;
|
||||||
|
|
||||||
|
async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
|
||||||
|
let conn = &Db::fetch(&rocket).unwrap().conn;
|
||||||
|
let _ = migration::Migrator::up(conn, None).await;
|
||||||
|
Ok(rocket)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn start() -> Result<(), rocket::Error> {
|
||||||
|
let mut building_rocket = rocket::build()
|
||||||
|
.attach(Db::init())
|
||||||
|
.attach(AdHoc::try_on_ignite("Migrations", run_migrations))
|
||||||
|
.mount(
|
||||||
|
"/swagger-ui/",
|
||||||
|
make_swagger_ui(&SwaggerUIConfig {
|
||||||
|
url: "../v1/openapi.json".to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.mount(
|
||||||
|
"/rapidoc/",
|
||||||
|
make_rapidoc(&RapiDocConfig {
|
||||||
|
title: Some("Rocket/SeaOrm - RapiDoc documentation | RapiDoc".to_owned()),
|
||||||
|
general: GeneralConfig {
|
||||||
|
spec_urls: vec![UrlObject::new("General", "../v1/openapi.json")],
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
hide_show: HideShowConfig {
|
||||||
|
allow_spec_url_load: false,
|
||||||
|
allow_spec_file_load: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.attach(cors());
|
||||||
|
|
||||||
|
let openapi_settings = rocket_okapi::settings::OpenApiSettings::default();
|
||||||
|
let custom_route_spec = (vec![], custom_openapi_spec());
|
||||||
|
mount_endpoints_and_merged_docs! {
|
||||||
|
building_rocket, "/v1".to_owned(), openapi_settings,
|
||||||
|
"/additional" => custom_route_spec,
|
||||||
|
"/okapi-example" => okapi_example::get_routes_and_docs(&openapi_settings),
|
||||||
|
};
|
||||||
|
|
||||||
|
building_rocket.launch().await.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cors() -> Cors {
|
||||||
|
let allowed_origins =
|
||||||
|
AllowedOrigins::some_exact(&["http://localhost:8000", "http://127.0.0.1:8000"]);
|
||||||
|
|
||||||
|
let cors = rocket_cors::CorsOptions {
|
||||||
|
allowed_origins,
|
||||||
|
allowed_methods: vec![Method::Get, Method::Post, Method::Delete]
|
||||||
|
.into_iter()
|
||||||
|
.map(From::from)
|
||||||
|
.collect(),
|
||||||
|
allowed_headers: AllowedHeaders::all(),
|
||||||
|
allow_credentials: true,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.to_cors()
|
||||||
|
.unwrap();
|
||||||
|
cors
|
||||||
|
}
|
||||||
|
|
||||||
|
fn custom_openapi_spec() -> OpenApi {
|
||||||
|
use rocket_okapi::okapi::openapi3::*;
|
||||||
|
OpenApi {
|
||||||
|
openapi: OpenApi::default_version(),
|
||||||
|
info: Info {
|
||||||
|
title: "SeaOrm-Rocket-Okapi Example".to_owned(),
|
||||||
|
description: Some("API Docs for Rocket/SeaOrm example".to_owned()),
|
||||||
|
terms_of_service: Some("https://github.com/SeaQL/sea-orm#license".to_owned()),
|
||||||
|
contact: Some(Contact {
|
||||||
|
name: Some("SeaOrm".to_owned()),
|
||||||
|
url: Some("https://github.com/SeaQL/sea-orm".to_owned()),
|
||||||
|
email: None,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
license: Some(License {
|
||||||
|
name: "MIT".to_owned(),
|
||||||
|
url: Some("https://github.com/SeaQL/sea-orm/blob/master/LICENSE-MIT".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
servers: vec![
|
||||||
|
Server {
|
||||||
|
url: "http://127.0.0.1:8000/v1".to_owned(),
|
||||||
|
description: Some("Localhost".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
Server {
|
||||||
|
url: "https://production-server.com/".to_owned(),
|
||||||
|
description: Some("Remote development server".to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let result = start();
|
||||||
|
|
||||||
|
println!("Rocket: deorbit.");
|
||||||
|
|
||||||
|
if let Some(err) = result.err() {
|
||||||
|
println!("Error: {}", err);
|
||||||
|
}
|
||||||
|
}
|
166
examples/rocket_okapi_example/api/src/okapi_example.rs
Normal file
166
examples/rocket_okapi_example/api/src/okapi_example.rs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
use dto::dto;
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use rocket_example_core::{Mutation, Query};
|
||||||
|
|
||||||
|
use sea_orm_rocket::Connection;
|
||||||
|
|
||||||
|
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||||
|
|
||||||
|
use crate::error;
|
||||||
|
use crate::pool;
|
||||||
|
use pool::Db;
|
||||||
|
|
||||||
|
pub use entity::post;
|
||||||
|
pub use entity::post::Entity as Post;
|
||||||
|
|
||||||
|
use rocket_okapi::settings::OpenApiSettings;
|
||||||
|
|
||||||
|
use rocket_okapi::{openapi, openapi_get_routes_spec};
|
||||||
|
|
||||||
|
const DEFAULT_POSTS_PER_PAGE: u64 = 5;
|
||||||
|
|
||||||
|
pub fn get_routes_and_docs(settings: &OpenApiSettings) -> (Vec<rocket::Route>, OpenApi) {
|
||||||
|
openapi_get_routes_spec![settings: create, update, list, get_by_id, delete, destroy]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type R<T> = std::result::Result<rocket::serde::json::Json<T>, error::Error>;
|
||||||
|
pub type DataResult<'a, T> =
|
||||||
|
std::result::Result<rocket::serde::json::Json<T>, rocket::serde::json::Error<'a>>;
|
||||||
|
|
||||||
|
/// # Add a new post
|
||||||
|
#[openapi(tag = "POST")]
|
||||||
|
#[post("/", data = "<post_data>")]
|
||||||
|
async fn create(
|
||||||
|
conn: Connection<'_, Db>,
|
||||||
|
post_data: DataResult<'_, post::Model>,
|
||||||
|
) -> R<Option<String>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
let form = post_data?.into_inner();
|
||||||
|
let cmd = Mutation::create_post(db, form);
|
||||||
|
match cmd.await {
|
||||||
|
Ok(_) => Ok(Json(Some("Post successfully added.".to_string()))),
|
||||||
|
Err(e) => {
|
||||||
|
let m = error::Error {
|
||||||
|
err: "Could not insert post".to_string(),
|
||||||
|
msg: Some(e.to_string()),
|
||||||
|
http_status_code: 400,
|
||||||
|
};
|
||||||
|
Err(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Update a post
|
||||||
|
#[openapi(tag = "POST")]
|
||||||
|
#[post("/<id>", data = "<post_data>")]
|
||||||
|
async fn update(
|
||||||
|
conn: Connection<'_, Db>,
|
||||||
|
id: i32,
|
||||||
|
post_data: DataResult<'_, post::Model>,
|
||||||
|
) -> R<Option<String>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
|
||||||
|
let form = post_data?.into_inner();
|
||||||
|
|
||||||
|
let cmd = Mutation::update_post_by_id(db, id, form);
|
||||||
|
match cmd.await {
|
||||||
|
Ok(_) => Ok(Json(Some("Post successfully updated.".to_string()))),
|
||||||
|
Err(e) => {
|
||||||
|
let m = error::Error {
|
||||||
|
err: "Could not update post".to_string(),
|
||||||
|
msg: Some(e.to_string()),
|
||||||
|
http_status_code: 400,
|
||||||
|
};
|
||||||
|
Err(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Get post list
|
||||||
|
#[openapi(tag = "POST")]
|
||||||
|
#[get("/?<page>&<posts_per_page>")]
|
||||||
|
async fn list(
|
||||||
|
conn: Connection<'_, Db>,
|
||||||
|
page: Option<u64>,
|
||||||
|
posts_per_page: Option<u64>,
|
||||||
|
) -> R<dto::PostsDto> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
|
||||||
|
// Set page number and items per page
|
||||||
|
let page = page.unwrap_or(1);
|
||||||
|
let posts_per_page = posts_per_page.unwrap_or(DEFAULT_POSTS_PER_PAGE);
|
||||||
|
if page == 0 {
|
||||||
|
let m = error::Error {
|
||||||
|
err: "error getting posts".to_string(),
|
||||||
|
msg: Some("'page' param cannot be zero".to_string()),
|
||||||
|
http_status_code: 400,
|
||||||
|
};
|
||||||
|
return Err(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (posts, num_pages) = Query::find_posts_in_page(db, page, posts_per_page)
|
||||||
|
.await
|
||||||
|
.expect("Cannot find posts in page");
|
||||||
|
|
||||||
|
Ok(Json(dto::PostsDto {
|
||||||
|
page,
|
||||||
|
posts_per_page,
|
||||||
|
num_pages,
|
||||||
|
posts,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # get post by Id
|
||||||
|
#[openapi(tag = "POST")]
|
||||||
|
#[get("/<id>")]
|
||||||
|
async fn get_by_id(conn: Connection<'_, Db>, id: i32) -> R<Option<post::Model>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
|
||||||
|
let post: Option<post::Model> = Query::find_post_by_id(db, id)
|
||||||
|
.await
|
||||||
|
.expect("could not find post");
|
||||||
|
Ok(Json(post))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # delete post by Id
|
||||||
|
#[openapi(tag = "POST")]
|
||||||
|
#[delete("/<id>")]
|
||||||
|
async fn delete(conn: Connection<'_, Db>, id: i32) -> R<Option<String>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
|
||||||
|
let cmd = Mutation::delete_post(db, id);
|
||||||
|
match cmd.await {
|
||||||
|
Ok(_) => Ok(Json(Some("Post successfully deleted.".to_string()))),
|
||||||
|
Err(e) => {
|
||||||
|
let m = error::Error {
|
||||||
|
err: "Error deleting post".to_string(),
|
||||||
|
msg: Some(e.to_string()),
|
||||||
|
http_status_code: 400,
|
||||||
|
};
|
||||||
|
Err(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # delete all posts
|
||||||
|
#[openapi(tag = "POST")]
|
||||||
|
#[delete("/")]
|
||||||
|
async fn destroy(conn: Connection<'_, Db>) -> R<Option<String>> {
|
||||||
|
let db = conn.into_inner();
|
||||||
|
|
||||||
|
let cmd = Mutation::delete_all_posts(db);
|
||||||
|
|
||||||
|
match cmd.await {
|
||||||
|
Ok(_) => Ok(Json(Some(
|
||||||
|
"All Posts were successfully deleted.".to_string(),
|
||||||
|
))),
|
||||||
|
Err(e) => {
|
||||||
|
let m = error::Error {
|
||||||
|
err: "Error deleting all posts at once".to_string(),
|
||||||
|
msg: Some(e.to_string()),
|
||||||
|
http_status_code: 400,
|
||||||
|
};
|
||||||
|
Err(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
examples/rocket_okapi_example/api/src/pool.rs
Normal file
41
examples/rocket_okapi_example/api/src/pool.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
use rocket_example_core::sea_orm;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use sea_orm::ConnectOptions;
|
||||||
|
use sea_orm_rocket::{rocket::figment::Figment, Config, Database};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Database, Debug)]
|
||||||
|
#[database("sea_orm")]
|
||||||
|
pub struct Db(SeaOrmPool);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SeaOrmPool {
|
||||||
|
pub conn: sea_orm::DatabaseConnection,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl sea_orm_rocket::Pool for SeaOrmPool {
|
||||||
|
type Error = sea_orm::DbErr;
|
||||||
|
|
||||||
|
type Connection = sea_orm::DatabaseConnection;
|
||||||
|
|
||||||
|
async fn init(figment: &Figment) -> Result<Self, Self::Error> {
|
||||||
|
let config = figment.extract::<Config>().unwrap();
|
||||||
|
let mut options: ConnectOptions = config.url.into();
|
||||||
|
options
|
||||||
|
.max_connections(config.max_connections as u32)
|
||||||
|
.min_connections(config.min_connections.unwrap_or_default())
|
||||||
|
.connect_timeout(Duration::from_secs(config.connect_timeout));
|
||||||
|
if let Some(idle_timeout) = config.idle_timeout {
|
||||||
|
options.idle_timeout(Duration::from_secs(idle_timeout));
|
||||||
|
}
|
||||||
|
let conn = sea_orm::Database::connect(options).await?;
|
||||||
|
|
||||||
|
Ok(SeaOrmPool { conn })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn borrow(&self) -> &Self::Connection {
|
||||||
|
&self.conn
|
||||||
|
}
|
||||||
|
}
|
29
examples/rocket_okapi_example/core/Cargo.toml
Normal file
29
examples/rocket_okapi_example/core/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "rocket-example-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
entity = { path = "../entity" }
|
||||||
|
|
||||||
|
[dependencies.sea-orm]
|
||||||
|
path = "../../../" # remove this line in your own project
|
||||||
|
version = "^0.10.0" # sea-orm version
|
||||||
|
features = [
|
||||||
|
"runtime-tokio-native-tls",
|
||||||
|
"sqlx-postgres",
|
||||||
|
# "sqlx-mysql",
|
||||||
|
# "sqlx-sqlite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = "1.20.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
mock = ["sea-orm/mock"]
|
||||||
|
|
||||||
|
[[test]]
|
||||||
|
name = "mock"
|
||||||
|
required-features = ["mock"]
|
7
examples/rocket_okapi_example/core/src/lib.rs
Normal file
7
examples/rocket_okapi_example/core/src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
mod mutation;
|
||||||
|
mod query;
|
||||||
|
|
||||||
|
pub use mutation::*;
|
||||||
|
pub use query::*;
|
||||||
|
|
||||||
|
pub use sea_orm;
|
53
examples/rocket_okapi_example/core/src/mutation.rs
Normal file
53
examples/rocket_okapi_example/core/src/mutation.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use ::entity::{post, post::Entity as Post};
|
||||||
|
use sea_orm::*;
|
||||||
|
|
||||||
|
pub struct Mutation;
|
||||||
|
|
||||||
|
impl Mutation {
|
||||||
|
pub async fn create_post(
|
||||||
|
db: &DbConn,
|
||||||
|
form_data: post::Model,
|
||||||
|
) -> Result<post::ActiveModel, DbErr> {
|
||||||
|
post::ActiveModel {
|
||||||
|
title: Set(form_data.title.to_owned()),
|
||||||
|
text: Set(form_data.text.to_owned()),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.save(db)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_post_by_id(
|
||||||
|
db: &DbConn,
|
||||||
|
id: i32,
|
||||||
|
form_data: post::Model,
|
||||||
|
) -> Result<post::Model, DbErr> {
|
||||||
|
let post: post::ActiveModel = Post::find_by_id(id)
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
.ok_or(DbErr::Custom("Cannot find post.".to_owned()))
|
||||||
|
.map(Into::into)?;
|
||||||
|
|
||||||
|
post::ActiveModel {
|
||||||
|
id: post.id,
|
||||||
|
title: Set(form_data.title.to_owned()),
|
||||||
|
text: Set(form_data.text.to_owned()),
|
||||||
|
}
|
||||||
|
.update(db)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_post(db: &DbConn, id: i32) -> Result<DeleteResult, DbErr> {
|
||||||
|
let post: post::ActiveModel = Post::find_by_id(id)
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
.ok_or(DbErr::Custom("Cannot find post.".to_owned()))
|
||||||
|
.map(Into::into)?;
|
||||||
|
|
||||||
|
post.delete(db).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_all_posts(db: &DbConn) -> Result<DeleteResult, DbErr> {
|
||||||
|
Post::delete_many().exec(db).await
|
||||||
|
}
|
||||||
|
}
|
26
examples/rocket_okapi_example/core/src/query.rs
Normal file
26
examples/rocket_okapi_example/core/src/query.rs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
use ::entity::{post, post::Entity as Post};
|
||||||
|
use sea_orm::*;
|
||||||
|
|
||||||
|
pub struct Query;
|
||||||
|
|
||||||
|
impl Query {
|
||||||
|
pub async fn find_post_by_id(db: &DbConn, id: i32) -> Result<Option<post::Model>, DbErr> {
|
||||||
|
Post::find_by_id(id).one(db).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If ok, returns (post models, num pages).
|
||||||
|
pub async fn find_posts_in_page(
|
||||||
|
db: &DbConn,
|
||||||
|
page: u64,
|
||||||
|
posts_per_page: u64,
|
||||||
|
) -> Result<(Vec<post::Model>, u64), DbErr> {
|
||||||
|
// Setup paginator
|
||||||
|
let paginator = Post::find()
|
||||||
|
.order_by_asc(post::Column::Id)
|
||||||
|
.paginate(db, posts_per_page);
|
||||||
|
let num_pages = paginator.num_pages().await?;
|
||||||
|
|
||||||
|
// Fetch paginated posts
|
||||||
|
paginator.fetch_page(page - 1).await.map(|p| (p, num_pages))
|
||||||
|
}
|
||||||
|
}
|
79
examples/rocket_okapi_example/core/tests/mock.rs
Normal file
79
examples/rocket_okapi_example/core/tests/mock.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
mod prepare;
|
||||||
|
|
||||||
|
use entity::post;
|
||||||
|
use prepare::prepare_mock_db;
|
||||||
|
use rocket_example_core::{Mutation, Query};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn main() {
|
||||||
|
let db = &prepare_mock_db();
|
||||||
|
|
||||||
|
{
|
||||||
|
let post = Query::find_post_by_id(db, 1).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(post.id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let post = Query::find_post_by_id(db, 5).await.unwrap().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(post.id, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let post = Mutation::create_post(
|
||||||
|
db,
|
||||||
|
post::Model {
|
||||||
|
id: 0,
|
||||||
|
title: "Title D".to_owned(),
|
||||||
|
text: "Text D".to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
post,
|
||||||
|
post::ActiveModel {
|
||||||
|
id: sea_orm::ActiveValue::Unchanged(6),
|
||||||
|
title: sea_orm::ActiveValue::Unchanged("Title D".to_owned()),
|
||||||
|
text: sea_orm::ActiveValue::Unchanged("Text D".to_owned())
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let post = Mutation::update_post_by_id(
|
||||||
|
db,
|
||||||
|
1,
|
||||||
|
post::Model {
|
||||||
|
id: 1,
|
||||||
|
title: "New Title A".to_owned(),
|
||||||
|
text: "New Text A".to_owned(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
post,
|
||||||
|
post::Model {
|
||||||
|
id: 1,
|
||||||
|
title: "New Title A".to_owned(),
|
||||||
|
text: "New Text A".to_owned(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let result = Mutation::delete_post(db, 5).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.rows_affected, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let result = Mutation::delete_all_posts(db).await.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result.rows_affected, 5);
|
||||||
|
}
|
||||||
|
}
|
50
examples/rocket_okapi_example/core/tests/prepare.rs
Normal file
50
examples/rocket_okapi_example/core/tests/prepare.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use ::entity::post;
|
||||||
|
use sea_orm::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "mock")]
|
||||||
|
pub fn prepare_mock_db() -> DatabaseConnection {
|
||||||
|
MockDatabase::new(DatabaseBackend::Postgres)
|
||||||
|
.append_query_results(vec![
|
||||||
|
vec![post::Model {
|
||||||
|
id: 1,
|
||||||
|
title: "Title A".to_owned(),
|
||||||
|
text: "Text A".to_owned(),
|
||||||
|
}],
|
||||||
|
vec![post::Model {
|
||||||
|
id: 5,
|
||||||
|
title: "Title C".to_owned(),
|
||||||
|
text: "Text C".to_owned(),
|
||||||
|
}],
|
||||||
|
vec![post::Model {
|
||||||
|
id: 6,
|
||||||
|
title: "Title D".to_owned(),
|
||||||
|
text: "Text D".to_owned(),
|
||||||
|
}],
|
||||||
|
vec![post::Model {
|
||||||
|
id: 1,
|
||||||
|
title: "Title A".to_owned(),
|
||||||
|
text: "Text A".to_owned(),
|
||||||
|
}],
|
||||||
|
vec![post::Model {
|
||||||
|
id: 1,
|
||||||
|
title: "New Title A".to_owned(),
|
||||||
|
text: "New Text A".to_owned(),
|
||||||
|
}],
|
||||||
|
vec![post::Model {
|
||||||
|
id: 5,
|
||||||
|
title: "Title C".to_owned(),
|
||||||
|
text: "Text C".to_owned(),
|
||||||
|
}],
|
||||||
|
])
|
||||||
|
.append_exec_results(vec![
|
||||||
|
MockExecResult {
|
||||||
|
last_insert_id: 6,
|
||||||
|
rows_affected: 1,
|
||||||
|
},
|
||||||
|
MockExecResult {
|
||||||
|
last_insert_id: 6,
|
||||||
|
rows_affected: 5,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.into_connection()
|
||||||
|
}
|
20
examples/rocket_okapi_example/dto/Cargo.toml
Normal file
20
examples/rocket_okapi_example/dto/Cargo.toml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "dto"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "dto"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { version = "0.5.0-rc.1", features = [
|
||||||
|
"json",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[dependencies.entity]
|
||||||
|
path = "../entity"
|
||||||
|
|
||||||
|
[dependencies.rocket_okapi]
|
||||||
|
version = "0.8.0-rc.2"
|
12
examples/rocket_okapi_example/dto/src/dto.rs
Normal file
12
examples/rocket_okapi_example/dto/src/dto.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use entity::*;
|
||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
use rocket_okapi::okapi::schemars::{self, JsonSchema};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct PostsDto {
|
||||||
|
pub page: u64,
|
||||||
|
pub posts_per_page: u64,
|
||||||
|
pub num_pages: u64,
|
||||||
|
pub posts: Vec<post::Model>,
|
||||||
|
}
|
1
examples/rocket_okapi_example/dto/src/lib.rs
Normal file
1
examples/rocket_okapi_example/dto/src/lib.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod dto;
|
21
examples/rocket_okapi_example/entity/Cargo.toml
Normal file
21
examples/rocket_okapi_example/entity/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "entity"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "entity"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { version = "0.5.0-rc.1", features = [
|
||||||
|
"json",
|
||||||
|
] }
|
||||||
|
|
||||||
|
[dependencies.sea-orm]
|
||||||
|
path = "../../../" # remove this line in your own project
|
||||||
|
version = "^0.10.0" # sea-orm version
|
||||||
|
|
||||||
|
[dependencies.rocket_okapi]
|
||||||
|
version = "0.8.0-rc.2"
|
4
examples/rocket_okapi_example/entity/src/lib.rs
Normal file
4
examples/rocket_okapi_example/entity/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
pub mod post;
|
21
examples/rocket_okapi_example/entity/src/post.rs
Normal file
21
examples/rocket_okapi_example/entity/src/post.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
use rocket_okapi::okapi::schemars::{self, JsonSchema};
|
||||||
|
use sea_orm::entity::prelude::*;
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize, FromForm, JsonSchema,
|
||||||
|
)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
#[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 {}
|
22
examples/rocket_okapi_example/migration/Cargo.toml
Normal file
22
examples/rocket_okapi_example/migration/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "migration"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "migration"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rocket = { version = "0.5.0-rc.1" }
|
||||||
|
async-std = { version = "^1", features = ["attributes", "tokio1"] }
|
||||||
|
|
||||||
|
[dependencies.sea-orm-migration]
|
||||||
|
path = "../../../sea-orm-migration" # remove this line in your own project
|
||||||
|
version = "^0.10.0" # sea-orm-migration version
|
||||||
|
features = [
|
||||||
|
# Enable following runtime and db backend features if you want to run migration via CLI
|
||||||
|
# "runtime-tokio-native-tls",
|
||||||
|
# "sqlx-postgres",
|
||||||
|
]
|
37
examples/rocket_okapi_example/migration/README.md
Normal file
37
examples/rocket_okapi_example/migration/README.md
Normal 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
|
||||||
|
```
|
12
examples/rocket_okapi_example/migration/src/lib.rs
Normal file
12
examples/rocket_okapi_example/migration/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
pub use sea_orm_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)]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.create_table(
|
||||||
|
Table::create()
|
||||||
|
.table(Posts::Table)
|
||||||
|
.if_not_exists()
|
||||||
|
.col(
|
||||||
|
ColumnDef::new(Posts::Id)
|
||||||
|
.integer()
|
||||||
|
.not_null()
|
||||||
|
.auto_increment()
|
||||||
|
.primary_key(),
|
||||||
|
)
|
||||||
|
.col(ColumnDef::new(Posts::Title).string().not_null())
|
||||||
|
.col(ColumnDef::new(Posts::Text).string().not_null())
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
manager
|
||||||
|
.drop_table(Table::drop().table(Posts::Table).to_owned())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Learn more at https://docs.rs/sea-query#iden
|
||||||
|
#[derive(Iden)]
|
||||||
|
enum Posts {
|
||||||
|
Table,
|
||||||
|
Id,
|
||||||
|
Title,
|
||||||
|
Text,
|
||||||
|
}
|
17
examples/rocket_okapi_example/migration/src/main.rs
Normal file
17
examples/rocket_okapi_example/migration/src/main.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
// Setting `DATABASE_URL` environment variable
|
||||||
|
let key = "DATABASE_URL";
|
||||||
|
if std::env::var(key).is_err() {
|
||||||
|
// Getting the database URL from Rocket.toml if it's not set
|
||||||
|
let figment = rocket::Config::figment();
|
||||||
|
let database_url: String = figment
|
||||||
|
.extract_inner("databases.sea_orm.url")
|
||||||
|
.expect("Cannot find Database URL in Rocket.toml");
|
||||||
|
std::env::set_var(key, database_url);
|
||||||
|
}
|
||||||
|
|
||||||
|
cli::run_cli(migration::Migrator).await;
|
||||||
|
}
|
BIN
examples/rocket_okapi_example/rapidoc.png
Normal file
BIN
examples/rocket_okapi_example/rapidoc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
3
examples/rocket_okapi_example/src/main.rs
Normal file
3
examples/rocket_okapi_example/src/main.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
rocket_example_api::main();
|
||||||
|
}
|
BIN
examples/rocket_okapi_example/swagger.png
Normal file
BIN
examples/rocket_okapi_example/swagger.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
@ -24,3 +24,8 @@ version = "0.5.0-rc.1"
|
|||||||
version = "0.5.0-rc.1"
|
version = "0.5.0-rc.1"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["json"]
|
features = ["json"]
|
||||||
|
|
||||||
|
[dependencies.rocket_okapi]
|
||||||
|
version = "0.8.0-rc.2"
|
||||||
|
default-features = false
|
||||||
|
optional = true
|
||||||
|
@ -9,6 +9,11 @@ use rocket::{error, info_, Build, Ignite, Phase, Rocket, Sentinel};
|
|||||||
use rocket::figment::providers::Serialized;
|
use rocket::figment::providers::Serialized;
|
||||||
use rocket::yansi::Paint;
|
use rocket::yansi::Paint;
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket_okapi")]
|
||||||
|
use rocket_okapi::gen::OpenApiGenerator;
|
||||||
|
#[cfg(feature = "rocket_okapi")]
|
||||||
|
use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
|
||||||
|
|
||||||
use crate::Pool;
|
use crate::Pool;
|
||||||
|
|
||||||
/// Derivable trait which ties a database [`Pool`] with a configuration name.
|
/// Derivable trait which ties a database [`Pool`] with a configuration name.
|
||||||
@ -205,6 +210,17 @@ impl<'a, D: Database> Connection<'a, D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rocket_okapi")]
|
||||||
|
impl<'r, D: Database> OpenApiFromRequest<'r> for Connection<'r, D> {
|
||||||
|
fn from_request_input(
|
||||||
|
_gen: &mut OpenApiGenerator,
|
||||||
|
_name: String,
|
||||||
|
_required: bool,
|
||||||
|
) -> rocket_okapi::Result<RequestHeaderInput> {
|
||||||
|
Ok(RequestHeaderInput::None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<D: Database> Fairing for Initializer<D> {
|
impl<D: Database> Fairing for Initializer<D> {
|
||||||
fn info(&self) -> Info {
|
fn info(&self) -> Info {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user