Skip to content

Commit

Permalink
feat: remove Generex and implement string generation from regex (6060)
Browse files Browse the repository at this point in the history
[WIP] Generating string from RegEx
---
ported the Go implementation
---
more cleanup
---
fmt
---
minor simplification
---
add reference links
---
finalize and remove generex
---
changelog
---
Merge branch 'main' into string-from-regex
  • Loading branch information
andreaTP authored Jun 21, 2024
1 parent 195cf18 commit 497afaf
Show file tree
Hide file tree
Showing 9 changed files with 331 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Fix #6008: removing the optional dependency on bouncy castle

#### Dependency Upgrade
* Fix #6052: Removed dependency on no longer maintained com.github.mifmif:generex

#### New Features

Expand Down
11 changes: 5 additions & 6 deletions openshift-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,6 @@
<artifactId>openshift-client-api</artifactId>
</dependency>

<dependency>
<groupId>com.github.mifmif</groupId>
<artifactId>generex</artifactId>
<version>${generex.version}</version>
</dependency>

<!-- Compile Only Dependencies -->
<dependency>
<groupId>io.sundr</groupId>
Expand Down Expand Up @@ -126,6 +120,11 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2015 Red Hat, Inc.
*
* 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
*
* http://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 io.fabric8.openshift.client.dsl.internal.core;

