22Default open source detector
33"""
44
5+ import json
56from typing import Dict , List , Optional , Tuple , Union , cast
67
78from dlt_openapi .config import Config
4041 BaseDetectionWarning ,
4142 DataResponseNoBodyWarning ,
4243 DataResponseUndetectedWarning ,
44+ PossiblePaginatorWarning ,
4345 PrimaryKeyNotFoundWarning ,
4446 UnresolvedPathParametersWarning ,
4547 UnsupportedSecuritySchemeWarning ,
@@ -64,6 +66,7 @@ def run(self, open_api: OpenapiParser) -> None:
6466
6567 # discover stuff from responses
6668 self .detect_paginators_and_responses (open_api .endpoints )
69+ self .detect_global_pagination (open_api )
6770
6871 # discover parent child relationship
6972 self .detect_parent_child_relationships (open_api .endpoints )
@@ -86,6 +89,7 @@ def detect_security_schemes(self, open_api: OpenapiParser) -> None:
8689 schemes = list (open_api .security_schemes .values ())
8790
8891 # detect scheme settings
92+ # TODO: make this a bit nicer
8993 for scheme in schemes :
9094
9195 if scheme .type == "apiKey" :
@@ -112,7 +116,7 @@ def detect_security_schemes(self, open_api: OpenapiParser) -> None:
112116
113117 # find default scheme
114118 if len (schemes ) and schemes [0 ].supported :
115- open_api .detected_default_security_scheme = schemes [0 ]
119+ open_api .detected_global_security_scheme = schemes [0 ]
116120 elif len (schemes ) and not schemes [0 ].supported :
117121 self ._add_warning (UnsupportedSecuritySchemeWarning (schemes [0 ].name ))
118122
@@ -200,6 +204,40 @@ def detect_paginators_and_responses(self, endpoints: EndpointCollection) -> None
200204 )
201205 self .detect_primary_key (endpoint , endpoint .detected_data_response , endpoint .path )
202206
207+ def detect_global_pagination (self , open_api : OpenapiParser ) -> None :
208+ """go through all detected paginators and see which one we can set as global"""
209+ paginator_by_key : Dict [str , Pagination ] = {}
210+ paginator_count : Dict [str , int ] = {}
211+
212+ # count how many every paginator appears
213+ for endpoint in open_api .endpoints .endpoints :
214+ if not endpoint .detected_pagination :
215+ continue
216+ params = endpoint .detected_pagination .paginator_config
217+ key = json .dumps (params , sort_keys = True )
218+ paginator_by_key [key ] = endpoint .detected_pagination
219+ paginator_count .setdefault (key , 0 )
220+ paginator_count [key ] += 1
221+
222+ # no paginators found
223+ if len (paginator_by_key ) == 0 :
224+ return
225+
226+ # sort dict by value descending, so most used paginator is at the top
227+ sorted_paginator_count = sorted (paginator_count .items (), key = lambda item : item [1 ] * - 1 )
228+
229+ # we only set a global paginator, if we found one paginator, or if the top paginator has
230+ # a higher count than the second most used one
231+ if not (len (paginator_by_key ) == 1 or sorted_paginator_count [0 ][1 ] > sorted_paginator_count [1 ][1 ]):
232+ return
233+
234+ global_paginator = paginator_by_key [sorted_paginator_count [0 ][0 ]]
235+
236+ # set global paginator on base object but also set on all endpoints
237+ open_api .detected_global_pagination = global_paginator
238+ for e in open_api .endpoints .endpoints :
239+ e .detected_global_pagination = global_paginator
240+
203241 def detect_primary_key (self , e : Endpoint , response : Response , path : str ) -> None :
204242 """detect the primary key from the payload"""
205243 if not response .detected_payload :
@@ -417,6 +455,10 @@ def detect_pagination(self, endpoint: Endpoint) -> Optional[Pagination]:
417455 #
418456 # Nothing found :(
419457 #
458+ pagination_params = [* cursor_params , * offset_params , * limit_params , * page_params ]
459+ if pagination_params :
460+ self ._add_warning (PossiblePaginatorWarning ([p .name for p in pagination_params ]), endpoint )
461+
420462 return None
421463
422464 def detect_parent_child_relationships (self , endpoints : EndpointCollection ) -> None :
0 commit comments