Fix invalid prefix when generating spring boot config (#1862)

Fix invalid prefix when generating spring boot config

In Spring Boot, the prefix passed to `@ConfigurationProperties` must
be in "canonical form".

The presence of this annotation allows spring boot to directly inject
properties into a class, but is not actually needed.

The correct behavior here is to omit this annotation if the property
name does not match Spring Boot's canonical form.
This commit is contained in:
Daniel Chao
2026-09-11 15:49:04 -07:00
committed by GitHub
parent 9b52a4fd82
commit e3881f6149
6 changed files with 173 additions and 3 deletions
@@ -1,5 +1,5 @@
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -86,3 +86,21 @@ fun shlex(input: String): List<String> {
return result
}
private val springPrefixRegex =
Regex(
"""
(?mx)
^
[a-z] # starts with lowercase letter
[a-z0-9]* # followed by zero or more lowercase letters or digits
(?:-[a-z0-9]+)* # followed by possibly kebab-cased
(?:\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*)* # followed by dot-separated nested prefixes
$
"""
.trimIndent()
)
/** Tells if this string is a valid prefix in Spring Boot's `@ConfigurationProperties` annotation */
val String.isValidConfigurationPropertiesPrefix: Boolean
get() = matches(springPrefixRegex)
@@ -0,0 +1,43 @@
/*
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pkl.commons
import org.junit.jupiter.api.Test
class StringsTest {
@Test
fun isValidConfigurationPropertiesPrefix() {
val passes = listOf("app.datasource", "my-app.service-config", "app2.v1-api")
val negatives =
listOf(
"myApp.service",
"my_app.service",
"1app.service",
"app..service",
".app.service / app.",
)
for (value in passes) {
assert(value.isValidConfigurationPropertiesPrefix) {
"$value should have been valid but was not"
}
}
for (value in negatives) {
assert(!value.isValidConfigurationPropertiesPrefix) {
"$value should not have been valid but was"
}
}
}
}