Skip to content

Commit

Permalink
- Instantiate via cls in classmethods
Browse files Browse the repository at this point in the history
- deduplicate_spdx_packages nested in SPDXSbom
- simplified spdx classes initiation from dict
- removed debug print

Signed-off-by: Jindrich Luza <jluza@redhat.com>
  • Loading branch information
midnightercz committed Aug 29, 2024
1 parent 51cca02 commit 0f308c0
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 57 deletions.
94 changes: 40 additions & 54 deletions cachi2/core/models/sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def from_package_dict(cls, package: dict[str, Any]) -> "Component":
A Cachi2 package has extra fields which are unnecessary and can cause validation errors.
"""
return Component(
return cls(
name=package.get("name", None),
version=package.get("version", None),
purl=package.get("purl", None),
Expand Down Expand Up @@ -192,25 +192,12 @@ def from_package_dict(cls, package: dict[str, Any]) -> "SPDXPackage":
"""Create a SPDXPackage from a Cachi2 package dictionary."""
external_refs = []
for er in package.get("externalRefs", []):
external_refs.append(
SPDXPackageExternalRef(
referenceLocator=er["referenceLocator"],
referenceType=er["referenceType"],
referenceCategory=er["referenceCategory"],
)
)
external_refs.append(SPDXPackageExternalRef(**er))
annotations = []
for er in package.get("annotations", []):
annotations.append(
SPDXPackageAnnotation(
annotator=er["annotator"],
annotationDate=er["annotationDate"],
annotationType=er["annotationType"],
comment=er["comment"],
)
)
return SPDXPackage(
name=package.get("name", None),
for an in package.get("annotations", []):
annotations.append(SPDXPackageAnnotation(**an))
return cls(
name=package["name"],
versionInfo=package.get("versionInfo", None),
externalRefs=external_refs,
annotations=annotations,
Expand All @@ -227,40 +214,6 @@ class SPDXCreationInfo(pydantic.BaseModel):
creators: list[str] = []


def deduplicate_spdx_packages(items: Iterable[SPDXPackage]) -> list[SPDXPackage]:
"""Deduplicate SPDX packages and merge external references.
If package with same name and version is found multiple times in the list,
merge external references of all the packages into one package.
"""
unique_items = {}
for item in items:
key = (item.name, item.versionInfo)
if key not in unique_items:
unique_items[key] = SPDXPackage(name=item.name, versionInfo=item.versionInfo)
unique_items[key].externalRefs = item.externalRefs[:]
unique_items[key].annotations = item.annotations[:]
else:
unique_items[key].externalRefs.extend(item.externalRefs)
unique_items[key].annotations.extend(item.annotations)

for item in unique_items.values():
item.externalRefs = sorted(
set(item.externalRefs),
key=lambda ref: (ref.referenceLocator, ref.referenceType, ref.referenceCategory),
)
item.annotations = sorted(
set(item.annotations),
key=lambda annotation: (
annotation.annotator,
annotation.annotationDate,
annotation.comment,
),
)

return sorted(unique_items.values(), key=lambda item: (item.name, item.versionInfo))


class SPDXSbom(pydantic.BaseModel):
"""Software bill of materials in the SPDX format.
Expand All @@ -276,7 +229,40 @@ class SPDXSbom(pydantic.BaseModel):
creationInfo: SPDXCreationInfo
packages: list[SPDXPackage] = []

@staticmethod
def deduplicate_spdx_packages(items: Iterable[SPDXPackage]) -> list[SPDXPackage]:
"""Deduplicate SPDX packages and merge external references.
If package with same name and version is found multiple times in the list,
merge external references of all the packages into one package.
"""
unique_items = {}
for item in items:
key = (item.name, item.versionInfo)
if key not in unique_items:
unique_items[key] = SPDXPackage(name=item.name, versionInfo=item.versionInfo)
unique_items[key].externalRefs = item.externalRefs[:]
unique_items[key].annotations = item.annotations[:]
else:
unique_items[key].externalRefs.extend(item.externalRefs)
unique_items[key].annotations.extend(item.annotations)

for item in unique_items.values():
item.externalRefs = sorted(
set(item.externalRefs),
key=lambda ref: (ref.referenceLocator, ref.referenceType, ref.referenceCategory),
)
item.annotations = sorted(
set(item.annotations),
key=lambda annotation: (
annotation.annotator,
annotation.annotationDate,
annotation.comment,
),
)
return sorted(unique_items.values(), key=lambda item: (item.name, item.versionInfo))

@pydantic.field_validator("packages")
def _unique_packages(cls, packages: list[SPDXPackage]) -> list[SPDXPackage]:
"""Sort and de-duplicate components."""
return deduplicate_spdx_packages(packages)
return cls.deduplicate_spdx_packages(packages)
4 changes: 1 addition & 3 deletions tests/unit/models/test_sbom.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
SPDXPackageAnnotation,
SPDXPackageExternalRef,
SPDXSbom,
deduplicate_spdx_packages,
)


Expand Down Expand Up @@ -465,7 +464,6 @@ def test_sort_and_dedupe_packages(self) -> None:
},
],
)
print(sbom.packages)
assert len(sbom.packages) == 5
assert sbom.packages == [
SPDXPackage(
Expand Down Expand Up @@ -612,7 +610,7 @@ def test_deduplicate_spdx_packages() -> None:
],
),
]
deduped_packages = deduplicate_spdx_packages(packages)
deduped_packages = SPDXSbom.deduplicate_spdx_packages(packages)
assert len(deduped_packages) == 2
assert deduped_packages == [
SPDXPackage(
Expand Down

0 comments on commit 0f308c0

Please sign in to comment.