Skip to content

Commit 54cfb88

Browse files
author
Kamil Bielecki
committed
feat(API): Extend ort run package API with curation info
This commit extends packages list for ORT Run API endpoint with information on curations applied. resolves #2324 Signed-off-by: Kamil Bielecki <[email protected]>
1 parent 6c494ba commit 54cfb88

File tree

7 files changed

+216
-92
lines changed

7 files changed

+216
-92
lines changed

api/v1/mapping/src/commonMain/kotlin/Mappings.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.eclipse.apoapsis.ortserver.api.v1.model.AnalyzerJob as ApiAnalyzerJob
2727
import org.eclipse.apoapsis.ortserver.api.v1.model.AnalyzerJobConfiguration as ApiAnalyzerJobConfiguration
2828
import org.eclipse.apoapsis.ortserver.api.v1.model.ComparisonOperator as ApiComparisonOperator
2929
import org.eclipse.apoapsis.ortserver.api.v1.model.CredentialsType as ApiCredentialsType
30+
import org.eclipse.apoapsis.ortserver.api.v1.model.Curation as ApiCuration
3031
import org.eclipse.apoapsis.ortserver.api.v1.model.EcosystemStats as ApiEcosystemStats
3132
import org.eclipse.apoapsis.ortserver.api.v1.model.EnvironmentConfig as ApiEnvironmentConfig
3233
import org.eclipse.apoapsis.ortserver.api.v1.model.EnvironmentVariableDeclaration as ApiEnvironmentVariableDeclaration
@@ -145,14 +146,15 @@ import org.eclipse.apoapsis.ortserver.model.runs.Issue
145146
import org.eclipse.apoapsis.ortserver.model.runs.OrtRuleViolation
146147
import org.eclipse.apoapsis.ortserver.model.runs.PackageFilters
147148
import org.eclipse.apoapsis.ortserver.model.runs.PackageManagerConfiguration
148-
import org.eclipse.apoapsis.ortserver.model.runs.PackageWithShortestDependencyPaths
149+
import org.eclipse.apoapsis.ortserver.model.runs.PackageWithShortestDependencyPathsAndCurations
149150
import org.eclipse.apoapsis.ortserver.model.runs.ProcessedDeclaredLicense
150151
import org.eclipse.apoapsis.ortserver.model.runs.Project
151152
import org.eclipse.apoapsis.ortserver.model.runs.RemoteArtifact
152153
import org.eclipse.apoapsis.ortserver.model.runs.ShortestDependencyPath
153154
import org.eclipse.apoapsis.ortserver.model.runs.VcsInfo
154155
import org.eclipse.apoapsis.ortserver.model.runs.advisor.Vulnerability
155156
import org.eclipse.apoapsis.ortserver.model.runs.advisor.VulnerabilityReference
157+
import org.eclipse.apoapsis.ortserver.model.runs.repository.PackageCurationData
156158
import org.eclipse.apoapsis.ortserver.model.util.ComparisonOperator
157159
import org.eclipse.apoapsis.ortserver.model.util.FilterOperatorAndValue
158160
import org.eclipse.apoapsis.ortserver.model.util.ListQueryParameters
@@ -857,7 +859,7 @@ fun ShortestDependencyPath.mapToApi() = ApiShortestDependencyPath(
857859
path = path.map { it.mapToApi() }
858860
)
859861

