Skip to content

Commit 2e915d7

Browse files
bxf12315mrizzi
authored andcommitted
chore: add new column concluded license into license export
1 parent 859fcc4 commit 2e915d7

File tree

4 files changed

+143
-22
lines changed

4 files changed

+143
-22
lines changed
Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use sea_orm::FromQueryResult;
2-
use trustify_entity::qualified_purl::CanonicalPurl;
2+
use trustify_entity::{
3+
labels::Labels, qualified_purl::CanonicalPurl, sbom_package_license::LicenseCategory,
4+
};
35
use uuid::Uuid;
46

5-
#[derive(Debug, Clone, Default)]
7+
#[derive(Debug, Clone, PartialEq, Default)]
68
pub struct SbomPackageLicense {
79
pub name: String,
810
pub group: Option<String>,
@@ -11,41 +13,70 @@ pub struct SbomPackageLicense {
1113
pub purl: Vec<Purl>,
1214
pub cpe: Vec<trustify_entity::cpe::Model>,
1315
/// List of all package license
14-
pub license_text: Option<String>,
16+
pub license_declared_text: Option<String>,
17+
pub license_concluded_text: Option<String>,
1518
}
1619

17-
#[derive(Debug, Clone, FromQueryResult)]
20+
#[derive(Debug, Clone, PartialEq, FromQueryResult)]
1821
pub struct Sbom {
1922
pub sbom_id: Uuid,
2023
pub node_id: String,
2124
pub sbom_namespace: String,
2225
}
2326

24-
#[derive(Debug, Clone, FromQueryResult)]
27+
#[derive(Debug, Clone, PartialEq, FromQueryResult)]
2528
pub struct Purl {
2629
pub purl: CanonicalPurl,
2730
}
2831

29-
#[derive(Debug, Clone, FromQueryResult)]
32+
#[derive(Debug, Clone, PartialEq, FromQueryResult)]
3033
pub struct SbomPackageLicenseBase {
3134
pub node_id: String,
3235
pub sbom_id: Uuid,
3336
pub name: String,
3437
pub group: Option<String>,
3538
pub version: Option<String>,
3639
pub license_text: Option<String>,
40+
pub license_type: Option<LicenseCategory>,
3741
}
3842

39-
#[derive(Debug, Clone, Default, FromQueryResult)]
43+
#[derive(Debug, Clone, Default, PartialEq, FromQueryResult)]
4044
pub struct SbomNameId {
4145
pub sbom_name: String,
4246
pub sbom_id: String,
47+
pub labels: Labels,
4348
}
4449

45-
#[derive(Debug, Clone, FromQueryResult)]
50+
#[derive(Debug, Clone, PartialEq, FromQueryResult)]
4651
pub struct ExtractedLicensingInfos {
4752
pub license_id: String,
4853
pub name: String,
4954
pub extracted_text: String,
5055
pub comment: String,
5156
}
57+
58+
#[derive(Debug, Clone, PartialEq)]
59+
pub struct MergedSbomPackageLicense {
60+
pub node_id: String,
61+
pub sbom_id: Uuid,
62+
pub name: String,
63+
pub group: Option<String>,
64+
pub version: Option<String>,
65+
pub license_declared_text: Option<String>,
66+
pub license_concluded_text: Option<String>,
67+
}
68+
69+
impl MergedSbomPackageLicense {
70+
pub fn apply_license(&mut self, license: &SbomPackageLicenseBase) {
71+
if let Some(license_type) = &license.license_type {
72+
match license_type {
73+
LicenseCategory::Declared => {
74+
self.license_declared_text = license.license_text.clone();
75+
}
76+
LicenseCategory::Concluded => {
77+
self.license_concluded_text = license.license_text.clone();
78+
}
79+
}
80+
}
81+
}
82+
}

modules/fundamental/src/license/service/license_export.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ impl LicenseExporter {
111111
"package version",
112112
"package purl",
113113
"package cpe",
114-
"license",
114+
"declared license",
115+
"concluded license",
115116
])?;
116117

117118
for extracted_licensing_info in self.extracted_licensing_infos {
@@ -134,7 +135,7 @@ impl LicenseExporter {
134135
let purl_list = package
135136
.purl
136137
.into_iter()
137-
.map(|purl| format!("{}", Purl::from(purl.purl)))
138+
.map(|purl| format!("{}", Purl::from(purl.purl.clone())))
138139
.collect::<Vec<_>>()
139140
.join("\n");
140141

@@ -146,7 +147,12 @@ impl LicenseExporter {
146147
&package.version.unwrap_or_default(),
147148
&purl_list,
148149
&alternate_package_reference,
149-
&package.license_text.unwrap_or_else(String::default),
150+
&package
151+
.license_declared_text
152+
.unwrap_or_else(String::default),
153+
&package
154+
.license_concluded_text
155+
.unwrap_or_else(String::default),
150156
])?;
151157
}
152158

