updates
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
import importlib
|
||||
from dataclasses import field
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
|
||||
from pydantic import ValidationError
|
||||
from pydantic.dataclasses import dataclass
|
||||
from typing_extensions import Self
|
||||
|
||||
from ..report.schemas.v3_0 import main as v3_0
|
||||
from .base import ReportSchemaVersion, SafetyBaseModel, ScanType
|
||||
from .file import FileModel
|
||||
from .metadata import MetadataModel
|
||||
from .project import ProjectModel
|
||||
from .telemetry import TelemetryModel
|
||||
|
||||
from pydantic.version import VERSION as PYDANTIC_VERSION
|
||||
|
||||
def version_validator(func):
|
||||
if PYDANTIC_VERSION.startswith("1."):
|
||||
from pydantic import validator
|
||||
return validator('verison', pre=True, always=True)
|
||||
else:
|
||||
from pydantic import field_validator
|
||||
return field_validator('verison', mode='before')
|
||||
|
||||
@dataclass
|
||||
class ReportModel(SafetyBaseModel):
|
||||
"""
|
||||
Used as an entrypoint to keep backwards compatibility with old formats.
|
||||
Use this model if you want to generate a standard JSON report.
|
||||
"""
|
||||
|
||||
DEFAULT_SCHEMA_VERSION = ReportSchemaVersion.v3_0
|
||||
|
||||
telemetry: TelemetryModel
|
||||
metadata: MetadataModel
|
||||
files: List[FileModel]
|
||||
projects: List[ProjectModel] = field(default_factory=lambda: [])
|
||||
version: ReportSchemaVersion = DEFAULT_SCHEMA_VERSION
|
||||
|
||||
@version_validator
|
||||
def validate_version(cls, version: ReportSchemaVersion) -> ReportSchemaVersion:
|
||||
versions = list(ReportSchemaVersion)
|
||||
if version not in (versions):
|
||||
raise ValueError(f"Invalid version, allowed versions are {versions}")
|
||||
return version
|
||||
|
||||
def as_v30(self) -> v3_0.Report:
|
||||
full = self.metadata.scan_type is ScanType.scan
|
||||
|
||||
projects_data = [p.as_v30(full=full) for p in self.projects]
|
||||
|
||||
if not all(isinstance(p, type(projects_data[0])) for p in projects_data):
|
||||
raise ValueError("All objects in projects_data must be of the same type")
|
||||
|
||||
results = v3_0.ScanResults(
|
||||
files=[f.as_v30() for f in self.files],
|
||||
projects=projects_data, # type: ignore
|
||||
)
|
||||
|
||||
meta_data = self.metadata.as_v30()
|
||||
|
||||
if not isinstance(meta_data, v3_0.Meta) or not isinstance(results, v3_0.ScanResults):
|
||||
raise TypeError("Expected instance of v3_0.Meta and v3_0.ScanResults")
|
||||
|
||||
report = v3_0.Report(meta=meta_data, scan_results=results)
|
||||
|
||||
return report
|
||||
|
||||
@classmethod
|
||||
def from_v30(cls, obj: v3_0.SchemaModelV30) -> 'ReportModel':
|
||||
|
||||
if not isinstance(obj, v3_0.Report):
|
||||
raise TypeError('Expected instance of v3_0.Report')
|
||||
|
||||
return ReportModel(
|
||||
version=ReportSchemaVersion(obj.meta.schema_version),
|
||||
telemetry=TelemetryModel.from_v30(obj.meta.telemetry),
|
||||
metadata=MetadataModel.from_v30(obj.meta),
|
||||
projects=[ProjectModel.from_v30(p) for p in obj.scan_results.projects],
|
||||
files=[FileModel.from_v30(f) for f in obj.scan_results.files],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def parse_report(
|
||||
cls, raw_report: Union[str, Path], schema: ReportSchemaVersion
|
||||
) -> Union['ReportModel', ValidationError]:
|
||||
parse = "parse_raw"
|
||||
|
||||
if isinstance(raw_report, Path):
|
||||
raw_report = raw_report.expanduser().resolve()
|
||||
parse = "parse_file"
|
||||
|
||||
target_schema = schema.value.replace(".", "_")
|
||||
module_name = "safety_schemas.report.schemas." f"v{target_schema}.main"
|
||||
|
||||
module = importlib.import_module(module_name)
|
||||
report_model = module.Report
|
||||
|
||||
# This will raise a validation error if the content is wrong
|
||||
validated_report = getattr(report_model, parse)(raw_report)
|
||||
|
||||
# TODO: Select the from from the version passed
|
||||
return ReportModel.from_v30(obj=validated_report)
|
||||
Reference in New Issue
Block a user