diff --git a/src/main/kotlin/model/OrtApi.kt b/src/main/kotlin/model/OrtApi.kt index 497920d..ffc570e 100644 --- a/src/main/kotlin/model/OrtApi.kt +++ b/src/main/kotlin/model/OrtApi.kt @@ -10,6 +10,7 @@ import org.ossreviewtoolkit.model.Identifier import org.ossreviewtoolkit.model.Issue import org.ossreviewtoolkit.model.LicenseSource import org.ossreviewtoolkit.model.OrtResult +import org.ossreviewtoolkit.model.Package import org.ossreviewtoolkit.model.Project import org.ossreviewtoolkit.model.Repository import org.ossreviewtoolkit.model.RepositoryProvenance @@ -109,6 +110,8 @@ class OrtApi( fun getCuratedPackageOrProject(id: Identifier): CuratedPackage? = result.getPackageOrProject(id) + fun getUncuratedPackageOrProject(id: Identifier): Package? = result.getUncuratedPackageOrProject(id) + fun getEvaluatorRun(): EvaluatorRun? = result.evaluator fun getEvaluatorStats(): EvaluatorStats = diff --git a/src/main/kotlin/ui/dependencies/Dependencies.kt b/src/main/kotlin/ui/dependencies/Dependencies.kt index c43baeb..585225e 100644 --- a/src/main/kotlin/ui/dependencies/Dependencies.kt +++ b/src/main/kotlin/ui/dependencies/Dependencies.kt @@ -43,6 +43,7 @@ import org.ossreviewtoolkit.model.Hash import org.ossreviewtoolkit.model.Identifier import org.ossreviewtoolkit.model.Issue import org.ossreviewtoolkit.model.Package +import org.ossreviewtoolkit.model.PackageCurationData import org.ossreviewtoolkit.model.PackageLinkage import org.ossreviewtoolkit.model.Project import org.ossreviewtoolkit.model.RemoteArtifact @@ -359,35 +360,29 @@ fun PackageDetails(item: DependencyTreePackage) { Text(text = "${item.id.name} ${item.id.version}", style = MaterialTheme.typography.h4) Column(modifier = Modifier.verticalScroll(scrollState).padding(top = 15.dp)) { - if (item.pkg == null) { - Text("No package information found.") + IdentifierSection(item.curatedPackage.metadata) - Divider(modifier = Modifier.padding(vertical = 10.dp)) - } else { - IdentifierSection(item.pkg.metadata) - - Divider(modifier = Modifier.padding(vertical = 10.dp)) + Divider(modifier = Modifier.padding(vertical = 10.dp)) - CopyrightSection(item.pkg.metadata, item.resolvedLicense) + CopyrightSection(item.curatedPackage.metadata, item.resolvedLicense) - Divider(modifier = Modifier.padding(vertical = 10.dp)) + Divider(modifier = Modifier.padding(vertical = 10.dp)) - LicenseSection(item.pkg.metadata, item.resolvedLicense) + LicenseSection(item.curatedPackage.metadata, item.resolvedLicense) - Divider(modifier = Modifier.padding(vertical = 10.dp)) + Divider(modifier = Modifier.padding(vertical = 10.dp)) - DescriptionSection(item.pkg.metadata.description) + DescriptionSection(item.curatedPackage.metadata.description) - Divider(modifier = Modifier.padding(vertical = 10.dp)) + Divider(modifier = Modifier.padding(vertical = 10.dp)) - PackageProvenanceSection(item.pkg.metadata) + PackageProvenanceSection(item.curatedPackage.metadata) - Divider(modifier = Modifier.padding(vertical = 10.dp)) + Divider(modifier = Modifier.padding(vertical = 10.dp)) - CurationSection(item.pkg) + CurationSection(item.uncuratedPackage, item.curatedPackage.curations) - Divider(modifier = Modifier.padding(vertical = 10.dp)) - } + Divider(modifier = Modifier.padding(vertical = 10.dp)) IssuesSection(item.issues) @@ -396,7 +391,7 @@ fun PackageDetails(item: DependencyTreePackage) { // TODO: Add vulnerability section. Row(verticalAlignment = Alignment.CenterVertically) { - val homepageUrl = item.pkg?.metadata?.homepageUrl.orEmpty() + val homepageUrl = item.curatedPackage?.metadata?.homepageUrl.orEmpty() if (homepageUrl.isNotBlank()) { WebLink("Homepage", homepageUrl) } @@ -461,7 +456,8 @@ private fun PackageDetailsPreview() { PackageDetails( DependencyTreePackage( id = pkg.metadata.id, - pkg = pkg, + uncuratedPackage = pkg.metadata, + curatedPackage = pkg, linkage = PackageLinkage.STATIC, issues = listOf(issue), resolvedLicense = resolvedLicense @@ -619,9 +615,10 @@ fun RepositoryColumn(vcs: VcsInfo, expanded: Boolean) { } @Composable -fun CurationSection(curatedPackage: CuratedPackage) { +fun CurationSection(uncuratedPackage: Package, curations: List) { Expandable(header = { - val noOrSize = if (curatedPackage.curations.isEmpty()) "No" else curatedPackage.curations.size + val noOrSize = if (curations.isEmpty()) "No" else curations.size + CaptionedText( caption = "CURATIONS", text = "$noOrSize curation(s) were applied to this package." @@ -631,56 +628,58 @@ fun CurationSection(curatedPackage: CuratedPackage) { modifier = Modifier.padding(top = 5.dp), verticalArrangement = Arrangement.spacedBy(2.dp) ) { - curatedPackage.curations.forEach { curation -> + curations.fold(uncuratedPackage.toCuratedPackage()) { base, curation -> Divider(thickness = 0.5.dp) ProvideTextStyle(MaterialTheme.typography.overline) { Text("Comment: ${curation.comment.toStringOrDash()}") curation.purl?.let { purl -> - Text("PURL: ${curatedPackage.metadata.purl} -> $purl") + Text("PURL: ${base.metadata.purl} -> $purl") } curation.cpe?.let { cpe -> - Text("CPE: ${curatedPackage.metadata.cpe} -> $cpe") + Text("CPE: ${base.metadata.cpe} -> $cpe") } curation.authors?.let { authors -> - Text("Authors: ${curatedPackage.metadata.authors} -> $authors") + Text("Authors: ${base.metadata.authors} -> $authors") } curation.concludedLicense?.let { concludedLicense -> - Text("Concluded license: ${curatedPackage.metadata.concludedLicense} -> $concludedLicense") + Text("Concluded license: ${base.metadata.concludedLicense} -> $concludedLicense") } curation.description?.let { description -> - Text("Description: ${curatedPackage.metadata.description} -> $description") + Text("Description: ${base.metadata.description} -> $description") } curation.homepageUrl?.let { homepageUrl -> - Text("Homepage: ${curatedPackage.metadata.homepageUrl} -> $homepageUrl") + Text("Homepage: ${base.metadata.homepageUrl} -> $homepageUrl") } curation.binaryArtifact?.let { binaryArtifact -> - Text("Binary artifact: ${curatedPackage.metadata.binaryArtifact} -> $binaryArtifact") + Text("Binary artifact: ${base.metadata.binaryArtifact} -> $binaryArtifact") } curation.vcs?.let { vcs -> - Text("VCS: ${curatedPackage.metadata.vcs} -> $vcs") + Text("VCS: ${base.metadata.vcs} -> $vcs") } curation.isMetadataOnly?.let { isMetadataOnly -> - Text("Is metadata only: ${curatedPackage.metadata.isMetadataOnly} -> $isMetadataOnly") + Text("Is metadata only: ${base.metadata.isMetadataOnly} -> $isMetadataOnly") } curation.isModified?.let { isModified -> - Text("Is modified: ${curatedPackage.metadata.isModified} -> $isModified") + Text("Is modified: ${base.metadata.isModified} -> $isModified") } if (curation.declaredLicenseMapping.isNotEmpty()) { Text("Declared license mapping: ${curation.declaredLicenseMapping}") } } + + curation.apply(base) } } } diff --git a/src/main/kotlin/ui/dependencies/DependenciesState.kt b/src/main/kotlin/ui/dependencies/DependenciesState.kt index d709120..6cde0a3 100644 --- a/src/main/kotlin/ui/dependencies/DependenciesState.kt +++ b/src/main/kotlin/ui/dependencies/DependenciesState.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.setValue import org.ossreviewtoolkit.model.CuratedPackage import org.ossreviewtoolkit.model.Identifier import org.ossreviewtoolkit.model.Issue +import org.ossreviewtoolkit.model.Package import org.ossreviewtoolkit.model.PackageLinkage import org.ossreviewtoolkit.model.Project import org.ossreviewtoolkit.model.Scope @@ -97,7 +98,8 @@ class DependencyTreeScope(val project: Project, val scope: Scope) : DependencyTr class DependencyTreePackage( val id: Identifier, - val pkg: CuratedPackage?, + val uncuratedPackage: Package, + val curatedPackage: CuratedPackage, val linkage: PackageLinkage, val issues: List, val resolvedLicense: ResolvedLicenseInfo? diff --git a/src/main/kotlin/ui/dependencies/DependenciesViewModel.kt b/src/main/kotlin/ui/dependencies/DependenciesViewModel.kt index 8358463..b7807d7 100644 --- a/src/main/kotlin/ui/dependencies/DependenciesViewModel.kt +++ b/src/main/kotlin/ui/dependencies/DependenciesViewModel.kt @@ -60,11 +60,16 @@ class DependenciesViewModel(private val ortModel: OrtModel) : ViewModel() { key = project.id.toCoordinates(), children = children ) - } ?: api.getCuratedPackage(id)?.let { pkg -> + } ?: api.getCuratedPackage(id)?.let { curatedPackage -> + val uncuratedPackage = checkNotNull(api.getUncuratedPackageOrProject(id)) { + "There must be an uncurated package if there is a curated one." + } + TreeNode( value = DependencyTreePackage( id = id, - pkg = pkg, + uncuratedPackage = uncuratedPackage, + curatedPackage = curatedPackage, linkage = linkage, issues = issues, resolvedLicense = resolvedLicenses.getValue(id)