|
2 | 2 | import asyncio |
3 | 3 | import json |
4 | 4 | import urllib.parse |
| 5 | + |
5 | 6 | from typing import Any |
6 | 7 | from typing import Literal |
7 | 8 |
|
8 | 9 | import asyncpg.connect_utils |
9 | 10 | import fastapi_structured_logging |
| 11 | + |
10 | 12 | from pydantic import BaseModel |
11 | 13 |
|
12 | | -from config import config |
13 | 14 | from config import DefaultConfig |
| 15 | +from config import config |
14 | 16 | from gitlab_model import GLEmojiAttributes |
15 | 17 | from gitlab_model import MergeRequestPayload |
16 | 18 | from gitlab_model import PipelinePayload |
17 | 19 |
|
| 20 | + |
18 | 21 | log = fastapi_structured_logging.get_logger() |
19 | 22 |
|
20 | 23 | __all__ = ["database", "dbh"] |
@@ -138,7 +141,77 @@ async def get_gitlab_instance_id_from_url(self, urlstr: str) -> int: |
138 | 141 | assert isinstance(gli_id, int) |
139 | 142 | return gli_id |
140 | 143 |
|
| 144 | + async def get_or_create_merge_request_ref_id(self, merge_request: MergeRequestPayload) -> int: |
| 145 | + """ |
| 146 | + Get or create an MR ref without updating payload. |
| 147 | +
|
| 148 | + Used for OOO check - we need the ID to query message refs, |
| 149 | + but we don't want to corrupt the payload with stale data. |
| 150 | + Only sets initial state on INSERT, never updates existing records. |
| 151 | + """ |
| 152 | + gitlab_instance_id = await self.get_gitlab_instance_id_from_url(merge_request.object_attributes.url) |
| 153 | + |
| 154 | + merge_ref_id = await self._generic_norm_upsert( |
| 155 | + table="merge_request_ref", |
| 156 | + identity_col="merge_request_ref_id", |
| 157 | + select_attrs={ |
| 158 | + "gitlab_instance_id": gitlab_instance_id, |
| 159 | + "gitlab_project_id": merge_request.object_attributes.target_project_id, |
| 160 | + "gitlab_merge_request_iid": merge_request.object_attributes.iid, |
| 161 | + }, |
| 162 | + insert_only_vals={ |
| 163 | + "gitlab_merge_request_id": merge_request.object_attributes.id, |
| 164 | + "head_pipeline_id": merge_request.object_attributes.head_pipeline_id, |
| 165 | + "merge_request_payload": merge_request.model_dump(), |
| 166 | + "merge_request_extra_state": { |
| 167 | + "version": 1, |
| 168 | + "opener": { |
| 169 | + "id": merge_request.user.id, |
| 170 | + "name": merge_request.user.name, |
| 171 | + "username": merge_request.user.username, |
| 172 | + }, |
| 173 | + "approvers": {}, |
| 174 | + "pipeline_statuses": {}, |
| 175 | + "emojis": {}, |
| 176 | + }, |
| 177 | + }, |
| 178 | + ) |
| 179 | + assert isinstance(merge_ref_id, int) |
| 180 | + return merge_ref_id |
| 181 | + |
| 182 | + async def update_merge_request_ref_payload( |
| 183 | + self, merge_request_ref_id: int, merge_request: MergeRequestPayload |
| 184 | + ) -> MergeRequestInfos: |
| 185 | + """ |
| 186 | + Update MR ref payload after OOO check has passed. |
| 187 | +
|
| 188 | + Called only for non-OOO events to update the stored payload. |
| 189 | + """ |
| 190 | + connection: asyncpg.Connection |
| 191 | + async with await database.acquire() as connection: |
| 192 | + row = await connection.fetchrow( |
| 193 | + """UPDATE merge_request_ref |
| 194 | + SET gitlab_merge_request_id = $1, |
| 195 | + head_pipeline_id = $2, |
| 196 | + merge_request_payload = $3 |
| 197 | + WHERE merge_request_ref_id = $4 |
| 198 | + RETURNING merge_request_ref_id, merge_request_payload, |
| 199 | + merge_request_extra_state, head_pipeline_id""", |
| 200 | + merge_request.object_attributes.id, |
| 201 | + merge_request.object_attributes.head_pipeline_id, |
| 202 | + merge_request.model_dump(), |
| 203 | + merge_request_ref_id, |
| 204 | + ) |
| 205 | + assert row is not None |
| 206 | + return MergeRequestInfos(**row) |
| 207 | + |
141 | 208 | async def get_merge_request_ref_infos(self, merge_request: MergeRequestPayload) -> MergeRequestInfos: |
| 209 | + """ |
| 210 | + Get or create MR ref AND update payload (legacy behavior). |
| 211 | +
|
| 212 | + Note: This updates payload on every call. For OOO-safe behavior, |
| 213 | + use get_or_create_merge_request_ref_id() + update_merge_request_ref_payload(). |
| 214 | + """ |
142 | 215 | gitlab_instance_id = await self.get_gitlab_instance_id_from_url(merge_request.object_attributes.url) |
143 | 216 |
|
144 | 217 | merge_ref = await self._generic_norm_upsert( |
@@ -212,7 +285,6 @@ async def _generic_norm_upsert( |
212 | 285 | insert_only_vals: dict[str, Any] | None = None, |
213 | 286 | extra_sel_cols: list[str] | None = None, |
214 | 287 | ) -> Any: |
215 | | - |
216 | 288 | if extra_insert_and_update_vals is None: |
217 | 289 | extra_insert_and_update_vals = {} |
218 | 290 |
|
|
0 commit comments