Skip to content

Commit cb08080

Browse files
committed
fix(repo): Adjust SwaggerUI to work with custom servers
1 parent 2430656 commit cb08080

File tree

6 files changed

+80
-38
lines changed

6 files changed

+80
-38
lines changed

crates/web-utils/src/router_builder.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ use axum::{
88
};
99
use tower::{Layer, Service};
1010

11-
use super::server::server_builder::with_prefixed_route;
12-
1311
pub struct RouterBuilder<S: Send + Sync + Clone + 'static> {
1412
router: Router<S>,
1513
base_path: String,
14+
prefix: Option<String>,
1615
}
1716

1817
impl<S: Send + Sync + Clone + 'static> RouterBuilder<S> {
1918
pub fn new(base_path: &str) -> Self {
2019
Self {
2120
router: Router::new(),
22-
base_path: with_prefixed_route(base_path),
21+
base_path: Self::safe_path(base_path),
22+
prefix: None,
2323
}
2424
}
2525

@@ -61,8 +61,32 @@ impl<S: Send + Sync + Clone + 'static> RouterBuilder<S> {
6161
self
6262
}
6363

64+
pub fn with_prefix(mut self, prefix: &str) -> Self {
65+
self.prefix = Some(prefix.to_string());
66+
self
67+
}
68+
6469
pub fn build(self) -> (String, Router<S>) {
65-
let path = self.path();
70+
let path = match self.prefix {
71+
Some(prefix) => Self::prefixed_route(&prefix, &self.base_path),
72+
None => self.base_path,
73+
};
6674
(path, self.router)
6775
}
76+
77+
fn safe_path(path: &str) -> String {
78+
if path.starts_with("/") {
79+
path.to_string()
80+
} else {
81+
format!("/{}", path)
82+
}
83+
}
84+
85+
fn prefixed_route(prefix: &str, route: &str) -> String {
86+
if route.starts_with('/') {
87+
format!("{prefix}/{}", route.trim_start_matches('/'))
88+
} else {
89+
format!("{prefix}/{route}")
90+
}
91+
}
6892
}

crates/web-utils/src/server/server_builder.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,7 @@ use super::{
2222
};
2323

2424
pub const API_VERSION: &str = "v1";
25-
26-
pub fn with_prefixed_route(route: &str) -> String {
27-
if route.starts_with('/') {
28-
format!("/api/{}/{}", API_VERSION, route.trim_start_matches('/'))
29-
} else {
30-
format!("/api/{}/{}", API_VERSION, route)
31-
}
32-
}
25+
pub const API_BASE_PATH: &str = "/api/v1";
3326

3427
pub struct Server {
3528
app: Router,
@@ -65,8 +58,8 @@ impl ServerBuilder {
6558
port: u16,
6659
) -> Server {
6760
let app = Router::new()
68-
.route(&with_prefixed_route("health"), get(get_health::<S>))
69-
.route(&with_prefixed_route("metrics"), get(get_metrics::<S>))
61+
.route("/health", get(get_health::<S>))
62+
.route("/metrics", get(get_metrics::<S>))
7063
.layer(TraceLayer::new_for_http().make_span_with(
7164
|request: &Request<_>| {
7265
let matched_path = request

postman/fuel-rest.postman.json

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,32 @@
2020
}
2121
],
2222
"request": {
23+
"auth": {
24+
"type": "bearer",
25+
"bearer": [
26+
{
27+
"key": "token",
28+
"value": "{{api_key}}",
29+
"type": "string"
30+
}
31+
]
32+
},
2333
"method": "POST",
2434
"header": [],
2535
"body": {
2636
"mode": "raw",
27-
"raw": "{\n \"username\": \"petko\"\n}",
37+
"raw": "{\n \"username\": \"petko\", \"role\": \"BUILDER\"\n}",
2838
"options": {
2939
"raw": {
3040
"language": "json"
3141
}
3242
}
3343
},
3444
"url": {
35-
"raw": "http://{{base_url}}/key/generate?password={{fuel_password}}",
45+
"raw": "http://{{base_url}}/keys/generate",
3646
"protocol": "http",
3747
"host": ["{{base_url}}"],
38-
"path": ["key", "generate"],
39-
"query": [
40-
{
41-
"key": "password",
42-
"value": "{{fuel_password}}"
43-
}
44-
]
48+
"path": ["key", "generate"]
4549
},
4650
"description": "This is a GET request and it is used to \"get\" data from an endpoint. There is no request body for a GET request, but you can use query parameters to help specify the resource you want data on (e.g., in this request, we have `id=1`).\n\nA successful GET response will have a `200 OK` status, and should include some kind of response body - for example, HTML web content or JSON data."
4751
},

services/api/src/server/handlers/open_api.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,12 @@ use fuel_streams_domains::{
4848
receipts::queryable::ReceiptsQuery,
4949
transactions::queryable::TransactionsQuery,
5050
};
51+
use fuel_web_utils::server::server_builder::API_BASE_PATH;
5152
use utoipa::{
52-
openapi::security::{ApiKey, ApiKeyValue, SecurityScheme},
53+
openapi::{
54+
security::{HttpAuthScheme, HttpBuilder, SecurityScheme},
55+
Server,
56+
},
5357
Modify,
5458
OpenApi,
5559
};
@@ -67,11 +71,20 @@ struct SecurityAddon;
6771

6872
impl Modify for SecurityAddon {
6973
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
70-
let components = openapi.components.as_mut().unwrap(); // we can unwrap safely since there already is components registered.
71-
components.add_security_scheme(
74+
openapi.components.as_mut().unwrap().add_security_scheme(
7275
"api_key",
73-
SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("api_key"))),
74-
)
76+
SecurityScheme::Http(
77+
HttpBuilder::new().scheme(HttpAuthScheme::Bearer).build(),
78+
),
79+
);
80+
}
81+
}
82+
83+
struct ServerAddon;
84+
85+
impl Modify for ServerAddon {
86+
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
87+
openapi.servers = Some(vec![Server::new(API_BASE_PATH)])
7588
}
7689
}
7790

