Skip to content

Commit 503fd35

Browse files
committed
chore(opossum reporter): Add review feedback
Signed-off-by: alexzurbonsen <[email protected]>
1 parent cae2824 commit 503fd35

File tree

4 files changed

+117
-91
lines changed

4 files changed

+117
-91
lines changed

plugins/reporters/opossum/build.gradle.kts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ dependencies {
3030

3131
ksp(projects.reporter)
3232

33+
implementation(libs.bundles.ks3)
34+
implementation(libs.kotlinx.serialization.core)
35+
implementation(libs.kotlinx.serialization.json)
36+
3337
implementation(projects.downloader)
3438
implementation(projects.model)
3539
implementation(projects.utils.commonUtils)
3640
implementation(projects.utils.ortUtils)
3741
implementation(projects.utils.spdxUtils)
38-
39-
implementation(libs.kotlinx.serialization.core)
40-
implementation(libs.kotlinx.serialization.json)
4142
}

plugins/reporters/opossum/src/funTest/kotlin/OpossumReporterFunTest.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,13 @@
1919

2020
package org.ossreviewtoolkit.plugins.reporters.opossum
2121

22-
import kotlinx.serialization.json.Json
23-
2422
import io.kotest.core.TestConfiguration
2523
import io.kotest.core.spec.style.WordSpec
2624
import io.kotest.engine.spec.tempdir
2725
import io.kotest.matchers.collections.shouldContain
2826
import io.kotest.matchers.shouldBe
2927
import io.kotest.matchers.string.shouldContain
28+
3029
import kotlinx.serialization.json.jsonArray
3130
import kotlinx.serialization.json.jsonObject
3231
import kotlinx.serialization.json.jsonPrimitive
@@ -48,7 +47,7 @@ class OpossumReporterFunTest : WordSpec({
4847
}
4948

5049
"create a parseable result and contain some expected values" {
51-
val json = Json.parseToJsonElement(reportStr).jsonObject
50+
val json = OpossumReporter.JSON.parseToJsonElement(reportStr).jsonObject
5251

5352
json["metadata"]?.jsonObject?.get("projectId")?.jsonPrimitive?.content shouldBe "0"
5453
json["resources"]?.jsonObject?.size shouldBe 2

plugins/reporters/opossum/src/main/kotlin/OpossumReporter.kt

Lines changed: 110 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,26 @@
1919

2020
package org.ossreviewtoolkit.plugins.reporters.opossum
2121

22-
import kotlinx.serialization.KSerializer
23-
import kotlinx.serialization.builtins.MapSerializer
24-
import kotlinx.serialization.builtins.serializer
25-
import kotlinx.serialization.encoding.Decoder
26-
import kotlinx.serialization.encoding.Encoder
27-
import kotlinx.serialization.Serializable
28-
import kotlinx.serialization.Transient
29-
import kotlinx.serialization.descriptors.PrimitiveKind
30-
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
31-
import kotlinx.serialization.descriptors.SerialDescriptor
32-
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
33-
import kotlinx.serialization.json.Json
22+
import io.ks3.java.typealiases.LocalDateTimeAsString
23+
import io.ks3.java.typealiases.UuidAsString
3424

3525
import java.io.File
3626
import java.time.LocalDateTime
3727
import java.util.UUID
3828

3929
import kotlin.math.min
4030

31+
import kotlinx.serialization.builtins.MapSerializer
32+
import kotlinx.serialization.builtins.serializer
33+
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
34+
import kotlinx.serialization.descriptors.SerialDescriptor
35+
import kotlinx.serialization.encoding.Decoder
36+
import kotlinx.serialization.encoding.Encoder
37+
import kotlinx.serialization.json.encodeToStream
38+
import kotlinx.serialization.json.Json
39+
import kotlinx.serialization.KSerializer
40+
import kotlinx.serialization.Serializable
41+
4142
import org.apache.logging.log4j.kotlin.logger
4243

4344
import org.ossreviewtoolkit.downloader.VcsHost
@@ -49,9 +50,9 @@ import org.ossreviewtoolkit.model.OrtResult
4950
import org.ossreviewtoolkit.model.Package
5051
import org.ossreviewtoolkit.model.Project
5152
import org.ossreviewtoolkit.model.ScanResult
52-
import org.ossreviewtoolkit.model.VcsInfo
5353
import org.ossreviewtoolkit.model.utils.getPurlType
5454
import org.ossreviewtoolkit.model.utils.toPurl
55+
import org.ossreviewtoolkit.model.VcsInfo
5556
import org.ossreviewtoolkit.plugins.api.OrtPlugin
5657
import org.ossreviewtoolkit.plugins.api.OrtPluginOption
5758
import org.ossreviewtoolkit.plugins.api.PluginDescriptor
@@ -104,6 +105,13 @@ class OpossumReporter(
104105
override val descriptor: PluginDescriptor = OpossumReporterFactory.descriptor,
105106
private val config: OpossumReporterConfig
106107
) : Reporter {
108+
companion object {
109+
internal val JSON = Json {
110+
explicitNulls = false
111+
encodeDefaults = true
112+
}
113+
}
114+
107115
override fun generateReport(input: ReporterInput, outputDir: File): List<Result<File>> {
108116
val reportFileResult = runCatching {
109117
val opossumInput = createOpossumInput(input, config.maxDepth)
@@ -116,42 +124,40 @@ class OpossumReporter(
116124
return listOf(reportFileResult)
117125
}
118126

119-
internal fun createOpossumInput(input: ReporterInput, maxDepth: Int = Int.MAX_VALUE): OpossumInput {
120-
return OpossumInputCreator().create(input, maxDepth)
121-
}
127+
internal fun createOpossumInput(input: ReporterInput, maxDepth: Int = Int.MAX_VALUE): OpossumInput =
128+
OpossumInputCreator().create(input, maxDepth)
122129

123130
private fun writeReport(outputFile: File, opossumInput: OpossumInput) {
124-
val jsonFile = createOrtTempDir().resolve("input.json")
125-
val json = Json {
126-
explicitNulls = false
127-
encodeDefaults = true
128-
}
131+
val inputJson = createOrtTempDir().resolve("input.json")
129132

130-
jsonFile.writeText(json.encodeToString(OpossumInput.serializer(), opossumInput))
133+
inputJson.writeReport(opossumInput)
131134

132-
jsonFile.packZip(outputFile)
133-
jsonFile.delete()
135+
inputJson.packZip(outputFile)
136+
inputJson.delete()
134137
}
135138

139+
private fun File.writeReport(opossumInput: OpossumInput): File =
140+
apply { outputStream().use { JSON.encodeToStream(opossumInput, it) } }
141+
136142
@Serializable
137-
internal data class OpossumInput(
143+
data class OpossumInput(
138144
val metadata: OpossumInputMetadata = OpossumInputMetadata(),
139145
val resources: OpossumResources,
140-
val externalAttributions: Map<@Serializable(UUIDSerializer::class) UUID, OpossumSignal>,
141-
val resourcesToAttributions: Map<String, Set<@Serializable(UUIDSerializer::class) UUID>>,
146+
val externalAttributions: Map<UuidAsString, OpossumSignalFlat>,
147+
val resourcesToAttributions: Map<String, Set<UuidAsString>>,
142148
val attributionBreakpoints: Set<String>,
143149
val filesWithChildren: Set<String>,
144150
val frequentLicenses: Set<OpossumFrequentLicense>,
145151
val baseUrlsForSources: Map<String, String>,
146152
val externalAttributionSources: Map<String, OpossumExternalAttributionSource>
147153
) {
148-
internal fun getSignalsForFile(file: String): List<OpossumSignal> =
154+
internal fun getSignalsForFile(file: String): List<OpossumSignalFlat> =
149155
resourcesToAttributions[file].orEmpty().mapNotNull { uuid -> externalAttributions[uuid] }
150156
}
151157

152-
internal class OpossumInputCreator {
158+
class OpossumInputCreator {
153159
private val resources: OpossumResources = OpossumResources()
154-
private val signals: MutableList<OpossumSignal> = mutableListOf()
160+
private val uuidToSignals: MutableMap<UUID, OpossumSignal> = mutableMapOf()
155161
private val pathToSignals: MutableMap<String, MutableSet<UUID>> = mutableMapOf()
156162
private val packageToRoot: MutableMap<Identifier, MutableMap<String, Int>> = mutableMapOf()
157163
private val attributionBreakpoints: MutableSet<String> = mutableSetOf()
@@ -192,7 +198,7 @@ class OpossumReporter(
192198
addScannerResults(id, results, maxDepth)
193199
}
194200

195-
val externalAttributions = signals.associateBy { it.uuid }
201+
val externalAttributions = uuidToSignals.mapValues { OpossumSignalFlat.create(it.value) }
196202
val resourcesToAttributions = pathToSignals.mapKeys {
197203
val trailingSlash = if (resources.isPathAFile(it.key)) "" else "/"
198204
resolvePath("${it.key}$trailingSlash")
@@ -254,19 +260,21 @@ class OpossumReporter(
254260
private fun addSignal(signal: OpossumSignal, paths: Set<String>) {
255261
if (paths.isEmpty()) return
256262

257-
val matchingSignal = signals.find { it.matches(signal) }
263+
val signalEntry = uuidToSignals.entries.find { it.value.matches(signal) }
258264

259-
val uuidOfSignal = if (matchingSignal == null) {
260-
signals += signal
261-
signal.uuid
265+
val matchingUuid = signalEntry?.key
266+
val uuidOfSignal = if (matchingUuid == null) {
267+
val uuid = UUID.randomUUID()
268+
uuidToSignals[uuid] = signal
269+
uuid
262270
} else {
263-
matchingSignal.uuid
271+
matchingUuid
264272
}
265273

266274
paths.forEach { path ->
267275
logger.debug {
268-
"add signal ${signal.packageName} ${signal.packageVersion} with namespace " +
269-
"${signal.packageNamespace} of of source ${signal.source} to $path"
276+
"Add signal ${signal.base.packageName} ${signal.base.packageVersion} with namespace " +
277+
"${signal.base.packageNamespace} of of source ${signal.base.source} to $path."
270278
}
271279

272280
resources.addResource(path)
@@ -467,13 +475,13 @@ class OpossumReporter(
467475
}
468476

469477
@Serializable
470-
internal data class OpossumInputMetadata(
478+
data class OpossumInputMetadata(
471479
val projectId: String = "0",
472-
val fileCreationDate: String = LocalDateTime.now().toString()
480+
val fileCreationDate: LocalDateTimeAsString = LocalDateTime.now()
473481
)
474482

475483
@Serializable(with = OpossumResourcesSerializer::class)
476-
internal data class OpossumResources(
484+
data class OpossumResources(
477485
val tree: MutableMap<String, OpossumResources> = mutableMapOf()
478486
) {
479487
private fun addResource(pathPieces: List<String>) {
@@ -523,9 +531,7 @@ class OpossumReporter(
523531
}
524532

525533
@Serializable
526-
internal data class OpossumSignal(
527-
@Transient
528-
val uuid: UUID = UUID.randomUUID(),
534+
data class OpossumSignalFlat(
529535
val source: OpossumSignalSource,
530536
val attributionConfidence: Int = 80,
531537
val packageType: String?,
@@ -541,6 +547,33 @@ class OpossumReporter(
541547
val comment: String?
542548
) {
543549
companion object {
550+
fun create(signal: OpossumSignal): OpossumSignalFlat =
551+
OpossumSignalFlat(
552+
source = signal.base.source,
553+
attributionConfidence = signal.attributionConfidence,
554+
packageType = signal.base.packageType,
555+
packageNamespace = signal.base.packageNamespace,
556+
packageName = signal.base.packageName,
557+
packageVersion = signal.base.packageVersion,
558+
copyright = signal.base.copyright,
559+
licenseName = signal.base.licenseName,
560+
url = signal.base.url,
561+
preSelected = signal.base.preSelected,
562+
followUp = signal.followUp,
563+
excludeFromNotice = signal.excludeFromNotice,
564+
comment = signal.base.comment
565+
)
566+
}
567+
}
568+
569+
data class OpossumSignal(
570+
val base: OpossumSignalBase,
571+
val attributionConfidence: Int = 80,
572+
val followUp: OpossumFollowUp?,
573+
val excludeFromNotice: Boolean
574+
) {
575+
companion object {
576+
@Suppress("LongParameterList")
544577
fun create(
545578
source: String,
546579
id: Identifier? = null,
@@ -551,50 +584,54 @@ class OpossumReporter(
551584
preSelected: Boolean = false,
552585
followUp: Boolean = false,
553586
excludeFromNotice: Boolean = false
554-
): OpossumSignal {
555-
return OpossumSignal(
556-
source = OpossumSignalSource(name = source),
557-
packageType = id?.getPurlType(),
558-
packageNamespace = id?.namespace,
559-
packageName = id?.name,
560-
packageVersion = id?.version,
561-
copyright = copyright,
562-
licenseName = license?.toString(),
563-
url = url,
564-
preSelected = preSelected,
587+
): OpossumSignal =
588+
OpossumSignal(
589+
base = OpossumSignalBase(
590+
source = OpossumSignalSource(name = source),
591+
packageType = id?.getPurlType().toString(),
592+
packageNamespace = id?.namespace,
593+
packageName = id?.name,
594+
packageVersion = id?.version,
595+
copyright = copyright,
596+
licenseName = license?.toString(),
597+
url = url,
598+
preSelected = preSelected,
599+
comment = comment
600+
),
565601
followUp = OpossumFollowUp.FOLLOW_UP.takeIf { followUp },
566-
excludeFromNotice = excludeFromNotice,
567-
comment = comment
602+
excludeFromNotice = excludeFromNotice
568603
)
569-
}
570604
}
571605

572-
fun matches(other: OpossumSignal): Boolean =
573-
source == other.source
574-
&& packageType == other.packageType
575-
&& packageNamespace == other.packageNamespace
576-
&& packageName == other.packageName
577-
&& packageVersion == other.packageVersion
578-
&& copyright == other.copyright
579-
&& licenseName == other.licenseName
580-
&& url == other.url
581-
&& preSelected == other.preSelected
582-
&& comment == other.comment
606+
data class OpossumSignalBase(
607+
val source: OpossumSignalSource,
608+
val packageType: String?,
609+
val packageNamespace: String?,
610+
val packageName: String?,
611+
val packageVersion: String?,
612+
val copyright: String?,
613+
val licenseName: String?,
614+
val url: String?,
615+
val preSelected: Boolean,
616+
val comment: String?
617+
)
618+
619+
fun matches(other: OpossumSignal): Boolean = base == other.base
583620
}
584621

585622
@Serializable
586-
internal data class OpossumSignalSource(
623+
data class OpossumSignalSource(
587624
val name: String,
588625
val documentConfidence: Int = 80
589626
)
590627

591628
@Serializable
592-
internal enum class OpossumFollowUp {
629+
enum class OpossumFollowUp {
593630
FOLLOW_UP
594631
}
595632

596633
@Serializable
597-
internal data class OpossumFrequentLicense(
634+
data class OpossumFrequentLicense(
598635
val shortName: String,
599636
val fullName: String?,
600637
val defaultText: String?
@@ -610,7 +647,7 @@ class OpossumReporter(
610647
}
611648

612649
@Serializable
613-
internal data class OpossumExternalAttributionSource(
650+
data class OpossumExternalAttributionSource(
614651
val name: String,
615652
val priority: Int
616653
)
@@ -623,18 +660,6 @@ private fun DependencyNode.getDependencies(): List<DependencyNode> =
623660
}
624661
}
625662

626-
private object UUIDSerializer : KSerializer<UUID> {
627-
override val descriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
628-
629-
override fun serialize(encoder: Encoder, value: UUID) {
630-
encoder.encodeString(value.toString())
631-
}
632-
633-
override fun deserialize(decoder: Decoder): UUID {
634-
return UUID.fromString(decoder.decodeString())
635-
}
636-
}
637-
638663
private object OpossumResourcesSerializer : KSerializer<OpossumReporter.OpossumResources> {
639664
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Resource")
640665

plugins/reporters/opossum/src/test/kotlin/OpossumReporterTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class OpossumReporterTest : WordSpec({
115115
baseUrlsForSources shouldNot beNull()
116116
externalAttributionSources shouldNot beNull()
117117
}
118+
118119
(opossumInput.metadata.projectId).toInt() shouldBe 0
119120
}
120121

0 commit comments

Comments
 (0)