8
8
from aws_lambda_powertools .event_handler .graphql_appsync .router import Router
9
9
from aws_lambda_powertools .utilities .data_classes import AppSyncResolverEvent
10
10
from aws_lambda_powertools .utilities .typing import LambdaContext
11
+ from aws_lambda_powertools .warnings import PowertoolsUserWarning
11
12
from tests .functional .utils import load_event
12
13
13
14
@@ -303,8 +304,106 @@ def test_include_router_merges_context():
303
304
assert app .context == router .context
304
305
305
306
307
+ def test_resolve_batch_processing_with_related_events ():
308
+ # GIVEN An event with multiple requests to fetch related posts for different post IDs.
309
+ event = [
310
+ {
311
+ "arguments" : {},
312
+ "identity" : "None" ,
313
+ "source" : {
314
+ "post_id" : "3" ,
315
+ "title" : "Third book" ,
316
+ },
317
+ "info" : {
318
+ "selectionSetList" : [
319
+ "title" ,
320
+ ],
321
+ "selectionSetGraphQL" : "{\n title\n }" ,
322
+ "fieldName" : "relatedPosts" ,
323
+ "parentTypeName" : "Post" ,
324
+ },
325
+ },
326
+ {
327
+ "arguments" : {},
328
+ "identity" : "None" ,
329
+ "source" : {
330
+ "post_id" : "4" ,
331
+ "title" : "Fifth book" ,
332
+ },
333
+ "info" : {
334
+ "selectionSetList" : [
335
+ "title" ,
336
+ ],
337
+ "selectionSetGraphQL" : "{\n title\n }" ,
338
+ "fieldName" : "relatedPosts" ,
339
+ "parentTypeName" : "Post" ,
340
+ },
341
+ },
342
+ {
343
+ "arguments" : {},
344
+ "identity" : "None" ,
345
+ "source" : {
346
+ "post_id" : "1" ,
347
+ "title" : "First book" ,
348
+ },
349
+ "info" : {
350
+ "selectionSetList" : [
351
+ "title" ,
352
+ ],
353
+ "selectionSetGraphQL" : "{\n title\n }" ,
354
+ "fieldName" : "relatedPosts" ,
355
+ "parentTypeName" : "Post" ,
356
+ },
357
+ },
358
+ ]
359
+
360
+ # GIVEN A dictionary of posts and a dictionary of related posts.
361
+ posts = {
362
+ "1" : {
363
+ "post_id" : "1" ,
364
+ "title" : "First book" ,
365
+ },
366
+ "2" : {
367
+ "post_id" : "2" ,
368
+ "title" : "Second book" ,
369
+ },
370
+ "3" : {
371
+ "post_id" : "3" ,
372
+ "title" : "Third book" ,
373
+ },
374
+ "4" : {
375
+ "post_id" : "4" ,
376
+ "title" : "Fourth book" ,
377
+ },
378
+ }
379
+
380
+ posts_related = {
381
+ "1" : [posts ["2" ]],
382
+ "2" : [posts ["3" ], posts ["4" ], posts ["1" ]],
383
+ "3" : [posts ["2" ], posts ["1" ]],
384
+ "4" : [posts ["3" ], posts ["1" ]],
385
+ }
386
+
387
+ app = AppSyncResolver ()
388
+
389
+ @app .batch_resolver (type_name = "Post" , field_name = "relatedPosts" )
390
+ def related_posts (event : AppSyncResolverEvent ) -> Optional [list ]:
391
+ return posts_related [event .source ["post_id" ]]
392
+
393
+ # WHEN related_posts function, which is the batch resolver, is called with the event.
394
+ result = app .resolve (event , LambdaContext ())
395
+
396
+ # THEN the result must be a list of related posts
397
+ assert result == [
398
+ posts_related ["3" ],
399
+ posts_related ["4" ],
400
+ posts_related ["1" ],
401
+ ]
402
+
403
+
306
404
# Batch resolver tests
307
- def test_resolve_batch_processing ():
405
+ def test_resolve_batch_processing_with_simple_queries ():
406
+ # GIVEN a list of events representing GraphQL queries for listing locations
308
407
event = [
309
408
{
310
409
"typeName" : "Query" ,
@@ -346,11 +445,12 @@ def test_resolve_batch_processing():
346
445
347
446
app = AppSyncResolver ()
348
447
448
+ # WHEN the batch resolver for the listLocations field is defined
349
449
@app .batch_resolver (field_name = "listLocations" )
350
450
def create_something (event : AppSyncResolverEvent ) -> Optional [list ]: # noqa AA03 VNE003
351
451
return event .source ["id" ] if event .source else None
352
452
353
- # Call the implicit handler
453
+ # THEN the resolver should correctly process the batch of queries
354
454
result = app .resolve (event , LambdaContext ())
355
455
assert result == [appsync_event ["source" ]["id" ] for appsync_event in event ]
356
456
@@ -359,6 +459,7 @@ def create_something(event: AppSyncResolverEvent) -> Optional[list]: # noqa AA0
359
459
360
460
361
461
def test_resolve_batch_processing_with_raise_on_exception ():
462
+ # GIVEN a list of events representing GraphQL queries for listing locations
362
463
event = [
363
464
{
364
465
"typeName" : "Query" ,
@@ -400,16 +501,18 @@ def test_resolve_batch_processing_with_raise_on_exception():
400
501
401
502
app = AppSyncResolver ()
402
503
504
+ # WHEN the sync batch resolver for the 'listLocations' field is defined with raise_on_error=True
403
505
@app .batch_resolver (field_name = "listLocations" , raise_on_error = True )
404
506
def create_something (event : AppSyncResolverEvent ) -> Optional [list ]: # noqa AA03 VNE003
405
507
raise RuntimeError
406
508
407
- # Call the implicit handler
509
+ # THEN the resolver should raise a RuntimeError when processing the batch of queries
408
510
with pytest .raises (RuntimeError ):
409
511
app .resolve (event , LambdaContext ())
410
512
411
513
412
514
def test_async_resolve_batch_processing_with_raise_on_exception ():
515
+ # GIVEN a list of events representing GraphQL queries for listing locations
413
516
event = [
414
517
{
415
518
"typeName" : "Query" ,
@@ -451,11 +554,12 @@ def test_async_resolve_batch_processing_with_raise_on_exception():
451
554
452
555
app = AppSyncResolver ()
453
556
557
+ # WHEN the async batch resolver for the 'listLocations' field is defined with raise_on_error=True
454
558
@app .async_batch_resolver (field_name = "listLocations" , raise_on_error = True )
455
559
async def create_something (event : AppSyncResolverEvent ) -> Optional [list ]: # noqa AA03 VNE003
456
560
raise RuntimeError
457
561
458
- # Call the implicit handler
562
+ # THEN the resolver should raise a RuntimeError when processing the batch of queries
459
563
with pytest .raises (RuntimeError ):
460
564
app .resolve (event , LambdaContext ())
461
565
@@ -515,6 +619,7 @@ def create_something(event: AppSyncResolverEvent) -> Optional[list]: # noqa AA0
515
619
516
620
517
621
def test_resolve_async_batch_processing_without_exception ():
622
+ # GIVEN a list of events representing GraphQL queries for listing locations
518
623
event = [
519
624
{
520
625
"typeName" : "Query" ,
@@ -556,16 +661,16 @@ def test_resolve_async_batch_processing_without_exception():
556
661
557
662
app = AppSyncResolver ()
558
663
664
+ # WHEN the batch resolver for the 'listLocations' field is defined with raise_on_error=False
559
665
@app .async_batch_resolver (field_name = "listLocations" , raise_on_error = False )
560
666
async def create_something (event : AppSyncResolverEvent ) -> Optional [list ]: # noqa AA03 VNE003
561
667
raise RuntimeError
562
668
563
- # Call the implicit handler
564
669
result = app .resolve (event , LambdaContext ())
565
- assert result == [None , None , None ]
566
670
567
- assert app .current_batch_event and len (app .current_batch_event ) == len (event )
568
- assert not app .current_event
671
+ # THEN the resolver should return None for each event in the batch
672
+ assert len (app .current_batch_event ) == len (event )
673
+ assert result == [None , None , None ]
569
674
570
675
571
676
def test_resolver_batch_with_resolver_not_found ():
@@ -596,7 +701,7 @@ def get_locations(event: AppSyncResolverEvent, name: str) -> str:
596
701
597
702
app .include_router (router )
598
703
599
- # THEN must fail with ValueError
704
+ # THEN must fail with ResolverNotFoundError
600
705
with pytest .raises (ResolverNotFoundError , match = "No resolver found for.*" ):
601
706
app .resolve (mock_event1 , LambdaContext ())
602
707
@@ -633,27 +738,27 @@ async def get_locations_async(event: AppSyncResolverEvent, name: str) -> str:
633
738
634
739
app .include_router (router )
635
740
636
- # THEN must fail with ValueError
637
- with pytest .warns (UserWarning , match = "Both synchronous and asynchronous resolvers*" ):
741
+ # THEN must raise a PowertoolsUserWarning
742
+ with pytest .warns (PowertoolsUserWarning , match = "Both synchronous and asynchronous resolvers*" ):
638
743
app .resolve (mock_event1 , LambdaContext ())
639
744
640
745
641
- def test_resolver_include_batch_resolver ():
642
- # GIVEN
746
+ def test_batch_resolver_with_router ():
747
+ # GIVEN an AppSyncResolver and a Router instance
643
748
app = AppSyncResolver ()
644
749
router = Router ()
645
750
646
751
@router .batch_resolver (type_name = "Query" , field_name = "listLocations" )
647
752
def get_locations (event : AppSyncResolverEvent , name : str ) -> str :
648
- return "get_locations#" + name + " #" + event .source ["id" ]
753
+ return f "get_locations#{ name } #" + event .source ["id" ]
649
754
650
- @app .batch_resolver (field_name = "listLocations2" )
755
+ @router .batch_resolver (field_name = "listLocations2" )
651
756
def get_locations2 (event : AppSyncResolverEvent , name : str ) -> str :
652
- return "get_locations2#" + name + " #" + event .source ["id" ]
757
+ return f "get_locations2#{ name } #" + event .source ["id" ]
653
758
759
+ # WHEN we include the routes
654
760
app .include_router (router )
655
761
656
- # WHEN
657
762
mock_event1 = [
658
763
{
659
764
"typeName" : "Query" ,
@@ -685,12 +790,13 @@ def get_locations2(event: AppSyncResolverEvent, name: str) -> str:
685
790
result1 = app .resolve (mock_event1 , LambdaContext ())
686
791
result2 = app .resolve (mock_event2 , LambdaContext ())
687
792
688
- # THEN
793
+ # THEN the resolvers should return the expected results
689
794
assert result1 == ["get_locations#value#1" ]
690
795
assert result2 == ["get_locations2#value#2" ]
691
796
692
797
693
798
def test_resolve_async_batch_processing ():
799
+ # GIVEN a list of events representing GraphQL queries for listing locations
694
800
event = [
695
801
{
696
802
"typeName" : "Query" ,
@@ -732,19 +838,20 @@ def test_resolve_async_batch_processing():
732
838
733
839
app = AppSyncResolver ()
734
840
841
+ # WHEN the async batch resolver for the 'listLocations' field is defined
735
842
@app .async_batch_resolver (field_name = "listLocations" )
736
843
async def create_something (event : AppSyncResolverEvent ) -> Optional [list ]:
737
844
return event .source ["id" ] if event .source else None
738
845
739
- # Call the implicit handler
846
+ # THEN the resolver should correctly process the batch of queries asynchronously
740
847
result = app .resolve (event , LambdaContext ())
741
848
assert result == [appsync_event ["source" ]["id" ] for appsync_event in event ]
742
849
743
850
assert app .current_batch_event and len (app .current_batch_event ) == len (event )
744
851
745
852
746
853
def test_resolve_async_batch_and_sync_singular_processing ():
747
- # GIVEN
854
+ # GIVEN a router with an async batch resolver for 'listLocations' and a sync singular resolver for 'listLocation'
748
855
app = AppSyncResolver ()
749
856
router = Router ()
750
857
@@ -758,7 +865,7 @@ def get_location(name: str) -> str:
758
865
759
866
app .include_router (router )
760
867
761
- # WHEN
868
+ # WHEN resolving a batch of events for async 'listLocations' and a singular event for 'listLocation'
762
869
mock_event1 = [
763
870
{
764
871
"typeName" : "Query" ,
@@ -778,7 +885,7 @@ def get_location(name: str) -> str:
778
885
result1 = app .resolve (mock_event1 , LambdaContext ())
779
886
result2 = app .resolve (mock_event2 , LambdaContext ())
780
887
781
- # THEN
888
+ # THEN the resolvers should return the expected results
782
889
assert result1 == ["get_locations#value#1" ]
783
890
assert result2 == "get_location#value"
784
891
@@ -790,11 +897,11 @@ def test_async_resolver_include_batch_resolver():
790
897
791
898
@router .async_batch_resolver (type_name = "Query" , field_name = "listLocations" )
792
899
async def get_locations (event : AppSyncResolverEvent , name : str ) -> str :
793
- return "get_locations#" + name + " #" + event .source ["id" ]
900
+ return f "get_locations#{ name } #" + event .source ["id" ]
794
901
795
902
@app .async_batch_resolver (field_name = "listLocations2" )
796
903
async def get_locations2 (event : AppSyncResolverEvent , name : str ) -> str :
797
- return "get_locations2#" + name + " #" + event .source ["id" ]
904
+ return f "get_locations2#{ name } #" + event .source ["id" ]
798
905
799
906
app .include_router (router )
800
907
0 commit comments