modules/fundamental/src/license/service/mod.rs

Lines changed: 79 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,45 @@ use crate::{
33
license::model::{
44
SpdxLicenseDetails, SpdxLicenseSummary,
55
sbom_license::{
6-
ExtractedLicensingInfos, Purl, SbomNameId, SbomPackageLicense, SbomPackageLicenseBase,
6+
ExtractedLicensingInfos, MergedSbomPackageLicense, Purl, SbomNameId,
7+
SbomPackageLicense, SbomPackageLicenseBase,
78
},
89
},
910
};
1011
use sea_orm::{ColumnTrait, ConnectionTrait, EntityTrait, QueryFilter, QuerySelect, RelationTrait};
1112
use sea_query::{Condition, JoinType};
13+
use std::collections::HashMap;
1214
use trustify_common::{
1315
db::query::Query,
1416
id::{Id, TrySelectForId},
1517
model::{Paginated, PaginatedResults},
1618
};
1719
use trustify_entity::{
1820
license, licensing_infos, qualified_purl, sbom, sbom_node, sbom_package, sbom_package_cpe_ref,
19-
sbom_package_license::{self, LicenseCategory},
20-
sbom_package_purl_ref,
21+
sbom_package_license, sbom_package_purl_ref,
2122
};
23+
use uuid::Uuid;
2224

2325
pub mod license_export;
2426

2527
pub struct LicenseService {}
2628

29+
#[derive(Debug, Clone)]
2730
pub struct LicenseExportResult {
2831
pub sbom_package_license: Vec<SbomPackageLicense>,
2932
pub extracted_licensing_infos: Vec<ExtractedLicensingInfos>,
3033
pub sbom_name_group_version: Option<SbomNameId>,
3134
}
3235

