Remove RSQL oracle as not used anymore (UI leftover) (#2397)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.rsql;
|
||||
|
||||
/**
|
||||
* An interface declaration which validates an RSQL based query syntax and
|
||||
* allows providing suggestions e.g. in case of syntax errors or current cursor
|
||||
* position.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RsqlValidationOracle {
|
||||
|
||||
/**
|
||||
* Parses and validates an given RSQL based query syntax and provides
|
||||
* suggestion based on syntax error and cursor positioning.
|
||||
*
|
||||
* @param rsqlQuery an RSQL based query string to parse.
|
||||
* @param cursorPosition the position of the cursor to retrieve suggestions at the
|
||||
* position. {@code -1} indicates for no cursor suggestion
|
||||
* @return a validation oracle context providing information about syntax
|
||||
* errors and possible suggestions for fixing the syntax error or at
|
||||
* the cursor position to replace tokens
|
||||
*/
|
||||
ValidationOracleContext suggest(final String rsqlQuery, final int cursorPosition);
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.rsql;
|
||||
|
||||
/**
|
||||
* A suggestion which contains the start and the end character position of the
|
||||
* suggested token of the suggestion of the token and the actual suggestion.
|
||||
*/
|
||||
public class SuggestToken {
|
||||
|
||||
private final int start;
|
||||
private final int end;
|
||||
private final String suggestion;
|
||||
private final String tokenImageName;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param start the character position of the start of the token
|
||||
* @param end the character position of the end of the token
|
||||
* @param tokenImageName the entered name of the token, e.g. could be the beginning of
|
||||
* the suggestion like 'na' or 'name'
|
||||
* @param suggestion the token suggestion
|
||||
*/
|
||||
public SuggestToken(final int start, final int end, final String tokenImageName, final String suggestion) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.tokenImageName = tokenImageName;
|
||||
this.suggestion = suggestion;
|
||||
}
|
||||
|
||||
public int getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
public int getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
public String getSuggestion() {
|
||||
return suggestion;
|
||||
}
|
||||
|
||||
public String getTokenImageName() {
|
||||
return tokenImageName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SuggestToken [start=" + start + ", end=" + end + ", suggestion=" + suggestion + ", tokenImageName="
|
||||
+ tokenImageName + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.rsql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The context which holds suggestions for the current cursor position.
|
||||
*/
|
||||
public class SuggestionContext {
|
||||
|
||||
private String rsqlQuery;
|
||||
private int cursorPosition;
|
||||
private List<SuggestToken> suggestions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public SuggestionContext() {
|
||||
// nothing to initialize
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param rsqlQuery the original RSQL based query the suggestions based on
|
||||
* @param cursorPosition the current cursor position
|
||||
* @param suggestions the suggestions for the current cursor position
|
||||
*/
|
||||
public SuggestionContext(final String rsqlQuery, final int cursorPosition, final List<SuggestToken> suggestions) {
|
||||
this.rsqlQuery = rsqlQuery;
|
||||
this.cursorPosition = cursorPosition;
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
public List<SuggestToken> getSuggestions() {
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
public void setSuggestions(final List<SuggestToken> suggestions) {
|
||||
this.suggestions = suggestions;
|
||||
}
|
||||
|
||||
public int getCursorPosition() {
|
||||
return cursorPosition;
|
||||
}
|
||||
|
||||
public void setCursorPosition(final int cursorPosition) {
|
||||
this.cursorPosition = cursorPosition;
|
||||
}
|
||||
|
||||
public String getRsqlQuery() {
|
||||
return rsqlQuery;
|
||||
}
|
||||
|
||||
public void setRsqlQuery(final String rsqlQuery) {
|
||||
this.rsqlQuery = rsqlQuery;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.rsql;
|
||||
|
||||
/**
|
||||
* An syntax error context object which holds the character position of the
|
||||
* syntax error and message.
|
||||
*/
|
||||
public class SyntaxErrorContext {
|
||||
|
||||
private int characterPosition = -1;
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public SyntaxErrorContext() {
|
||||
// nothing to initialize
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param characterPosition the position of the character within the RSQL query string the
|
||||
* error occurs.
|
||||
* @param errorMessage the error message with further information
|
||||
*/
|
||||
public SyntaxErrorContext(final int characterPosition, final String errorMessage) {
|
||||
this.characterPosition = characterPosition;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public int getCharacterPosition() {
|
||||
return characterPosition;
|
||||
}
|
||||
|
||||
public void setCharacterPosition(final int characterPosition) {
|
||||
this.characterPosition = characterPosition;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public void setErrorMessage(final String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.rsql;
|
||||
|
||||
/**
|
||||
* A context object which contains information about validation and suggestions
|
||||
* of a parsed RSQL query.
|
||||
*/
|
||||
public class ValidationOracleContext {
|
||||
|
||||
private boolean syntaxError;
|
||||
|
||||
private SuggestionContext suggestionContext;
|
||||
|
||||
private SyntaxErrorContext syntaxErrorContext;
|
||||
|
||||
public boolean isSyntaxError() {
|
||||
return syntaxError;
|
||||
}
|
||||
|
||||
public void setSyntaxError(final boolean syntaxError) {
|
||||
this.syntaxError = syntaxError;
|
||||
}
|
||||
|
||||
public SuggestionContext getSuggestionContext() {
|
||||
return suggestionContext;
|
||||
}
|
||||
|
||||
public void setSuggestionContext(final SuggestionContext suggestionContext) {
|
||||
this.suggestionContext = suggestionContext;
|
||||
}
|
||||
|
||||
public SyntaxErrorContext getSyntaxErrorContext() {
|
||||
return syntaxErrorContext;
|
||||
}
|
||||
|
||||
public void setSyntaxErrorContext(final SyntaxErrorContext syntaxErrorContext) {
|
||||
this.syntaxErrorContext = syntaxErrorContext;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,6 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRollou
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition;
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.DefaultRsqlVisitorFactory;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
@@ -160,7 +159,6 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlConfigHolder;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlVisitorFactory;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
@@ -340,12 +338,6 @@ public class RepositoryApplicationConfiguration {
|
||||
return new AfterTransactionCommitDefaultServiceExecutor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RsqlValidationOracle rsqlValidationOracle() {
|
||||
return new RsqlParserValidationOracle();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
QuotaManagement staticQuotaManagement(final HawkbitSecurityProperties securityProperties) {
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.rsql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cz.jirutka.rsql.parser.ParseException;
|
||||
import cz.jirutka.rsql.parser.RSQLParserException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.ParseExceptionWrapper.TokenWrapper;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
|
||||
import org.eclipse.hawkbit.repository.rsql.SuggestToken;
|
||||
import org.eclipse.hawkbit.repository.rsql.SuggestionContext;
|
||||
import org.eclipse.hawkbit.repository.rsql.SyntaxErrorContext;
|
||||
import org.eclipse.hawkbit.repository.rsql.ValidationOracleContext;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* An implementation of {@link RsqlValidationOracle} which retrieves the
|
||||
* exception using the {@link ParseException} to retrieve the suggestions.
|
||||
*
|
||||
* The suggestion only works when there are syntax errors existing because the
|
||||
* information about current and next tokens in the RSQL syntax are from the
|
||||
* {@link ParseException}.
|
||||
*
|
||||
* There is a feature request on the GitHub project
|
||||
* <a href="https://github.com/jirutka/rsql-parser/issues/22">https://github.com
|
||||
* /jirutka/rsql-parser/issues/22</a>
|
||||
*/
|
||||
@Slf4j
|
||||
public class RsqlParserValidationOracle implements RsqlValidationOracle {
|
||||
|
||||
@SuppressWarnings("java:S1872") // intentionally don't use class but name - class could be unavailable
|
||||
@Override
|
||||
public ValidationOracleContext suggest(final String rsqlQuery, final int cursorPosition) {
|
||||
final List<SuggestToken> expectedTokens = new ArrayList<>();
|
||||
final ValidationOracleContext context = new ValidationOracleContext();
|
||||
context.setSyntaxError(true);
|
||||
final SuggestionContext suggestionContext = new SuggestionContext();
|
||||
context.setSuggestionContext(suggestionContext);
|
||||
final SyntaxErrorContext errorContext = new SyntaxErrorContext();
|
||||
context.setSyntaxErrorContext(errorContext);
|
||||
|
||||
try {
|
||||
RSQLUtility.validateRsqlFor(rsqlQuery, TargetFields.class);
|
||||
context.setSyntaxError(false);
|
||||
suggestionContext.getSuggestions().addAll(getLogicalOperatorSuggestion(rsqlQuery));
|
||||
} catch (final RSQLParameterSyntaxException | RSQLParserException ex) {
|
||||
setExceptionDetails(rsqlQuery, new Exception(ex.getCause().getCause()), expectedTokens);
|
||||
errorContext.setErrorMessage(getCustomMessage(ex.getCause().getMessage(), expectedTokens));
|
||||
suggestionContext.setSuggestions(expectedTokens);
|
||||
log.trace("Syntax exception on parsing :", ex);
|
||||
} catch (final RSQLParameterUnsupportedFieldException | IllegalArgumentException ex) {
|
||||
errorContext.setErrorMessage(getCustomMessage(ex.getMessage(), null));
|
||||
log.trace("Illegal argument on parsing :", ex);
|
||||
} catch (final JpaSystemException e) {
|
||||
// noop
|
||||
} catch (final RuntimeException e) {
|
||||
if (!"org.eclipse.persistence.exceptions.ConversionException".equals(e.getClass().getName())) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private static Collection<? extends SuggestToken> getLogicalOperatorSuggestion(final String rsqlQuery) {
|
||||
if (!rsqlQuery.endsWith(" ")) {
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
final int currentQueryLength = rsqlQuery.length();
|
||||
// only return and/or suggestion when there is a space at the end
|
||||
final Collection<String> tokenImages = TokenDescription.getTokenImage(TokenDescription.LOGICAL_OP);
|
||||
final List<SuggestToken> logicalOps = new ArrayList<>(tokenImages.size());
|
||||
for (final String tokenImage : tokenImages) {
|
||||
logicalOps.add(
|
||||
new SuggestToken(currentQueryLength, currentQueryLength + tokenImage.length(), null, tokenImage));
|
||||
}
|
||||
return logicalOps;
|
||||
}
|
||||
}
|
||||
|
||||
private static void setExceptionDetails(final String rsqlQuery, final Exception ex,
|
||||
final List<SuggestToken> expectedTokens) {
|
||||
final ParseException parseException = findParseException(ex);
|
||||
if (parseException == null) {
|
||||
expectedTokens.addAll(getComparatorOperatorSuggestions(rsqlQuery));
|
||||
} else {
|
||||
expectedTokens.addAll(getNextTokens(parseException));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<SuggestToken> getNextTokens(final ParseException parseException) {
|
||||
final List<SuggestToken> listTokens = new ArrayList<>();
|
||||
final ParseExceptionWrapper parseExceptionWrapper = new ParseExceptionWrapper(parseException);
|
||||
final int[][] expectedTokenSequence = parseException.expectedTokenSequences;
|
||||
final TokenWrapper currentToken = parseExceptionWrapper.getCurrentToken();
|
||||
if (currentToken == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final TokenWrapper nextToken = currentToken.getNext();
|
||||
final int currentTokenKind = currentToken.getKind();
|
||||
final String currentTokenImageName = currentToken.getImage();
|
||||
final int nextTokenBeginColumn = nextToken.getBeginColumn();
|
||||
final int currentTokenEndColumn = currentToken.getEndColumn();
|
||||
|
||||
// token == 5 is the field token, reverse engineering.
|
||||
if (currentTokenKind == 5) {
|
||||
final Optional<List<SuggestToken>> handleFieldTokenSuggestion = handleFieldTokenSuggestion(
|
||||
currentTokenImageName, nextTokenBeginColumn, currentTokenEndColumn);
|
||||
if (handleFieldTokenSuggestion.isPresent()) {
|
||||
return handleFieldTokenSuggestion.get();
|
||||
}
|
||||
}
|
||||
|
||||
for (final int[] is : expectedTokenSequence) {
|
||||
addSuggestionOnTokenImage(listTokens, nextTokenBeginColumn, currentTokenEndColumn, is);
|
||||
}
|
||||
return listTokens;
|
||||
}
|
||||
|
||||
private static List<SuggestToken> getComparatorOperatorSuggestions(final String rsqlQuery) {
|
||||
// only return comparator operators suggestions when there is a '=' or
|
||||
// '!' symbol at the end
|
||||
final String mapKeyOperatorPattern = "(\\w+)\\.\\w+[=!]{1}$";
|
||||
final Matcher mapKeyOperatorMatcher = Pattern.compile(mapKeyOperatorPattern).matcher(rsqlQuery);
|
||||
|
||||
if (mapKeyOperatorMatcher.find() && FieldNameDescription.isMap(mapKeyOperatorMatcher.group(1))) {
|
||||
final int currentQueryLength = rsqlQuery.length() - 1;
|
||||
final Collection<String> tokenImages = TokenDescription.getTokenImage(TokenDescription.COMPARATOR);
|
||||
return tokenImages.stream().map(tokenImage -> new SuggestToken(currentQueryLength,
|
||||
currentQueryLength + tokenImage.length(), null, tokenImage)).toList();
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static void addSuggestionOnTokenImage(final List<SuggestToken> listTokens, final int nextTokenBeginColumn,
|
||||
final int currentTokenEndColumn, final int[] is) {
|
||||
for (final int i : is) {
|
||||
final Collection<String> tokenImage = TokenDescription.getTokenImage(i);
|
||||
if (!CollectionUtils.isEmpty(tokenImage)) {
|
||||
tokenImage.forEach(image -> listTokens.add(new SuggestToken(currentTokenEndColumn + 1,
|
||||
nextTokenBeginColumn + image.length(), null, image)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Optional<List<SuggestToken>> handleFieldTokenSuggestion(final String currentTokenImageName,
|
||||
final int nextTokenBeginColumn, final int currentTokenEndColumn) {
|
||||
final boolean containsDot = currentTokenImageName.indexOf('.') != -1;
|
||||
if (shouldSuggestTopLevelFieldNames(currentTokenImageName, containsDot)) {
|
||||
return Optional
|
||||
.of(FieldNameDescription.toTopSuggestToken(nextTokenBeginColumn - currentTokenImageName.length(),
|
||||
nextTokenBeginColumn + currentTokenImageName.length(), currentTokenImageName));
|
||||
} else if (shouldSuggestDotToken(currentTokenImageName, containsDot)) {
|
||||
return Optional
|
||||
.of(Arrays.asList(new SuggestToken(currentTokenEndColumn, nextTokenBeginColumn + 1, null, ".")));
|
||||
} else if (shouldSuggestSubTokenFieldNames(currentTokenImageName, containsDot)) {
|
||||
return handleSubtokenSuggestion(currentTokenImageName, nextTokenBeginColumn);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static boolean shouldSuggestSubTokenFieldNames(final String currentTokenImageName,
|
||||
final boolean containsDot) {
|
||||
return containsDot && !FieldNameDescription.containsValue(currentTokenImageName);
|
||||
}
|
||||
|
||||
private static boolean shouldSuggestDotToken(final String currentTokenImageName, final boolean containsDot) {
|
||||
return !containsDot && (FieldNameDescription.hasSubEntries(currentTokenImageName)
|
||||
|| FieldNameDescription.isMap(currentTokenImageName));
|
||||
}
|
||||
|
||||
private static boolean shouldSuggestTopLevelFieldNames(final String currentTokenImageName,
|
||||
final boolean containsDot) {
|
||||
return !containsDot && !FieldNameDescription.containsValue(currentTokenImageName)
|
||||
&& !FieldNameDescription.hasSubEntries(currentTokenImageName);
|
||||
}
|
||||
|
||||
private static Optional<List<SuggestToken>> handleSubtokenSuggestion(final String currentTokenImageName,
|
||||
final int nextTokenBeginColumn) {
|
||||
final String[] split = currentTokenImageName.split("\\.");
|
||||
for (final String string : split) {
|
||||
if (FieldNameDescription.containsValue(string)) {
|
||||
final String subTokenImage = split.length > 1 ? split[1] : null;
|
||||
final int subTokenBegin = nextTokenBeginColumn + currentTokenImageName.indexOf('.') + 1;
|
||||
return Optional.of(FieldNameDescription.toSubSuggestToken(subTokenBegin, subTokenBegin + 1, string,
|
||||
subTokenImage));
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static ParseException findParseException(final Throwable e) {
|
||||
if (e instanceof ParseException parseException) {
|
||||
return parseException;
|
||||
} else if (e.getCause() != null) {
|
||||
return findParseException(e.getCause());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String getCustomMessage(final String message, final List<SuggestToken> expectedTokens) {
|
||||
String builder = message;
|
||||
|
||||
if (!message.contains(":")) {
|
||||
return builder;
|
||||
}
|
||||
|
||||
builder = message.substring(message.indexOf(':') + 1);
|
||||
if (builder.contains("Was expecting")) {
|
||||
builder = builder.substring(0, builder.lastIndexOf("Was expecting"));
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(expectedTokens)) {
|
||||
final StringBuilder tokens = new StringBuilder();
|
||||
expectedTokens.stream().forEach(value -> tokens.append(value.getSuggestion()).append(","));
|
||||
builder = builder.concat("Was expecting :" + tokens.substring(0, tokens.length() - 1));
|
||||
}
|
||||
builder = builder.replace('\r', ' ');
|
||||
builder = builder.replace('\n', ' ');
|
||||
builder = builder.replace(">", " ");
|
||||
builder = builder.replace("<", " ");
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
// Token map with logical and comparator operator that are used for context
|
||||
// sensitive help on search query.
|
||||
private static final class TokenDescription {
|
||||
|
||||
private static final Map<Integer, List<String>> TOKEN_MAP = new HashMap<>();
|
||||
|
||||
private static final int LOGICAL_OP = 8;
|
||||
private static final int COMPARATOR = 12;
|
||||
|
||||
static {
|
||||
TOKEN_MAP.put(LOGICAL_OP, List.of("and", "or"));
|
||||
TOKEN_MAP.put(COMPARATOR, List.of("==", "!=", "=ge=", "=le=", "=gt=", "=lt=", "=in=", "=out="));
|
||||
}
|
||||
|
||||
private TokenDescription() {
|
||||
|
||||
}
|
||||
|
||||
private static Collection<String> getTokenImage(final int tokenIndex) {
|
||||
return TOKEN_MAP.get(tokenIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FieldNameDescription {
|
||||
|
||||
private static final Set<String> FIELD_NAMES = Arrays.stream(TargetFields.values())
|
||||
.map(field -> field.toString().toLowerCase()).collect(Collectors.toSet());
|
||||
|
||||
private static final Map<String, List<String>> SUB_NAMES = Arrays.stream(TargetFields.values()).collect(
|
||||
Collectors.toMap(field -> field.toString().toLowerCase(), TargetFields::getSubEntityAttributes));
|
||||
|
||||
private FieldNameDescription() {
|
||||
|
||||
}
|
||||
|
||||
private static boolean hasSubEntries(final String tokenImageName) {
|
||||
String tmpTokenName = tokenImageName;
|
||||
if (tokenImageName.contains(".")) {
|
||||
final String[] split = tokenImageName.split("\\.");
|
||||
if (split.length <= 0) {
|
||||
return false;
|
||||
}
|
||||
tmpTokenName = split[0];
|
||||
}
|
||||
final String finalTmpTokenName = tmpTokenName;
|
||||
return Arrays.stream(TargetFields.values())
|
||||
.filter(field -> field.toString().equalsIgnoreCase(finalTmpTokenName))
|
||||
.map(TargetFields::getSubEntityAttributes).mapToLong(List::size).sum() > 0;
|
||||
}
|
||||
|
||||
private static boolean isMap(final String tokenImageName) {
|
||||
return Arrays.stream(TargetFields.values())
|
||||
.filter(field -> field.toString().equalsIgnoreCase(tokenImageName)).findFirst()
|
||||
.map(TargetFields::isMap).orElse(false);
|
||||
}
|
||||
|
||||
private static List<SuggestToken> toTopSuggestToken(final int beginToken, final int endToken,
|
||||
final String tokenImageName) {
|
||||
return FIELD_NAMES.stream()
|
||||
.map(field -> new SuggestToken(beginToken, endToken, tokenImageName, field.toLowerCase()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static List<SuggestToken> toSubSuggestToken(final int beginToken, final int endToken,
|
||||
final String topToken, final String tokenImageName) {
|
||||
return Arrays.stream(TargetFields.values()).filter(field -> field.toString().equalsIgnoreCase(topToken))
|
||||
.map(TargetFields::getSubEntityAttributes).flatMap(List::stream)
|
||||
.map(subEntity -> new SuggestToken(beginToken, endToken, tokenImageName, subEntity))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static boolean containsValue(final String imageName) {
|
||||
if (!imageName.contains(".")) {
|
||||
return FIELD_NAMES.stream().anyMatch(value -> value.equalsIgnoreCase(imageName));
|
||||
}
|
||||
final String[] split = imageName.split("\\.");
|
||||
if (split.length > 1 && FIELD_NAMES.contains(split[0].toLowerCase())) {
|
||||
return SUB_NAMES.get(split[0].toLowerCase()).stream()
|
||||
.anyMatch(subname -> (split[0] + "." + subname).equalsIgnoreCase(imageName));
|
||||
}
|
||||
return FIELD_NAMES.stream().anyMatch(value -> value.equalsIgnoreCase(imageName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.rsql;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.rsql.RsqlValidationOracle;
|
||||
import org.eclipse.hawkbit.repository.rsql.SuggestToken;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
@Feature("Component Tests - Repository")
|
||||
@Story("RSQL filter suggestion")
|
||||
@SuppressWarnings("java:S6813") // constructor injects are not possible for test classes
|
||||
class RSQLParserValidationOracleTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
private static final String[] OP_SUGGESTIONS = new String[] { "==", "!=", "=ge=", "=le=", "=gt=", "=lt=", "=in=",
|
||||
"=out=" };
|
||||
private static final String[] FIELD_SUGGESTIONS = Arrays.stream(TargetFields.values())
|
||||
.map(field -> field.name().toLowerCase()).toArray(String[]::new);
|
||||
private static final String[] AND_OR_SUGGESTIONS = new String[] { "and", "or" };
|
||||
private static final String[] NAME_VERSION_SUGGESTIONS = new String[] { "name", "version" };
|
||||
|
||||
@Autowired
|
||||
private RsqlValidationOracle rsqlValidationOracle;
|
||||
|
||||
@Test
|
||||
@Description("Verifies that suggestions contains all possible field names")
|
||||
void suggestionContainsAllFieldNames() {
|
||||
final String rsqlQuery = "na";
|
||||
final List<String> currentSuggestions = getSuggestions(rsqlQuery);
|
||||
assertThat(currentSuggestions).containsOnly(FIELD_SUGGESTIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that suggestions only contains the allowed operators")
|
||||
void suggestionContainsOnlyOperators() {
|
||||
final String rsqlQuery = "name";
|
||||
final List<String> currentSuggestions = getSuggestions(rsqlQuery);
|
||||
assertThat(currentSuggestions).containsOnly(OP_SUGGESTIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that suggestions only contains operator to combine RSQL filters (and, or)")
|
||||
void suggestionContainsOnlyAndOrOperator() {
|
||||
final String rsqlQuery = "name==a ";
|
||||
final List<String> currentSuggestions = getSuggestions(rsqlQuery);
|
||||
assertThat(currentSuggestions).containsOnly(AND_OR_SUGGESTIONS);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that sub suggestions are shown")
|
||||
void suggestionContainsSubFieldSuggestions() {
|
||||
final String rsqlQuery = "assignedds.";
|
||||
final List<String> currentSuggestions = getSuggestions(rsqlQuery);
|
||||
assertThat(currentSuggestions).containsOnly(NAME_VERSION_SUGGESTIONS);
|
||||
}
|
||||
|
||||
private List<String> getSuggestions(final String rsqlQuery) {
|
||||
return rsqlValidationOracle
|
||||
.suggest(rsqlQuery, -1).getSuggestionContext().getSuggestions().stream()
|
||||
.map(SuggestToken::getSuggestion)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user