Skip to content

Commit

Permalink
feat: "default" and "description" columns added to configuration prop…
Browse files Browse the repository at this point in the history
…erties table in About page (#191)

Refs: #190
  • Loading branch information
grigoriev authored Oct 18, 2024
1 parent c3e6dcf commit 94daaba
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package ch.sbb.polarion.extension.generic.properties;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Properties;

public class ExtendedProperties extends Properties {

private final HashMap<Object, String> defaultValues = new HashMap<>();
private final HashMap<Object, String> descriptions = new HashMap<>();

public ExtendedProperties() {
super();
}

public ExtendedProperties(int size) {
super(size);
}

@Override
public synchronized Object setProperty(@NotNull String key, @NotNull String value) {
return this.setProperty(key, value, null, null);
}

public synchronized Object setProperty(@NotNull String key, @NotNull String value, @Nullable String defaultValue, @Nullable String description) {
return this.put(key, value, defaultValue, description);
}

public synchronized Object put(@NotNull Object key, @NotNull Object value, @Nullable String defaultValue, @Nullable String description) {
defaultValues.put(key, defaultValue);
descriptions.put(key, description);
return super.put(key, value);
}

public synchronized @Nullable String getDescription(@NotNull Object key) {
return descriptions.get(key);
}

public synchronized @Nullable String getDefaultValue(@NotNull Object key) {
return defaultValues.get(key);
}

@Override
public synchronized Object put(@NotNull Object key, @NotNull Object value) {
return this.put(key, value, null, null);
}

@Override
public synchronized boolean equals(Object o) {
return super.equals(o);
}

@Override
public synchronized int hashCode() {
return super.hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,52 @@
import com.polarion.core.config.impl.SystemValueReader;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Properties;

@Getter
public class ExtensionConfiguration implements IExtensionConfiguration {

private static final String DEBUG = "debug";
public static final boolean DEBUG_DEFAULT_VALUE = false;
private static final String DEBUG_DESCRIPTION = "Enable debug mode for current extension";

@NotNull
protected final String propertyPrefix;

@Override
@SuppressWarnings("unused")
public boolean isDebug() {
return SystemValueReader.getInstance().readBoolean(propertyPrefix + DEBUG, false);
return SystemValueReader.getInstance().readBoolean(propertyPrefix + DEBUG, DEBUG_DEFAULT_VALUE);
}

@SuppressWarnings("unused")
public String getDebugDescription() {
return DEBUG_DESCRIPTION;
}

@SuppressWarnings("unused")
public Boolean getDebugDefault() {
return DEBUG_DEFAULT_VALUE;
}

protected ExtensionConfiguration() {
this.propertyPrefix = ContextUtils.getConfigurationPropertiesPrefix();
}

@Override
public final @NotNull Properties getProperties() {
public final @NotNull ExtendedProperties getProperties() {
List<String> supportedProperties = getSupportedProperties();
Properties properties = new Properties(supportedProperties.size());
ExtendedProperties properties = new ExtendedProperties(supportedProperties.size());
ExtensionConfiguration configuration = CurrentExtensionConfiguration.getInstance().getExtensionConfiguration();

for (String supportedProperty : supportedProperties) {
String key = getPropertyPrefix() + supportedProperty;
String value = GetterFinder.getValue(configuration, supportedProperty);
@NotNull String key = getPropertyPrefix() + supportedProperty;
@Nullable String value = GetterFinder.getValue(configuration, supportedProperty);
@Nullable String defaultValue = GetterFinder.getDefaultValue(configuration, supportedProperty);
@Nullable String description = GetterFinder.getDescription(configuration, supportedProperty);
if (value != null) {
properties.setProperty(key, value);
properties.setProperty(key, value, defaultValue, description);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,42 @@
@UtilityClass
public class GetterFinder {

@SuppressWarnings({"squid:S1166", "findbugs:REC_CATCH_EXCEPTION"}) // no need to log or rethrow exception by design, findbugs wrongly assumes that getClass().getMethod() won't throw exceptions
public static @Nullable <T extends ExtensionConfiguration> String getValue(@NotNull T extensionConfiguration, @NotNull String propertyName) {
String getterMethodName = toCamelCaseGetter(propertyName);
return invokeGetter(extensionConfiguration, propertyName, "");
}

try {
Method getter = extensionConfiguration.getClass().getMethod("get" + getterMethodName);
return String.valueOf(getter.invoke(extensionConfiguration));
} catch (Exception ignored) {
//ignore
public static @Nullable <T extends ExtensionConfiguration> String getDescription(@NotNull T extensionConfiguration, @NotNull String propertyName) {
return invokeGetter(extensionConfiguration, propertyName, "Description");
}

public static @Nullable <T extends ExtensionConfiguration> String getDefaultValue(@NotNull T extensionConfiguration, @NotNull String propertyName) {
return invokeGetter(extensionConfiguration, propertyName, "DefaultValue");
}

private static @Nullable <T extends ExtensionConfiguration> String invokeGetter(@NotNull T extensionConfiguration, @NotNull String propertyName, @NotNull String suffix) {
String getterMethodName = toCamelCaseGetter(propertyName) + suffix;

// try "get" prefix
String result = invokeMethod(extensionConfiguration, "get" + getterMethodName);
if (result != null) {
return result;
}

// try "is" prefix for boolean types
return invokeMethod(extensionConfiguration, "is" + getterMethodName);
}

@SuppressWarnings({"squid:S1166", "findbugs:REC_CATCH_EXCEPTION"}) // no need to log or rethrow exception by design, findbugs wrongly assumes that getClass().getMethod() won't throw exceptions
private static @Nullable <T extends ExtensionConfiguration> String invokeMethod(@NotNull T extensionConfiguration, @NotNull String methodName) {
try {
Method getter = extensionConfiguration.getClass().getMethod("is" + getterMethodName);
if (getter.getReturnType() == boolean.class) {
return String.valueOf(getter.invoke(extensionConfiguration));
Method getter = extensionConfiguration.getClass().getMethod(methodName);
if (methodName.startsWith("is") && getter.getReturnType() != boolean.class) {
return null;
}
return String.valueOf(getter.invoke(extensionConfiguration));
} catch (Exception ignored) {
//ignore
return null;
}

return null;
}

private static @NotNull String toCamelCaseGetter(@NotNull String propertyName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Properties;

public interface IExtensionConfiguration {

boolean isDebug();

@NotNull Properties getProperties();
@NotNull ExtendedProperties getProperties();

@NotNull List<String> getSupportedProperties();

Expand Down
29 changes: 17 additions & 12 deletions app/src/main/resources/META-INF/resources/common/jsp/about.jsp
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
<%@ page import="ch.sbb.polarion.extension.generic.configuration.ConfigurationStatus" %>
<%@ page import="ch.sbb.polarion.extension.generic.configuration.ConfigurationStatusProvider" %>
<%@ page import="ch.sbb.polarion.extension.generic.properties.CurrentExtensionConfiguration" %>
<%@ page import="ch.sbb.polarion.extension.generic.properties.ExtendedProperties" %>
<%@ page import="ch.sbb.polarion.extension.generic.rest.model.Context" %>
<%@ page import="ch.sbb.polarion.extension.generic.rest.model.Version" %>
<%@ page import="ch.sbb.polarion.extension.generic.util.ExtensionInfo" %>
<%@ page import="ch.sbb.polarion.extension.generic.util.VersionUtils" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.Properties" %>
<%@ page import="org.jetbrains.annotations.Nullable" %>
<%@ page import="java.io.InputStream" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Set" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="ch.sbb.polarion.extension.generic.rest.model.Context" %>
<%@ page import="ch.sbb.polarion.extension.generic.configuration.ConfigurationStatus" %>
<%@ page import="ch.sbb.polarion.extension.generic.configuration.ConfigurationStatusProvider" %>
<%@ page import="java.util.Collection" %>
<%@ page import="java.util.Collections" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.Set" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<%!
private static final String ABOUT_TABLE_ROW = "<tr><td>%s</td><td>%s</td></tr>";
private static final String CONFIGURATION_PROPERTIES_TABLE_ROW = "<tr><td>%s</td><td>%s</td></tr>";
private static final String CONFIGURATION_PROPERTIES_TABLE_ROW = "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>";
private static final String CHECK_CONFIGURATION_TABLE_ROW = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>";
Context context = ExtensionInfo.getInstance().getContext();
Version version = ExtensionInfo.getInstance().getVersion();
Properties properties = CurrentExtensionConfiguration.getInstance().getExtensionConfiguration().getProperties();
ExtendedProperties extensionConfigurationProperties = CurrentExtensionConfiguration.getInstance().getExtensionConfiguration().getProperties();
%>

<head>
Expand Down Expand Up @@ -72,20 +73,24 @@
<tr>
<th>Configuration property</th>
<th>Value</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<%
Set<Object> keySet = properties.keySet();
Set<Object> keySet = extensionConfigurationProperties.keySet();
List<String> propertyNames = new ArrayList<>();
for (Object key : keySet) {
propertyNames.add((String) key);
}
Collections.sort(propertyNames);
for (String key : propertyNames) {
String value = properties.getProperty(key);
String row = CONFIGURATION_PROPERTIES_TABLE_ROW.formatted(key, value);
@Nullable String value = extensionConfigurationProperties.getProperty(key);
@Nullable String defaultValue = extensionConfigurationProperties.getDefaultValue(key);
@Nullable String description = extensionConfigurationProperties.getDescription(key);
String row = CONFIGURATION_PROPERTIES_TABLE_ROW.formatted(key, value, defaultValue == null ? "" : defaultValue, description == null ? "" : description);
out.println(row);
}
%>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package ch.sbb.polarion.extension.generic.properties;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

class ExtendedPropertiesTest {

private ExtendedProperties props;

@BeforeEach
void setUp() {
props = new ExtendedProperties();
}

@Test
void testSetPropertyWithDescriptionAndDefaultValue() {
props.setProperty("username", "admin", "root", "User for admin tasks");

assertEquals("admin", props.getProperty("username"));
assertEquals("User for admin tasks", props.getDescription("username"));
assertEquals("root", props.getDefaultValue("username"));
}

@Test
void testSetPropertyWithoutDescriptionAndDefaultValue() {
props.setProperty("language", "EN");

assertEquals("EN", props.getProperty("language"));
assertNull(props.getDescription("language"));
assertNull(props.getDefaultValue("language"));
}

@Test
void testOverridePropertyKeepsNewDescriptionAndDefaultValue() {
props.setProperty("timeout", "30", "60", "Timeout in seconds");
props.setProperty("timeout", "45", "90", "Updated timeout description");

assertEquals("45", props.getProperty("timeout"));
assertEquals("Updated timeout description", props.getDescription("timeout"));
assertEquals("90", props.getDefaultValue("timeout"));
}

@Test
void testNullDescriptionAndDefaultValueHandledProperly() {
props.setProperty("color", "blue", null, null);

assertEquals("blue", props.getProperty("color"));
assertNull(props.getDescription("color"));
assertNull(props.getDefaultValue("color"));
}

@Test
void testPutMethodWithDescriptionAndDefaultValue() {
props.put("fontSize", "12", "10", "Size of font in points");

assertEquals("12", props.getProperty("fontSize"));
assertEquals("Size of font in points", props.getDescription("fontSize"));
assertEquals("10", props.getDefaultValue("fontSize"));
}

@Test
void testPutMethodWithoutDescriptionAndDefaultValue() {
props.put("theme", "dark");

assertEquals("dark", props.getProperty("theme"));
assertNull(props.getDescription("theme"));
assertNull(props.getDefaultValue("theme"));
}

@Test
void testOverrideWithNullValues() {
props.setProperty("path", "/home/user", "File path", "/root");
props.setProperty("path", "/home/new_user", null, null);

assertEquals("/home/new_user", props.getProperty("path"));
assertNull(props.getDescription("path"));
assertNull(props.getDefaultValue("path"));
}

}

0 comments on commit 94daaba

Please sign in to comment.