Skip to content

Commit

Permalink
refactor(package-curation-providers)!: Migrate to the new plugin API
Browse files Browse the repository at this point in the history
Migrate the package curation provider API to the new plugin API. Do some
required refactorings along the way, like aligning the constructor with
the requirements of the plugin API or inlining the configuration class
for the SW360 provider.

Signed-off-by: Martin Nonnenmacher <martin.nonnenmacher@bosch.com>
  • Loading branch information
mnonnenmacher committed Sep 2, 2024
1 parent 40e0133 commit 934c6aa
Show file tree
Hide file tree
Showing 19 changed files with 181 additions and 116 deletions.
1 change: 1 addition & 0 deletions plugins/package-curation-providers/api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ plugins {

dependencies {
api(projects.model)
api(projects.plugins.api)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ package org.ossreviewtoolkit.plugins.packagecurationproviders.api

import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.PackageCuration
import org.ossreviewtoolkit.plugins.api.Plugin

/**
* A provider for [PackageCuration]s.
*/
fun interface PackageCurationProvider {
interface PackageCurationProvider : Plugin {
/**
* Return all available [PackageCuration]s which are applicable to any of the given [packages].
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ import org.apache.logging.log4j.kotlin.logger

import org.ossreviewtoolkit.model.ResolvedPackageCurations.Companion.REPOSITORY_CONFIGURATION_PROVIDER_ID
import org.ossreviewtoolkit.model.config.ProviderPluginConfiguration
import org.ossreviewtoolkit.utils.common.Plugin
import org.ossreviewtoolkit.utils.common.TypedConfigurablePluginFactory
import org.ossreviewtoolkit.plugins.api.PluginConfig
import org.ossreviewtoolkit.plugins.api.PluginFactory
import org.ossreviewtoolkit.utils.common.getDuplicates

/**
* The extension point for [PackageCurationProvider]s.
*/
interface PackageCurationProviderFactory<CONFIG> : TypedConfigurablePluginFactory<CONFIG, PackageCurationProvider> {
interface PackageCurationProviderFactory : PluginFactory<PackageCurationProvider> {
companion object {
/**
* All [package curation provider factories][PackageCurationProviderFactory] available in the classpath,
* associated by their names.
*/
val ALL by lazy { Plugin.getAll<PackageCurationProviderFactory<*>>() }
val ALL by lazy { PluginFactory.getAll<PackageCurationProviderFactory, PackageCurationProvider>() }

/**
* Return a new (identifier, provider instance) tuple for each
Expand All @@ -48,7 +48,7 @@ interface PackageCurationProviderFactory<CONFIG> : TypedConfigurablePluginFactor
it.enabled
}.mapNotNull {
ALL[it.type]?.let { factory ->
it.id to factory.create(it.options, it.secrets)
it.id to factory.create(PluginConfig(it.options, it.secrets))
}.also { factory ->
factory ?: logger.error {
"Curation provider of type '${it.type}' is enabled in configuration but not available in the " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,27 @@ package org.ossreviewtoolkit.plugins.packagecurationproviders.api

import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.PackageCuration
import org.ossreviewtoolkit.plugins.api.PluginDescriptor

/**
* The default [PluginDescriptor] for a [SimplePackageCurationProvider]. Classes inheriting from this class
* have to provide their own descriptor.
*/
private val pluginDescriptor = PluginDescriptor(
id = "Simple",
displayName = "Simple Package Curation Provider",
description = "A simple package curation provider, which provides a fixed set of package curations."
)

/**
* A [PackageCurationProvider] that provides the specified [packageCurations].
*/
open class SimplePackageCurationProvider(val packageCurations: List<PackageCuration>) : PackageCurationProvider {
open class SimplePackageCurationProvider(
override val descriptor: PluginDescriptor,
val packageCurations: List<PackageCuration>
) : PackageCurationProvider {
constructor(packageCurations: List<PackageCuration>) : this(pluginDescriptor, packageCurations)

override fun getCurationsFor(packages: Collection<Package>): Set<PackageCuration> =
packageCurations.filterTo(mutableSetOf()) { curation ->
packages.any { pkg -> curation.isApplicable(pkg.id) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@

plugins {
// Apply precompiled plugins.
id("ort-library-conventions")
id("ort-plugin-conventions")
}

dependencies {
api(projects.clients.clearlyDefinedClient)
api(projects.plugins.packageCurationProviders.packageCurationProviderApi)

ksp(projects.plugins.packageCurationProviders.packageCurationProviderApi)

testImplementation(libs.wiremock)
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ import kotlin.time.Duration.Companion.seconds
import org.ossreviewtoolkit.clients.clearlydefined.ClearlyDefinedService.Server
import org.ossreviewtoolkit.model.Identifier
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.plugins.api.PluginConfig
import org.ossreviewtoolkit.utils.spdx.toSpdx

class ClearlyDefinedPackageCurationProviderFunTest : WordSpec({
"The production server" should {
val provider = ClearlyDefinedPackageCurationProvider()
val provider = ClearlyDefinedPackageCurationProviderFactory().create(PluginConfig())

"return an existing curation for the javax.servlet-api Maven package" {
val packages = createPackagesFromIds("Maven:javax.servlet:javax.servlet-api:3.1.0")
Expand Down Expand Up @@ -76,7 +77,10 @@ class ClearlyDefinedPackageCurationProviderFunTest : WordSpec({
serverUrl = Server.PRODUCTION.apiUrl,
minTotalLicenseScore = 80
)
val provider = ClearlyDefinedPackageCurationProvider(config)
val provider = ClearlyDefinedPackageCurationProvider(
ClearlyDefinedPackageCurationProviderFactory().descriptor,
config
)

// Use an id which is known to have non-empty results from an earlier test.
val packages = createPackagesFromIds("Maven:org.slf4j:slf4j-log4j12:1.7.30")
Expand All @@ -89,7 +93,7 @@ class ClearlyDefinedPackageCurationProviderFunTest : WordSpec({
}

"be retrieved for packages without a namespace" {
val provider = ClearlyDefinedPackageCurationProvider()
val provider = ClearlyDefinedPackageCurationProviderFactory().create(PluginConfig())
val packages = createPackagesFromIds("NPM::acorn:0.6.0")

withRetry {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import okhttp3.OkHttpClient
import org.apache.logging.log4j.kotlin.logger

import org.ossreviewtoolkit.clients.clearlydefined.ClearlyDefinedService
import org.ossreviewtoolkit.clients.clearlydefined.ClearlyDefinedService.Server
import org.ossreviewtoolkit.clients.clearlydefined.ComponentType
import org.ossreviewtoolkit.clients.clearlydefined.Coordinates
import org.ossreviewtoolkit.clients.clearlydefined.SourceLocation
Expand All @@ -41,9 +40,11 @@ import org.ossreviewtoolkit.model.RemoteArtifact
import org.ossreviewtoolkit.model.VcsInfoCurationData
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.utils.toClearlyDefinedCoordinates
import org.ossreviewtoolkit.plugins.api.OrtPlugin
import org.ossreviewtoolkit.plugins.api.OrtPluginOption
import org.ossreviewtoolkit.plugins.api.PluginDescriptor
import org.ossreviewtoolkit.plugins.packagecurationproviders.api.PackageCurationProvider
import org.ossreviewtoolkit.plugins.packagecurationproviders.api.PackageCurationProviderFactory
import org.ossreviewtoolkit.utils.common.Options
import org.ossreviewtoolkit.utils.common.collectMessages
import org.ossreviewtoolkit.utils.ort.OkHttpClientHelper
import org.ossreviewtoolkit.utils.ort.runBlocking
Expand All @@ -57,41 +58,33 @@ data class ClearlyDefinedPackageCurationProviderConfig(
/**
* The URL of the ClearlyDefined server to use.
*/
@OrtPluginOption(defaultValue = "https://api.clearlydefined.io")
val serverUrl: String,

/**
* The minimum total score for a curation to be accepted. Must lie within 0 to 100.
*/
@OrtPluginOption(defaultValue = "0")
val minTotalLicenseScore: Int
)

class ClearlyDefinedPackageCurationProviderFactory :
PackageCurationProviderFactory<ClearlyDefinedPackageCurationProviderConfig> {
override val type = "ClearlyDefined"

override fun create(config: ClearlyDefinedPackageCurationProviderConfig) =
ClearlyDefinedPackageCurationProvider(config)

override fun parseConfig(options: Options, secrets: Options) =
ClearlyDefinedPackageCurationProviderConfig(
serverUrl = options["serverUrl"] ?: Server.PRODUCTION.apiUrl,
minTotalLicenseScore = options["minTotalLicenseScore"]?.toInt() ?: 0
)
}

/**
* A provider for curated package metadata from the [ClearlyDefined](https://clearlydefined.io/) service.
*/
@OrtPlugin(
name = "ClearlyDefined",
description = "Provides package curation data from the ClearlyDefined service.",
factory = PackageCurationProviderFactory::class
)
class ClearlyDefinedPackageCurationProvider(
override val descriptor: PluginDescriptor,
private val config: ClearlyDefinedPackageCurationProviderConfig,
client: OkHttpClient? = null
) : PackageCurationProvider {
constructor(serverUrl: String, client: OkHttpClient? = null) : this(
ClearlyDefinedPackageCurationProviderConfig(serverUrl, minTotalLicenseScore = 0), client
constructor(descriptor: PluginDescriptor, config: ClearlyDefinedPackageCurationProviderConfig) : this(
descriptor, config, null
)

constructor(server: Server = Server.PRODUCTION) : this(server.apiUrl)

private val service by lazy {
ClearlyDefinedService.create(config.serverUrl, client ?: OkHttpClientHelper.buildClient())
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ class ClearlyDefinedPackageCurationProviderTest : WordSpec({
readTimeout(Duration.ofMillis(1))
}

val provider = ClearlyDefinedPackageCurationProvider("http://localhost:${server.port()}", client)
val provider = ClearlyDefinedPackageCurationProvider(
ClearlyDefinedPackageCurationProviderFactory().descriptor,
ClearlyDefinedPackageCurationProviderConfig(serverUrl = "http://localhost:${server.port()}", 0),
client
)

val id = Identifier("Maven:some-ns:some-component:1.2.3")
val packages = listOf(Package.EMPTY.copy(id = id))

Expand Down
4 changes: 3 additions & 1 deletion plugins/package-curation-providers/file/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

plugins {
// Apply precompiled plugins.
id("ort-library-conventions")
id("ort-plugin-conventions")
}

dependencies {
api(projects.plugins.packageCurationProviders.packageCurationProviderApi)

ksp(projects.plugins.packageCurationProviders.packageCurationProviderApi)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@ import java.io.IOException
import org.ossreviewtoolkit.model.FileFormat
import org.ossreviewtoolkit.model.PackageCuration
import org.ossreviewtoolkit.model.readValue
import org.ossreviewtoolkit.plugins.api.OrtPlugin
import org.ossreviewtoolkit.plugins.api.OrtPluginOption
import org.ossreviewtoolkit.plugins.api.PluginDescriptor
import org.ossreviewtoolkit.plugins.packagecurationproviders.api.PackageCurationProvider
import org.ossreviewtoolkit.plugins.packagecurationproviders.api.PackageCurationProviderFactory
import org.ossreviewtoolkit.plugins.packagecurationproviders.api.SimplePackageCurationProvider
import org.ossreviewtoolkit.utils.common.Options
import org.ossreviewtoolkit.utils.common.getDuplicates
import org.ossreviewtoolkit.utils.ort.ORT_PACKAGE_CURATIONS_DIRNAME
import org.ossreviewtoolkit.utils.ort.ORT_PACKAGE_CURATIONS_FILENAME
Expand All @@ -37,57 +40,59 @@ data class FilePackageCurationProviderConfig(
/**
* The path of the package curation file or directory.
*/
val path: File,
val path: String,

/**
* A flag to denote whether the path is required to exist.
*/
@OrtPluginOption(defaultValue = "false")
val mustExist: Boolean
)

open class FilePackageCurationProviderFactory : PackageCurationProviderFactory<FilePackageCurationProviderConfig> {
override val type = "File"

override fun create(config: FilePackageCurationProviderConfig) = FilePackageCurationProvider(config)

override fun parseConfig(options: Options, secrets: Options) =
FilePackageCurationProviderConfig(
path = File(options.getValue("path")),
mustExist = options["mustExist"]?.toBooleanStrict() != false
)
}

class DefaultFilePackageCurationProviderFactory : FilePackageCurationProviderFactory() {
override val type = "DefaultFile"

override fun parseConfig(options: Options, secrets: Options) =
FilePackageCurationProviderConfig(
path = ortConfigDirectory.resolve(ORT_PACKAGE_CURATIONS_FILENAME),
mustExist = false
)
}

class DefaultDirPackageCurationProviderFactory : FilePackageCurationProviderFactory() {
override val type = "DefaultDir"
@OrtPlugin(
name = "Default File Package Curation Provider",
description = "A package curation provider that loads package curations from the default file.",
factory = PackageCurationProviderFactory::class
)
class DefaultFilePackageCurationProvider(descriptor: PluginDescriptor) : FilePackageCurationProvider(
descriptor,
FilePackageCurationProviderConfig(
path = ortConfigDirectory.resolve(ORT_PACKAGE_CURATIONS_FILENAME).absolutePath,
mustExist = false
)
)

override fun parseConfig(options: Options, secrets: Options) =
FilePackageCurationProviderConfig(
path = ortConfigDirectory.resolve(ORT_PACKAGE_CURATIONS_DIRNAME),
mustExist = false
)
}
@OrtPlugin(
name = "Default Dir Package Curation Provider",
description = "A package curation provider that loads package curations from the default directory.",
factory = PackageCurationProviderFactory::class
)
class DefaultDirPackageCurationProvider(descriptor: PluginDescriptor) : FilePackageCurationProvider(
descriptor,
FilePackageCurationProviderConfig(
path = ortConfigDirectory.resolve(ORT_PACKAGE_CURATIONS_DIRNAME).absolutePath,
mustExist = false
)
)

/**
* A [PackageCurationProvider] that loads [PackageCuration]s from all given curation files. Supports all file formats
* specified in [FileFormat].
*/
class FilePackageCurationProvider(
vararg paths: File?
) : SimplePackageCurationProvider(readCurationFiles(paths.filterNotNull())) {
constructor(config: FilePackageCurationProviderConfig) : this(
config.path.takeUnless { !it.exists() && !config.mustExist }
@OrtPlugin(
name = "File Package Curation Provider",
description = "A package curation provider that loads package curations from files.",
factory = PackageCurationProviderFactory::class
)
open class FilePackageCurationProvider(descriptor: PluginDescriptor, vararg paths: File?) :
SimplePackageCurationProvider(descriptor, readCurationFiles(paths.filterNotNull())) {
constructor(descriptor: PluginDescriptor, config: FilePackageCurationProviderConfig) : this(
descriptor,
File(config.path).takeUnless { !it.exists() && !config.mustExist }
)

constructor(vararg paths: File?) : this(FilePackageCurationProviderFactory().descriptor, *paths)

companion object {
/**
* Read a list of [PackageCuration]s from existing [paths], which can either point to files or directories. In
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@

plugins {
// Apply precompiled plugins.
id("ort-library-conventions")
id("ort-plugin-conventions")
}

dependencies {
api(projects.plugins.packageCurationProviders.packageCurationProviderApi)

ksp(projects.plugins.packageCurationProviders.packageCurationProviderApi)

implementation(projects.downloader)
}
Loading

0 comments on commit 934c6aa

Please sign in to comment.