Skip to content

Commit d8da3a1

Browse files
committed
feat(config): Extend AdminConfig for the Reporter
Add a new `ReporterConfig` class that holds configuration settings related to the Reporter. When loading the admin configuration, such an instance is now created and stored in the admin configuration. To simplify parsing of subsections in the admin configuration, introduce a new helper extension function. Signed-off-by: Oliver Heger <[email protected]>
1 parent 0c7fc7e commit d8da3a1

File tree

4 files changed

+429
-45
lines changed

4 files changed

+429
-45
lines changed

services/admin-config/src/main/kotlin/AdminConfig.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import org.ossreviewtoolkit.model.config.DownloaderConfiguration
2525
import org.ossreviewtoolkit.model.config.ScannerConfiguration
2626
import org.ossreviewtoolkit.utils.ort.ORT_COPYRIGHT_GARBAGE_FILENAME
2727
import org.ossreviewtoolkit.utils.ort.ORT_EVALUATOR_RULES_FILENAME
28+
import org.ossreviewtoolkit.utils.ort.ORT_HOW_TO_FIX_TEXT_PROVIDER_FILENAME
2829
import org.ossreviewtoolkit.utils.ort.ORT_LICENSE_CLASSIFICATIONS_FILENAME
2930
import org.ossreviewtoolkit.utils.ort.ORT_RESOLUTIONS_FILENAME
3031

