Coverage for model/utils/request.py: 88%
32 statements
« prev ^ index » next coverage.py v7.9.2, created at 2026-07-24 16:42 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2026-07-24 16:42 +0000
1import inspect
2import httpx
3from fastapi import Depends, Request
4from mongo import engine
5from .response import NOJException
7__all__ = ('get_doc', 'get_ip', 'get_http_client')
10def get_doc(src_param: str, cls, param_type=str):
11 """
12 Depends factory: resolves a MongoEngine document from a path/query parameter.
14 Usage:
15 @router.get('/{username}')
16 def handler(target_user = get_doc('username', User)): ...
18 @router.get('/{problem_id}')
19 def handler(problem = get_doc('problem_id', Problem, int)): ...
20 """
22 def dependency(**kwargs):
23 val = kwargs[src_param]
24 try:
25 doc = cls(val)
26 if not doc:
27 raise engine.DoesNotExist(f'{cls.__name__} not found')
28 return doc
29 except engine.DoesNotExist as e:
30 raise NOJException(str(e), 404)
31 except engine.ValidationError:
32 raise NOJException('Invalid parameter', 400)
34 # Give the function a proper named signature so FastAPI can inject the path param
35 param = inspect.Parameter(
36 src_param,
37 kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
38 annotation=param_type,
39 )
40 dependency.__signature__ = inspect.Signature([param])
41 dependency.__annotations__ = {src_param: param_type}
42 return Depends(dependency)
45def get_http_client(request: Request) -> httpx.Client:
46 return request.app.state.http_client
49def get_ip(request: Request) -> str:
50 # cf-connecting-ip is set by Cloudflare and is more reliable than X-Forwarded-For
51 cf_ip = request.headers.get('cf-connecting-ip', '').strip()
52 if cf_ip:
53 return cf_ip
54 forwarded_for = request.headers.get('X-Forwarded-For',
55 '').split(',')[-1].strip()
56 if forwarded_for:
57 return forwarded_for
58 return request.client.host if request.client else ''