Extract QL support in a top level module (#2808)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-11-14 14:19:36 +02:00
committed by GitHub
parent df4464406e
commit c5ea265e0f
71 changed files with 454 additions and 485 deletions

View File

@@ -1,69 +0,0 @@
# hawkBit Migration Guides
## Release 0.2
### Configuration Property changes
- `hawkbit.server.controller._` have changed to `hawkbit.server.ddi._`
- `info.build._` have changed to `hawkbit.server.build._`
- `hawkbit.server.demo._` have changed to `hawkbit.server.ui.demo._`
- `hawkbit.server.email.support` has changed to `hawkbit.server.ui.links.support`
- `hawkbit.server.email.request.account` has changed to `hawkbit.server.ui.links.requestAccount`
- `hawkbit.server.im.login.url` has changed to `hawkbit.server.ui.links.userManagement`
### REST API model changes for clients
- ENTITYPagedList classes have been removed; generic `PagedList` used instead (e.g. `PagedList<TargetRest>` instead
of `TargetPagedList`).
- ENTITYsrest classes have been removed; `List<ENTITYrest>` used instead (e.g. `List<TargetRest>` instead
of `TargetsRest`)
### Renamed api annotations
- Annotation `org.eclipse.hawkbit.rest.resource.EnableRestResources` has changed
to `org.eclipse.hawkbit.mgmt.annotation.EnableMgmtApi`
- Annotation `org.eclipse.hawkbit.ddi.resource.EnableDirectDeviceApi` has changed
to `org.eclipse.hawkbit.ddi.annotation.EnableDdiApi`
### Renamed maven modules
- Module `hawkbit-mgmt-api-client` has changed to `hawkbit-example-mgmt-simulator`
## Milestone 0.3.0M6
### Configuration Property changes
- `hawkbit.server.security.dos.maxTargetsPerManualAssignment` has changed
to `hawkbit.server.security.dos.maxTargetDistributionSetAssignmentsPerManualAssignment`
## Milestone 0.3.0M7
### Configuration Property changes
- Due to Spring Boot version upgrade (
see [spring boot 2.2 deprecations](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.2-Release-Notes#deprecations-in-spring-boot-22)) `server.use-forward-headers`
has changed to `server.forward-headers-strategy`
## Upgrade from Master Branch (after 0.3.0M6) to 0.3.0M7
Due to changes in the DB migration scripts within PR [#1017](https://github.com/eclipse-hawkbit/hawkbit/pull/1017) the
Hawkbit will not start up if one of the following cases is true:
- DB2 database is used
- MSSQL database is used and the `sp_action` table is not empty
- PostgreSql database is used and the `sp_action` table is not empty
The script was fixed with PR [#1061](https://github.com/eclipse-hawkbit/hawkbit/pull/1061).
In case you upgrade from 0.3.0M6 to 0.3.0M7 there is no issue. But if you have built the Hawkbit from the master branch
between PR [#1017](https://github.com/eclipse-hawkbit/hawkbit/pull/1017) and
PR [#1061](https://github.com/eclipse-hawkbit/hawkbit/pull/1061), use PostgreSQL or MSSQL and upgrade to 0.3.0M7, it
will fail at startup with the message: `Validate failed: Migration checksum mismatch for migration version 1.12.16`
This can be fixed by adapting the schema_version table of the database. The checksum field of the entry with the version
1.12.16 has to be changed (mind the minus):
- -1684307461 for MSSQL
- -596342656 for PostgreSql
Example for MSSQL: `UPDATE schema_version SET checksum=-1684307461 WHERE version='1.12.16'`

View File

@@ -88,7 +88,7 @@ public final class MgmtTargetMapper {
.withRel(MgmtRestConstants.TARGET_V1_ATTRIBUTES).expand());
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionHistory(response.getControllerId(), null, 0,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
ActionFields.ID.getJpaEntityFieldName() + ":" + SortDirection.DESC))
ActionFields.ID.getName() + ":" + SortDirection.DESC))
.withRel(MgmtRestConstants.TARGET_V1_ACTIONS).expand());
response.add(linkTo(methodOn(MgmtTargetRestApi.class).getMetadata(response.getControllerId()))
.withRel("metadata").expand());
@@ -289,7 +289,7 @@ public final class MgmtTargetMapper {
result.add(linkTo(methodOn(MgmtTargetRestApi.class).getActionStatusList(controllerId, action.getId(), 0,
MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT_VALUE,
ActionStatusFields.ID.getJpaEntityFieldName() + ":" + SortDirection.DESC))
ActionStatusFields.ID.getName() + ":" + SortDirection.DESC))
.withRel(MgmtRestConstants.TARGET_V1_ACTION_STATUS).expand());
final Rollout rollout = action.getRollout();

View File

@@ -38,7 +38,7 @@ public final class PagingUtility {
public static Sort sanitizeTargetSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TargetFields.CONTROLLERID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, TargetFields.CONTROLLERID.getName());
}
return Sort.by(SortUtility.parse(TargetFields.class, sortParam));
}
@@ -46,7 +46,7 @@ public final class PagingUtility {
public static Sort sanitizeTargetTypeSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TargetTypeFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, TargetTypeFields.ID.getName());
}
return Sort.by(SortUtility.parse(TargetTypeFields.class, sortParam));
}
@@ -54,7 +54,7 @@ public final class PagingUtility {
public static Sort sanitizeTagSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TagFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, TagFields.ID.getName());
}
return Sort.by(SortUtility.parse(TagFields.class, sortParam));
}
@@ -62,7 +62,7 @@ public final class PagingUtility {
public static Sort sanitizeTargetFilterQuerySortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, TargetFilterQueryFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, TargetFilterQueryFields.ID.getName());
}
return Sort.by(SortUtility.parse(TargetFilterQueryFields.class, sortParam));
}
@@ -70,7 +70,7 @@ public final class PagingUtility {
public static Sort sanitizeSoftwareModuleSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, SoftwareModuleFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, SoftwareModuleFields.ID.getName());
}
return Sort.by(SortUtility.parse(SoftwareModuleFields.class, sortParam));
}
@@ -78,7 +78,7 @@ public final class PagingUtility {
public static Sort sanitizeSoftwareModuleTypeSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, SoftwareModuleTypeFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, SoftwareModuleTypeFields.ID.getName());
}
return Sort.by(SortUtility.parse(SoftwareModuleTypeFields.class, sortParam));
}
@@ -86,7 +86,7 @@ public final class PagingUtility {
public static Sort sanitizeDistributionSetSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, DistributionSetFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, DistributionSetFields.ID.getName());
}
return Sort.by(SortUtility.parse(DistributionSetFields.class, sortParam));
}
@@ -94,7 +94,7 @@ public final class PagingUtility {
public static Sort sanitizeDistributionSetTypeSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, DistributionSetTypeFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, DistributionSetTypeFields.ID.getName());
}
return Sort.by(SortUtility.parse(DistributionSetTypeFields.class, sortParam));
}
@@ -103,7 +103,7 @@ public final class PagingUtility {
if (sortParam == null) {
// default sort is DESC in case of action to match behavior
// of management UI (last entry on top)
return Sort.by(Direction.DESC, ActionFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.DESC, ActionFields.ID.getName());
}
return Sort.by(SortUtility.parse(ActionFields.class, sortParam));
}
@@ -112,7 +112,7 @@ public final class PagingUtility {
if (sortParam == null) {
// default sort is DESC in case of action status to match behavior
// of management UI (last entry on top)
return Sort.by(Direction.DESC, ActionStatusFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.DESC, ActionStatusFields.ID.getName());
}
return Sort.by(SortUtility.parse(ActionStatusFields.class, sortParam));
}
@@ -120,7 +120,7 @@ public final class PagingUtility {
public static Sort sanitizeRolloutSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, RolloutFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, RolloutFields.ID.getName());
}
return Sort.by(SortUtility.parse(RolloutFields.class, sortParam));
}
@@ -128,7 +128,7 @@ public final class PagingUtility {
public static Sort sanitizeRolloutGroupSortParam(final String sortParam) {
if (sortParam == null) {
// default
return Sort.by(Direction.ASC, RolloutGroupFields.ID.getJpaEntityFieldName());
return Sort.by(Direction.ASC, RolloutGroupFields.ID.getName());
}
return Sort.by(SortUtility.parse(RolloutGroupFields.class, sortParam));
}

View File

@@ -18,7 +18,7 @@ import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterSyntaxErrorException;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedDirectionException;
import org.eclipse.hawkbit.mgmt.rest.resource.exception.SortParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.ql.QueryField;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
@@ -79,7 +79,7 @@ public final class SortUtility {
throw new SortParameterUnsupportedDirectionException(e);
}
orders.add(new Order(sortDirection, identifier.getJpaEntityFieldName()));
orders.add(new Order(sortDirection, identifier.getName()));
} else {
throw new SortParameterSyntaxErrorException();
}