@@ -36,6 +37,9 @@ class AdminConfig(
3637
/** The configuration for the Scanner worker. */
3738
val scannerConfig: ScannerConfig = DEFAULT_SCANNER_CONFIG,
3839

40+
/** The configuration for the Reporter worker. */
41+
val reporterConfig: ReporterConfig = DEFAULT_REPORTER_CONFIG,
42+
3943
/** The configuration for the Notifier worker. */
4044
val notifierConfig: NotifierConfig = DEFAULT_NOTIFIER_CONFIG,
4145

@@ -94,6 +98,17 @@ class AdminConfig(
9498
disableJiraNotifications = false
9599
)
96100

101+
/**
102+
* A default [ReporterConfig] instance that is used if the admin configuration does not contain any
103+
* reporter-specific settings. This instance is not really useful, however, since it does not support any
104+
* reports. Typically, some reports should be configured.
105+
*/
106+
val DEFAULT_REPORTER_CONFIG = ReporterConfig(
107+
reportDefinitions = emptyMap(),
108+
howToFixTextProviderFile = ORT_HOW_TO_FIX_TEXT_PROVIDER_FILENAME,
109+
customLicenseTextDir = null
110+
)
111+
97112
/**
98113
* An empty default configuration. This is going to be used if no path to a configuration file is specified,
99114
* and the default path does not exist.

services/admin-config/src/main/kotlin/AdminConfigService.kt

Lines changed: 122 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import org.eclipse.apoapsis.ortserver.config.ConfigException
3030
import org.eclipse.apoapsis.ortserver.config.ConfigManager
3131
import org.eclipse.apoapsis.ortserver.config.Context
3232
import org.eclipse.apoapsis.ortserver.config.Path
33+
import org.eclipse.apoapsis.ortserver.model.ReportNameMapping
34+
import org.eclipse.apoapsis.ortserver.model.ReporterAsset
3335
import org.eclipse.apoapsis.ortserver.model.SourceCodeOrigin
3436
import org.eclipse.apoapsis.ortserver.utils.config.getBooleanOrDefault
3537
import org.eclipse.apoapsis.ortserver.utils.config.getConfigOrEmpty
@@ -41,6 +43,8 @@ import org.eclipse.apoapsis.ortserver.utils.config.getStringOrDefault
4143
import org.eclipse.apoapsis.ortserver.utils.config.getStringOrNull
4244
import org.eclipse.apoapsis.ortserver.utils.config.withPath
4345

46+
import org.ossreviewtoolkit.utils.ort.ORT_HOW_TO_FIX_TEXT_PROVIDER_FILENAME
47+
4448
import org.slf4j.LoggerFactory
4549

4650
/**
@@ -53,6 +57,7 @@ import org.slf4j.LoggerFactory
5357
* unspecified, and the [DEFAULT_PATH] must not exist. In all other cases, a missing configuration file causes an
5458
* exception to be thrown.
5559
*/
60+
@Suppress("TooManyFunctions")
5661
class AdminConfigService(
5762
private val configManager: ConfigManager
5863
) {
@@ -69,6 +74,9 @@ class AdminConfigService(
6974
/** The name of the section containing the scanner-related configuration. */
7075
private const val SCANNER_SECTION = "scanner"
7176

77+
/** The name of the section containing the reporter-related configuration. */
78+
private const val REPORTER_SECTION = "reporter"
79+
7280
/** The name of the section containing the notifier-related configuration. */
7381
private const val NOTIFIER_SECTION = "notifier"
7482

@@ -78,6 +86,9 @@ class AdminConfigService(
7886
/** The name of the subsection containing the Jira server configuration for the notifier. */
7987
private const val NOTIFIER_JIRA_SECTION = "jira"
8088

89+
/** An object to obtain default values for the mail server configuration. */
90+
private val defaultMailServerConfig = MailServerConfiguration(fromAddress = "")
91+
8192
private val logger = LoggerFactory.getLogger(AdminConfigService::class.java)
8293

8394
/**
@@ -91,6 +102,7 @@ class AdminConfigService(
91102

92103
return AdminConfig(
93104
scannerConfig = parseScannerConfig(config),
105+
reporterConfig = parseReporterConfig(config),
94106
notifierConfig = parseNotifierConfig(config),
95107
defaultRuleSet = defaultRuleSet,
96108
ruleSets = ruleSets,
@@ -130,20 +142,19 @@ class AdminConfigService(
130142
* [ScannerConfig] instance.
131143
*/
132144
private fun parseScannerConfig(config: Config): ScannerConfig =
133-
config.withPath(SCANNER_SECTION)?.let { c ->
134-
val scannerConfig = c.getConfig(SCANNER_SECTION)
145+
config.parseObjectOrDefault(SCANNER_SECTION, AdminConfig.DEFAULT_SCANNER_CONFIG) {
135146
ScannerConfig(
136-
detectedLicenseMappings = scannerConfig.getObjectOrDefault("detectedLicenseMappings") {
147+
detectedLicenseMappings = getObjectOrDefault("detectedLicenseMappings") {
137148
ConfigValueFactory.fromMap(AdminConfig.DEFAULT_SCANNER_CONFIG.detectedLicenseMappings)
138149
}.mapValues { it.value.unwrapped().toString() },
139-
ignorePatterns = scannerConfig.getStringListOrDefault("ignorePatterns") {
150+
ignorePatterns = getStringListOrDefault("ignorePatterns") {
140151
AdminConfig.DEFAULT_SCANNER_CONFIG.ignorePatterns
141152
},
142-
sourceCodeOrigins = scannerConfig.getStringListOrDefault("sourceCodeOrigins") {
153+
sourceCodeOrigins = getStringListOrDefault("sourceCodeOrigins") {
143154
AdminConfig.DEFAULT_SCANNER_CONFIG.sourceCodeOrigins.map { it.name }
144155
}.map { SourceCodeOrigin.valueOf(it.uppercase()) }
145156
)
146-
} ?: AdminConfig.DEFAULT_SCANNER_CONFIG
157+
}
147158

148159
/**
149160
* Validate the given [scannerConfig]. Add found issues to the given [issues] list.
@@ -164,72 +175,93 @@ class AdminConfigService(
164175
* corresponding [NotifierConfig] instance.
165176
*/
166177
private fun parseNotifierConfig(config: Config): NotifierConfig =
167-
config.withPath(NOTIFIER_SECTION)?.let { c ->
168-
val notifierConfig = c.getConfig(NOTIFIER_SECTION)
178+
config.parseObjectOrDefault(NOTIFIER_SECTION, AdminConfig.DEFAULT_NOTIFIER_CONFIG) {
169179
NotifierConfig(
170-
notifierRules = notifierConfig.getStringOrDefault(
180+
notifierRules = getStringOrDefault(
171181
"notifierRules",
172182
AdminConfig.DEFAULT_NOTIFIER_CONFIG.notifierRules
173183
),
174-
disableMailNotifications = notifierConfig.getBooleanOrDefault(
184+
disableMailNotifications = getBooleanOrDefault(
175185
"disableMailNotifications",
176186
AdminConfig.DEFAULT_NOTIFIER_CONFIG.disableMailNotifications
177187
),
178-
disableJiraNotifications = notifierConfig.getBooleanOrDefault(
188+
disableJiraNotifications = getBooleanOrDefault(
179189
"disableJiraNotifications",
180190
AdminConfig.DEFAULT_NOTIFIER_CONFIG.disableJiraNotifications
181191
),
182-
mail = parseNotifierMailConfig(notifierConfig),
183-
jira = parseNotifierJiraConfig(notifierConfig)
192+
mail = parseNotifierMailConfig(this),
193+
jira = parseNotifierJiraConfig(this)
184194
)
185-
} ?: AdminConfig.DEFAULT_NOTIFIER_CONFIG
195+
}
186196

187197
/**
188198
* Parse the mail server configuration for the notifier from the given [config] if it is defined.
189199
*/
190200
private fun parseNotifierMailConfig(config: Config): MailServerConfiguration? =
191-
config.withPath(NOTIFIER_MAIL_SECTION)?.let { mailConfig ->
192-
val defaults = MailServerConfiguration(fromAddress = "")
193-
with(mailConfig.getConfig(NOTIFIER_MAIL_SECTION)) {
194-
MailServerConfiguration(
195-
hostName = getStringOrDefault("host", defaults.hostName),
196-
port = getIntOrDefault("port", defaults.port),
197-
username = getStringOrDefault("username", defaults.username),
198-
password = getStringOrDefault("password", defaults.password),
199-
useSsl = getBooleanOrDefault("ssl", defaults.useSsl),
200-
fromAddress = getString("fromAddress")
201-
)
202-
}
201+
config.parseObjectOrDefault(NOTIFIER_MAIL_SECTION, null) {
202+
MailServerConfiguration(
203+
hostName = getStringOrDefault("host", defaultMailServerConfig.hostName),
204+
port = getIntOrDefault("port", defaultMailServerConfig.port),
205+
username = getStringOrDefault("username", defaultMailServerConfig.username),
206+
password = getStringOrDefault("password", defaultMailServerConfig.password),
207+
useSsl = getBooleanOrDefault("ssl", defaultMailServerConfig.useSsl),
208+
fromAddress = getString("fromAddress")
209+
)
203210
}
204211

205212
/**
206213
* Parse the Jira server configuration for the notifier from the given [config] if it is defined.
207214
*/
208215
private fun parseNotifierJiraConfig(config: Config): JiraRestClientConfiguration? =
209-
config.withPath(NOTIFIER_JIRA_SECTION)?.let { jiraConfig ->
210-
with(jiraConfig.getConfig(NOTIFIER_JIRA_SECTION)) {
211-
JiraRestClientConfiguration(
212-
serverUrl = getString("url"),
213-
username = getString("username"),
214-
password = getString("password"),
215-
)
216-
}
216+
config.parseObjectOrDefault(NOTIFIER_JIRA_SECTION, null) {
217+
JiraRestClientConfiguration(
218+
serverUrl = getString("url"),
219+
username = getString("username"),
220+
password = getString("password"),
221+
)
217222
}
218223

219-
private fun parseMavenCentralMirror(config: Config): MavenCentralMirror? {
220-
if (!config.hasPath("mavenCentralMirror")) return null
224+
/**
225+
* Parse the properties related to the reporter configuration from the given [config] and return a
226+
* corresponding [ReporterConfig] instance.
227+
*/
228+
private fun parseReporterConfig(config: Config): ReporterConfig =
229+
config.parseObjectOrDefault(REPORTER_SECTION, AdminConfig.DEFAULT_REPORTER_CONFIG) {
230+
ReporterConfig(
231+
howToFixTextProviderFile = getStringOrDefault(
232+
"howToFixTextProviderFile",
233+
ORT_HOW_TO_FIX_TEXT_PROVIDER_FILENAME
234+
),
235+
customLicenseTextDir = getStringOrNull("customLicenseTextDir"),
236+
reportDefinitions = parseReportDefinitions(this)
237+
)
238+
}
221239

222-
val mirrorConfig = config.getConfig("mavenCentralMirror")
240+
/**
241+
* Parse the report definitions defined in the given [config].
242+
*/
243+
private fun parseReportDefinitions(config: Config): Map<String, ReportDefinition> =
244+
config.getObjectOrEmpty("reports").filterValues { it is ConfigObject }
245+
.mapValues { entry ->
246+
parseReportDefinition((entry.value as ConfigObject).toConfig())
247+
}
223248

224-
return MavenCentralMirror(
225-
id = mirrorConfig.getString("id"),
226-
name = mirrorConfig.getString("name"),
227-
url = mirrorConfig.getString("url"),
228-
mirrorOf = mirrorConfig.getString("mirrorOf"),
229-
usernameSecret = mirrorConfig.getStringOrNull("username"),
230-
passwordSecret = mirrorConfig.getStringOrNull("password")
249+
/**
250+
* Parse a [ReportDefinition] at the root of the given [config].
251+
*/
252+
private fun parseReportDefinition(config: Config): ReportDefinition =
253+
ReportDefinition(
254+
pluginId = config.getString("pluginId"),
255+
assetFiles = parseReporterAssets(config, "assetFiles", isDirectory = false),
256+
assetDirectories = parseReporterAssets(config, "assetDirectories", isDirectory = true),
257+
nameMapping = config.parseObjectOrDefault("nameMapping", null) {
258+
ReportNameMapping(
259+
namePrefix = getString("namePrefix"),
260+
startIndex = getIntOrDefault("startIndex", 1),
261+
alwaysAppendIndex = getBooleanOrDefault("alwaysAppendIndex", false)
262+
)
263+
}
231264
)
232-
}
233265

234266
/**
235267
* Return a [Set] with the paths to all configuration files referenced by this [AdminConfig]. This is used
@@ -262,6 +294,51 @@ class AdminConfigService(
262294
add(path)
263295
}
264296
}
297+
298+
/**
299+
* Parse a list of [ReporterAsset]s from the given [config] at the specified [path]. If [isDirectory] is
300+
* *true*, make sure that all source paths end with a trailing slash.
301+
*/
302+
private fun parseReporterAssets(config: Config, path: String, isDirectory: Boolean): List<ReporterAsset> =
303+
if (config.hasPath(path)) {
304+
config.getConfigList(path).map { c ->
305+
ReporterAsset(
306+
sourcePath = directoryPath(c.getString("sourcePath"), isDirectory),
307+
targetFolder = c.getStringOrNull("targetFolder"),
308+
targetName = c.getStringOrNull("targetName")
309+
)
310+
}
311+
} else {
312+
emptyList()
313+
}
314+
315+
private fun parseMavenCentralMirror(config: Config): MavenCentralMirror? =
316+
config.parseObjectOrDefault("mavenCentralMirror", null) {
317+
MavenCentralMirror(
318+
id = getString("id"),
319+
name = getString("name"),
320+
url = getString("url"),
321+
mirrorOf = getString("mirrorOf"),
322+
usernameSecret = getStringOrNull("username"),
323+
passwordSecret = getStringOrNull("password")
324+
)
325+
}
326+
327+
/**
328+
* Parse an object defined at the given [path] in this [Config] using the provided [parser] function.
329+
* Return the given [default] object if the path does not exist.
330+
*/
331+
private fun <T> Config.parseObjectOrDefault(path: String, default: T, parser: Config.() -> T): T =
332+
withPath(path)?.let { c ->
333+
parser(c.getConfig(path))
334+
} ?: default
335+
336+
/**
337+
* Make sure the given [path] points to a directory if [isDirectory] is *true* by appending a trailing slash if
338+
* necessary.
339+
*/
340+
private fun directoryPath(path: String, isDirectory: Boolean): String =
341+
if (isDirectory && !path.endsWith("/")) "$path/" else path
265342
}
266343

267344
/**

0 commit comments

Comments
 (0)