Extract QL support in a top level module (#2808)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
69
MIGRATION.md
69
MIGRATION.md
@@ -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'`
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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())));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
@@ -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());
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
@@ -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");
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user