|
4 | 4 | import datetime |
5 | 5 | from enum import Enum |
6 | 6 | import os |
7 | | -from dataclasses import dataclass |
| 7 | +from dataclasses import dataclass, field |
8 | 8 | from typing import Optional, List |
9 | 9 | import uuid |
10 | 10 | from flask import current_app |
11 | | -from marshmallow import ValidationError, fields, EXCLUDE, post_dump, validates_schema |
| 11 | +from marshmallow import ( |
| 12 | + ValidationError, |
| 13 | + fields, |
| 14 | + EXCLUDE, |
| 15 | + post_dump, |
| 16 | + validates_schema, |
| 17 | + post_load, |
| 18 | +) |
12 | 19 | from pathvalidate import sanitize_filename |
13 | 20 |
|
14 | 21 | from .utils import ( |
@@ -234,3 +241,113 @@ def patch_field(self, data, **kwargs): |
234 | 241 | if not data.get("diff"): |
235 | 242 | data.pop("diff") |
236 | 243 | return data |
| 244 | + |
| 245 | + |
| 246 | +@dataclass |
| 247 | +class DeltaDiffFile: |
| 248 | + """Diff file path in diffs list""" |
| 249 | + |
| 250 | + path: str |
| 251 | + |
| 252 | + |
| 253 | +class DeltaChangeDiffFileSchema(ma.Schema): |
| 254 | + """Schema for diff file path in diffs list""" |
| 255 | + |
| 256 | + path = fields.String(required=True) |
| 257 | + |
| 258 | + |
| 259 | +@dataclass |
| 260 | +class DeltaChangeBase(File): |
| 261 | + """Base class for changes stored in json list or returned from delta endpoint""" |
| 262 | + |
| 263 | + change: PushChangeType |
| 264 | + version: int |
| 265 | + |
| 266 | + |
| 267 | +@dataclass |
| 268 | +class DeltaChangeMerged(DeltaChangeBase): |
| 269 | + """Delta item with merged diffs to list of multiple diff files""" |
| 270 | + |
| 271 | + diffs: List[DeltaDiffFile] = field(default_factory=list) |
| 272 | + |
| 273 | + def to_data_delta(self): |
| 274 | + """Convert DeltaMerged to DeltaData with single diff""" |
| 275 | + result = DeltaChange( |
| 276 | + path=self.path, |
| 277 | + size=self.size, |
| 278 | + checksum=self.checksum, |
| 279 | + change=self.change, |
| 280 | + version=self.version, |
| 281 | + ) |
| 282 | + if self.diffs: |
| 283 | + result.diff = self.diffs[0].path |
| 284 | + return result |
| 285 | + |
| 286 | + |
| 287 | +@dataclass |
| 288 | +class DeltaChange(DeltaChangeBase): |
| 289 | + """Change items stored in database as list of this item with single diff file""" |
| 290 | + |
| 291 | + diff: Optional[str] = None |
| 292 | + |
| 293 | + def to_merged(self) -> DeltaChangeMerged: |
| 294 | + """Convert to DeltaMerged with multiple diffs""" |
| 295 | + result = DeltaChangeMerged( |
| 296 | + path=self.path, |
| 297 | + size=self.size, |
| 298 | + checksum=self.checksum, |
| 299 | + change=self.change, |
| 300 | + version=self.version, |
| 301 | + ) |
| 302 | + if self.diff: |
| 303 | + result.diffs = [DeltaDiffFile(path=self.diff)] |
| 304 | + return result |
| 305 | + |
| 306 | + |
| 307 | +class DeltaChangeBaseSchema(ma.Schema): |
| 308 | + """Base schema for delta json and response from delta endpoint""" |
| 309 | + |
| 310 | + path = fields.String(required=True) |
| 311 | + size = fields.Integer(required=True) |
| 312 | + checksum = fields.String(required=True) |
| 313 | + version = fields.Integer(required=True) |
| 314 | + change = fields.Enum(PushChangeType, by_value=True, required=True) |
| 315 | + |
| 316 | + |
| 317 | +class DeltaChangeSchema(DeltaChangeBaseSchema): |
| 318 | + """Schema for change data in changes column""" |
| 319 | + |
| 320 | + diff = fields.String(required=False) |
| 321 | + |
| 322 | + @post_load |
| 323 | + def make_object(self, data, **kwargs): |
| 324 | + return DeltaChange(**data) |
| 325 | + |
| 326 | + @post_dump |
| 327 | + def patch_field(self, data, **kwargs): |
| 328 | + # drop 'diff' key entirely if empty or None as database would expect |
| 329 | + if not data.get("diff"): |
| 330 | + data.pop("diff", None) |
| 331 | + return data |
| 332 | + |
| 333 | + |
| 334 | +class DeltaChangeItemSchema(DeltaChangeBaseSchema): |
| 335 | + """Schema for delta changes response""" |
| 336 | + |
| 337 | + diffs = fields.List(fields.Nested(DeltaChangeDiffFileSchema())) |
| 338 | + |
| 339 | + @post_dump |
| 340 | + def patch_field(self, data, **kwargs): |
| 341 | + # drop 'diffs' key entirely if empty or None as clients would expect |
| 342 | + if not data.get("diffs"): |
| 343 | + data.pop("diffs", None) |
| 344 | + return data |
| 345 | + |
| 346 | + |
| 347 | +class DeltaChangeRespSchema(ma.Schema): |
| 348 | + """Schema for list of delta changes wrapped in items field""" |
| 349 | + |
| 350 | + items = fields.List(fields.Nested(DeltaChangeItemSchema())) |
| 351 | + |
| 352 | + class Meta: |
| 353 | + unknown = EXCLUDE |
0 commit comments