3
3
"""
4
4
5
5
import logging
6
- import time
6
+ from contextlib import asynccontextmanager
7
7
from pathlib import Path
8
- from secrets import token_hex
9
- from typing import Any , Awaitable , Callable
8
+ from typing import Any , AsyncGenerator
10
9
11
10
import sentry_sdk
12
- from asgi_correlation_id import CorrelationIdMiddleware
11
+ from dockerflow import checks
12
+ from dockerflow .fastapi import router as dockerflow_router
13
+ from dockerflow .fastapi .middleware import (
14
+ MozlogRequestSummaryLogger ,
15
+ RequestIdMiddleware ,
16
+ )
17
+ from dockerflow .version import get_version
13
18
from fastapi import FastAPI , Request , Response , status
14
19
from fastapi .encoders import jsonable_encoder
15
20
from fastapi .exceptions import RequestValidationError
16
21
from fastapi .responses import JSONResponse
17
22
from fastapi .staticfiles import StaticFiles
18
23
19
- from jbi .environment import get_settings , get_version
20
- from jbi .log import CONFIG , format_request_summary_fields
24
+ import jbi .jira
25
+ from jbi .configuration import ACTIONS
26
+ from jbi .environment import get_settings
27
+ from jbi .log import CONFIG
21
28
from jbi .router import router
22
29
23
30
SRC_DIR = Path (__file__ ).parent
31
+ APP_DIR = Path (__file__ ).parents [1 ]
24
32
25
33
settings = get_settings ()
26
- version_info = get_version ()
34
+ version_info = get_version (APP_DIR )
27
35
28
36
logging .config .dictConfig (CONFIG )
29
37
@@ -47,37 +55,62 @@ def traces_sampler(sampling_context: dict[str, Any]) -> float:
47
55
)
48
56
49
57
58
+ # https://github.com/tiangolo/fastapi/discussions/9241
59
+ @asynccontextmanager
60
+ async def lifespan (app : FastAPI ) -> AsyncGenerator [None , None ]:
61
+ jira_service = jbi .jira .get_service ()
62
+ bugzilla_service = jbi .bugzilla .get_service ()
63
+
64
+ checks .register (bugzilla_service .check_bugzilla_connection , name = "bugzilla.up" )
65
+ checks .register (
66
+ bugzilla_service .check_bugzilla_webhooks ,
67
+ name = "bugzilla.all_webhooks_enabled" ,
68
+ )
69
+
70
+ checks .register (jira_service .check_jira_connection , name = "jira.up" )
71
+ checks .register_partial (
72
+ jira_service .check_jira_all_projects_are_visible ,
73
+ ACTIONS ,
74
+ name = "jira.all_projects_are_visible" ,
75
+ )
76
+ checks .register_partial (
77
+ jira_service .check_jira_all_projects_have_permissions ,
78
+ ACTIONS ,
79
+ name = "jira.all_projects_have_permissions" ,
80
+ )
81
+ checks .register_partial (
82
+ jira_service .check_jira_all_project_custom_components_exist ,
83
+ ACTIONS ,
84
+ name = "jira.all_project_custom_components_exist" ,
85
+ )
86
+ checks .register_partial (
87
+ jira_service .check_jira_all_project_issue_types_exist ,
88
+ ACTIONS ,
89
+ name = "jira.all_project_issue_types_exist" ,
90
+ )
91
+ checks .register (jira_service .check_jira_pandoc_install , name = "jira.pandoc_install" )
92
+
93
+ yield
94
+
95
+
50
96
app = FastAPI (
51
97
title = "Jira Bugzilla Integration (JBI)" ,
52
98
description = "Platform providing synchronization of Bugzilla bugs to Jira issues." ,
53
99
version = version_info ["version" ],
54
100
debug = settings .app_debug ,
101
+ lifespan = lifespan ,
55
102
)
56
103
57
- app .include_router (router )
58
- app .mount ("/static" , StaticFiles (directory = SRC_DIR / "static" ), name = "static" )
104
+ app .state .APP_DIR = APP_DIR
105
+ app .state .DOCKERFLOW_HEARTBEAT_FAILED_STATUS_CODE = 503
106
+ app .state .DOCKERFLOW_SUMMARY_LOG_QUERYSTRING = True
59
107
108
+ app .include_router (router )
109
+ app .include_router (dockerflow_router )
110
+ app .add_middleware (RequestIdMiddleware )
111
+ app .add_middleware (MozlogRequestSummaryLogger )
60
112
61
- @app .middleware ("http" )
62
- async def request_summary (
63
- request : Request , call_next : Callable [[Request ], Awaitable [Response ]]
64
- ) -> Response :
65
- """Middleware to log request info"""
66
- summary_logger = logging .getLogger ("request.summary" )
67
- request_time = time .time ()
68
- try :
69
- response = await call_next (request )
70
- log_fields = format_request_summary_fields (
71
- request , request_time , status_code = response .status_code
72
- )
73
- summary_logger .info ("" , extra = log_fields )
74
- return response
75
- except Exception as exc :
76
- log_fields = format_request_summary_fields (
77
- request , request_time , status_code = status .HTTP_500_INTERNAL_SERVER_ERROR
78
- )
79
- summary_logger .info (exc , extra = log_fields )
80
- raise
113
+ app .mount ("/static" , StaticFiles (directory = SRC_DIR / "static" ), name = "static" )
81
114
82
115
83
116
@app .exception_handler (RequestValidationError )
@@ -100,11 +133,3 @@ async def validation_exception_handler(
100
133
status_code = status .HTTP_422_UNPROCESSABLE_ENTITY ,
101
134
content = {"detail" : jsonable_encoder (exc .errors ())},
102
135
)
103
-
104
-
105
- app .add_middleware (
106
- CorrelationIdMiddleware ,
107
- header_name = "X-Request-Id" ,
108
- generator = lambda : token_hex (16 ),
109
- validator = None ,
110
- )
0 commit comments