Skip to content

Commit

Permalink
Allow set OpenAPI UIs title, keywords and description (#507)
Browse files Browse the repository at this point in the history
* Allow set OpenAPI UIs title, keywords and description

* update example
  • Loading branch information
chrislearn authored Nov 24, 2023
1 parent 8325670 commit d23fc50
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 35 deletions.
34 changes: 17 additions & 17 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"

[workspace.package]
version = "0.58.4"
version = "0.58.5"
authors = ["Chrislearn Young <[email protected]>"]
edition = "2021"
rust-version = "1.67"
Expand Down Expand Up @@ -83,23 +83,23 @@ rustls = "0.21"
rustls-pemfile = "1"
rust-embed = { version = ">= 6, <= 8" }
salvo-utils = { version = "0.0.6", default-features = true }
salvo_macros = { version = "0.58.4", path = "crates/macros", default-features = false }
salvo_core = { version = "0.58.4", path = "crates/core", default-features = false }
salvo_extra = { version = "0.58.4", path = "crates/extra", default-features = false }
salvo-compression = { version = "0.58.4", path = "crates/compression", default-features = false }
salvo-cache = { version = "0.58.4", path = "crates/cache", default-features = false }
salvo-cors = { version = "0.58.4", path = "crates/cors", default-features = false }
salvo-csrf = { version = "0.58.4", path = "crates/csrf", default-features = false }
salvo-flash = { version = "0.58.4", path = "crates/flash", default-features = false }
salvo_macros = { version = "0.58.5", path = "crates/macros", default-features = false }
salvo_core = { version = "0.58.5", path = "crates/core", default-features = false }
salvo_extra = { version = "0.58.5", path = "crates/extra", default-features = false }
salvo-compression = { version = "0.58.5", path = "crates/compression", default-features = false }
salvo-cache = { version = "0.58.5", path = "crates/cache", default-features = false }
salvo-cors = { version = "0.58.5", path = "crates/cors", default-features = false }
salvo-csrf = { version = "0.58.5", path = "crates/csrf", default-features = false }
salvo-flash = { version = "0.58.5", path = "crates/flash", default-features = false }
salvo-http3 = { version = "0.0.4", default-features = false }
salvo-jwt-auth = { version = "0.58.4", path = "crates/jwt-auth", default-features = false }
salvo-oapi = { version = "0.58.4", path = "./crates/oapi", default-features = false }
salvo-oapi-macros = { version = "0.58.4", path = "crates/oapi-macros", default-features = false }
salvo-otel = { version = "0.58.4", path = "crates/otel", default-features = false }
salvo-proxy = { version = "0.58.4", path = "crates/proxy", default-features = false }
salvo-rate-limiter = { version = "0.58.4", path = "crates/rate-limiter", default-features = false }
salvo-serve-static = { version = "0.58.4", path = "crates/serve-static", default-features = false }
salvo-session = { version = "0.58.4", path = "crates/session", default-features = false }
salvo-jwt-auth = { version = "0.58.5", path = "crates/jwt-auth", default-features = false }
salvo-oapi = { version = "0.58.5", path = "./crates/oapi", default-features = false }
salvo-oapi-macros = { version = "0.58.5", path = "crates/oapi-macros", default-features = false }
salvo-otel = { version = "0.58.5", path = "crates/otel", default-features = false }
salvo-proxy = { version = "0.58.5", path = "crates/proxy", default-features = false }
salvo-rate-limiter = { version = "0.58.5", path = "crates/rate-limiter", default-features = false }
salvo-serve-static = { version = "0.58.5", path = "crates/serve-static", default-features = false }
salvo-session = { version = "0.58.5", path = "crates/session", default-features = false }
serde = "1"
serde_json = "1"
serde-xml-rs = "0.6"
Expand Down
52 changes: 50 additions & 2 deletions crates/oapi/src/rapidoc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const INDEX_TMPL: &str = r#"
<!doctype html>
<html>
<head>
<title>{{title}}</title>
{{keywords}}
{{description}}
<meta charset="utf-8">
<script type="module" src="{{lib_url}}"></script>
</head>
Expand All @@ -24,6 +27,12 @@ const INDEX_TMPL: &str = r#"
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct RapiDoc {
/// The title of the html page. The default title is "RapiDoc".
pub title: String,
/// The version of the html page.
pub keywords: Option<String>,
/// The description of the html page.
pub description: Option<String>,
/// The lib url path.
pub lib_url: String,
/// The spec url path.
Expand All @@ -43,11 +52,32 @@ impl RapiDoc {
/// ```
pub fn new(spec_url: impl Into<String>) -> Self {
Self {
title: "RapiDoc".into(),
keywords: None,
description: None,
lib_url: "https://unpkg.com/rapidoc/dist/rapidoc-min.js".into(),
spec_url: spec_url.into(),
}
}

/// Set title of the html page. The default title is "RapiDoc".
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}

/// Set keywords of the html page.
pub fn keywords(mut self, keywords: impl Into<String>) -> Self {
self.keywords = Some(keywords.into());
self
}

/// Set description of the html page.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}

/// Set the lib url path.
pub fn lib_url(mut self, lib_url: impl Into<String>) -> Self {
self.lib_url = lib_url.into();
Expand All @@ -63,9 +93,27 @@ impl RapiDoc {
#[async_trait]
impl Handler for RapiDoc {
async fn handle(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) {
let keywords = self
.keywords
.as_ref()
.map(|s| {
format!(
"<meta name=\"keywords\" content=\"{}\">",
s.split(',').map(|s| s.trim()).collect::<Vec<_>>().join(",")
)
})
.unwrap_or_default();
let description = self
.description
.as_ref()
.map(|s| format!("<meta name=\"description\" content=\"{}\">", s))
.unwrap_or_default();
let html = INDEX_TMPL
.replace("{{lib_url}}", &self.lib_url)
.replace("{{spec_url}}", &self.spec_url);
.replacen("{{lib_url}}", &self.lib_url, 1)
.replacen("{{spec_url}}", &self.spec_url, 1)
.replacen("{{title}}", &self.title, 1)
.replacen("{{keywords}}", &keywords, 1)
.replacen("{{description}}", &description, 1);
res.render(Text::Html(html));
}
}
53 changes: 50 additions & 3 deletions crates/oapi/src/redoc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const INDEX_TMPL: &str = r#"
<!DOCTYPE html>
<html>
<head>
<title>Redoc</title>
<title>{{title}}</title>
{{keywords}}
{{description}}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="{{css_url}}" rel="stylesheet"/>
Expand Down Expand Up @@ -41,6 +43,12 @@ const INDEX_TMPL: &str = r#"
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct ReDoc {
/// The title of the html page. The default title is "Scalar".
pub title: String,
/// The version of the html page.
pub keywords: Option<String>,
/// The description of the html page.
pub description: Option<String>,
/// The lib url path.
pub lib_url: String,
/// The spec url path.
Expand All @@ -61,11 +69,32 @@ impl ReDoc {
/// ```
pub fn new(spec_url: impl Into<String>) -> Self {
Self {
title: "ReDoc".into(),
keywords: None,
description: None,
lib_url: "https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js".into(),
spec_url: spec_url.into(),
}
}

/// Set title of the html page. The default title is "Scalar".
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}

/// Set keywords of the html page.
pub fn keywords(mut self, keywords: impl Into<String>) -> Self {
self.keywords = Some(keywords.into());
self
}

/// Set description of the html page.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}

/// Set the lib url path.
pub fn lib_url(mut self, lib_url: impl Into<String>) -> Self {
self.lib_url = lib_url.into();
Expand All @@ -81,9 +110,27 @@ impl ReDoc {
#[async_trait]
impl Handler for ReDoc {
async fn handle(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) {
let keywords = self
.keywords
.as_ref()
.map(|s| {
format!(
"<meta name=\"keywords\" content=\"{}\">",
s.split(',').map(|s| s.trim()).collect::<Vec<_>>().join(",")
)
})
.unwrap_or_default();
let description = self
.description
.as_ref()
.map(|s| format!("<meta name=\"description\" content=\"{}\">", s))
.unwrap_or_default();
let html = INDEX_TMPL
.replace("{{lib_url}}", &self.lib_url)
.replace("{{spec_url}}", &self.spec_url);
.replacen("{{lib_url}}", &self.lib_url, 1)
.replacen("{{spec_url}}", &self.spec_url, 1)
.replacen("{{title}}", &self.title, 1)
.replacen("{{keywords}}", &keywords, 1)
.replacen("{{description}}", &description, 1);
res.render(Text::Html(html));
}
}
54 changes: 50 additions & 4 deletions crates/oapi/src/scalar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ const INDEX_TMPL: &str = r#"
<!DOCTYPE html>
<html>
<head>
<title>Scalar</title>
<title>{{title}}</title>
{{keywords}}
{{description}}
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
Expand All @@ -34,6 +36,12 @@ const INDEX_TMPL: &str = r#"
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct Scalar {
/// The title of the html page. The default title is "Scalar".
pub title: String,
/// The version of the html page.
pub keywords: Option<String>,
/// The description of the html page.
pub description: Option<String>,
/// The lib url path.
pub lib_url: String,
/// The spec url path.
Expand All @@ -53,18 +61,38 @@ impl Scalar {
/// ```
pub fn new(spec_url: impl Into<String>) -> Self {
Self {
title: "Scalar".into(),
keywords: None,
description: None,
lib_url: "https://cdn.jsdelivr.net/npm/@scalar/api-reference".into(),
spec_url: spec_url.into(),
}
}

/// Set title of the html page. The default title is "Scalar".
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}

/// Set keywords of the html page.
pub fn keywords(mut self, keywords: impl Into<String>) -> Self {
self.keywords = Some(keywords.into());
self
}

/// Set description of the html page.
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}

/// Set the lib url path.
pub fn lib_url(mut self, lib_url: impl Into<String>) -> Self {
self.lib_url = lib_url.into();
self
}


/// Consusmes the [`Scalar`] and returns [`Router`] with the [`Scalar`] as handler.
pub fn into_router(self, path: impl Into<String>) -> Router {
Router::with_path(path.into()).goal(self)
Expand All @@ -73,9 +101,27 @@ impl Scalar {
#[async_trait]
impl Handler for Scalar {
async fn handle(&self, _req: &mut Request, _depot: &mut Depot, res: &mut Response, _ctrl: &mut FlowCtrl) {
let keywords = self
.keywords
.as_ref()
.map(|s| {
format!(
"<meta name=\"keywords\" content=\"{}\">",
s.split(',').map(|s| s.trim()).collect::<Vec<_>>().join(",")
)
})
.unwrap_or_default();
let description = self
.description
.as_ref()
.map(|s| format!("<meta name=\"description\" content=\"{}\">", s))
.unwrap_or_default();
let html = INDEX_TMPL
.replace("{{lib_url}}", &self.lib_url)
.replace("{{spec_url}}", &self.spec_url);
.replacen("{{lib_url}}", &self.lib_url, 1)
.replacen("{{spec_url}}", &self.spec_url, 1)
.replacen("{{title}}", &self.title, 1)
.replacen("{{keywords}}", &keywords, 1)
.replacen("{{description}}", &description, 1);
res.render(Text::Html(html));
}
}
Loading

0 comments on commit d23fc50

Please sign in to comment.