@@ -166,6 +179,6 @@ use super::{
166179
(name = "Receipts", description = "Receipts retrieval endpoints"),
167180
(name = "Transactions", description = "Transactions retrieval endpoints"),
168181
),
169-
modifiers(&SecurityAddon)
182+
modifiers(&SecurityAddon, &ServerAddon)
170183
)]
171184
pub struct ApiDoc;

services/api/src/server/routes.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use fuel_streams_store::{db::DbItem, record::RecordPointer};
1313
use fuel_web_utils::{
1414
api_key::middleware::ApiKeyMiddleware,
1515
router_builder::RouterBuilder,
16+
server::server_builder::API_BASE_PATH,
1617
};
1718
use open_api::ApiDoc;
1819
use serde::Serialize;
@@ -204,7 +205,7 @@ pub fn create_routes(state: &ServerState) -> Router {
204205
))
205206
.build();
206207

207-
let (key_path, key_router) = RouterBuilder::new("/key")
208+
let (key_path, key_router) = RouterBuilder::new("/keys")
208209
.related("/generate", post(api_key::generate_api_key))
209210
.with_layer(from_fn(api_key::validate_manage_api_keys_scope))
210211
.with_layer(from_fn_with_state(
@@ -213,14 +214,16 @@ pub fn create_routes(state: &ServerState) -> Router {
213214
))
214215
.build();
215216

216-
app.nest(&blocks_path, blocks_router)
217+
let routes = Router::new()
218+
.nest(&blocks_path, blocks_router)
217219
.nest(&accounts_path, accounts_router)
218220
.nest(&contracts_path, contracts_router)
219221
.nest(&inputs_path, inputs_router)
220222
.nest(&outputs_path, outputs_router)
221223
.nest(&receipts_path, receipts_router)
222224
.nest(&transactions_path, transactions_router)
223225
.nest(&utxos_path, utxos_router)
224-
.nest(&key_path, key_router)
225-
.with_state(state.clone())
226+
.nest(&key_path, key_router);
227+
228+
app.nest(API_BASE_PATH, routes).with_state(state.clone())
226229
}

services/webserver/src/server/routes.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use axum::{middleware::from_fn_with_state, routing::get, Router};
22
use fuel_web_utils::{
33
api_key::middleware::ApiKeyMiddleware,
44
router_builder::RouterBuilder,
5+
server::server_builder::API_BASE_PATH,
56
};
67

78
use super::handlers;
@@ -15,11 +16,15 @@ pub fn create_routes(state: &ServerState) -> Router {
1516

1617
let (ws_path, ws_router) = RouterBuilder::new("/ws")
1718
.root(get(handlers::websocket::get_websocket))
18-
.with_layer(from_fn_with_state(
19-
auth_params.clone(),
20-
ApiKeyMiddleware::handler,
21-
))
2219
.build();
2320

24-
app.nest(&ws_path, ws_router).with_state(state.to_owned())
21+
let routes =
22+
Router::new()
23+
.nest(&ws_path, ws_router)
24+
.layer(from_fn_with_state(
25+
auth_params.clone(),
26+
ApiKeyMiddleware::handler,
27+
));
28+
29+
app.nest(API_BASE_PATH, routes).with_state(state.clone())
2530
}

0 commit comments

Comments
 (0)