mirror of https://github.com/fafhrd91/actix-web
Refactor route registration to use RouteInfo struct & cargo clippy
This commit is contained in:
parent
68f01edb72
commit
b75fcd8fac
|
|
@ -187,13 +187,16 @@ impl AppService {
|
||||||
let parent_scope_id = self.scope_id_stack.last().copied();
|
let parent_scope_id = self.scope_id_stack.last().copied();
|
||||||
|
|
||||||
for full_path in full_paths {
|
for full_path in full_paths {
|
||||||
self.introspector.borrow_mut().register_service(
|
let info = crate::introspection::RouteInfo::new(
|
||||||
full_path,
|
full_path,
|
||||||
methods.clone(),
|
methods.clone(),
|
||||||
guard_names.clone(),
|
guard_names.clone(),
|
||||||
guard_details.clone(),
|
guard_details.clone(),
|
||||||
patterns.clone(),
|
patterns.clone(),
|
||||||
resource_name.clone(),
|
resource_name.clone(),
|
||||||
|
);
|
||||||
|
self.introspector.borrow_mut().register_service(
|
||||||
|
info,
|
||||||
is_resource,
|
is_resource,
|
||||||
is_prefix,
|
is_prefix,
|
||||||
scope_id,
|
scope_id,
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,37 @@ pub struct RouteDetail {
|
||||||
is_resource: bool,
|
is_resource: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Input data for registering routes with the introspector.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RouteInfo {
|
||||||
|
full_path: String,
|
||||||
|
methods: Vec<Method>,
|
||||||
|
guards: Vec<String>,
|
||||||
|
guard_details: Vec<GuardReport>,
|
||||||
|
patterns: Vec<String>,
|
||||||
|
resource_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RouteInfo {
|
||||||
|
pub fn new(
|
||||||
|
full_path: String,
|
||||||
|
methods: Vec<Method>,
|
||||||
|
guards: Vec<String>,
|
||||||
|
guard_details: Vec<GuardReport>,
|
||||||
|
patterns: Vec<String>,
|
||||||
|
resource_name: Option<String>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
full_path,
|
||||||
|
methods,
|
||||||
|
guards,
|
||||||
|
guard_details,
|
||||||
|
patterns,
|
||||||
|
resource_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct GuardReport {
|
pub struct GuardReport {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
@ -273,28 +304,15 @@ impl IntrospectionCollector {
|
||||||
|
|
||||||
pub fn register_service(
|
pub fn register_service(
|
||||||
&mut self,
|
&mut self,
|
||||||
full_path: String,
|
info: RouteInfo,
|
||||||
methods: Vec<Method>,
|
|
||||||
guards: Vec<String>,
|
|
||||||
guard_details: Vec<GuardReport>,
|
|
||||||
patterns: Vec<String>,
|
|
||||||
resource_name: Option<String>,
|
|
||||||
is_resource: bool,
|
is_resource: bool,
|
||||||
is_prefix: bool,
|
is_prefix: bool,
|
||||||
scope_id: Option<usize>,
|
scope_id: Option<usize>,
|
||||||
parent_scope_id: Option<usize>,
|
parent_scope_id: Option<usize>,
|
||||||
) {
|
) {
|
||||||
let full_path = normalize_path(&full_path);
|
let full_path = normalize_path(&info.full_path);
|
||||||
|
|
||||||
self.register_pattern_detail(
|
self.register_pattern_detail(&full_path, &info, is_resource);
|
||||||
full_path.clone(),
|
|
||||||
methods.clone(),
|
|
||||||
guards.clone(),
|
|
||||||
guard_details.clone(),
|
|
||||||
patterns.clone(),
|
|
||||||
resource_name.clone(),
|
|
||||||
is_resource,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.registrations.push(Registration {
|
self.registrations.push(Registration {
|
||||||
order: self.next_registration_order,
|
order: self.next_registration_order,
|
||||||
|
|
@ -303,33 +321,16 @@ impl IntrospectionCollector {
|
||||||
parent_scope_id,
|
parent_scope_id,
|
||||||
full_path,
|
full_path,
|
||||||
is_prefix,
|
is_prefix,
|
||||||
methods,
|
methods: info.methods,
|
||||||
guards,
|
guards: info.guards,
|
||||||
});
|
});
|
||||||
self.next_registration_order += 1;
|
self.next_registration_order += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_route(
|
pub fn register_route(&mut self, info: RouteInfo, scope_id: Option<usize>) {
|
||||||
&mut self,
|
let full_path = normalize_path(&info.full_path);
|
||||||
full_path: String,
|
|
||||||
methods: Vec<Method>,
|
|
||||||
guards: Vec<String>,
|
|
||||||
guard_details: Vec<GuardReport>,
|
|
||||||
patterns: Vec<String>,
|
|
||||||
resource_name: Option<String>,
|
|
||||||
scope_id: Option<usize>,
|
|
||||||
) {
|
|
||||||
let full_path = normalize_path(&full_path);
|
|
||||||
|
|
||||||
self.register_pattern_detail(
|
self.register_pattern_detail(&full_path, &info, true);
|
||||||
full_path.clone(),
|
|
||||||
methods.clone(),
|
|
||||||
guards.clone(),
|
|
||||||
guard_details.clone(),
|
|
||||||
patterns.clone(),
|
|
||||||
resource_name.clone(),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.registrations.push(Registration {
|
self.registrations.push(Registration {
|
||||||
order: self.next_registration_order,
|
order: self.next_registration_order,
|
||||||
|
|
@ -338,8 +339,8 @@ impl IntrospectionCollector {
|
||||||
parent_scope_id: None,
|
parent_scope_id: None,
|
||||||
full_path,
|
full_path,
|
||||||
is_prefix: false,
|
is_prefix: false,
|
||||||
methods,
|
methods: info.methods,
|
||||||
guards,
|
guards: info.guards,
|
||||||
});
|
});
|
||||||
self.next_registration_order += 1;
|
self.next_registration_order += 1;
|
||||||
}
|
}
|
||||||
|
|
@ -366,36 +367,32 @@ impl IntrospectionCollector {
|
||||||
/// Registers details for a route pattern.
|
/// Registers details for a route pattern.
|
||||||
pub fn register_pattern_detail(
|
pub fn register_pattern_detail(
|
||||||
&mut self,
|
&mut self,
|
||||||
full_path: String,
|
full_path: &str,
|
||||||
methods: Vec<Method>,
|
info: &RouteInfo,
|
||||||
guards: Vec<String>,
|
|
||||||
guard_details: Vec<GuardReport>,
|
|
||||||
patterns: Vec<String>,
|
|
||||||
resource_name: Option<String>,
|
|
||||||
is_resource: bool,
|
is_resource: bool,
|
||||||
) {
|
) {
|
||||||
let full_path = normalize_path(&full_path);
|
let full_path = normalize_path(full_path);
|
||||||
|
|
||||||
self.details
|
self.details
|
||||||
.entry(full_path)
|
.entry(full_path)
|
||||||
.and_modify(|d| {
|
.and_modify(|d| {
|
||||||
update_unique(&mut d.methods, &methods);
|
update_unique(&mut d.methods, &info.methods);
|
||||||
update_unique(&mut d.guards, &guards);
|
update_unique(&mut d.guards, &info.guards);
|
||||||
merge_guard_reports(&mut d.guard_details, &guard_details);
|
merge_guard_reports(&mut d.guard_details, &info.guard_details);
|
||||||
update_unique(&mut d.patterns, &patterns);
|
update_unique(&mut d.patterns, &info.patterns);
|
||||||
if d.resource_name.is_none() {
|
if d.resource_name.is_none() {
|
||||||
d.resource_name = resource_name.clone();
|
d.resource_name = info.resource_name.clone();
|
||||||
}
|
}
|
||||||
if !d.is_resource && is_resource {
|
if !d.is_resource && is_resource {
|
||||||
d.is_resource = true;
|
d.is_resource = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.or_insert(RouteDetail {
|
.or_insert(RouteDetail {
|
||||||
methods,
|
methods: info.methods.clone(),
|
||||||
guards,
|
guards: info.guards.clone(),
|
||||||
guard_details,
|
guard_details: info.guard_details.clone(),
|
||||||
patterns,
|
patterns: info.patterns.clone(),
|
||||||
resource_name,
|
resource_name: info.resource_name.clone(),
|
||||||
is_resource,
|
is_resource,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -413,13 +410,8 @@ impl IntrospectionCollector {
|
||||||
let mut assembled = String::new();
|
let mut assembled = String::new();
|
||||||
|
|
||||||
for part in parts.iter() {
|
for part in parts.iter() {
|
||||||
if assembled.is_empty() {
|
|
||||||
assembled.push('/');
|
assembled.push('/');
|
||||||
assembled.push_str(part);
|
assembled.push_str(part);
|
||||||
} else {
|
|
||||||
assembled.push('/');
|
|
||||||
assembled.push_str(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
let child_full_path = assembled.clone();
|
let child_full_path = assembled.clone();
|
||||||
let existing_child_index = current_node
|
let existing_child_index = current_node
|
||||||
|
|
@ -939,18 +931,36 @@ avoid exposing introspection endpoints in production"
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn route_info(
|
||||||
|
full_path: &str,
|
||||||
|
methods: Vec<Method>,
|
||||||
|
guards: Vec<String>,
|
||||||
|
guard_details: Vec<GuardReport>,
|
||||||
|
patterns: Vec<String>,
|
||||||
|
resource_name: Option<String>,
|
||||||
|
) -> RouteInfo {
|
||||||
|
RouteInfo::new(
|
||||||
|
full_path.to_string(),
|
||||||
|
methods,
|
||||||
|
guards,
|
||||||
|
guard_details,
|
||||||
|
patterns,
|
||||||
|
resource_name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn report_includes_resources_without_methods() {
|
fn report_includes_resources_without_methods() {
|
||||||
let mut collector = IntrospectionCollector::new();
|
let mut collector = IntrospectionCollector::new();
|
||||||
collector.register_route(
|
let info = route_info(
|
||||||
"/no-guards".to_string(),
|
"/no-guards",
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
|
collector.register_route(info, None);
|
||||||
let tree = collector.finalize();
|
let tree = collector.finalize();
|
||||||
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
||||||
|
|
||||||
|
|
@ -979,15 +989,15 @@ mod tests {
|
||||||
}],
|
}],
|
||||||
}];
|
}];
|
||||||
|
|
||||||
collector.register_route(
|
let info = route_info(
|
||||||
"/meta".to_string(),
|
"/meta",
|
||||||
vec![Method::GET],
|
vec![Method::GET],
|
||||||
vec!["Header(accept, text/plain)".to_string()],
|
vec!["Header(accept, text/plain)".to_string()],
|
||||||
guard_details,
|
guard_details,
|
||||||
vec!["/meta".to_string()],
|
vec!["/meta".to_string()],
|
||||||
Some("meta-resource".to_string()),
|
Some("meta-resource".to_string()),
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
|
collector.register_route(info, None);
|
||||||
|
|
||||||
let tree = collector.finalize();
|
let tree = collector.finalize();
|
||||||
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
||||||
|
|
@ -1024,15 +1034,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn conflicting_method_guards_mark_unreachable() {
|
fn conflicting_method_guards_mark_unreachable() {
|
||||||
let mut collector = IntrospectionCollector::new();
|
let mut collector = IntrospectionCollector::new();
|
||||||
collector.register_route(
|
let info = route_info(
|
||||||
"/all-guard".to_string(),
|
"/all-guard",
|
||||||
vec![Method::GET, Method::POST],
|
vec![Method::GET, Method::POST],
|
||||||
vec!["AllGuard(GET, POST)".to_string()],
|
vec!["AllGuard(GET, POST)".to_string()],
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
|
collector.register_route(info, None);
|
||||||
let tree = collector.finalize();
|
let tree = collector.finalize();
|
||||||
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
||||||
|
|
||||||
|
|
@ -1052,50 +1062,44 @@ mod tests {
|
||||||
let mut collector = IntrospectionCollector::new();
|
let mut collector = IntrospectionCollector::new();
|
||||||
|
|
||||||
let scope_a = collector.next_scope_id();
|
let scope_a = collector.next_scope_id();
|
||||||
collector.register_service(
|
let info = route_info(
|
||||||
"/extra".to_string(),
|
"/extra",
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
true,
|
|
||||||
true,
|
|
||||||
Some(scope_a),
|
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
collector.register_route(
|
collector.register_service(info, true, true, Some(scope_a), None);
|
||||||
"/extra/ping".to_string(),
|
let info = route_info(
|
||||||
|
"/extra/ping",
|
||||||
vec![Method::GET],
|
vec![Method::GET],
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
Some(scope_a),
|
|
||||||
);
|
);
|
||||||
|
collector.register_route(info, Some(scope_a));
|
||||||
|
|
||||||
let scope_b = collector.next_scope_id();
|
let scope_b = collector.next_scope_id();
|
||||||
collector.register_service(
|
let info = route_info(
|
||||||
"/extra".to_string(),
|
"/extra",
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
true,
|
|
||||||
true,
|
|
||||||
Some(scope_b),
|
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
collector.register_route(
|
collector.register_service(info, true, true, Some(scope_b), None);
|
||||||
"/extra/ping".to_string(),
|
let info = route_info(
|
||||||
|
"/extra/ping",
|
||||||
vec![Method::POST],
|
vec![Method::POST],
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
Some(scope_b),
|
|
||||||
);
|
);
|
||||||
|
collector.register_route(info, Some(scope_b));
|
||||||
|
|
||||||
let tree = collector.finalize();
|
let tree = collector.finalize();
|
||||||
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
||||||
|
|
@ -1121,24 +1125,24 @@ mod tests {
|
||||||
fn shadowed_routes_include_context() {
|
fn shadowed_routes_include_context() {
|
||||||
let mut collector = IntrospectionCollector::new();
|
let mut collector = IntrospectionCollector::new();
|
||||||
|
|
||||||
collector.register_route(
|
let info = route_info(
|
||||||
"/shadow".to_string(),
|
"/shadow",
|
||||||
vec![Method::GET],
|
vec![Method::GET],
|
||||||
vec!["GET".to_string()],
|
vec!["GET".to_string()],
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
collector.register_route(
|
collector.register_route(info, None);
|
||||||
"/shadow".to_string(),
|
let info = route_info(
|
||||||
|
"/shadow",
|
||||||
vec![Method::GET],
|
vec![Method::GET],
|
||||||
vec!["GET".to_string()],
|
vec!["GET".to_string()],
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
None,
|
None,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
|
collector.register_route(info, None);
|
||||||
|
|
||||||
let tree = collector.finalize();
|
let tree = collector.finalize();
|
||||||
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
let items: Vec<IntrospectionReportItem> = (&tree.root).into();
|
||||||
|
|
|
||||||
|
|
@ -473,15 +473,18 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
for full_path in &full_paths {
|
for full_path in &full_paths {
|
||||||
config.introspector.borrow_mut().register_route(
|
let info = crate::introspection::RouteInfo::new(
|
||||||
full_path.clone(),
|
full_path.clone(),
|
||||||
methods.clone(),
|
methods.clone(),
|
||||||
guard_names.clone(),
|
guard_names.clone(),
|
||||||
guard_details.clone(),
|
guard_details.clone(),
|
||||||
patterns.clone(),
|
patterns.clone(),
|
||||||
resource_name.clone(),
|
resource_name.clone(),
|
||||||
scope_id,
|
|
||||||
);
|
);
|
||||||
|
config
|
||||||
|
.introspector
|
||||||
|
.borrow_mut()
|
||||||
|
.register_route(info, scope_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,12 +40,12 @@ async fn introspection_report_includes_details_and_metadata() {
|
||||||
.service(
|
.service(
|
||||||
web::resource(["/alpha", "/beta"])
|
web::resource(["/alpha", "/beta"])
|
||||||
.name("multi")
|
.name("multi")
|
||||||
.route(web::get().to(|| async { HttpResponse::Ok() })),
|
.route(web::get().to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.service(
|
.service(
|
||||||
web::resource("/guarded")
|
web::resource("/guarded")
|
||||||
.guard(guard::Header("accept", "text/plain"))
|
.guard(guard::Header("accept", "text/plain"))
|
||||||
.route(web::get().to(|| async { HttpResponse::Ok() })),
|
.route(web::get().to(HttpResponse::Ok)),
|
||||||
)
|
)
|
||||||
.service(
|
.service(
|
||||||
web::scope("/scoped")
|
web::scope("/scoped")
|
||||||
|
|
@ -53,10 +53,7 @@ async fn introspection_report_includes_details_and_metadata() {
|
||||||
.configure(|cfg| {
|
.configure(|cfg| {
|
||||||
cfg.external_resource("scope-external", "https://scope.example/{id}");
|
cfg.external_resource("scope-external", "https://scope.example/{id}");
|
||||||
})
|
})
|
||||||
.service(
|
.service(web::resource("/item").route(web::get().to(HttpResponse::Ok))),
|
||||||
web::resource("/item")
|
|
||||||
.route(web::get().to(|| async { HttpResponse::Ok() })),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.service(web::resource("/introspection").route(web::get().to(introspection_handler)))
|
.service(web::resource("/introspection").route(web::get().to(introspection_handler)))
|
||||||
.service(
|
.service(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue