Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(opossum reporter): Migrate serialization to kotlinx #9484

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

alexzurbonsen
Copy link

@alexzurbonsen alexzurbonsen commented Nov 21, 2024

[FOR REVIEW]: Best reviewed commit by commit. First commit of this PR contains the actual changes. Second commit just moves code around to improve readability.

Context

Closes #8839

Summary

Migrates the OpossumReporter plugin away from Jackson serialization to use kotlinx serialization.

To get properly typed data structures for serialization the previous untyped Map<*, *>s are substituted by properly typed data classes. This leads to the creation of an OpossumInputCreator class that holds intermediate data structures needed for processing of the Reporter input.

Test Plan

  1. Automated tests:

OpossumReporterTest
OpossumReporterFunTest

  1. Manual test: Compare two opossum files with ORT builds from main and this branch

repeat for each branch:

./gradlew installDist
cli/build/install/ort/bin/ort analyze -i <INPUT_PROJECT> -o ./RESULT -f JSON 
cli/build/install/ort/bin/ort report -i /path/to/analyzer-result.json -o ./RESULT -f Opossum 

Unzip report.opossum files and validate the input.json s with this little python script:
https://gist.github.com/alexzurbonsen/69e38586dcffbb6b988b010bbd096759

@alexzurbonsen alexzurbonsen force-pushed the migrate-opossum-reporter-to-kotlinx-serialization branch 3 times, most recently from 7b1a948 to b3aefdb Compare November 21, 2024 18:26
Comment on lines 129 to 139
fun create(
source: String,
id: Identifier? = null,
url: String? = null,
license: SpdxExpression? = null,
copyright: String? = null,
comment: String? = null,
preSelected: Boolean = false,
followUp: Boolean = false,
excludeFromNotice: Boolean = false
): OpossumSignal {

Check warning

Code scanning / detekt

The more parameters a function has the more complex it is. Long parameter lists are often used to control complex algorithms and violate the Single Responsibility Principle. Prefer functions with short parameter lists.

The function create(source: String, id: Identifier?, url: String?, license: SpdxExpression?, copyright: String?, comment: String?, preSelected: Boolean, followUp: Boolean, excludeFromNotice: Boolean) has too many parameters. The current threshold is set to 8.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may suppress this via @Suppress("LongParameterList").

}

return opossumInput
internal fun createOpossumInput(input: ReporterInput, maxDepth: Int = Int.MAX_VALUE): OpossumInput {

Check notice

Code scanning / QDJVMC

Class member can have 'private' visibility

Function 'createOpossumInput' could be private
Copy link

codecov bot commented Nov 21, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 67.64%. Comparing base (9103ac2) to head (91b1609).
Report is 9 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #9484      +/-   ##
============================================
- Coverage     67.93%   67.64%   -0.30%     
  Complexity     1289     1289              
============================================
  Files           249      249              
  Lines          8792     8830      +38     
  Branches        913      926      +13     
============================================
  Hits           5973     5973              
- Misses         2433     2471      +38     
  Partials        386      386              
Flag Coverage Δ
funTest-non-docker 33.33% <ø> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.


🚨 Try these New Features:

@alexzurbonsen alexzurbonsen force-pushed the migrate-opossum-reporter-to-kotlinx-serialization branch from b3aefdb to 98ee401 Compare November 21, 2024 19:50
@alexzurbonsen alexzurbonsen changed the title chore(opossum reporter): migrate serialization to kotlinx chore(opossum reporter): Migrate serialization to kotlinx Nov 21, 2024
@alexzurbonsen alexzurbonsen force-pushed the migrate-opossum-reporter-to-kotlinx-serialization branch from 98ee401 to 91b1609 Compare November 22, 2024 16:56
Migrates the OpossumReporter plugin away from Jackson serialization
to use kotlinx serialization. To get properly typed data structures for
serialization the previous untyped Map<*, *> are substituted by
properly typed data classes. This leads to the creation of an
OpossumInputCreator class that holds intermediate data structures
needed for processing of the Reporter input.

Signed-off-by: alexzurbonsen <[email protected]>
Put public method at the top, put caller above callees
etc.

Signed-off-by: alexzurbonsen <[email protected]>
@alexzurbonsen alexzurbonsen force-pushed the migrate-opossum-reporter-to-kotlinx-serialization branch from 91b1609 to 4bf27e2 Compare November 22, 2024 18:04
Comment on lines -139 to -143
opossumInput.packageToRoot.values.forAll { levelForPath ->
levelForPath.keys.forAll { path ->
fileList shouldContain resolvePath(path)
}
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleting this because otherwise I would have to write a separate test for OpossumInputCreator (and make the property packageToRoot non private, I guess).

But it seems like this test does not add much value. The same file paths are in resourcesToAttributions, via addSignal and later processing of pathToSignals. And we have a test for paths in resourcesToAttributions against the same list.

@alexzurbonsen alexzurbonsen marked this pull request as ready for review November 22, 2024 18:16
@alexzurbonsen alexzurbonsen requested a review from a team as a code owner November 22, 2024 18:16
@alexzurbonsen
Copy link
Author

Hi @sschuberth, it is ready for review now. Let me know what you think. Removed the untyped maps.

Serialization of the recursive data structure OpossumResources was a bit tricky and I decided to not support deserialization. I assume there is no use case for it anyways.

@@ -563,3 +622,31 @@ private fun DependencyNode.getDependencies(): List<DependencyNode> =
this += dependencyNodes.map { it.getStableReference() }
}
}

private object UUIDSerializer : KSerializer<UUID> {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There wasn't a UUID serializer yet and only few other usages of UUIDs. So I put it here as a private class for now. But let me know if you want to see this somewhere else.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again Ks3 comes to the rescue 😉 It already provides a UUID serializer:

https://github.com/Kantis/ks3/blob/f2ec0f5811c73ce140718b355ec61ddf751eaec5/doc/jdk.md?plain=1#L42

@@ -20,6 +20,9 @@
plugins {
// Apply precompiled plugins.
id("ort-plugin-conventions")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit message nits:

  • We prefer imperative mood, i.e. "Migrates" should say "Migrate".
  • We usually start capitalized after ":" in the title.

val followUp: Boolean = false,
val excludeFromNotice: Boolean = false,
val uuid: UUID = UUID.randomUUID()
@Transient
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a left-over from not having an UUID serializer yet?

Comment on lines 129 to 139
fun create(
source: String,
id: Identifier? = null,
url: String? = null,
license: SpdxExpression? = null,
copyright: String? = null,
comment: String? = null,
preSelected: Boolean = false,
followUp: Boolean = false,
excludeFromNotice: Boolean = false
): OpossumSignal {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may suppress this via @Suppress("LongParameterList").

followUp: Boolean = false,
excludeFromNotice: Boolean = false
): OpossumSignal {
return OpossumSignal(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: You could turn this into a function expression via = instead of return.

preSelected = preSelected,
followUp = OpossumFollowUp.FOLLOW_UP.takeIf { followUp },
excludeFromNotice = excludeFromNotice,
comment = comment,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Please avoid trailing commas.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same in other places.

Comment on lines 584 to 587
val json = Json {
explicitNulls = false
encodeDefaults = true
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be made available as an internal top-level property for other places like tests to use. When doing that, please name the property JSON.

explicitNulls = false
encodeDefaults = true
}
jsonFile.writeText(json.encodeToString(OpossumInput.serializer(), opossumInput))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If files may get big, maybe better use a streaming approach like at

internal fun File.writeReport(model: AOSD20): File = apply { outputStream().use { JSON.encodeToStream(model, it) } }


return opossumInput
internal fun createOpossumInput(input: ReporterInput, maxDepth: Int = Int.MAX_VALUE): OpossumInput {
return OpossumInputCreator().create(input, maxDepth)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could as well be a function expression.

@@ -563,3 +622,31 @@ private fun DependencyNode.getDependencies(): List<DependencyNode> =
this += dependencyNodes.map { it.getStableReference() }
}
}

private object UUIDSerializer : KSerializer<UUID> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again Ks3 comes to the rescue 😉 It already provides a UUID serializer:

https://github.com/Kantis/ks3/blob/f2ec0f5811c73ce140718b355ec61ddf751eaec5/doc/jdk.md?plain=1#L42

@@ -213,18 +201,18 @@ class OpossumReporterTest : WordSpec({

"create issues containing all issues" {
val issuesFromFirstPackage =
opossumInput.getSignalsForFile("/pom.xml/compile/first-package-group/[email protected]")
opossumInput.getSignalsForFile("/pom.xml/compile/first-package-group/[email protected]/")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the trailing slash is not needed and could be removed again to reduce the diff?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate the Opossum reporter from Jackson to kotlinx-serialization (KxS)
2 participants