36+
#[derive(Eq, Hash, PartialEq)]
37+
pub struct SbomPackageLicenseBasekey {
38+
pub node_id: String,
39+
pub sbom_id: Uuid,
40+
pub name: String,
41+
pub group: Option<String>,
42+
pub version: Option<String>,
43+
}
44+
3345
impl Default for LicenseService {
3446
fn default() -> Self {
3547
Self::new()
@@ -46,12 +58,13 @@ impl LicenseService {
4658
id: Id,
4759
connection: &C,
4860
) -> Result<LicenseExportResult, Error> {
49-
let name_version_group = sbom::Entity::find()
61+
let name_version_group: Option<SbomNameId> = sbom::Entity::find()
5062
.try_filter(id.clone())?
5163
.join(JoinType::Join, sbom::Relation::SbomNode.def())
5264
.select_only()
5365
.column_as(sbom::Column::DocumentId, "sbom_id")
5466
.column_as(sbom_node::Column::Name, "sbom_name")
67+
.column_as(sbom::Column::Labels, "labels")
5568
.into_model::<SbomNameId>()
5669
.one(connection)
5770
.await?;
@@ -68,23 +81,78 @@ impl LicenseService {
6881
JoinType::InnerJoin,
6982
sbom_package_license::Relation::License.def(),
7083
)
71-
.filter(
72-
Condition::all()
73-
.add(sbom_package_license::Column::LicenseType.eq(LicenseCategory::Declared)),
74-
)
7584
.select_only()
7685
.column_as(sbom::Column::SbomId, "sbom_id")
7786
.column_as(sbom_package::Column::NodeId, "node_id")
7887
.column_as(sbom_node::Column::Name, "name")
7988
.column_as(sbom_package::Column::Group, "group")
8089
.column_as(sbom_package::Column::Version, "version")
8190
.column_as(license::Column::Text, "license_text")
91+
.column_as(sbom_package_license::Column::LicenseType, "license_type")
8292
.into_model::<SbomPackageLicenseBase>()
8393
.all(connection)
8494
.await?;
8595

96+
fn merge_package_licenses_for_spdx(
97+
package_licenses: Vec<SbomPackageLicenseBase>,
98+
) -> Vec<MergedSbomPackageLicense> {
99+
let mut grouped: HashMap<SbomPackageLicenseBasekey, MergedSbomPackageLicense> =
100+
HashMap::new();
101+
102+
for license in package_licenses {
103+
let key = SbomPackageLicenseBasekey {
104+
node_id: license.node_id.clone(),
105+
sbom_id: license.sbom_id,
106+
name: license.name.clone(),
107+
group: license.group.clone(),
108+
version: license.version.clone(),
109+
};
110+
111+
grouped
112+
.entry(key)
113+
.or_insert_with(|| MergedSbomPackageLicense {
114+
node_id: license.node_id.clone(),
115+
sbom_id: license.sbom_id,
116+
name: license.name.clone(),
117+
group: license.group.clone(),
118+
version: license.version.clone(),
119+
license_declared_text: None,
120+
license_concluded_text: None,
121+
})
122+
.apply_license(&license);
123+
}
124+
grouped.into_values().collect()
125+
}
126+
127+
fn merge_package_licenses_for_cydx(
128+
package_licenses: Vec<SbomPackageLicenseBase>,
129+
) -> Vec<MergedSbomPackageLicense> {
130+
package_licenses
131+
.into_iter()
132+
.map(|cydx| MergedSbomPackageLicense {
133+
node_id: cydx.node_id,
134+
sbom_id: cydx.sbom_id,
135+
name: cydx.name,
136+
group: cydx.group,
137+
version: cydx.version,
138+
license_declared_text: cydx.license_text,
139+
license_concluded_text: None,
140+
})
141+
.collect()
142+
}
143+
144+
let package_license_list = if let Some(nvg) = name_version_group.clone() {
145+
if nvg.labels.0.get("type").map(String::as_str) == Some("spdx") {
146+
merge_package_licenses_for_spdx(package_license)
147+
} else {
148+
merge_package_licenses_for_cydx(package_license)
149+
}
150+
} else {
151+
merge_package_licenses_for_cydx(package_license)
152+
};
153+
86154
let mut sbom_package_list = Vec::new();
87-
for spl in package_license {
155+
for spl in package_license_list {
88156
let result_purl: Vec<Purl> = sbom_package_purl_ref::Entity::find()
89157
.join(JoinType::Join, sbom_package_purl_ref::Relation::Purl.def())
90158
.filter(
@@ -123,7 +191,8 @@ impl LicenseService {
123191
version: spl.version,
124192
purl: result_purl,
125193
cpe: result_cpe,
126-
license_text: spl.license_text,
194+
license_declared_text: spl.license_declared_text,
195+
license_concluded_text: spl.license_concluded_text,
127196
});
128197
}
129198
let license_info_list: Vec<ExtractedLicensingInfos> = licensing_infos::Entity::find()

modules/fundamental/tests/sbom/license.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ async fn test_spdx(ctx: &TrustifyContext) -> Result<(), anyhow::Error> {
5858
sbom_package_license::Entity::find().all(&ctx.db).await?;
5959

6060
assert_eq!(2084, license_result.sbom_package_license.len());
61+
62+
let package_license_result = license_result
63+
.sbom_package_license
64+
.iter()
65+
.find(|sl| sl.name == "rubygem-bundler_ext");
66+
assert_ne!(Option::None, package_license_result);
67+
if let Some(plr) = package_license_result {
68+
assert_eq!(Some("LicenseRef-0"), plr.license_declared_text.as_deref());
69+
assert_eq!(
70+
Some("Apache-2.0 AND MIT"),
71+
plr.license_concluded_text.as_deref()
72+
);
73+
}
6174
assert_eq!(2084, sp.len());
6275
assert_eq!(4168, spl.len());
6376
assert_eq!(49, license_result.extracted_licensing_infos.len());
@@ -115,7 +128,9 @@ async fn test_license_export_spdx(ctx: &TrustifyContext) -> Result<(), anyhow::E
115128
assert_eq!(1976, sbom_licenses.matches("pkg:npm/").count());
116129
assert_eq!(2185, sbom_licenses.matches("pkg:golang/").count());
117130
assert_eq!(1191, sbom_licenses.matches("pkg:rpm/").count());
118-
assert_eq!(4664, sbom_licenses.matches("NOASSERTION").count());
131+
assert_eq!(8972, sbom_licenses.matches("NOASSERTION").count());
132+
assert_eq!(1, sbom_licenses.matches("declared license").count());
133+
assert_eq!(1, sbom_licenses.matches("concluded license").count());
119134
}
120135
Ok(path) if path.file_name().unwrap_or_default() == "MTV-2.6_license_ref.csv" => {
121136
licenses_ref_csv_found = true;

0 commit comments

Comments
 (0)