final class CharRange {
private final char start;
private final char end;

public CharRange(char start, char end) {
this.start = start;
this.end = end;
}

public char start() {
return start;
}

public char end() {
return end;
}

public String rangeStr() {
return new String(new char[] { start, end });
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright (C) 2015 Red Hat, Inc.
*
* 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
*
* http://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 io.fabric8.openshift.client.dsl.internal.core;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

// from: https://github.com/openshift/library-go/blob/aed018c215a122871be1768155cf9f3e658278fc/pkg/template/generator/expressionvalue.go
public class ExpressionValueGenerator {
private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String NUMERALS = "0123456789";
private static final String SYMBOLS = "~!@#$%^&*()-_+={}[]\\|<,>.?/\"';:`";
private static final String ASCII = ALPHABET + NUMERALS + SYMBOLS;

private static final Pattern RANGE_EXP = Pattern.compile("([\\\\]?[a-zA-Z0-9]\\-?[a-zA-Z0-9]?)");
private static final Pattern GENERATORS_EXP = Pattern.compile("\\[([a-zA-Z0-9\\-\\\\]+)\\](\\{([0-9]+)\\})");
private static final Pattern EXPRESSION_EXP = Pattern.compile("\\[(\\\\w|\\\\d|\\\\a|\\\\A)|([a-zA-Z0-9]\\-[a-zA-Z0-9])+\\]");

private final Random random;

public ExpressionValueGenerator(Random random) {
this.random = random;
}

public String generateValue(final String expression) {
String result = expression;
Matcher matcher = GENERATORS_EXP.matcher(result);
while (matcher.find()) {
String matched = result.substring(matcher.start(), matcher.end());
String ranges = getRange(matched);
int length = Integer.parseInt(getLength(matched));

result = replaceWithGenerated(
result,
matched,
findExpressionPos(ranges),
length,
random);
matcher = GENERATORS_EXP.matcher(result);
}
return result;
}

private static String alphabetSlice(char from, char to) {
int leftPos = ASCII.indexOf(from);
int rightPos = ASCII.lastIndexOf(to) + 1;
if (leftPos > rightPos) {
throw new IllegalArgumentException("invalid range specified: " + from + "-" + to);
}
return ASCII.substring(leftPos, rightPos);
}

private static String replaceWithGenerated(String s, String expression, List<CharRange> ranges, int length, Random random) {
StringBuilder alphabet = new StringBuilder();
for (CharRange r : ranges) {
switch (r.rangeStr()) {
case "\\w":
alphabet.append(ALPHABET).append(NUMERALS).append("_");
break;
case "\\d":
alphabet.append(NUMERALS);
break;
case "\\a":
alphabet.append(ALPHABET).append(NUMERALS);
break;
case "\\A":
alphabet.append(SYMBOLS);
break;
default:
alphabet.append(alphabetSlice(r.start(), r.end()));
break;
}
}
String alphabetStr = removeDuplicateChars(alphabet.toString());
StringBuilder result = new StringBuilder(length);
for (int i = 0; i < length; i++) {
result.append(alphabetStr.charAt(random.nextInt(alphabetStr.length())));
}
return s.replace(expression, result.toString());
}

protected static String removeDuplicateChars(String input) {
return input.chars()
.distinct()
.mapToObj(c -> String.valueOf((char) c))
.collect(Collectors.joining());
}

private static List<CharRange> findExpressionPos(String s) {
Matcher matcher = RANGE_EXP.matcher(s);
List<CharRange> result = new ArrayList<>();
while (matcher.find()) {
result.add(new CharRange(s.charAt(matcher.start()), s.charAt(matcher.end() - 1)));
}
return result;
}

private static String getRange(String s) {
int lastOpenCurly = s.lastIndexOf("{");
String expr = s.substring(0, lastOpenCurly);
if (!EXPRESSION_EXP.matcher(expr).find()) {
throw new IllegalArgumentException("malformed expression syntax: " + expr);
}
return expr;
}

private static String getLength(String s) {
int lastOpenCurly = s.lastIndexOf("{");
String lengthStr = s.substring(lastOpenCurly + 1, s.length() - 1);
int length = Integer.parseInt(lengthStr);
if (length > 0 && length <= 255) {
return lengthStr;
}
throw new IllegalArgumentException("invalid range: must be within [1-255] characters (" + length + ")");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package io.fabric8.openshift.client.dsl.internal.core;

import com.fasterxml.jackson.core.type.TypeReference;
import com.mifmif.common.regex.Generex;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.KubernetesList;
import io.fabric8.kubernetes.api.model.KubernetesListBuilder;
Expand Down Expand Up @@ -50,6 +49,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;

import static io.fabric8.openshift.client.OpenShiftAPIGroups.TEMPLATE;

Expand Down Expand Up @@ -185,8 +185,8 @@ public KubernetesList processLocally(Map<String, String> valuesMap) {
} else if (Utils.isNotNullOrEmpty(parameter.getValue())) {
parameterValue = parameter.getValue();
} else if (EXPRESSION.equals(parameter.getGenerate())) {
Generex generex = new Generex(parameter.getFrom());
parameterValue = generex.random();
ExpressionValueGenerator valueGenerator = new ExpressionValueGenerator(ThreadLocalRandom.current());
parameterValue = valueGenerator.generateValue(parameter.getFrom());
} else if (parameter.getRequired() == null || !parameter.getRequired()) {
parameterValue = "";
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* Copyright (C) 2015 Red Hat, Inc.
*
* 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
*
* http://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 io.fabric8.openshift.client.dsl.internal.core;

import org.junit.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;

import java.util.Random;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

// from: https://github.com/openshift/library-go/blob/aed018c215a122871be1768155cf9f3e658278fc/pkg/template/generator/expressionvalue_test.go
public class ExpressionValueGeneratorTest {

static class ExpressionValuesArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
return Stream.of(
Arguments.of("test[0-9]{1}x", "test6x"),
Arguments.of("[0-1]{8}", "11100011"),
Arguments.of("0x[A-F0-9]{4}", "0x545A"),
Arguments.of("[a-zA-Z0-9]{8}", "KaP00gmR"),
Arguments.of("test[A-Z0-9]{4}template", "testQU7Etemplate"),
Arguments.of("[\\d]{3}", "645"),
Arguments.of("[\\w]{20}", "0V86t3tosHvHdzUwQKTB"),
Arguments.of("[\\a]{10}", "KaP00gmRS2"),
Arguments.of("[\\A]{10}", ">|>~-{'>,]"),
Arguments.of("strongPassword[\\w]{3}[\\A]{3}", "strongPassword0V8~-{"),
Arguments.of("admin[0-9]{2}[A-Z]{2}", "admin64DK"),
Arguments.of("admin[0-9]{2}test[A-Z]{2}", "admin64testDK"));
}
}

@ParameterizedTest
@ArgumentsSource(ExpressionValuesArgumentsProvider.class)
public void generateStringFromRegEx(String input, String expected) {
// Arrange
Random random = new Random();
random.setSeed(7);
ExpressionValueGenerator generator = new ExpressionValueGenerator(random);

// Act
String result = generator.generateValue(input);

// Assert
assertEquals(expected, result);
}

static class DuplicatesArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {
return Stream.of(
Arguments.of("abcdefgh", "abcdefgh"),
Arguments.of("abcabc", "abc"),
Arguments.of("1111111", "1"),
Arguments.of("1234567890", "1234567890"),
Arguments.of("test@@", "tes@"));
}
}

@ParameterizedTest
@ArgumentsSource(DuplicatesArgumentsProvider.class)
public void removeDuplicatedCharacters(String input, String expected) {
// Arrange
ExpressionValueGenerator generator = new ExpressionValueGenerator(new Random());

// Act
String result = ExpressionValueGenerator.removeDuplicateChars(input);

// Assert
assertEquals(expected, result);
}

@Test
public void maformedSyntax() {
// Arrange
ExpressionValueGenerator generator = new ExpressionValueGenerator(new Random());

// Act
IllegalArgumentException result = assertThrows(IllegalArgumentException.class,
() -> generator.generateValue("[ABC]{3}"));

// Assert
assertTrue(result.getMessage().contains("malformed"));
assertTrue(result.getMessage().contains("syntax"));
}

@Test
public void invalidRange() {
// Arrange
ExpressionValueGenerator generator = new ExpressionValueGenerator(new Random());

// Act
IllegalArgumentException result = assertThrows(IllegalArgumentException.class,
() -> generator.generateValue("[Z-A]{3}"));

// Assert
assertTrue(result.getMessage().contains("invalid"));
assertTrue(result.getMessage().contains("range"));
}

@Test
public void rangeOutOfBound() {
// Arrange
ExpressionValueGenerator generator = new ExpressionValueGenerator(new Random());

// Act
IllegalArgumentException result = assertThrows(IllegalArgumentException.class,
() -> generator.generateValue("[A-Z]{300}"));

// Assert
assertTrue(result.getMessage().contains("invalid"));
assertTrue(result.getMessage().contains("range"));
}

@Test
public void zeroRange() {
// Arrange
ExpressionValueGenerator generator = new ExpressionValueGenerator(new Random());

// Act
IllegalArgumentException result = assertThrows(IllegalArgumentException.class,
() -> generator.generateValue("[A-Z]{0}"));

// Assert
assertTrue(result.getMessage().contains("invalid"));
assertTrue(result.getMessage().contains("range"));
}
}
1 change: 0 additions & 1 deletion platforms/karaf/features/src/main/resources/feature.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
<bundle dependency='true'>mvn:com.fasterxml.jackson.datatype/jackson-datatype-jsr310/${jackson.bundle.version}</bundle>
<bundle dependency='true'>mvn:org.yaml/snakeyaml/${snakeyaml.bundle.version}</bundle>
<bundle dependency='true'>mvn:org.snakeyaml/snakeyaml-engine/${snakeyaml.version}</bundle>
<bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.generex/${generex.bundle.version}</bundle>
<bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.automaton/${automaton.bundle.version}</bundle>
<bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.conscrypt-openjdk/${conscrypt-openjdk-uber.bundle.version}</bundle>
<bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.okhttp/${okhttp.bundle.version}</bundle>
Expand Down
Loading

0 comments on commit 497afaf

Please sign in to comment.