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

[3.17] 3.17.3 backports 2 #44897

Merged
merged 3 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
<smallrye-config.version>3.10.2</smallrye-config.version>
<smallrye-health.version>4.1.0</smallrye-health.version>
<smallrye-metrics.version>4.0.0</smallrye-metrics.version>
<smallrye-open-api.version>4.0.3</smallrye-open-api.version>
<smallrye-open-api.version>4.0.4</smallrye-open-api.version>
<smallrye-graphql.version>2.11.0</smallrye-graphql.version>
<smallrye-fault-tolerance.version>6.6.3</smallrye-fault-tolerance.version>
<smallrye-jwt.version>4.6.1</smallrye-jwt.version>
Expand Down
21 changes: 16 additions & 5 deletions docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2994,10 +2994,9 @@ If there is a message bundle method that accepts a single parameter of an enum t
@Message <1>
String methodName(MyEnum enum);
----
<1> The value is intentionally not provided. There's also no key for the method in a localized file.

Then it receives a generated template:
<1> The value is intentionally not provided. There's also no key/value pair for this method in a localized file.

Then it receives a generated template like:
[source,html]
----
{#when enumParamName}
Expand All @@ -3006,15 +3005,27 @@ Then it receives a generated template:
{/when}
----

Furthermore, a special message method is generated for each enum constant. Finally, each localized file must contain keys and values for all constant message keys:
Furthermore, a special message method is generated for each enum constant.
Finally, each localized file must contain keys and values for all enum constants:

[source,poperties]
----
methodName_CONSTANT1=Value 1
methodName_CONSTANT2=Value 2
----

In a template, an enum constant can be localized with a message bundle method like `{msg:methodName(enumConstant)}`.
// We need to escape the first underscore
// See https://docs.asciidoctor.org/asciidoc/latest/subs/prevent/
[IMPORTANT]
.Message keys for enum constants
====
By default, the message key consists of the method name followed by the `\_` separator and the constant name.
If any constant name of a particular enum contains the `_` or the `$` character then the `\_$` separator must be used for all message keys for this enum instead.
For example, `methodName_$CONSTANT_1=Value 1` or `methodName_$CONSTANT$1=Value 1`.
A constant of a localized enum may not contain the `_$` separator.
====

In a template, the localized message for an enum constant can be obtained with a message bundle method like `{msg:methodName(enumConstant)}`.

TIP: There is also <<convenient-annotation-for-enums,`@TemplateEnum`>> - a convenient annotation to access enum constants in a template.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -865,16 +865,20 @@ private Map<String, String> parseKeyToTemplateFromLocalizedFile(ClassInfo bundle
* @param key
* @param bundleInterface
* @return {@code true} if the given key represents an enum constant message key, such as {@code myEnum_CONSTANT1}
* @see #toEnumConstantKey(String, String)
*/
boolean isEnumConstantMessageKey(String key, IndexView index, ClassInfo bundleInterface) {
if (key.isBlank()) {
return false;
}
int lastIdx = key.lastIndexOf("_");
return isEnumConstantMessageKey("_$", key, index, bundleInterface)
|| isEnumConstantMessageKey("_", key, index, bundleInterface);
}

private boolean isEnumConstantMessageKey(String separator, String key, IndexView index, ClassInfo bundleInterface) {
int lastIdx = key.lastIndexOf(separator);
if (lastIdx != -1 && lastIdx != key.length()) {
String methodName = key.substring(0, lastIdx);
String constant = key.substring(lastIdx + 1, key.length());
String constant = key.substring(lastIdx + separator.length(), key.length());
MethodInfo method = messageBundleMethod(bundleInterface, methodName);
if (method != null && method.parametersCount() == 1) {
Type paramType = method.parameterType(0);
Expand Down Expand Up @@ -1021,11 +1025,12 @@ private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo d
// We need some special handling for enum message bundle methods
// A message bundle method that accepts an enum and has no message template receives a generated template:
// {#when enumParamName}
// {#is CONSTANT1}{msg:org_acme_MyEnum_CONSTANT1}
// {#is CONSTANT2}{msg:org_acme_MyEnum_CONSTANT2}
// {#is CONSTANT_1}{msg:myEnum_$CONSTANT_1}
// {#is CONSTANT_2}{msg:myEnum_$CONSTANT_2}
// ...
// {/when}
// Furthermore, a special message method is generated for each enum constant
// These methods are used to handle the {msg:myEnum$CONSTANT_1} and {msg:myEnum$CONSTANT_2}
if (messageTemplate == null && method.parametersCount() == 1) {
Type paramType = method.parameterType(0);
if (paramType.kind() == org.jboss.jandex.Type.Kind.CLASS) {
Expand All @@ -1036,9 +1041,12 @@ private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo d
.append("}");
Set<String> enumConstants = maybeEnum.fields().stream().filter(FieldInfo::isEnumConstant)
.map(FieldInfo::name).collect(Collectors.toSet());
String separator = enumConstantSeparator(enumConstants);
for (String enumConstant : enumConstants) {
// org_acme_MyEnum_CONSTANT1
String enumConstantKey = toEnumConstantKey(method.name(), enumConstant);
// myEnum_CONSTANT
// myEnum_$CONSTANT_1
// myEnum_$CONSTANT$NEXT
String enumConstantKey = toEnumConstantKey(method.name(), separator, enumConstant);
String enumConstantTemplate = messageTemplates.get(enumConstantKey);
if (enumConstantTemplate == null) {
throw new TemplateException(
Expand All @@ -1052,6 +1060,10 @@ private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo d
.append(":")
.append(enumConstantKey)
.append("}");
// For each constant we generate a method:
// myEnum_CONSTANT(MyEnum val)
// myEnum_$CONSTANT_1(MyEnum val)
// myEnum_$CONSTANT$NEXT(MyEnum val)
generateEnumConstantMessageMethod(bundleCreator, bundleName, locale, bundleInterface,
defaultBundleInterface, enumConstantKey, keyMap, enumConstantTemplate,
messageTemplateMethods);
Expand Down Expand Up @@ -1132,8 +1144,21 @@ private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo d
return generatedName.replace('/', '.');
}

private String toEnumConstantKey(String methodName, String enumConstant) {
return methodName + "_" + enumConstant;
private String enumConstantSeparator(Set<String> enumConstants) {
for (String constant : enumConstants) {
if (constant.contains("_$")) {
throw new MessageBundleException("A constant of a localized enum may not contain '_$': " + constant);
}
if (constant.contains("$") || constant.contains("_")) {
// If any of the constants contains "_" or "$" then "_$" is used
return "_$";
}
}
return "_";
}

private String toEnumConstantKey(String methodName, String separator, String enumConstant) {
return methodName + separator + enumConstant;
}

private void generateEnumConstantMessageMethod(ClassCreator bundleCreator, String bundleName, String locale,
Expand Down Expand Up @@ -1165,7 +1190,7 @@ private void generateEnumConstantMessageMethod(ClassCreator bundleCreator, Strin
// No expression/tag - no need to use qute
enumConstantMethod.returnValue(enumConstantMethod.load(messageTemplate));
} else {
// Obtain the template, e.g. msg_org_acme_MyEnum_CONSTANT1
// Obtain the template, e.g. msg_myEnum$CONSTANT_1
ResultHandle template = enumConstantMethod.invokeStaticMethod(
io.quarkus.qute.deployment.Descriptors.BUNDLES_GET_TEMPLATE,
enumConstantMethod.load(templateId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateEnum;
import io.quarkus.qute.i18n.Message;
import io.quarkus.qute.i18n.MessageBundle;
import io.quarkus.test.QuarkusUnitTest;
Expand All @@ -18,24 +19,47 @@ public class MessageBundleEnumTest {
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Messages.class, MyEnum.class)
.addClasses(Messages.class, MyEnum.class, UnderscoredEnum.class, AnotherUnderscoredEnum.class)
.addAsResource("messages/enu.properties")
.addAsResource("messages/enu_cs.properties")
.addAsResource(new StringAsset(
"{enu:myEnum(MyEnum:ON)}::{enu:myEnum(MyEnum:OFF)}::{enu:myEnum(MyEnum:UNDEFINED)}::"
+ "{enu:shortEnum(MyEnum:ON)}::{enu:shortEnum(MyEnum:OFF)}::{enu:shortEnum(MyEnum:UNDEFINED)}::"
+ "{enu:foo(MyEnum:ON)}::{enu:foo(MyEnum:OFF)}::{enu:foo(MyEnum:UNDEFINED)}::"
+ "{enu:locFileOverride(MyEnum:ON)}::{enu:locFileOverride(MyEnum:OFF)}::{enu:locFileOverride(MyEnum:UNDEFINED)}"),
"templates/foo.html"));
"templates/foo.html")
.addAsResource(new StringAsset(
"{enu:underscored(UnderscoredEnum:A_B)}::{enu:underscored(UnderscoredEnum:FOO_BAR_BAZ)}::{enu:underscored_foo(AnotherUnderscoredEnum:NEXT_B)}::{enu:underscored$foo(AnotherUnderscoredEnum:NEXT_B)}::{enu:uncommon(UncommonEnum:NEXT$B)}"),
"templates/bar.html"));

@Inject
Template foo;

@Inject
Template bar;

@Test
public void testMessages() {
assertEquals("On::Off::Undefined::1::0::U::+::-::_::on::off::undefined", foo.render());
assertEquals("Zapnuto::Vypnuto::Nedefinováno::1::0::N::+::-::_::zap::vyp::nedef",
foo.instance().setLocale("cs").render());
assertEquals("A/B::Foo/Bar/Baz::NEXT::NEXT::NEXT", bar.render());
}

@TemplateEnum
public enum UnderscoredEnum {
A_B,
FOO_BAR_BAZ
}

@TemplateEnum
public enum AnotherUnderscoredEnum {
NEXT_B
}

@TemplateEnum
public enum UncommonEnum {
NEXT$B
}

@MessageBundle(value = "enu", locale = "en")
Expand Down Expand Up @@ -69,6 +93,22 @@ public interface Messages {
@Message
String locFileOverride(MyEnum myEnum);

// maps to underscored_$A_B, underscored_$FOO_BAR_BAZ
@Message
String underscored(UnderscoredEnum val);

// maps to underscored_foo_$NEXT_B
@Message
String underscored_foo(AnotherUnderscoredEnum val);

// maps to underscored$foo_$NEXT_B
@Message
String underscored$foo(AnotherUnderscoredEnum val);

// maps to uncommon_$NEXT$B
@Message
String uncommon(UncommonEnum val);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.quarkus.qute.deployment.i18n;

import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.qute.TemplateEnum;
import io.quarkus.qute.deployment.MessageBundleException;
import io.quarkus.qute.i18n.Message;
import io.quarkus.qute.i18n.MessageBundle;
import io.quarkus.test.QuarkusUnitTest;

public class MessageBundleInvalidEnumConstantTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addClasses(Messages.class, UnderscoredEnum.class)
.addAsResource("messages/enu_invalid.properties"))
.setExpectedException(MessageBundleException.class, true);

@Test
public void testMessages() {
fail();
}

@TemplateEnum
public enum UnderscoredEnum {

A_B,

}

@MessageBundle(value = "enu_invalid")
public interface Messages {

@Message
String underscored(UnderscoredEnum constants);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,13 @@ locFileOverride={#when myEnum}\
{#is ON}on\
{#is OFF}off\
{#else}undefined\
{/when}
{/when}

underscored_$A_B=A/B
underscored_$FOO_BAR_BAZ=Foo/Bar/Baz

underscored_foo_$NEXT_B=NEXT

underscored$foo_$NEXT_B=NEXT

uncommon_$NEXT$B=NEXT
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,13 @@ locFileOverride={#when myEnum}\
{#is ON}zap\
{#is OFF}vyp\
{#else}nedef\
{/when}
{/when}

underscored_$A_B=A/B
underscored_$FOO_BAR_BAZ=Foo/Bar/Baz

underscored_foo_$NEXT_B=NEXT

underscored$foo_$NEXT_B=NEXT

uncommon_$NEXT$B=NEXT
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
underscored_$A_B=A/B
underscored_$FOO_BAR_BAZ=Foo/Bar/Baz
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* There is a convenient way to localize enums.
* <p>
* If there is a message bundle method that accepts a single parameter of an enum type and has no message template defined then
* it receives a generated template:
* it receives a generated template like:
*
* <pre>
* {#when enumParamName}
Expand All @@ -37,14 +37,20 @@
* </pre>
*
* Furthermore, a special message method is generated for each enum constant. Finally, each localized file must contain keys and
* values for all constant message keys:
* values for all enum constants.
*
* <pre>
* methodName_CONSTANT1=Value 1
* methodName_CONSTANT2=Value 2
* </pre>
*
* In a template, an enum constant can be localized with a message bundle method {@code msg:methodName(enumConstant)}.
* By default, the message key consists of the method name followed by the {@code _} separator and the constant name. If any
* constant name of a particular enum contains the {@code _} or the {@code $} character then the {@code _$} separator must be
* used for all message keys for this enum instead. For example, {@code methodName_$CONSTANT_1=Value 1} or
* {@code methodName_$CONSTANT$1=Value 1}.
* </p>
* In a template, the localized message for an enum constant can be obtained with a message bundle method like
* {@code msg:methodName(enumConstant)}.
*
* @see MessageBundle
*/
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/oidc-wiremock/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.smallrye.certs</groupId>
<artifactId>smallrye-certificate-generator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public Uni<OidcTenantConfig> resolve(RoutingContext context,
} else if (path.endsWith("bearer-certificate-full-chain-root-only")) {
OidcTenantConfig config = new OidcTenantConfig();
config.setTenantId("bearer-certificate-full-chain-root-only");
config.getCertificateChain().setTrustStoreFile(Path.of("truststore-rootcert.p12"));
config.getCertificateChain().setTrustStoreFile(Path.of("target/chain/truststore-rootcert.p12"));
config.getCertificateChain().setTrustStorePassword("storepassword");
config.getCertificateChain().setLeafCertificateName("www.quarkustest.com");
return Uni.createFrom().item(config);
Expand Down
Loading
Loading