View File

@@ -384,7 +384,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest {
assertThat(actions).hasSize(2);
updateActionStatus(actions.get(0), Status.FINISHED, null, "test");
final PageRequest pageRequest = PageRequest.of(0, 1000, Direction.ASC, ActionFields.ID.getJpaEntityFieldName());
final PageRequest pageRequest = PageRequest.of(0, 1000, Direction.ASC, ActionFields.ID.getName());
final Action action = deploymentManagement.findActionsByTarget(knownTargetId, pageRequest).getContent().get(0);
final ActionStatus status = deploymentManagement.findActionStatusByAction(action.getId(), PAGE).getContent()

View File

@@ -14,20 +14,14 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository</artifactId>
<artifactId>hawkbit-parent</artifactId>
<version>${revision}</version>
</parent>
<artifactId>hawkbit-repository-jpa-ql</artifactId>
<name>hawkBit :: Query Language</name>
<artifactId>hawkbit-ql-jpa</artifactId>
<name>hawkBit :: Query Language :: JPA</name>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.jpa</artifactId>
@@ -63,9 +57,8 @@
<!-- Test -->
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-test</artifactId>
<version>${project.version}</version>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -7,18 +7,18 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.EQ;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GT;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GTE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.IN;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LIKE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LT;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LTE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT_IN;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT_LIKE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.EQ;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.GT;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.GTE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.IN;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LIKE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LT;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LTE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NOT_IN;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NOT_LIKE;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -30,7 +30,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.function.BiPredicate;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator;
import org.eclipse.hawkbit.ql.Node.Comparison.Operator;
import org.springframework.core.ResolvableType;
/**

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql;
import java.util.ArrayList;
import java.util.List;

View File

@@ -0,0 +1,44 @@
/**
* 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.ql;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/**
* Exception used by the REST API in case of RSQL search filter query.
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class QueryException extends RuntimeException {
public enum ErrorCode {
INVALID_SYNTAX,
UNSUPPORTED_FIELD,
GENERIC // an other
}
@Getter
private final ErrorCode errorCode;
public QueryException(final ErrorCode errorCode, final String message) {
this(errorCode, message, null);
}
public QueryException(final ErrorCode errorCode, final Throwable cause) {
this(errorCode, null, cause);
}
public QueryException(final ErrorCode errorCode, final String message, final Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
}

View File

@@ -7,13 +7,13 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.qfields;
package org.eclipse.hawkbit.ql;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import jakarta.validation.constraints.NotNull;
import lombok.NonNull;
/**
* A query field interface extended by all the fields that could be used in queries.
@@ -27,10 +27,12 @@ public interface QueryField {
String SUB_ATTRIBUTE_SPLIT_REGEX = "\\" + SUB_ATTRIBUTE_SEPARATOR;
/**
* @return the string representation of the underlying persistence field name e.g. in case of sorting.
* Returns the entity field name, e.g. the JPA entity field name.
*
* @return the string representation of the underlying persistence field name.
*/
@NotNull
String getJpaEntityFieldName();
@NonNull
String getName();
/**
* @return all sub entities attributes.

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import java.util.ArrayList;
import java.util.List;
@@ -22,13 +22,11 @@ import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrLookup;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.repository.exception.QueryException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.ql.EntityMatcher;
import org.eclipse.hawkbit.ql.Node;
import org.eclipse.hawkbit.ql.Node.Comparison;
import org.eclipse.hawkbit.ql.QueryException;
import org.eclipse.hawkbit.ql.QueryField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
@@ -38,9 +36,8 @@ import org.springframework.core.annotation.Order;
import org.springframework.data.jpa.domain.Specification;
/**
* A utility class which is able to parse RSQL strings into an spring data
* {@link Specification} which then can be enhanced sql queries to filter
* entities. RSQL parser library: https://github.com/jirutka/rsql-parser
* A utility class which is able to parse RSQL strings into an spring data {@link Specification} which then can be enhanced sql queries to
* filter entities. It has out of the box support for the RSQL parser library: https://github.com/jirutka/rsql-parser
*
* <ul>
* <li>{@code Equal to: ==}</li>
@@ -60,15 +57,12 @@ import org.springframework.data.jpa.domain.Specification;
* <li>{@code name==targetId1 or description==plugAndPlay or updateStatus==UNKNOWN}</li>
* </ul>
* <p>
* There is also a mechanism that allows to refer to known macros that can
* resolved by an optional {@link StrLookup} (cp.
* There is also a mechanism that allows to refer to known macros that can resolved by an optional {@link StrLookup} (cp.
* {@link VirtualPropertyResolver}).<br>
* An example that queries for all overdue targets using the ${OVERDUE_TS}
* placeholder introduced by {@link VirtualPropertyResolver} looks like
* this:<br>
* An example that queries for all overdue targets using the ${OVERDUE_TS} placeholder introduced by {@link VirtualPropertyResolver} looks
* like this:<br>
* <em>lastControllerRequestAt=le=${OVERDUE_TS}</em><br>
* It is possible to escape a macro expression by using a second '$':
* $${OVERDUE_TS} would prevent the ${OVERDUE_TS} token from being expanded.
* It is possible to escape a macro expression by using a second '$': $${OVERDUE_TS} would prevent the ${OVERDUE_TS} token from being expanded.
*/
@Slf4j
@Getter
@@ -94,9 +88,8 @@ public class QLSupport implements ApplicationListener<ContextRefreshedEvent> {
private boolean caseInsensitiveDB;
private QueryParser parser;
private List<NodeTransformer> nodeTransformers;
private List<NodeTransformer> nodeTransformers = List.of();
private EntityManager entityManager;
private VirtualPropertyResolver virtualPropertyResolver;
/**
* @return The holder singleton instance.
@@ -106,7 +99,7 @@ public class QLSupport implements ApplicationListener<ContextRefreshedEvent> {
}
@Autowired
void setQueryParser(final QueryParser parser) {
public void setQueryParser(final QueryParser parser) {
this.parser = parser;
}
@@ -124,11 +117,6 @@ public class QLSupport implements ApplicationListener<ContextRefreshedEvent> {
this.entityManager = entityManager;
}
@Autowired(required = false)
void setVirtualPropertyResolver(final VirtualPropertyResolver virtualPropertyResolver) {
this.virtualPropertyResolver = virtualPropertyResolver;
}
public Node parse(final String query) {
return parse(query, null);
}
@@ -144,9 +132,7 @@ public class QLSupport implements ApplicationListener<ContextRefreshedEvent> {
* @param query the rsql query to be parsed
* @param queryFieldType the enum class type which implements the {@link QueryField}
* @return a specification which can be used with JPA
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the
* given {@code fieldNameProvider}
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
* @throws QueryException if the RSQL syntax is wrong or if a field in the RSQL string is used but not provided by the given {@code queryFieldType}
*/
public <A extends Enum<A> & QueryField, T> Specification<T> buildSpec(final String query, final Class<A> queryFieldType) {
return new SpecificationBuilder<T>(ignoreCase && !caseInsensitiveDB)
@@ -154,8 +140,7 @@ public class QLSupport implements ApplicationListener<ContextRefreshedEvent> {
}
public <A extends Enum<A> & QueryField, T> Specification<T> buildSpec(final Node query, final Class<A> queryFieldType) {
return new SpecificationBuilder<T>(ignoreCase && !caseInsensitiveDB)
.specification(transform(query, queryFieldType));
return new SpecificationBuilder<T>(ignoreCase && !caseInsensitiveDB).specification(transform(query, queryFieldType));
}
@SuppressWarnings("java:S1117") // it is again ignoreCase

View File

@@ -7,17 +7,19 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GT;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GTE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.IN;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LIKE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LT;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LTE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT_IN;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT_LIKE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.GT;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.GTE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.IN;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LIKE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LT;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LTE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NOT_IN;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NOT_LIKE;
import static org.eclipse.hawkbit.ql.QueryException.ErrorCode.INVALID_SYNTAX;
import static org.eclipse.hawkbit.ql.QueryException.ErrorCode.UNSUPPORTED_FIELD;
import java.util.ArrayList;
import java.util.Arrays;
@@ -49,11 +51,11 @@ import jakarta.persistence.metamodel.SetAttribute;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Logical;
import org.eclipse.hawkbit.ql.Node;
import org.eclipse.hawkbit.ql.Node.Comparison;
import org.eclipse.hawkbit.ql.Node.Comparison.Operator;
import org.eclipse.hawkbit.ql.Node.Logical;
import org.eclipse.hawkbit.ql.QueryException;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.ObjectUtils;
@@ -129,14 +131,15 @@ public class SpecificationBuilder<T> {
}
}
@SuppressWarnings({ "unchecked", "java:S3776" }) // java:S3776 - easier to read at one place
@SuppressWarnings("java:S3776") // java:S3776 - easier to read at one place
private Predicate predicate(final Comparison comparison) {
final String[] split = comparison.getKey().split("\\.", 2); // { attributeName [, sub attribute / map key]
final Attribute<? super T, ?> attribute = root.getModel().getAttribute(split[0]);
final Operator op = comparison.getOp();
if (attribute instanceof MapAttribute<?, ?, ?>) {
if (split.length < 2 || ObjectUtils.isEmpty(split[1])) {
throw new RSQLParameterUnsupportedFieldException(
throw new QueryException(
UNSUPPORTED_FIELD,
String.format("No key for the map field found. Syntax is: %s.<key name>", getPathContext(comparison)));
}
if (comparison.getValue() == null) {
@@ -144,7 +147,8 @@ public class SpecificationBuilder<T> {
return switch (op) {
case EQ -> cb.isNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1])));
case NE -> cb.isNotNull(toMapValuePath(pathResolver.getJoinOn(attribute, split[1])));
default -> throw new RSQLParameterSyntaxException(
default -> throw new QueryException(
INVALID_SYNTAX,
String.format("Operator %s is not supported for map fields with value null", op));
};
} else {
@@ -155,7 +159,8 @@ public class SpecificationBuilder<T> {
}
} else if (attribute instanceof SetAttribute<?, ?> setAttribute) {
if (split.length < 2 || ObjectUtils.isEmpty(split[1])) {
throw new RSQLParameterUnsupportedFieldException(
throw new QueryException(
UNSUPPORTED_FIELD,
String.format("No mapping key for a set field found. Syntax is: %s.<ref name>", getPathContext(comparison)));
}
if (isNot(op)) {
@@ -243,24 +248,24 @@ public class SpecificationBuilder<T> {
: element)
.toList();
if (values.isEmpty()) {
throw new RSQLParameterSyntaxException("RSQL values must not be empty", null);
throw new QueryException(INVALID_SYNTAX, "RSQL values must not be empty");
} else if (values.size() == 1) {
final Operator op = comparison.getOp();
// enum, boolean or null - doesn't support >, >=, <, <=
if (!(values.get(0) instanceof String)) {
if (values.get(0) instanceof Number) {
if (op == LIKE || op == NOT_LIKE) {
throw new RSQLParameterSyntaxException(op + " operator could not be applied number", null);
throw new QueryException(INVALID_SYNTAX, op + " operator could not be applied number");
}
} else if (op == GT || op == GTE || op == LT || op == LTE || op == LIKE || op == NOT_LIKE) {
final String errorMsg = values.get(0) == null ? "null value" : "enum or boolean field";
throw new RSQLParameterSyntaxException(op + " operator could not be applied to " + errorMsg, null);
throw new QueryException(INVALID_SYNTAX, op + " operator could not be applied to " + errorMsg);
}
}
} else {
final Operator op = comparison.getOp();
if (op != IN && op != NOT_IN) {
throw new RSQLParameterSyntaxException(op + " operator shall have exactly one value", null);
throw new QueryException(INVALID_SYNTAX, op + " operator shall have exactly one value");
}
}
return values;
@@ -276,9 +281,9 @@ public class SpecificationBuilder<T> {
if ("true".equals(value) || "false".equals(value)) {
return Boolean.valueOf(value);
} else {
throw new RSQLParameterSyntaxException(
String.format(
"The value of %S is not well formed. Only a boolean (true or false) value will be expected",
throw new QueryException(
INVALID_SYNTAX,
String.format("The value of %S is not well formed. Only a boolean (true or false) value will be expected",
getPathContext(comparison)));
}
}
@@ -307,14 +312,11 @@ public class SpecificationBuilder<T> {
} else {
log.debug("Provided value '{}' cannot be transformed into the correct enum type {}", value.toUpperCase(), javaType, e);
}
throw new RSQLParameterUnsupportedFieldException(
String.format(
"Value of %s must be one of the following: %s",
throw new QueryException(
UNSUPPORTED_FIELD,
String.format("Value of %s must be one of the following: %s",
getPathContext(comparison),
Arrays.stream(tmpEnumType.getEnumConstants())
.map(Enum::name)
.map(String::toLowerCase)
.toList()),
Arrays.stream(tmpEnumType.getEnumConstants()).map(Enum::name).map(String::toLowerCase).toList()),
e);
}
}
@@ -331,7 +333,7 @@ public class SpecificationBuilder<T> {
if (path instanceof Join<?, ?> join) {
return deepGetPath(join.join(subAttributeName, JoinType.LEFT), subAttributeNameSplit, startIndex);
} else {
throw new RSQLParameterSyntaxException("Unexpected sub attribute " + subAttributeName);
throw new QueryException(INVALID_SYNTAX, "Unexpected sub attribute " + subAttributeName);
}
}
}

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql.utils;
package org.eclipse.hawkbit.ql.jpa.utils;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql.utils;
package org.eclipse.hawkbit.ql.jpa.utils;
import java.lang.reflect.InvocationTargetException;
@@ -17,15 +17,16 @@ import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.ql.QueryField;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.ql.rsql.RsqlParser;
public class HawkbitQlToSql {
public class QlToSql {
private final EntityManager entityManager;
private final boolean isEclipselink;
public HawkbitQlToSql(final EntityManager entityManager) {
public QlToSql(final EntityManager entityManager) {
this.entityManager = entityManager;
isEclipselink = entityManager.getProperties().keySet().stream().anyMatch(key -> key.startsWith("eclipselink."));
}
@@ -56,7 +57,8 @@ public class HawkbitQlToSql {
final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {
final CriteriaQuery<T> query = entityManager.getCriteriaBuilder().createQuery(domainClass);
final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
return query.where(QLSupport.getInstance().<A, T> buildSpec(rsql, fieldsClass)
.toPredicate(query.from(domainClass), cb.createQuery(domainClass), cb));
final QLSupport qlSupport = QLSupport.getInstance();
qlSupport.setQueryParser(RsqlParser::parse);
return query.where(qlSupport.<A, T> buildSpec(rsql, fieldsClass).toPredicate(query.from(domainClass), cb.createQuery(domainClass), cb));
}
}

View File

@@ -7,20 +7,22 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.eclipse.hawkbit.repository.qfields.QueryField.SUB_ATTRIBUTE_SEPARATOR;
import static org.eclipse.hawkbit.repository.qfields.QueryField.SUB_ATTRIBUTE_SPLIT_REGEX;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.EQ;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GT;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.GTE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.IN;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LIKE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LT;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.LTE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NE;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT_IN;
import static org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison.Operator.NOT_LIKE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.EQ;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.GT;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.GTE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.IN;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LIKE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LT;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.LTE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NE;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NOT_IN;
import static org.eclipse.hawkbit.ql.Node.Comparison.Operator.NOT_LIKE;
import static org.eclipse.hawkbit.ql.QueryException.ErrorCode.INVALID_SYNTAX;
import static org.eclipse.hawkbit.ql.QueryException.ErrorCode.UNSUPPORTED_FIELD;
import static org.eclipse.hawkbit.ql.QueryField.SUB_ATTRIBUTE_SEPARATOR;
import static org.eclipse.hawkbit.ql.QueryField.SUB_ATTRIBUTE_SPLIT_REGEX;
import java.util.HashSet;
import java.util.List;
@@ -39,11 +41,10 @@ import cz.jirutka.rsql.parser.ast.RSQLVisitor;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.ql.Node;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison;
import org.eclipse.hawkbit.ql.Node;
import org.eclipse.hawkbit.ql.Node.Comparison;
import org.eclipse.hawkbit.ql.QueryException;
import org.eclipse.hawkbit.ql.QueryField;
/**
* {@link RsqlParser} parses RSQL query stings to {@link Node} objects. Doing that it does the following:
@@ -85,11 +86,9 @@ public class RsqlParser {
private static Node parse(final String rsql, final UnaryOperator<String> keyResolver) {
try {
return RSQL_PARSER
.parse(rsql)
.accept(new RsqlVisitor(keyResolver));
return RSQL_PARSER.parse(rsql).accept(new RsqlVisitor(keyResolver));
} catch (final RSQLParserException e) {
throw new RSQLParameterSyntaxException(e);
throw new QueryException(INVALID_SYNTAX, e);
}
}
@@ -103,27 +102,27 @@ public class RsqlParser {
try {
enumValue = Enum.valueOf(rsqlQueryFieldType, enumName);
} catch (final IllegalArgumentException e) {
throw new RSQLParameterUnsupportedFieldException(e);
throw new QueryException(UNSUPPORTED_FIELD, e);
}
final String attribute;
if (firstSeparatorIndex == -1) { // just field name without sub-attribute
if (enumValue.getSubEntityAttributes().isEmpty()) {
// no sub-attributes -> simple field
attribute = enumValue.getJpaEntityFieldName();
attribute = enumValue.getName();
} else {
// just enum name for a complex type (with sub-attributes), should have single (default!) sub-attribute
if (enumValue.isMap()) {
throw new RSQLParameterUnsupportedFieldException("No key specified for a map type " + enumValue);
throw new QueryException(UNSUPPORTED_FIELD, "No key specified for a map type " + enumValue);
} else {
final String defaultSubEntityAttribute = enumValue.getDefaultSubEntityAttribute();
if (defaultSubEntityAttribute != null) {
// single sub attribute - so, treat it as a default
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + defaultSubEntityAttribute;
attribute = enumValue.getName() + SUB_ATTRIBUTE_SEPARATOR + defaultSubEntityAttribute;
} else {
throw new RSQLParameterUnsupportedFieldException(
String.format(
"The given search parameter field {%s} requires one of the following sub-attributes %s",
throw new QueryException(
UNSUPPORTED_FIELD,
String.format("The given search parameter field {%s} requires one of the following sub-attributes %s",
key, enumValue.getSubEntityAttributes()));
}
}
@@ -131,19 +130,19 @@ public class RsqlParser {
} else { // field name with sub-attribute
if (enumValue.isMap()) {
// map, the part after the enum name is the key of the map
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + key.substring(firstSeparatorIndex + 1);
attribute = enumValue.getName() + SUB_ATTRIBUTE_SEPARATOR + key.substring(firstSeparatorIndex + 1);
} else if (enumValue.getSubEntityAttributes().isEmpty()) {
// simple type without sub-attributes, so the sub-attribute is not allowed
throw new RSQLParameterUnsupportedFieldException("Sub-attributes not supported for simple field " + enumValue);
throw new QueryException(UNSUPPORTED_FIELD, "Sub-attributes not supported for simple field " + enumValue);
} else {
final String[] subAttribute = key.substring(firstSeparatorIndex + 1).split(SUB_ATTRIBUTE_SPLIT_REGEX, 2);
attribute = enumValue.getJpaEntityFieldName() + SUB_ATTRIBUTE_SEPARATOR + enumValue.getSubEntityAttributes().stream()
attribute = enumValue.getName() + SUB_ATTRIBUTE_SEPARATOR + enumValue.getSubEntityAttributes().stream()
.filter(attr -> attr.equalsIgnoreCase(subAttribute[0])) // case normalized
.findFirst()
.map(attr -> subAttribute.length == 1 ? attr : attr + key.substring(firstSeparatorIndex + 1 + attr.length()))
.orElseThrow(() -> new RSQLParameterUnsupportedFieldException(
String.format(
"The given search field {%s} has unsupported sub-attributes. Supported sub-attributes are %s",
.orElseThrow(() -> new QueryException(
UNSUPPORTED_FIELD,
String.format("The given search field {%s} has unsupported sub-attributes. Supported sub-attributes are %s",
key, enumValue.getSubEntityAttributes())));
}
}

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import java.util.Map;
import java.util.Set;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import static org.assertj.core.api.Assertions.assertThat;
@@ -19,8 +19,9 @@ import java.util.stream.StreamSupport;
import jakarta.persistence.EntityManager;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.jpa.ql.utils.HawkbitQlToSql;
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParser;
import org.eclipse.hawkbit.ql.EntityMatcher;
import org.eclipse.hawkbit.ql.jpa.utils.QlToSql;
import org.eclipse.hawkbit.ql.rsql.RsqlParser;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
@@ -30,9 +31,7 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.jpa.domain.Specification;
@SuppressWarnings("java:S5961") // complex check because the matter is very complex
@DataJpaTest(properties = {
"spring.jpa.database=H2"
}, excludeAutoConfiguration = { FlywayAutoConfiguration.class })
@DataJpaTest(properties = { "spring.jpa.database=H2" }, excludeAutoConfiguration = { FlywayAutoConfiguration.class })
@EnableAutoConfiguration
@Slf4j
class SpecificationBuilderTest {
@@ -494,10 +493,8 @@ class SpecificationBuilderTest {
try {
assertThat(result).containsExactlyInAnyOrder(refResult.toArray(Root[]::new));
} catch (final AssertionError e) {
log.error(
"Fail to get expected result for RSQL: {} with SQL query: {}",
rsql, new HawkbitQlToSql(entityManager).toSQL(Root.class, null, rsql),
e);
log.error("Fail to get expected result for RSQL: {} with SQL query: {}",
rsql, new QlToSql(entityManager).toSQL(Root.class, null, rsql), e);
throw e;
}
return result;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.ql;
package org.eclipse.hawkbit.ql.jpa;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;

View File

@@ -22,6 +22,11 @@
<name>hawkBit :: Repository :: API</name>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-ql-jpa</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-artifact-api</artifactId>

View File

@@ -1,41 +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.exception;
import java.io.Serial;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.exception.SpServerError;
/**
* Exception used by the REST API in case of RSQL search filter query.
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public abstract class QueryException extends AbstractServerRtException {
QueryException(final SpServerError error) {
super(error);
}
QueryException(final SpServerError error, final String message) {
super(error, message);
}
QueryException(final SpServerError error, final Throwable cause) {
super(error, cause);
}
QueryException(final SpServerError error, final String message, final Throwable cause) {
super(error, message, cause);
}
}

View File

@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RSQLParameterSyntaxException extends QueryException {
public class RSQLParameterSyntaxException extends AbstractServerRtException {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -21,7 +21,7 @@ import org.eclipse.hawkbit.exception.SpServerError;
*/
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RSQLParameterUnsupportedFieldException extends QueryException {
public class RSQLParameterUnsupportedFieldException extends AbstractServerRtException {
@Serial
private static final long serialVersionUID = 1L;

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.qfields;
import java.util.List;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Sort and search fields for actions.
@@ -29,21 +30,21 @@ public enum ActionFields implements QueryField {
LASTMODIFIEDBY("lastModifiedBy"),
WEIGHT("weight"),
TARGET("target",
TargetFields.ID.getJpaEntityFieldName(), TargetFields.NAME.getJpaEntityFieldName(),
TargetFields.UPDATESTATUS.getJpaEntityFieldName(), TargetFields.IPADDRESS.getJpaEntityFieldName()),
TargetFields.ID.getName(), TargetFields.NAME.getName(),
TargetFields.UPDATESTATUS.getName(), TargetFields.IPADDRESS.getName()),
DISTRIBUTIONSET("distributionSet",
DistributionSetFields.ID.getJpaEntityFieldName(),
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName(),
DistributionSetFields.TYPE.getJpaEntityFieldName()),
ROLLOUT("rollout", RolloutFields.ID.getJpaEntityFieldName(), RolloutFields.NAME.getJpaEntityFieldName()),
ROLLOUTGROUP("rolloutGroup", RolloutGroupFields.ID.getJpaEntityFieldName(), RolloutGroupFields.NAME.getJpaEntityFieldName()),
DistributionSetFields.ID.getName(),
DistributionSetFields.NAME.getName(), DistributionSetFields.VERSION.getName(),
DistributionSetFields.TYPE.getName()),
ROLLOUT("rollout", RolloutFields.ID.getName(), RolloutFields.NAME.getName()),
ROLLOUTGROUP("rolloutGroup", RolloutGroupFields.ID.getName(), RolloutGroupFields.NAME.getName()),
EXTERNALREF("externalRef");
private final String jpaEntityFieldName;
private final String name;
private final List<String> subEntityAttributes;
ActionFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
ActionFields(final String name, final String... subEntityAttributes) {
this.name = name;
this.subEntityAttributes = List.of(subEntityAttributes);
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.qfields;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Sort and search fields for action status.
@@ -25,9 +26,9 @@ public enum ActionStatusFields implements QueryField {
CREATEDAT("createdAt"), // same as REPORTEDAT
CREATEDBY("createdBy");
private final String jpaEntityFieldName;
private final String name;
ActionStatusFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
ActionStatusFields(final String name) {
this.name = name;
}
}

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.qfields;
import java.util.List;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the DistributionSet model which can be used in the
@@ -22,9 +23,9 @@ public enum DistributionSetFields implements QueryField {
ID("id"),
TYPE("type",
DistributionSetTypeFields.ID.getJpaEntityFieldName(),
DistributionSetTypeFields.KEY.getJpaEntityFieldName(),
DistributionSetTypeFields.NAME.getJpaEntityFieldName()),
DistributionSetTypeFields.ID.getName(),
DistributionSetTypeFields.KEY.getName(),
DistributionSetTypeFields.NAME.getName()),
NAME("name"),
DESCRIPTION("description"),
CREATEDAT("createdAt"),
@@ -32,22 +33,22 @@ public enum DistributionSetFields implements QueryField {
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy"),
VERSION("version"),
MODULE("modules", SoftwareModuleFields.ID.getJpaEntityFieldName(), SoftwareModuleFields.NAME.getJpaEntityFieldName()),
MODULE("modules", SoftwareModuleFields.ID.getName(), SoftwareModuleFields.NAME.getName()),
TAG("tags", "name"),
METADATA("metadata"),
VALID("valid");
private final String jpaEntityFieldName;
private final String name;
private final List<String> subEntityAttributes;
DistributionSetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
DistributionSetFields(final String name, final String... subEntityAttributes) {
this.name = name;
this.subEntityAttributes = List.of(subEntityAttributes);
}
@Override
public String getDefaultSubEntityAttribute() {
return this == TYPE ? DistributionSetTypeFields.KEY.getJpaEntityFieldName() : QueryField.super.getDefaultSubEntityAttribute();
return this == TYPE ? DistributionSetTypeFields.KEY.getName() : QueryField.super.getDefaultSubEntityAttribute();
}
@Override

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.qfields;
import java.util.List;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the Tag model which can be used in the REST API e.g.
@@ -22,23 +23,23 @@ import lombok.Getter;
@Getter
public enum DistributionSetTagFields implements QueryField {
ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getJpaEntityFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()),
COLOUR(TagFields.COLOUR.getJpaEntityFieldName()),
ID(TagFields.ID.getName()),
NAME(TagFields.NAME.getName()),
DESCRIPTION(TagFields.DESCRIPTION.getName()),
COLOUR(TagFields.COLOUR.getName()),
CREATEDAT("createdAt"),
CREATEDBY("createdBy"),
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy"),
DISTRIBUTIONSET(
"assignedToDistributionSet",
DistributionSetFields.ID.getJpaEntityFieldName(), DistributionSetFields.NAME.getJpaEntityFieldName());
DistributionSetFields.ID.getName(), DistributionSetFields.NAME.getName());
private final String jpaEntityFieldName;
private final String name;
private final List<String> subEntityAttributes;
DistributionSetTagFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
DistributionSetTagFields(final String name, final String... subEntityAttributes) {
this.name = name;
this.subEntityAttributes = List.of(subEntityAttributes);
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.qfields;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the DistributionSetType model which can be used in
@@ -27,9 +28,9 @@ public enum DistributionSetTypeFields implements QueryField {
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy");
private final String jpaEntityFieldName;
private final String name;
DistributionSetTypeFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
DistributionSetTypeFields(final String name) {
this.name = name;
}
}

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.qfields;
import java.util.List;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the Rollout model which can be used in the REST API e.g. for sorting etc.
@@ -29,15 +30,15 @@ public enum RolloutFields implements QueryField {
LASTMODIFIEDBY("lastModifiedBy"),
DISTRIBUTIONSET(
"distributionSet",
DistributionSetFields.ID.getJpaEntityFieldName(),
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName(),
DistributionSetFields.TYPE.getJpaEntityFieldName());
DistributionSetFields.ID.getName(),
DistributionSetFields.NAME.getName(), DistributionSetFields.VERSION.getName(),
DistributionSetFields.TYPE.getName());
private final String jpaEntityFieldName;
private final String name;
private final List<String> subEntityAttributes;
RolloutFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
RolloutFields(final String name, final String... subEntityAttributes) {
this.name = name;
this.subEntityAttributes = List.of(subEntityAttributes);
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.qfields;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the RolloutGroup model which can be used in the REST API e.g. for sorting etc.
@@ -25,9 +26,9 @@ public enum RolloutGroupFields implements QueryField {
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy");
private final String jpaEntityFieldName;
private final String name;
RolloutGroupFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
RolloutGroupFields(final String name) {
this.name = name;
}
}

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.qfields;
import java.util.List;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the SoftwareModule model which can be used in the REST API e.g. for sorting etc.
@@ -21,9 +22,9 @@ public enum SoftwareModuleFields implements QueryField {
ID("id"),
TYPE("type",
SoftwareModuleTypeFields.ID.getJpaEntityFieldName(),
SoftwareModuleTypeFields.KEY.getJpaEntityFieldName(),
SoftwareModuleTypeFields.NAME.getJpaEntityFieldName()),
SoftwareModuleTypeFields.ID.getName(),
SoftwareModuleTypeFields.KEY.getName(),
SoftwareModuleTypeFields.NAME.getName()),
NAME("name"),
DESCRIPTION("description"),
VERSION("version"),
@@ -33,18 +34,18 @@ public enum SoftwareModuleFields implements QueryField {
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy");
private final String jpaEntityFieldName;
private final String name;
private final List<String> subEntityAttributes;
SoftwareModuleFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
SoftwareModuleFields(final String name, final String... subEntityAttributes) {
this.name = name;
this.subEntityAttributes = List.of(subEntityAttributes);
}
@Override
public String getDefaultSubEntityAttribute() {
return this == TYPE ? SoftwareModuleTypeFields.KEY.getJpaEntityFieldName() : QueryField.super.getDefaultSubEntityAttribute();
return this == TYPE ? SoftwareModuleTypeFields.KEY.getName() : QueryField.super.getDefaultSubEntityAttribute();
}
@Override

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.qfields;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the SoftwareModuleType model which can be used in the REST API e.g. for sorting etc.
@@ -27,9 +28,9 @@ public enum SoftwareModuleTypeFields implements QueryField {
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy");
private final String jpaEntityFieldName;
private final String name;
SoftwareModuleTypeFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
SoftwareModuleTypeFields(final String name) {
this.name = name;
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.qfields;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the Tag model which can be used in the REST API e.g. for sorting etc.
@@ -26,9 +27,9 @@ public enum TagFields implements QueryField {
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy");
private final String jpaEntityFieldName;
private final String name;
TagFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
TagFields(final String name) {
this.name = name;
}
}

View File

@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.repository.qfields;
import java.util.List;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the Target model which can be used in the REST API
@@ -33,36 +34,36 @@ public enum TargetFields implements QueryField {
ATTRIBUTE("controllerAttributes"),
GROUP("group"),
ASSIGNEDDS("assignedDistributionSet",
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName()),
DistributionSetFields.NAME.getName(), DistributionSetFields.VERSION.getName()),
INSTALLEDDS("installedDistributionSet",
DistributionSetFields.NAME.getJpaEntityFieldName(), DistributionSetFields.VERSION.getJpaEntityFieldName()),
TAG("tags", TagFields.NAME.getJpaEntityFieldName()),
DistributionSetFields.NAME.getName(), DistributionSetFields.VERSION.getName()),
TAG("tags", TagFields.NAME.getName()),
LASTCONTROLLERREQUESTAT("lastTargetQuery"),
METADATA("metadata"),
TYPE("targetType",
TargetTypeFields.ID.getJpaEntityFieldName(),
TargetTypeFields.KEY.getJpaEntityFieldName(),
TargetTypeFields.NAME.getJpaEntityFieldName()),
TargetTypeFields.ID.getName(),
TargetTypeFields.KEY.getName(),
TargetTypeFields.NAME.getName()),
// kept just for backward compatibility for backward compatibility
// could be removed only if in the systems there are no active auto assignments or rollouts (dynamic or starting) with that condition
// to be reconsidered if and when to be removed
@Deprecated(forRemoval = true, since = "0.10.0")
TARGETTYPE("targetType",
TargetTypeFields.ID.getJpaEntityFieldName(),
TargetTypeFields.KEY.getJpaEntityFieldName(),
TargetTypeFields.NAME.getJpaEntityFieldName());
TargetTypeFields.ID.getName(),
TargetTypeFields.KEY.getName(),
TargetTypeFields.NAME.getName());
private final String jpaEntityFieldName;
private final String name;
private final List<String> subEntityAttributes;
TargetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this.jpaEntityFieldName = jpaEntityFieldName;
TargetFields(final String name, final String... subEntityAttributes) {
this.name = name;
this.subEntityAttributes = List.of(subEntityAttributes);
}
@Override
public String getDefaultSubEntityAttribute() {
return this == TYPE ? TargetTypeFields.KEY.getJpaEntityFieldName() : QueryField.super.getDefaultSubEntityAttribute();
return this == TYPE ? TargetTypeFields.KEY.getName() : QueryField.super.getDefaultSubEntityAttribute();
}
@Override

View File

@@ -13,6 +13,7 @@ import java.util.Collections;
import java.util.List;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the Target model which can be used in the REST API e.g. for sorting etc.
@@ -28,19 +29,19 @@ public enum TargetFilterQueryFields implements QueryField {
LASTMODIFIEDBY("lastModifiedBy"),
AUTOASSIGNDISTRIBUTIONSET("autoAssignDistributionSet", "name", "version");
private final String jpaEntityFieldName;
private final String name;
private final List<String> subEntityAttributes;
TargetFilterQueryFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, Collections.emptyList());
TargetFilterQueryFields(final String name) {
this(name, Collections.emptyList());
}
TargetFilterQueryFields(final String jpaEntityFieldName, final String... subEntityAttribues) {
this(jpaEntityFieldName, List.of(subEntityAttribues));
TargetFilterQueryFields(final String name, final String... subEntityAttribues) {
this(name, List.of(subEntityAttribues));
}
TargetFilterQueryFields(final String jpaEntityFieldName, final List<String> subEntityAttribues) {
this.jpaEntityFieldName = jpaEntityFieldName;
TargetFilterQueryFields(final String name, final List<String> subEntityAttribues) {
this.name = name;
this.subEntityAttributes = subEntityAttribues;
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.qfields;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the Tag model which can be used in the REST API e.g. for sorting etc.
@@ -18,18 +19,18 @@ import lombok.Getter;
@Getter
public enum TargetTagFields implements QueryField {
ID(TagFields.ID.getJpaEntityFieldName()),
NAME(TagFields.NAME.getJpaEntityFieldName()),
DESCRIPTION(TagFields.DESCRIPTION.getJpaEntityFieldName()),
COLOUR(TagFields.COLOUR.getJpaEntityFieldName()),
ID(TagFields.ID.getName()),
NAME(TagFields.NAME.getName()),
DESCRIPTION(TagFields.DESCRIPTION.getName()),
COLOUR(TagFields.COLOUR.getName()),
CREATEDAT("createdAt"),
CREATEDBY("createdBy"),
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy");
private final String jpaEntityFieldName;
private final String name;
TargetTagFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
TargetTagFields(final String name) {
this.name = name;
}
}

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.qfields;
import lombok.Getter;
import org.eclipse.hawkbit.ql.QueryField;
/**
* Describing the fields of the TargetType model which can be used in the REST API
@@ -26,9 +27,9 @@ public enum TargetTypeFields implements QueryField {
LASTMODIFIEDAT("lastModifiedAt"),
LASTMODIFIEDBY("lastModifiedBy");
private final String jpaEntityFieldName;
private final String name;
TargetTypeFields(final String jpaEntityFieldName) {
this.jpaEntityFieldName = jpaEntityFieldName;
TargetTypeFields(final String name) {
this.name = name;
}
}

View File

@@ -16,6 +16,7 @@ import java.util.List;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ScanResult;
import org.eclipse.hawkbit.ql.QueryField;
import org.junit.jupiter.api.Test;
class QueryFieldsTest {

View File

@@ -18,8 +18,11 @@ import java.util.Map;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.exception.GenericSpServerException;
import org.eclipse.hawkbit.ql.QueryException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.security.access.AccessDeniedException;
@@ -66,6 +69,9 @@ public class ExceptionMapper {
* @param e the thrown and catch exception
* @return the mapped exception
*/
// java:S3776 - complex but flat
// java:S3358 - easy to read
@SuppressWarnings({ "java:S3776", "java:S3358" })
public static Exception map(final Exception e) {
if (log.isTraceEnabled()) {
log.trace("Handling exception {}", e.getClass().getName(), e);
@@ -86,7 +92,8 @@ public class ExceptionMapper {
if (EXCEPTION_MAPPING.containsKey(mappedEx.getName())) {
try {
return (Exception) Class.forName(EXCEPTION_MAPPING.get(mappedEx.getName())).getConstructor(Throwable.class).newInstance(e);
} catch (final ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
} catch (final ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException ex) {
log.error(ex.getMessage(), ex);
return e;
}
@@ -101,12 +108,21 @@ public class ExceptionMapper {
if (mappingClass != null) {
try {
return (Exception) Class.forName(mappingClass).getConstructor(Throwable.class).newInstance(e);
} catch (final ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
} catch (final ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
InvocationTargetException ex) {
log.error(ex.getMessage(), ex);
return e;
}
}
if (e instanceof QueryException qe) {
return qe.getErrorCode() == QueryException.ErrorCode.INVALID_SYNTAX
? new RSQLParameterSyntaxException(qe.getMessage(), qe.getCause())
: (qe.getErrorCode() == QueryException.ErrorCode.UNSUPPORTED_FIELD
? new RSQLParameterUnsupportedFieldException(qe.getMessage(), qe.getCause())
: new GenericSpServerException(qe));
}
return e;
}

View File

@@ -22,10 +22,15 @@ import org.aopalliance.intercept.MethodInvocation;
import org.eclipse.hawkbit.artifact.encryption.ArtifactEncryption;
import org.eclipse.hawkbit.artifact.encryption.ArtifactEncryptionSecretsStorage;
import org.eclipse.hawkbit.artifact.encryption.ArtifactEncryptionService;
import org.eclipse.hawkbit.ql.Node.Comparison;
import org.eclipse.hawkbit.ql.QueryField;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.ql.jpa.QLSupport.NodeTransformer;
import org.eclipse.hawkbit.ql.jpa.QLSupport.QueryParser;
import org.eclipse.hawkbit.ql.rsql.RsqlParser;
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConfiguration;
import org.eclipse.hawkbit.repository.RepositoryProperties;
@@ -41,6 +46,8 @@ import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoActionCleanup;
@@ -58,10 +65,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
import org.eclipse.hawkbit.repository.jpa.ql.Node.Comparison;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport.NodeTransformer;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport.QueryParser;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
@@ -71,7 +74,6 @@ import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.scheduler.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAction;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator;
@@ -79,15 +81,13 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluati
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
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.RsqlParser;
import org.eclipse.hawkbit.repository.jpa.scheduler.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.jpa.scheduler.JpaAutoAssignExecutor;
import org.eclipse.hawkbit.repository.jpa.scheduler.JpaRolloutHandler;
import org.eclipse.hawkbit.repository.jpa.scheduler.RolloutScheduler;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
@@ -413,7 +413,8 @@ public class JpaRepositoryConfiguration {
* @return a new {@link AutoActionCleanup} bean
*/
@Bean
AutoCleanupScheduler.CleanupTask actionCleanup(final DeploymentManagement deploymentManagement, final TenantConfigurationManagement configManagement) {
AutoCleanupScheduler.CleanupTask actionCleanup(
final DeploymentManagement deploymentManagement, final TenantConfigurationManagement configManagement) {
return new AutoActionCleanup(deploymentManagement, configManagement);
}

View File

@@ -14,17 +14,17 @@ import java.util.Set;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.qfields.DistributionSetFields;
import org.eclipse.hawkbit.repository.qfields.DistributionSetTypeFields;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.qfields.TargetTagFields;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
// utility class to validate authorities when ACM is enabled
@NoArgsConstructor(access = AccessLevel.PRIVATE)

View File

@@ -18,9 +18,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.ql.QueryField;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.core.GrantedAuthority;

View File

@@ -36,9 +36,10 @@ import jakarta.persistence.criteria.Root;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.ql.QueryField;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.repository.RepositoryManagement;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
@@ -48,7 +49,6 @@ import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
import org.springframework.cache.Cache;

View File

@@ -24,9 +24,9 @@ import java.util.stream.Collectors;
import jakarta.persistence.EntityManager;
import org.eclipse.hawkbit.ql.QueryField;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.MetadataSupport;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
import org.eclipse.hawkbit.repository.jpa.model.WithMetadata;

View File

@@ -46,6 +46,7 @@ import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.ConfirmationManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.DeploymentManagement;
@@ -56,7 +57,6 @@ import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.SecurityTokenGeneratorHolder;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.UpdateMode;
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
@@ -79,7 +79,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
@@ -101,6 +100,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;

View File

@@ -38,7 +38,7 @@ import jakarta.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.eclipse.hawkbit.repository.qfields.ActionFields;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
@@ -63,7 +63,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
@@ -86,6 +85,7 @@ import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.eclipse.hawkbit.repository.qfields.ActionFields;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;

View File

@@ -28,7 +28,8 @@ import java.util.stream.Collectors;
import jakarta.persistence.EntityManager;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.qfields.DistributionSetFields;
import org.eclipse.hawkbit.ql.Node;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -44,8 +45,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.ql.Node;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
@@ -56,6 +55,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Statistic;
import org.eclipse.hawkbit.repository.qfields.DistributionSetFields;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;

View File

@@ -22,10 +22,9 @@ import jakarta.persistence.criteria.ListJoin;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.eclipse.hawkbit.repository.qfields.RolloutGroupFields;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
@@ -39,7 +38,6 @@ import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
@@ -47,6 +45,8 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.qfields.RolloutGroupFields;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;

View File

@@ -32,12 +32,12 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.im.authentication.SpRole;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
import org.eclipse.hawkbit.repository.qfields.RolloutFields;
import org.eclipse.hawkbit.repository.RolloutHelper;
import org.eclipse.hawkbit.repository.RolloutManagement;
import org.eclipse.hawkbit.repository.RolloutStatusCache;
@@ -61,7 +61,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
@@ -85,6 +84,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.qfields.RolloutFields;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.utils.ObjectCopyUtil;

View File

@@ -19,11 +19,10 @@ import jakarta.persistence.EntityManager;
import cz.jirutka.rsql.parser.RSQLParserException;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.ContextAware;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.qfields.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
@@ -36,7 +35,6 @@ import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
@@ -44,6 +42,8 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.qfields.TargetFilterQueryFields;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
@@ -80,7 +80,8 @@ class JpaTargetFilterQueryManagement
protected JpaTargetFilterQueryManagement(
final TargetFilterQueryRepository targetFilterQueryRepository, final EntityManager entityManager,
final TargetManagement<? extends Target> targetManagement, final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final TargetManagement<? extends Target> targetManagement,
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
final QuotaManagement quotaManagement, final TenantConfigurationManagement tenantConfigurationManagement,
final RepositoryProperties repositoryProperties,
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware, final AuditorAware<String> auditorAware) {

View File

@@ -34,8 +34,8 @@ import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.MapAttribute;
import jakarta.validation.constraints.NotEmpty;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -50,7 +50,6 @@ import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -58,6 +57,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page;

View File

@@ -30,21 +30,21 @@ import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.repository.TenantConfigurationRepository;
import org.eclipse.hawkbit.repository.model.PollStatus;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;

View File

@@ -7,11 +7,11 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.jpa.ql.Node;
import org.eclipse.hawkbit.ql.Node;
import org.junit.jupiter.api.Test;
class NodeTest {

View File

@@ -7,14 +7,14 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import org.eclipse.hawkbit.repository.qfields.ActionFields;
import org.eclipse.hawkbit.repository.TargetManagement.Create;
import org.eclipse.hawkbit.repository.exception.QueryException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -22,9 +22,9 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.qfields.ActionFields;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import org.springframework.orm.jpa.vendor.Database;
@@ -65,7 +65,9 @@ class RsqlActionFieldsTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(ActionFields.ID.name() + "=in=(" + action.getId() + ",10000000)", 1);
assertRSQLQuery(ActionFields.ID.name() + "=out=(" + action.getId() + ",10000000)", 10);
}/**
}
/**
* Test action by status
*/
@Test
@@ -76,7 +78,7 @@ class RsqlActionFieldsTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(ActionFields.ACTIVE.name() + "=out=(" + true + ")", 6);
final String rsql = ActionFields.ACTIVE.name() + "==true2";
assertThatExceptionOfType(QueryException.class)
assertThatExceptionOfType(RSQLParameterSyntaxException.class)
.as("RSQLParameterUnsupportedFieldException because active cannot be compared with 'true2'")
.isThrownBy(() -> assertRSQLQuery(rsql, 5));
}
@@ -92,7 +94,7 @@ class RsqlActionFieldsTest extends AbstractJpaIntegrationTest {
assertRSQLQuery(ActionFields.STATUS.name() + "=out=(" + Status.RUNNING + ")", 6);
final String rsql = ActionFields.STATUS.name() + "==not_a_status";
assertThatExceptionOfType(QueryException.class)
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
.as("RSQLParameterUnsupportedFieldException because status cannot be compared with 'not_a_status'")
.isThrownBy(() -> assertRSQLQuery(rsql, 5));
}
@@ -127,8 +129,7 @@ class RsqlActionFieldsTest extends AbstractJpaIntegrationTest {
}
private void assertRSQLQuery(final String rsql, final long expectedEntities) {
final Slice<Action> findEntity = deploymentManagement.findActionsByTarget(rsql, target.getControllerId(),
PageRequest.of(0, 100));
final Slice<Action> findEntity = deploymentManagement.findActionsByTarget(rsql, target.getControllerId(), PAGE);
final long countAllEntities = deploymentManagement.countActionsByTarget(rsql, target.getControllerId());
assertThat(findEntity).isNotNull();
assertThat(findEntity.getContent()).hasSize((int) expectedEntities);

View File

@@ -7,10 +7,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.repository.jpa.rsql.RsqlParser.parse;
import static org.eclipse.hawkbit.ql.rsql.RsqlParser.parse;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.junit.jupiter.api.Test;

View File

@@ -7,17 +7,17 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.qfields.RolloutFields;
import org.eclipse.hawkbit.repository.RolloutManagement.Create;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.qfields.RolloutFields;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
@@ -60,7 +60,8 @@ class RsqlRolloutFieldTest extends AbstractJpaIntegrationTest {
private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId, final String targetFilterQuery) {
return rolloutManagement.create(
Create.builder()
.distributionSet(distributionSetManagement.find(distributionSetId).get()).name(name).targetFilterQuery(targetFilterQuery)
.distributionSet(distributionSetManagement.find(distributionSetId).get()).name(name)
.targetFilterQuery(targetFilterQuery)
.build(),
amountGroups,
false,

View File

@@ -7,11 +7,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.qfields.RolloutGroupFields;
import org.eclipse.hawkbit.repository.RolloutManagement.Create;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -19,6 +18,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
import org.eclipse.hawkbit.repository.qfields.RolloutGroupFields;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
@@ -98,11 +98,11 @@ class RsqlRolloutGroupFieldTest extends AbstractJpaIntegrationTest {
assertThat(countTargetsAll).isEqualTo(expectedTargets);
}
private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId,
final String targetFilterQuery) {
private Rollout createRollout(final String name, final int amountGroups, final long distributionSetId, final String targetFilterQuery) {
return rolloutManagement.create(
Create.builder()
.distributionSet(distributionSetManagement.find(distributionSetId).get()).name(name).targetFilterQuery(targetFilterQuery)
.distributionSet(distributionSetManagement.find(distributionSetId).get()).name(name)
.targetFilterQuery(targetFilterQuery)
.build(),
amountGroups, false, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());

View File

@@ -7,16 +7,16 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValueCreate;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

View File

@@ -7,14 +7,14 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.Constants;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;

View File

@@ -7,16 +7,16 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.qfields.TagFields;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.qfields.TagFields;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -15,19 +15,22 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.Arrays;
import java.util.Map;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.ql.QueryException;
import org.eclipse.hawkbit.ql.QueryField;
import org.eclipse.hawkbit.ql.jpa.QLSupport;
import org.eclipse.hawkbit.repository.TargetManagement.Create;
import org.eclipse.hawkbit.repository.TargetTagManagement;
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
import org.eclipse.hawkbit.repository.TargetTypeManagement;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -344,27 +347,26 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
*/
@Test
void rsqlValidTargetFields() {
final QLSupport qlSupport = QLSupport.getInstance();
qlSupport.validate(
validate(
"ID == '0123' and NAME == abcd and DESCRIPTION == absd and CREATEDAT =lt= 0123 and LASTMODIFIEDAT =gt= 0123" +
" and CONTROLLERID == 0123 and UPDATESTATUS == PENDING and IPADDRESS == 0123 and LASTCONTROLLERREQUESTAT == 0123" +
" and tag == beta",
TargetFields.class, JpaTarget.class);
qlSupport.validate(
validate(
"ASSIGNEDDS.name == abcd and ASSIGNEDDS.version == 0123 and INSTALLEDDS.name == abcd and INSTALLEDDS.version == 0123",
TargetFields.class, JpaTarget.class);
qlSupport.validate(
validate(
"ATTRIBUTE.subkey1 == test and ATTRIBUTE.subkey2 == test and METADATA.metakey1 == abcd and METADATA.metavalue2 == asdfg",
TargetFields.class, JpaTarget.class);
qlSupport.validate(
validate(
"CREATEDAT =lt= ${NOW_TS} and LASTMODIFIEDAT =ge= ${OVERDUE_TS}",
TargetFields.class, JpaTarget.class);
qlSupport.validate(
validate(
"ATTRIBUTE.test.dot == test and ATTRIBUTE.subkey2 == test and METADATA.test.dot == abcd and METADATA.metavalue2 == asdfg",
TargetFields.class, JpaTarget.class);
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
.isThrownBy(() -> qlSupport.validate("wrongfield == abcd", TargetFields.class, JpaTarget.class));
.isThrownBy(() -> validate("wrongfield == abcd", TargetFields.class, JpaTarget.class));
}
/**
@@ -418,8 +420,15 @@ class RsqlTargetFieldTest extends AbstractJpaIntegrationTest {
}
private void assertRSQLQueryThrowsException(final String rsql) {
final QLSupport qlSupport = QLSupport.getInstance();
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class)
.isThrownBy(() -> qlSupport.validate(rsql, TargetFields.class, JpaTarget.class));
.isThrownBy(() -> validate(rsql, TargetFields.class, JpaTarget.class));
}
private <A extends Enum<A> & QueryField> void validate(final String query, final Class<A> queryFieldType, final Class<?> jpaType) {
try {
QLSupport.getInstance().validate(query, queryFieldType, jpaType);
} catch (final QueryException e) {
throw ExceptionMapper.mapRe(e);
}
}
}

View File

@@ -7,17 +7,17 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.eclipse.hawkbit.repository.qfields.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.qfields.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

View File

@@ -7,20 +7,20 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.eclipse.hawkbit.repository.qfields.QueryField;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.ql.QueryField;
import org.eclipse.hawkbit.ql.jpa.utils.QlToSql;
import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.ql.utils.HawkbitQlToSql;
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
import org.eclipse.hawkbit.repository.qfields.TargetFields;
import org.eclipse.hawkbit.repository.test.TestConfiguration;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -40,7 +40,7 @@ import org.springframework.test.context.ContextConfiguration;
class RsqlToSqlTest {
private static final boolean FULL = Boolean.getBoolean("full");
private HawkbitQlToSql rsqlToSQL;
private QlToSql rsqlToSQL;
@Test
void printPG() {
@@ -106,16 +106,22 @@ class RsqlToSqlTest {
@Test
void printComplex() {
print(JpaTarget.class, TargetFields.class, "attribute.key1==00 and (attribute.key2==02 or attribute.key2==01)");
print(JpaTarget.class, TargetFields.class, "(attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==01");
print(JpaTarget.class, TargetFields.class, "(attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==01 and updateStatus!=pending");
print(JpaTarget.class, TargetFields.class, "((attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==03 and updateStatus!=pending)");
print(JpaTarget.class, TargetFields.class, "((attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==01 and updateStatus!=pending)");
print(JpaTarget.class, TargetFields.class,
"(attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==01");
print(JpaTarget.class, TargetFields.class,
"(attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==01 and updateStatus!=pending");
print(JpaTarget.class, TargetFields.class,
"((attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==03 and updateStatus!=pending)");
print(JpaTarget.class, TargetFields.class,
"((attribute.key1==00 or attribute.key1==01) and (attribute.key2==02 or attribute.key2==01) and attribute.key3==01 and updateStatus!=pending)");
}
@Test
void printVeryComplex() {
print(JpaTarget.class, TargetFields.class, "(attribute.key1==00 or attribute.key1==01) and ((attribute.key2==02 or attribute.key2==01) and (attribute.key4==02 or attribute.key5==01)) and attribute.key3==01 and updateStatus!=pending");
print(JpaTarget.class, TargetFields.class, "(attribute.key1==00 or attribute.key1==01) and ((attribute.key2==02 or attribute.key2==01) or (attribute.key4==02 or attribute.key5==01)) and attribute.key3==01 and updateStatus!=pending");
print(JpaTarget.class, TargetFields.class,
"(attribute.key1==00 or attribute.key1==01) and ((attribute.key2==02 or attribute.key2==01) and (attribute.key4==02 or attribute.key5==01)) and attribute.key3==01 and updateStatus!=pending");
print(JpaTarget.class, TargetFields.class,
"(attribute.key1==00 or attribute.key1==01) and ((attribute.key2==02 or attribute.key2==01) or (attribute.key4==02 or attribute.key5==01)) and attribute.key3==01 and updateStatus!=pending");
}
private static String from(final String sql) {
@@ -124,7 +130,7 @@ class RsqlToSqlTest {
@PersistenceContext
private void setEntityManager(final EntityManager entityManager) {
rsqlToSQL = new HawkbitQlToSql(entityManager);
rsqlToSQL = new QlToSql(entityManager);
}
private <T, A extends Enum<A> & QueryField> void print(final Class<T> domainClass, final Class<A> fieldsClass, final String rsql) {

View File

@@ -7,7 +7,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.hawkbit.repository.jpa.rsql;
package org.eclipse.hawkbit.ql.rsql;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@@ -16,9 +16,9 @@ import java.util.concurrent.Callable;
import org.apache.commons.text.StringSubstitutor;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
import org.eclipse.hawkbit.repository.helper.TenantConfigurationManagementHolder;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;

View File

@@ -1199,7 +1199,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void assignDistributionSetAndAddFinishedActionStatus() {
final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, ActionStatusFields.ID.getJpaEntityFieldName());
final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, ActionStatusFields.ID.getName());
final DeploymentResult deployResWithDsA = prepareComplexRepo("undep-A-T", 2, "dep-A-T", 4, 1, "dsA");
final DeploymentResult deployResWithDsB = prepareComplexRepo("undep-B-T", 3, "dep-B-T", 5, 1, "dsB");

View File

@@ -28,7 +28,6 @@
<module>hawkbit-repository-jpa-api</module>
<module>hawkbit-repository-jpa-eclipselink</module>
<module>hawkbit-repository-jpa-hibernate</module>
<module>hawkbit-repository-jpa-ql</module>
<module>hawkbit-repository-jpa</module>
<module>hawkbit-repository-jpa-flyway</module>

View File

@@ -709,6 +709,7 @@
</profiles>
<modules>
<module>hawkbit-ql-jpa</module>
<module>hawkbit-core</module>
<module>hawkbit-security-core</module>
<module>hawkbit-artifact</module>