Skip to content

Commit

Permalink
fix: options mapping refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
Jumas committed Oct 2, 2024
1 parent a1fb311 commit ecd0054
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,17 @@
import ch.sbb.polarion.extension.generic.fields.model.FieldMetadata;
import ch.sbb.polarion.extension.generic.service.PolarionService;
import ch.sbb.polarion.extension.generic.util.EnumUtils;
import com.polarion.alm.shared.util.StringUtils;
import ch.sbb.polarion.extension.generic.util.OptionsMappingUtils;
import com.polarion.alm.tracker.model.IWorkItem;
import com.polarion.platform.persistence.IEnumOption;
import com.polarion.platform.persistence.IEnumeration;
import com.polarion.subterra.base.data.model.IEnumType;
import lombok.SneakyThrows;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.function.Supplier;

public class StringToEnumOptionConverter implements IConverter<String, IEnumOption> {

Expand All @@ -36,6 +33,7 @@ public String convertBack(@NotNull IEnumOption value, @NotNull ConverterContext
return value.getName();
}

@SneakyThrows
@Nullable
@SuppressWarnings({"squid:S5852", "squid:S3776"}) // regex is safe here, ignore cognitive complexity warning
private IEnumOption getEnumerationOptionForField(@NotNull FieldMetadata fieldMetadata, @NotNull String initialValue, @NotNull ConverterContext context) {
Expand All @@ -46,29 +44,16 @@ private IEnumOption getEnumerationOptionForField(@NotNull FieldMetadata fieldMet
if (FieldType.unwrapIfListType(fieldMetadata.getType()) instanceof IEnumType enumType) {//attempt to unwrap type coz this converter may be used from ListConverter
IEnumeration<IEnumOption> enumeration = new PolarionService().getEnumeration(enumType, context.getContextId());

Map<String, String> enumMapping = Optional.ofNullable(context.getEnumsMapping()) //mapping may be null
.orElse(new HashMap<>())
.getOrDefault(fieldMetadata.getId(), new HashMap<>())
.entrySet().stream()
.filter(entry -> !StringUtils.isEmptyTrimmed(entry.getValue())) //remove entries with null/empty/blank values
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

List<String> existingOptionIds = enumeration.getAllOptions().stream().map(EnumUtils::getEnumId).toList();
for (Map.Entry<String, String> entry : enumMapping.entrySet()) {
//enumMapping may contain non-actual data coz enum elements may be changed after enumMapping definition
//therefore we have to check whether this particular option still exists or not
if (existingOptionIds.contains(entry.getKey()) && Arrays.asList(StringUtils.getNotNull(entry.getValue()).trim().split("\\s*,\\s*")).contains(value) || entry.getKey().equals(value)) {
return enumeration.getAllOptions().stream()
.filter(o -> entry.getKey().equals(EnumUtils.getEnumId(o)))
.findFirst()
.orElseThrow();
}
// first we attempt to find the option using custom mapping
String mappingOptionKey = OptionsMappingUtils.getMappedOptionKey(fieldMetadata.getId(), value, context.getEnumsMapping());
if (mappingOptionKey != null) {
return enumeration.getAllOptions().stream().filter(o -> mappingOptionKey.equals(EnumUtils.getEnumId(o)))
.findFirst().orElseThrow((Supplier<Throwable>) () -> new IllegalArgumentException("Value %s mapped to unknown key %s".formatted(value, mappingOptionKey)));
}

try {
//the rest unmapped options will be processed using keys/values
List<IEnumOption> unmappedOptions = enumeration.getAllOptions().stream().filter(o -> !enumMapping.containsKey(EnumUtils.getEnumId(o))).toList();
return findEnumOption(value, enumType, unmappedOptions);
// as a last stand try to find the option using keys/values
return findEnumOption(value, enumType, enumeration);
} catch (EnumOptionNotFoundException e) {
if (value.isBlank()) {
return handleEmptyValue(fieldMetadata, enumType, enumeration.getAllOptions());
Expand Down Expand Up @@ -106,16 +91,16 @@ private IEnumOption findEnumDefaultValue(@NotNull IEnumType enumType, @NotNull L

@NotNull
@SuppressWarnings("squid:S1166") // no need to log or rethrow exception by design
private static IEnumOption findEnumOption(@NotNull String value, @NotNull IEnumType enumType, @NotNull List<IEnumOption> options) throws EnumOptionNotFoundException {
private static IEnumOption findEnumOption(@NotNull String value, @NotNull IEnumType enumType, @NotNull IEnumeration<IEnumOption> enumeration) throws EnumOptionNotFoundException {
try {
//first we try to find value by ID
return options.stream()
return enumeration.getAllOptions().stream()
.filter(option -> EnumUtils.getEnumId(option).equals(value))
.findFirst()
.orElseThrow(() -> new EnumOptionNotFoundByIdException(String.format("EnumOption with id '%s' not found", value)));
} catch (EnumOptionNotFoundByIdException e) {
//if nothing found then we peek first option by name ignore case
return options.stream()
// peek first option by name ignore case
return enumeration.getAllOptions().stream()
.filter(option -> option.getName().trim().equalsIgnoreCase(value)) // enum names in Polarion can have spaces at the end
.findFirst()
.orElseThrow(() -> new EnumOptionNotFoundException(String.format("Unsupported value '%s' for enum '%s'", value, enumType.getEnumerationId())));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ch.sbb.polarion.extension.generic.util;

import com.polarion.alm.shared.util.StringUtils;
import lombok.experimental.UtilityClass;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@UtilityClass
public class OptionsMappingUtils {

String EMPTY_VALUE = "(empty)";

@NotNull
public Map<String, String> getMappingForFieldId(String fieldId, Map<String, Map<String, String>> commonMapping) {
return Optional.ofNullable(commonMapping)
.orElse(new HashMap<>())
.getOrDefault(fieldId, new HashMap<>())
.entrySet().stream()
.filter(entry -> !StringUtils.isEmptyTrimmed(entry.getValue())) //remove entries with null/empty/blank values
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

@Nullable
public String getMappedOptionKey(String fieldId, @Nullable Object initialValue, @Nullable Map<String, Map<String, String>> commonMapping) {
if (initialValue == null || initialValue instanceof String) {
String value = StringUtils.isEmptyTrimmed((String) initialValue) ? EMPTY_VALUE : ((String) initialValue);
for (Map.Entry<String, String> entry : getMappingForFieldId(fieldId, commonMapping).entrySet()) {
if (Stream.of(StringUtils.getNotNull(entry.getValue()).split(",")).anyMatch(v -> StringUtils.areEqualTrimmed(v, value))) {
return entry.getKey();
}
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,6 @@ void testGetEnumOptionByMapping() {

assertEquals(new EnumOption(YES_NO_ENUM_ID, YES.left()), ConverterTestUtils.process("yes", context, fieldMetadata));

//default key/value option identification hidden by the implicit mapping
assertThrows(IllegalArgumentException.class,
() -> ConverterTestUtils.process("Ja", context, fieldMetadata),
"Unsupported value 'Ja' for enum ''");

//just an unknown option
assertThrows(IllegalArgumentException.class,
() -> ConverterTestUtils.process("Probably", context, fieldMetadata),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ch.sbb.polarion.extension.generic.util;

import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

class OptionsMappingUtilsTest {

@Test
void testGetMappingForFieldId() {
assertEquals(new HashMap<>(), OptionsMappingUtils.getMappingForFieldId("fieldId", null));
assertEquals(new HashMap<>(), OptionsMappingUtils.getMappingForFieldId("fieldId", Map.of()));
assertEquals(new HashMap<>(), OptionsMappingUtils.getMappingForFieldId("fieldId", Map.of("fieldId", new HashMap<>())));
assertEquals(Map.of("key3", "someValue"), OptionsMappingUtils.getMappingForFieldId("fieldId", Map.of("fieldId", Map.of("key1", "", "key2", " ", "key3", "someValue"))));
}

@Test
void testGetMappedOptionKey() {
assertNull(OptionsMappingUtils.getMappedOptionKey("field", null, null));
assertNull(OptionsMappingUtils.getMappedOptionKey("field", Boolean.TRUE, null));
assertNull(OptionsMappingUtils.getMappedOptionKey("field", 42, Map.of("field", Map.of("someKey", "42"))));
assertNull(OptionsMappingUtils.getMappedOptionKey("field", "", null));
assertNull(OptionsMappingUtils.getMappedOptionKey("field", " ", null));
assertNull(OptionsMappingUtils.getMappedOptionKey("field", " ", Map.of("field", Map.of())));
assertNull(OptionsMappingUtils.getMappedOptionKey("field", null, Map.of("field", Map.of("someKey", "someValue"))));
assertEquals("someKey", OptionsMappingUtils.getMappedOptionKey("field", "someValue", Map.of("field", Map.of("someKey", "someValue"))));
assertEquals("someKey", OptionsMappingUtils.getMappedOptionKey("field", " someValue ", Map.of("field", Map.of("someKey", "someValue"))));
assertEquals("someKey", OptionsMappingUtils.getMappedOptionKey("field", "someValue", Map.of("field", Map.of("someKey", " someValue"))));
assertNull(OptionsMappingUtils.getMappedOptionKey("field", "", Map.of("field", Map.of("someKey", ""))));
assertEquals("someKey", OptionsMappingUtils.getMappedOptionKey("field", "", Map.of("field", Map.of("someKey", "(empty),someValue"))));
assertEquals("someKey", OptionsMappingUtils.getMappedOptionKey("field", " ", Map.of("field", Map.of("someKey", "(empty) ,someValue"))));
assertEquals("someKey", OptionsMappingUtils.getMappedOptionKey("field", null, Map.of("field", Map.of("someKey", "(empty) ,someValue"))));
}

}

0 comments on commit ecd0054

Please sign in to comment.