860-
fun PackageWithShortestDependencyPaths.mapToApi() = ApiPackage(
862+
fun PackageWithShortestDependencyPathsAndCurations.mapToApi() = ApiPackage(
861863
pkg.identifier.mapToApi(),
862864
pkg.purl,
863865
pkg.cpe,
@@ -872,7 +874,17 @@ fun PackageWithShortestDependencyPaths.mapToApi() = ApiPackage(
872874
pkg.vcsProcessed.mapToApi(),
873875
pkg.isMetadataOnly,
874876
pkg.isModified,
875-
shortestDependencyPaths.map { it.mapToApi() }
877+
shortestDependencyPaths.map { it.mapToApi() },
878+
curations.map { it.mapToApi() }.toSet()
879+
)
880+
881+
fun PackageCurationData.mapToApi(): ApiCuration = ApiCuration(
882+
concludedLicense ?: "",
883+
comment ?: "",
884+
description ?: "",
885+
homepageUrl ?: "",
886+
authors ?: emptySet(),
887+
declaredLicenseMapping
876888
)
877889

878890
fun ApiPackageFilters.mapToModel(): PackageFilters =
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (C) 2025 The ORT Server Authors (See <https://github.com/eclipse-apoapsis/ort-server/blob/main/NOTICE>)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
package org.eclipse.apoapsis.ortserver.api.v1.model
21+
22+
import kotlinx.serialization.Serializable
23+
24+
@Serializable
25+
data class Curation(
26+
val concludedLicense: String,
27+
val comment: String,
28+
val description: String,
29+
val homepageUrl: String,
30+
val authors: Set<String>,
31+
val declaredLicenseMapping: Map<String, String>
32+
)

api/v1/model/src/commonMain/kotlin/Package.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ data class Package(
3737
val vcsProcessed: VcsInfo,
3838
val isMetadataOnly: Boolean = false,
3939
val isModified: Boolean = false,
40-
val shortestDependencyPaths: List<ShortestDependencyPath>
40+
val shortestDependencyPaths: List<ShortestDependencyPath>,
41+
val curations: Set<Curation>
4142
)
4243

4344
/**

core/src/main/kotlin/api/RunsRoute.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ import org.eclipse.apoapsis.ortserver.model.authorization.RepositoryPermission
7676
import org.eclipse.apoapsis.ortserver.model.repositories.OrtRunRepository
7777
import org.eclipse.apoapsis.ortserver.model.runs.Issue
7878
import org.eclipse.apoapsis.ortserver.model.runs.OrtRuleViolation
79-
import org.eclipse.apoapsis.ortserver.model.runs.PackageWithShortestDependencyPaths
79+
import org.eclipse.apoapsis.ortserver.model.runs.PackageWithShortestDependencyPathsAndCurations
8080
import org.eclipse.apoapsis.ortserver.model.runs.Project
8181
import org.eclipse.apoapsis.ortserver.services.IssueService
8282
import org.eclipse.apoapsis.ortserver.services.OrtRunService
@@ -245,7 +245,7 @@ fun Route.runs() = route("runs") {
245245
.listForOrtRunId(ortRun.id, pagingOptions.mapToModel(), filters.mapToModel())
246246

247247
val pagedResponse = packagesForOrtRun
248-
.mapToApi(PackageWithShortestDependencyPaths::mapToApi)
248+
.mapToApi(PackageWithShortestDependencyPathsAndCurations::mapToApi)
249249
.toSearchResponse(filters)
250250

251251
call.respond(HttpStatusCode.OK, pagedResponse)

core/src/main/kotlin/apiDocs/RunsDocs.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import kotlin.time.Duration.Companion.minutes
2828
import kotlinx.datetime.Clock
2929

3030
import org.eclipse.apoapsis.ortserver.api.v1.model.ComparisonOperator
31+
import org.eclipse.apoapsis.ortserver.api.v1.model.Curation
3132
import org.eclipse.apoapsis.ortserver.api.v1.model.EcosystemStats
3233
import org.eclipse.apoapsis.ortserver.api.v1.model.FilterOperatorAndValue
3334
import org.eclipse.apoapsis.ortserver.api.v1.model.Identifier
@@ -428,6 +429,16 @@ val getPackagesByRunId: RouteConfig.() -> Unit = {
428429
Identifier("Maven", "org.example", "other", "1.0")
429430
)
430431
)
432+
),
433+
curations = setOf(
434+
Curation(
435+
concludedLicense = "MIT",
436+
comment = "A comment",
437+
description = "",
438+
homepageUrl = "https://example.com/namespace/name",
439+
authors = setOf("John Doe <[email protected]>"),
440+
declaredLicenseMapping = emptyMap()
441+
)
431442
)
432443
)
433444
),

core/src/test/kotlin/api/RunsRouteIntegrationTest.kt

Lines changed: 130 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ import org.eclipse.apoapsis.ortserver.api.v1.model.VulnerabilityRating
9191
import org.eclipse.apoapsis.ortserver.api.v1.model.VulnerabilityWithIdentifier
9292
import org.eclipse.apoapsis.ortserver.config.ConfigManager
9393
import org.eclipse.apoapsis.ortserver.core.shouldHaveBody
94+
import org.eclipse.apoapsis.ortserver.dao.repositories.repositoryconfiguration.RepositoryConfigurationsPackageCurationsTable
9495
import org.eclipse.apoapsis.ortserver.dao.utils.toDatabasePrecision
9596
import org.eclipse.apoapsis.ortserver.logaccess.LogFileCriteria
9697
import org.eclipse.apoapsis.ortserver.logaccess.LogFileProviderFactoryForTesting
@@ -126,6 +127,13 @@ import org.eclipse.apoapsis.ortserver.model.runs.advisor.AdvisorResult
126127
import org.eclipse.apoapsis.ortserver.model.runs.advisor.Vulnerability
127128
import org.eclipse.apoapsis.ortserver.model.runs.advisor.VulnerabilityReference
128129
import org.eclipse.apoapsis.ortserver.model.runs.reporter.Report
130+
import org.eclipse.apoapsis.ortserver.model.runs.repository.Curations
131+
import org.eclipse.apoapsis.ortserver.model.runs.repository.Excludes
132+
import org.eclipse.apoapsis.ortserver.model.runs.repository.LicenseChoices
133+
import org.eclipse.apoapsis.ortserver.model.runs.repository.PackageCuration
134+
import org.eclipse.apoapsis.ortserver.model.runs.repository.PackageCurationData
135+
import org.eclipse.apoapsis.ortserver.model.runs.repository.RepositoryAnalyzerConfiguration
136+
import org.eclipse.apoapsis.ortserver.model.runs.repository.Resolutions
129137
import org.eclipse.apoapsis.ortserver.model.util.asPresent
130138
import org.eclipse.apoapsis.ortserver.services.DefaultAuthorizationService
131139
import org.eclipse.apoapsis.ortserver.services.OrganizationService
@@ -993,6 +1001,115 @@ class RunsRouteIntegrationTest : AbstractIntegrationTest({
9931001
val identifier1 = Identifier("Maven", "com.example", "example", "1.0")
9941002
val identifier2 = Identifier("Maven", "com.example", "example2", "1.0")
9951003

1004+
dbExtension.fixtures.repositoryConfigurationRepository.create(
1005+
ortRunId = ortRun.id,
1006+
curations = Curations(
1007+
packages = listOf(
1008+
PackageCuration(
1009+
id = identifier1,
1010+
data = PackageCurationData(
1011+
comment = "comment",
1012+
description = "description",
1013+
concludedLicense = "license"
1014+
)
1015+
)
1016+
)
1017+
),
1018+
analyzerConfig = RepositoryAnalyzerConfiguration(),
1019+
excludes = Excludes(),
1020+
resolutions = Resolutions(),
1021+
packageConfigurations = listOf(),
1022+
licenseChoices = LicenseChoices(),
1023+
provenanceSnippetChoices = listOf()
1024+
)
1025+
1026+
val package1 = Package(
1027+
identifier1,
1028+
purl = "pkg:maven/com.example/[email protected]",
1029+
cpe = null,
1030+
authors = setOf("Author One", "Author Two"),
1031+
declaredLicenses = setOf("License1", "License2", "License3"),
1032+
ProcessedDeclaredLicense(
1033+
spdxExpression = "Expression",
1034+
mappedLicenses = mapOf(
1035+
"License 1" to "Mapped License 1",
1036+
"License 2" to "Mapped License 2",
1037+
),
1038+
unmappedLicenses = setOf("License 1", "License 2", "License 3", "License 4")
1039+
),
1040+
description = "An example package",
1041+
homepageUrl = "https://example.com",
1042+
binaryArtifact = RemoteArtifact(
1043+
"https://example.com/example-1.0.jar",
1044+
"sha1:value",
1045+
"SHA-1"
1046+
),
1047+
sourceArtifact = RemoteArtifact(
1048+
"https://example.com/example-1.0-sources.jar",
1049+
"sha1:value",
1050+
"SHA-1"
1051+
),
1052+
vcs = VcsInfo(
1053+
RepositoryType("GIT"),
1054+
"https://example.com/git",
1055+
"revision",
1056+
"path"
1057+
),
1058+
vcsProcessed = VcsInfo(
1059+
RepositoryType("GIT"),
1060+
"https://example.com/git",
1061+
"revision",
1062+
"path"
1063+
),
1064+
isMetadataOnly = false,
1065+
isModified = false
1066+
)
1067+
1068+
val package2 = Package(
1069+
identifier2,
1070+
purl = "pkg:maven/com.example/[email protected]",
1071+
cpe = null,
1072+
authors = emptySet(),
1073+
declaredLicenses = emptySet(),
1074+
ProcessedDeclaredLicense(
1075+
spdxExpression = "Expression",
1076+
mappedLicenses = emptyMap(),
1077+
unmappedLicenses = emptySet()
1078+
),
1079+
description = "Another example package",
1080+
homepageUrl = "https://example.com",
1081+
binaryArtifact = RemoteArtifact(
1082+
"https://example.com/example2-1.0.jar",
1083+
"sha1:value",
1084+
"SHA-1"
1085+
),
1086+
sourceArtifact = RemoteArtifact(
1087+
"https://example.com/example2-1.0-sources.jar",
1088+
"sha1:value",
1089+
"SHA-1"
1090+
),
1091+
vcs = VcsInfo(
1092+
RepositoryType("GIT"),
1093+
"https://example.com/git",
1094+
"revision",
1095+
"path"
1096+
),
1097+
vcsProcessed = VcsInfo(
1098+
RepositoryType("GIT"),
1099+
"https://example.com/git",
1100+
"revision",
1101+
"path"
1102+
),
1103+
isMetadataOnly = false,
1104+
isModified = false
1105+
)
1106+
1107+
dbExtension.fixtures.createPackageCurationData(
1108+
dbExtension.fixtures.getPackageCurationData(package1, "cmt1", "dsc1", "lic1")
1109+
)
1110+
1111+
RepositoryConfigurationsPackageCurationsTable
1112+
9961113
dbExtension.fixtures.analyzerRunRepository.create(
9971114
analyzerJobId = analyzerJob.id,
9981115
startTime = Clock.System.now().toDatabasePrecision(),
@@ -1014,87 +1131,7 @@ class RunsRouteIntegrationTest : AbstractIntegrationTest({
10141131
skipExcluded = true
10151132
),
10161133
projects = setOf(project),
1017-
packages = setOf(
1018-
Package(
1019-
identifier1,
1020-
purl = "pkg:maven/com.example/[email protected]",
1021-
cpe = null,
1022-
authors = setOf("Author One", "Author Two"),
1023-
declaredLicenses = setOf("License1", "License2", "License3"),
1024-
ProcessedDeclaredLicense(
1025-
spdxExpression = "Expression",
1026-
mappedLicenses = mapOf(
1027-
"License 1" to "Mapped License 1",
1028-
"License 2" to "Mapped License 2",
1029-
),
1030-
unmappedLicenses = setOf("License 1", "License 2", "License 3", "License 4")
1031-
),
1032-
description = "An example package",
1033-
homepageUrl = "https://example.com",
1034-
binaryArtifact = RemoteArtifact(
1035-
"https://example.com/example-1.0.jar",
1036-
"sha1:value",
1037-
"SHA-1"
1038-
),
1039-
sourceArtifact = RemoteArtifact(
1040-
"https://example.com/example-1.0-sources.jar",
1041-
"sha1:value",
1042-
"SHA-1"
1043-
),
1044-
vcs = VcsInfo(
1045-
RepositoryType("GIT"),
1046-
"https://example.com/git",
1047-
"revision",
1048-
"path"
1049-
),
1050-
vcsProcessed = VcsInfo(
1051-
RepositoryType("GIT"),
1052-
"https://example.com/git",
1053-
"revision",
1054-
"path"
1055-
),
1056-
isMetadataOnly = false,
1057-
isModified = false
1058-
),
1059-
Package(
1060-
identifier2,
1061-
purl = "pkg:maven/com.example/[email protected]",
1062-
cpe = null,
1063-
authors = emptySet(),
1064-
declaredLicenses = emptySet(),
1065-
ProcessedDeclaredLicense(
1066-
spdxExpression = "Expression",
1067-
mappedLicenses = emptyMap(),
1068-
unmappedLicenses = emptySet()
1069-
),
1070-
description = "Another example package",
1071-
homepageUrl = "https://example.com",
1072-
binaryArtifact = RemoteArtifact(
1073-
"https://example.com/example2-1.0.jar",
1074-
"sha1:value",
1075-
"SHA-1"
1076-
),
1077-
sourceArtifact = RemoteArtifact(
1078-
"https://example.com/example2-1.0-sources.jar",
1079-
"sha1:value",
1080-
"SHA-1"
1081-
),
1082-
vcs = VcsInfo(
1083-
RepositoryType("GIT"),
1084-
"https://example.com/git",
1085-
"revision",
1086-
"path"
1087-
),
1088-
vcsProcessed = VcsInfo(
1089-
RepositoryType("GIT"),
1090-
"https://example.com/git",
1091-
"revision",
1092-
"path"
1093-
),
1094-
isMetadataOnly = false,
1095-
isModified = false
1096-
)
1097-
),
1134+
packages = setOf(package1, package2),
10981135
issues = emptyList(),
10991136
dependencyGraphs = emptyMap(),
11001137
shortestDependencyPaths = mapOf(
@@ -1135,14 +1172,21 @@ class RunsRouteIntegrationTest : AbstractIntegrationTest({
11351172
it.scope shouldBe "compileClassPath"
11361173
it.path shouldBe emptyList()
11371174
}
1175+
1176+
curations shouldHaveSize(1)
11381177
}
11391178

1140-
last().shortestDependencyPaths.shouldBeSingleton {
1141-
it.projectIdentifier shouldBe project.identifier.mapToApi()
1142-
it.scope shouldBe "compileClassPath"
1143-
it.path shouldBe listOf(identifier1.mapToApi())
1179+
with(last()) {
1180+
identifier.name shouldBe "example2"
1181+
1182+
shortestDependencyPaths.shouldBeSingleton {
1183+
it.projectIdentifier shouldBe project.identifier.mapToApi()
1184+
it.scope shouldBe "compileClassPath"
1185+
it.path shouldBe listOf(identifier1.mapToApi())
1186+
}
1187+
1188+
curations shouldHaveSize(0)
11441189
}
1145-
last().identifier.name shouldBe "example2"
11461190
}
11471191
}
11481192
}

0 commit comments

Comments
 (0)