mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 22:55:43 +00:00 
			
		
		
		
	feat(backend): api schema stats (#9478)
* feat(backend): Add API Schema stats * add scope stats * fix scope output
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
"""Schema processing functions for cleaning up generated schema."""
 | 
			
		||||
 | 
			
		||||
from itertools import chain
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
from drf_spectacular.openapi import AutoSchema
 | 
			
		||||
@@ -78,3 +79,47 @@ def postprocess_required_nullable(result, generator, request, public):
 | 
			
		||||
            schema.pop('required')
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def postprocess_print_stats(result, generator, request, public):
 | 
			
		||||
    """Prints statistics against schema."""
 | 
			
		||||
    rlt_dict = {}
 | 
			
		||||
    for path in result['paths']:
 | 
			
		||||
        for method in result['paths'][path]:
 | 
			
		||||
            sec = result['paths'][path][method].get('security', [])
 | 
			
		||||
            scopes = list(filter(None, (item.get('oauth2') for item in sec)))
 | 
			
		||||
            rlt_dict[f'{path}:{method}'] = {
 | 
			
		||||
                'method': method,
 | 
			
		||||
                'oauth': list(chain(*scopes)),
 | 
			
		||||
                'sec': sec is None,
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
    # Get paths without oauth2
 | 
			
		||||
    no_oauth2 = [
 | 
			
		||||
        path for path, details in rlt_dict.items() if not any(details['oauth'])
 | 
			
		||||
    ]
 | 
			
		||||
    no_oauth2_wa = [path for path in no_oauth2 if not path.startswith('/api/auth/v1/')]
 | 
			
		||||
    # Get paths without security
 | 
			
		||||
    no_security = [path for path, details in rlt_dict.items() if details['sec']]
 | 
			
		||||
    # Get path counts per scope
 | 
			
		||||
    scopes = {}
 | 
			
		||||
    for path, details in rlt_dict.items():
 | 
			
		||||
        if details['oauth']:
 | 
			
		||||
            for scope in details['oauth']:
 | 
			
		||||
                if scope not in scopes:
 | 
			
		||||
                    scopes[scope] = []
 | 
			
		||||
                scopes[scope].append(path)
 | 
			
		||||
    # Sort scopes by keys
 | 
			
		||||
    scopes = dict(sorted(scopes.items()))
 | 
			
		||||
 | 
			
		||||
    # Print statistics
 | 
			
		||||
    print('\nSchema statistics:')
 | 
			
		||||
    print(f'Paths without oauth2:                   {len(no_oauth2)}')
 | 
			
		||||
    print(f'Paths without oauth2 (without allauth): {len(no_oauth2_wa)}')
 | 
			
		||||
    print(f'Paths without security:                 {len(no_security)}\n')
 | 
			
		||||
    print('Scope stats:')
 | 
			
		||||
    for scope, paths in scopes.items():
 | 
			
		||||
        print(f'  {scope}: {len(paths)}')
 | 
			
		||||
    print()
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
 
 | 
			
		||||
@@ -1434,6 +1434,7 @@ SPECTACULAR_SETTINGS = {
 | 
			
		||||
    'POSTPROCESSING_HOOKS': [
 | 
			
		||||
        'drf_spectacular.hooks.postprocess_schema_enums',
 | 
			
		||||
        'InvenTree.schema.postprocess_required_nullable',
 | 
			
		||||
        'InvenTree.schema.postprocess_print_stats',
 | 
			
		||||
    ],
 | 
			
		||||
    'ENUM_NAME_OVERRIDES': {
 | 
			
		||||
        'UserTypeEnum': 'users.models.UserProfile.UserType',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user