@@ -7,6 +7,8 @@ use http::Uri;
7
7
use indexmap:: IndexMap ;
8
8
use std:: { borrow:: Cow , fmt} ;
9
9
10
+ use crate :: config:: HttpTriggerRouteConfig ;
11
+
10
12
/// Router for the HTTP trigger.
11
13
#[ derive( Clone , Debug ) ]
12
14
pub struct Router {
@@ -15,6 +17,7 @@ pub struct Router {
15
17
}
16
18
17
19
/// A detected duplicate route.
20
+ #[ derive( Debug ) ] // Needed to call `expect_err` on `Router::build`
18
21
pub struct DuplicateRoute {
19
22
/// The duplicated route pattern.
20
23
pub route : RoutePattern ,
@@ -28,14 +31,23 @@ impl Router {
28
31
/// Builds a router based on application configuration.
29
32
pub fn build < ' a > (
30
33
base : & str ,
31
- component_routes : impl IntoIterator < Item = ( & ' a str , & ' a str ) > ,
34
+ component_routes : impl IntoIterator < Item = ( & ' a str , & ' a HttpTriggerRouteConfig ) > ,
32
35
) -> Result < ( Self , Vec < DuplicateRoute > ) > {
33
36
let mut routes = IndexMap :: new ( ) ;
34
37
let mut duplicates = vec ! [ ] ;
35
38
36
- let routes_iter = component_routes. into_iter ( ) . map ( |( component_id, route) | {
37
- ( RoutePattern :: from ( base, route) , component_id. to_string ( ) )
38
- } ) ;
39
+ let routes_iter = component_routes
40
+ . into_iter ( )
41
+ . filter_map ( |( component_id, route) | {
42
+ match route {
43
+ HttpTriggerRouteConfig :: Route ( r) => {
44
+ Some ( Ok ( ( RoutePattern :: from ( base, r) , component_id. to_string ( ) ) ) )
45
+ }
46
+ HttpTriggerRouteConfig :: IsRoutable ( false ) => None ,
47
+ HttpTriggerRouteConfig :: IsRoutable ( true ) => Some ( Err ( anyhow ! ( "route must be a string pattern or 'false': component '{component_id}' has route = 'true'" ) ) ) ,
48
+ }
49
+ } )
50
+ . collect :: < Result < Vec < _ > > > ( ) ?;
39
51
40
52
for ( route, component_id) in routes_iter {
41
53
let replaced = routes. insert ( route. clone ( ) , component_id. clone ( ) ) ;
@@ -417,10 +429,10 @@ mod route_tests {
417
429
let ( routes, duplicates) = Router :: build (
418
430
"/" ,
419
431
vec ! [
420
- ( "/" , "/" ) ,
421
- ( "/foo" , "/foo" ) ,
422
- ( "/bar" , "/bar" ) ,
423
- ( "/whee/..." , "/whee/..." ) ,
432
+ ( "/" , & "/" . into ( ) ) ,
433
+ ( "/foo" , & "/foo" . into ( ) ) ,
434
+ ( "/bar" , & "/bar" . into ( ) ) ,
435
+ ( "/whee/..." , & "/whee/..." . into ( ) ) ,
424
436
] ,
425
437
)
426
438
. unwrap ( ) ;
@@ -434,10 +446,10 @@ mod route_tests {
434
446
let ( routes, _) = Router :: build (
435
447
"/" ,
436
448
vec ! [
437
- ( "/" , "/" ) ,
438
- ( "/foo" , "/foo" ) ,
439
- ( "/bar" , "/bar" ) ,
440
- ( "/whee/..." , "/whee/..." ) ,
449
+ ( "/" , & "/" . into ( ) ) ,
450
+ ( "/foo" , & "/foo" . into ( ) ) ,
451
+ ( "/bar" , & "/bar" . into ( ) ) ,
452
+ ( "/whee/..." , & "/whee/..." . into ( ) ) ,
441
453
] ,
442
454
)
443
455
. unwrap ( ) ;
@@ -453,10 +465,10 @@ mod route_tests {
453
465
let ( routes, duplicates) = Router :: build (
454
466
"/" ,
455
467
vec ! [
456
- ( "/" , "/" ) ,
457
- ( "first /foo" , "/foo" ) ,
458
- ( "second /foo" , "/foo" ) ,
459
- ( "/whee/..." , "/whee/..." ) ,
468
+ ( "/" , & "/" . into ( ) ) ,
469
+ ( "first /foo" , & "/foo" . into ( ) ) ,
470
+ ( "second /foo" , & "/foo" . into ( ) ) ,
471
+ ( "/whee/..." , & "/whee/..." . into ( ) ) ,
460
472
] ,
461
473
)
462
474
. unwrap ( ) ;
@@ -470,10 +482,10 @@ mod route_tests {
470
482
let ( routes, duplicates) = Router :: build (
471
483
"/" ,
472
484
vec ! [
473
- ( "/" , "/" ) ,
474
- ( "first /foo" , "/foo" ) ,
475
- ( "second /foo" , "/foo" ) ,
476
- ( "/whee/..." , "/whee/..." ) ,
485
+ ( "/" , & "/" . into ( ) ) ,
486
+ ( "first /foo" , & "/foo" . into ( ) ) ,
487
+ ( "second /foo" , & "/foo" . into ( ) ) ,
488
+ ( "/whee/..." , & "/whee/..." . into ( ) ) ,
477
489
] ,
478
490
)
479
491
. unwrap ( ) ;
@@ -482,4 +494,37 @@ mod route_tests {
482
494
assert_eq ! ( "first /foo" , duplicates[ 0 ] . replaced_id) ;
483
495
assert_eq ! ( "second /foo" , duplicates[ 0 ] . effective_id) ;
484
496
}
497
+
498
+ #[ test]
499
+ fn unroutable_routes_are_skipped ( ) {
500
+ let ( routes, _) = Router :: build (
501
+ "/" ,
502
+ vec ! [
503
+ ( "/" , & "/" . into( ) ) ,
504
+ ( "/foo" , & "/foo" . into( ) ) ,
505
+ ( "private" , & HttpTriggerRouteConfig :: IsRoutable ( false ) ) ,
506
+ ( "/whee/..." , & "/whee/..." . into( ) ) ,
507
+ ] ,
508
+ )
509
+ . unwrap ( ) ;
510
+
511
+ assert_eq ! ( 3 , routes. routes. len( ) ) ;
512
+ assert ! ( !routes. routes. values( ) . any( |c| c == "private" ) ) ;
513
+ }
514
+
515
+ #[ test]
516
+ fn unroutable_routes_have_to_be_unroutable_thats_just_common_sense ( ) {
517
+ let e = Router :: build (
518
+ "/" ,
519
+ vec ! [
520
+ ( "/" , & "/" . into( ) ) ,
521
+ ( "/foo" , & "/foo" . into( ) ) ,
522
+ ( "bad component" , & HttpTriggerRouteConfig :: IsRoutable ( true ) ) ,
523
+ ( "/whee/..." , & "/whee/..." . into( ) ) ,
524
+ ] ,
525
+ )
526
+ . expect_err ( "should not have accepted a 'route = true'" ) ;
527
+
528
+ assert ! ( e. to_string( ) . contains( "bad component" ) ) ;
529
+ }
485
530
}
0 commit comments