@@ -58,7 +58,7 @@ public DynamicControllerEndpointMatcherPolicyTest()
5858 _ => Task . CompletedTask ,
5959 new EndpointMetadataCollection ( new object [ ]
6060 {
61- new DynamicControllerRouteValueTransformerMetadata ( typeof ( CustomTransformer ) , null ) ,
61+ new DynamicControllerRouteValueTransformerMetadata ( typeof ( CustomTransformer ) , State ) ,
6262 } ) ,
6363 "dynamic" ) ;
6464
@@ -71,7 +71,8 @@ public DynamicControllerEndpointMatcherPolicyTest()
7171 services . AddScoped < CustomTransformer > ( s =>
7272 {
7373 var transformer = new CustomTransformer ( ) ;
74- transformer . Transform = ( c , values ) => Transform ( c , values ) ;
74+ transformer . Transform = ( c , values , state ) => Transform ( c , values , state ) ;
75+ transformer . Filter = ( c , values , state , candidates ) => Filter ( c , values , state , candidates ) ;
7576 return transformer ;
7677 } ) ;
7778 Services = services . BuildServiceProvider ( ) ;
@@ -91,7 +92,11 @@ public DynamicControllerEndpointMatcherPolicyTest()
9192
9293 private IServiceProvider Services { get ; }
9394
94- private Func < HttpContext , RouteValueDictionary , ValueTask < RouteValueDictionary > > Transform { get ; set ; }
95+ private Func < HttpContext , RouteValueDictionary , object , ValueTask < RouteValueDictionary > > Transform { get ; set ; }
96+
97+ private Func < HttpContext , RouteValueDictionary , object , IReadOnlyList < Endpoint > , ValueTask < IReadOnlyList < Endpoint > > > Filter { get ; set ; } = ( _ , __ , ___ , e ) => new ValueTask < IReadOnlyList < Endpoint > > ( e ) ;
98+
99+ private object State { get ; } = new object ( ) ;
95100
96101 [ Fact ]
97102 public async Task ApplyAsync_NoMatch ( )
@@ -106,7 +111,7 @@ public async Task ApplyAsync_NoMatch()
106111 var candidates = new CandidateSet ( endpoints , values , scores ) ;
107112 candidates . SetValidity ( 0 , false ) ;
108113
109- Transform = ( c , values ) =>
114+ Transform = ( c , values , state ) =>
110115 {
111116 throw new InvalidOperationException ( ) ;
112117 } ;
@@ -135,7 +140,7 @@ public async Task ApplyAsync_HasMatchNoEndpointFound()
135140
136141 var candidates = new CandidateSet ( endpoints , values , scores ) ;
137142
138- Transform = ( c , values ) =>
143+ Transform = ( c , values , state ) =>
139144 {
140145 return new ValueTask < RouteValueDictionary > ( new RouteValueDictionary ( ) ) ;
141146 } ;
@@ -166,7 +171,7 @@ public async Task ApplyAsync_HasMatchFindsEndpoint_WithoutRouteValues()
166171
167172 var candidates = new CandidateSet ( endpoints , values , scores ) ;
168173
169- Transform = ( c , values ) =>
174+ Transform = ( c , values , state ) =>
170175 {
171176 return new ValueTask < RouteValueDictionary > ( new RouteValueDictionary ( new
172177 {
@@ -212,12 +217,13 @@ public async Task ApplyAsync_HasMatchFindsEndpoint_WithRouteValues()
212217
213218 var candidates = new CandidateSet ( endpoints , values , scores ) ;
214219
215- Transform = ( c , values ) =>
220+ Transform = ( c , values , state ) =>
216221 {
217222 return new ValueTask < RouteValueDictionary > ( new RouteValueDictionary ( new
218223 {
219224 controller = "Home" ,
220225 action = "Index" ,
226+ state
221227 } ) ) ;
222228 } ;
223229
@@ -242,13 +248,162 @@ public async Task ApplyAsync_HasMatchFindsEndpoint_WithRouteValues()
242248 {
243249 Assert . Equal ( "controller" , kvp . Key ) ;
244250 Assert . Equal ( "Home" , kvp . Value ) ;
245- } ,
251+ } ,
252+ kvp =>
253+ {
254+ Assert . Equal ( "slug" , kvp . Key ) ;
255+ Assert . Equal ( "test" , kvp . Value ) ;
256+ } ,
257+ kvp =>
258+ {
259+ Assert . Equal ( "state" , kvp . Key ) ;
260+ Assert . Same ( State , kvp . Value ) ;
261+ } ) ;
262+ Assert . True ( candidates . IsValidCandidate ( 0 ) ) ;
263+ }
264+
265+ [ Fact ]
266+ public async Task ApplyAsync_CanDiscardFoundEndpoints ( )
267+ {
268+ // Arrange
269+ var policy = new DynamicControllerEndpointMatcherPolicy ( Selector , Comparer ) ;
270+
271+ var endpoints = new [ ] { DynamicEndpoint , } ;
272+ var values = new RouteValueDictionary [ ] { new RouteValueDictionary ( new { slug = "test" , } ) , } ;
273+ var scores = new [ ] { 0 , } ;
274+
275+ var candidates = new CandidateSet ( endpoints , values , scores ) ;
276+
277+ Transform = ( c , values , state ) =>
278+ {
279+ return new ValueTask < RouteValueDictionary > ( new RouteValueDictionary ( new
280+ {
281+ controller = "Home" ,
282+ action = "Index" ,
283+ state
284+ } ) ) ;
285+ } ;
286+
287+ Filter = ( c , values , state , endpoints ) =>
288+ {
289+ return new ValueTask < IReadOnlyList < Endpoint > > ( Array . Empty < Endpoint > ( ) ) ;
290+ } ;
291+
292+ var httpContext = new DefaultHttpContext ( )
293+ {
294+ RequestServices = Services ,
295+ } ;
296+
297+ // Act
298+ await policy . ApplyAsync ( httpContext , candidates ) ;
299+
300+ // Assert
301+ Assert . False ( candidates . IsValidCandidate ( 0 ) ) ;
302+ }
303+
304+ [ Fact ]
305+ public async Task ApplyAsync_CanReplaceFoundEndpoints ( )
306+ {
307+ // Arrange
308+ var policy = new DynamicControllerEndpointMatcherPolicy ( Selector , Comparer ) ;
309+
310+ var endpoints = new [ ] { DynamicEndpoint , } ;
311+ var values = new RouteValueDictionary [ ] { new RouteValueDictionary ( new { slug = "test" , } ) , } ;
312+ var scores = new [ ] { 0 , } ;
313+
314+ var candidates = new CandidateSet ( endpoints , values , scores ) ;
315+
316+ Transform = ( c , values , state ) =>
317+ {
318+ return new ValueTask < RouteValueDictionary > ( new RouteValueDictionary ( new
319+ {
320+ controller = "Home" ,
321+ action = "Index" ,
322+ state
323+ } ) ) ;
324+ } ;
325+
326+ Filter = ( c , values , state , endpoints ) => new ValueTask < IReadOnlyList < Endpoint > > ( new [ ]
327+ {
328+ new Endpoint ( ( ctx ) => Task . CompletedTask , new EndpointMetadataCollection ( Array . Empty < object > ( ) ) , "ReplacedEndpoint" )
329+ } ) ;
330+
331+ var httpContext = new DefaultHttpContext ( )
332+ {
333+ RequestServices = Services ,
334+ } ;
335+
336+ // Act
337+ await policy . ApplyAsync ( httpContext , candidates ) ;
338+
339+ // Assert
340+ Assert . Collection (
341+ candidates [ 0 ] . Values . OrderBy ( kvp => kvp . Key ) ,
342+ kvp =>
343+ {
344+ Assert . Equal ( "action" , kvp . Key ) ;
345+ Assert . Equal ( "Index" , kvp . Value ) ;
346+ } ,
347+ kvp =>
348+ {
349+ Assert . Equal ( "controller" , kvp . Key ) ;
350+ Assert . Equal ( "Home" , kvp . Value ) ;
351+ } ,
246352 kvp =>
247353 {
248354 Assert . Equal ( "slug" , kvp . Key ) ;
249355 Assert . Equal ( "test" , kvp . Value ) ;
356+ } ,
357+ kvp =>
358+ {
359+ Assert . Equal ( "state" , kvp . Key ) ;
360+ Assert . Same ( State , kvp . Value ) ;
250361 } ) ;
362+ Assert . Equal ( "ReplacedEndpoint" , candidates [ 0 ] . Endpoint . DisplayName ) ;
363+ Assert . True ( candidates . IsValidCandidate ( 0 ) ) ;
364+ }
365+
366+ [ Fact ]
367+ public async Task ApplyAsync_CanExpandTheListOfFoundEndpoints ( )
368+ {
369+ // Arrange
370+ var policy = new DynamicControllerEndpointMatcherPolicy ( Selector , Comparer ) ;
371+
372+ var endpoints = new [ ] { DynamicEndpoint , } ;
373+ var values = new RouteValueDictionary [ ] { new RouteValueDictionary ( new { slug = "test" , } ) , } ;
374+ var scores = new [ ] { 0 , } ;
375+
376+ var candidates = new CandidateSet ( endpoints , values , scores ) ;
377+
378+ Transform = ( c , values , state ) =>
379+ {
380+ return new ValueTask < RouteValueDictionary > ( new RouteValueDictionary ( new
381+ {
382+ controller = "Home" ,
383+ action = "Index" ,
384+ state
385+ } ) ) ;
386+ } ;
387+
388+ Filter = ( c , values , state , endpoints ) => new ValueTask < IReadOnlyList < Endpoint > > ( new [ ]
389+ {
390+ ControllerEndpoints [ 1 ] , ControllerEndpoints [ 2 ]
391+ } ) ;
392+
393+ var httpContext = new DefaultHttpContext ( )
394+ {
395+ RequestServices = Services ,
396+ } ;
397+
398+ // Act
399+ await policy . ApplyAsync ( httpContext , candidates ) ;
400+
401+ // Assert
402+ Assert . Equal ( 2 , candidates . Count ) ;
251403 Assert . True ( candidates . IsValidCandidate ( 0 ) ) ;
404+ Assert . True ( candidates . IsValidCandidate ( 1 ) ) ;
405+ Assert . Same ( ControllerEndpoints [ 1 ] , candidates [ 0 ] . Endpoint ) ;
406+ Assert . Same ( ControllerEndpoints [ 2 ] , candidates [ 1 ] . Endpoint ) ;
252407 }
253408
254409 private class TestDynamicControllerEndpointSelector : DynamicControllerEndpointSelector
@@ -261,11 +416,18 @@ public TestDynamicControllerEndpointSelector(EndpointDataSource dataSource)
261416
262417 private class CustomTransformer : DynamicRouteValueTransformer
263418 {
264- public Func < HttpContext , RouteValueDictionary , ValueTask < RouteValueDictionary > > Transform { get ; set ; }
419+ public Func < HttpContext , RouteValueDictionary , object , ValueTask < RouteValueDictionary > > Transform { get ; set ; }
420+
421+ public Func < HttpContext , RouteValueDictionary , object , IReadOnlyList < Endpoint > , ValueTask < IReadOnlyList < Endpoint > > > Filter { get ; set ; }
265422
266423 public override ValueTask < RouteValueDictionary > TransformAsync ( HttpContext httpContext , RouteValueDictionary values )
267424 {
268- return Transform ( httpContext , values ) ;
425+ return Transform ( httpContext , values , State ) ;
426+ }
427+
428+ public override ValueTask < IReadOnlyList < Endpoint > > FilterAsync ( HttpContext httpContext , RouteValueDictionary values , IReadOnlyList < Endpoint > endpoints )
429+ {
430+ return Filter ( httpContext , values , State , endpoints ) ;
269431 }
270432 }
271433 }
0 commit comments