From 158da91c742274c5623ba0f5de74c196de1bae17 Mon Sep 17 00:00:00 2001 From: Jeroen Laverman Date: Wed, 9 Dec 2020 09:04:08 +0100 Subject: [PATCH 01/10] Update host of sandbox to new eclipse projects.io --- .../.sandbox/stacks/sandbox/docker-compose-stack.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml b/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml index 2c55bafc1..14622d4fc 100644 --- a/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml +++ b/hawkbit-runtime/.sandbox/stacks/sandbox/docker-compose-stack.yml @@ -37,8 +37,8 @@ services: - 'SPRING_RABBITMQ_HOST=rabbitmq' - 'SPRING_RABBITMQ_USERNAME=guest' - 'SPRING_RABBITMQ_PASSWORD=guest' - - 'VIRTUAL_HOST=hawkbit.eclipse.org' - - 'LETSENCRYPT_HOST=hawkbit.eclipse.org' + - 'VIRTUAL_HOST=hawkbit.eclipseprojects.io' + - 'LETSENCRYPT_HOST=hawkbit.eclipseprojects.io' # --------------------- # hawkBit simulator From 76fa389eeb256b6a3e76f37670d4ec585a3c8738 Mon Sep 17 00:00:00 2001 From: Dominic Schabel Date: Tue, 15 Dec 2020 17:13:17 +0100 Subject: [PATCH 02/10] Unused time field removed from DDI docs Back in the days "time" was specified in API but never implemented/evaluated by hawkBit. So we better remove it from the docs in order to avoid confusion. Signed-off-by: Dominic Schabel --- .../hawkbit/rest/ddi/documentation/DdiApiModelProperties.java | 2 -- .../rest/ddi/documentation/RootControllerDocumentationTest.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java index adc842c90..845f4f9b0 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java @@ -16,8 +16,6 @@ final class DdiApiModelProperties { // Direct Device Integration API static final String CONTROLLER_ID = "id of the controller"; - static final String TARGET_TIME = "time on the target device"; - static final String TARGET_STATUS = "target action status"; static final String TARGET_EXEC_STATUS = "status of the action execution"; diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java index fba390a53..002036996 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java @@ -182,7 +182,6 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio parameterWithName("controllerId").description(DdiApiModelProperties.CONTROLLER_ID), parameterWithName("actionId").description(DdiApiModelProperties.ACTION_ID_CANCELED)), requestFields(optionalRequestFieldWithPath("id").description(DdiApiModelProperties.ACTION_ID), - optionalRequestFieldWithPath("time").description(DdiApiModelProperties.TARGET_TIME), requestFieldWithPath("status").description(DdiApiModelProperties.TARGET_STATUS), requestFieldWithPath("status.execution") .description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum") @@ -386,7 +385,6 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio parameterWithName("actionId").description(DdiApiModelProperties.ACTION_ID)), requestFields(optionalRequestFieldWithPath("id").description(DdiApiModelProperties.ACTION_ID), - optionalRequestFieldWithPath("time").description(DdiApiModelProperties.TARGET_TIME), requestFieldWithPath("status").description(DdiApiModelProperties.TARGET_STATUS), requestFieldWithPath("status.execution") .description(DdiApiModelProperties.TARGET_EXEC_STATUS).type("enum") From 6df3a04f517f532a3bcae26a2067e3e22e2c81c1 Mon Sep 17 00:00:00 2001 From: Dominic Schabel Date: Wed, 16 Dec 2020 17:42:41 +0100 Subject: [PATCH 03/10] Removed "time" field from tests Signed-off-by: Dominic Schabel --- .../eclipse/hawkbit/rest/util/JsonBuilder.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java index 5dc1581a6..2c537525d 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java +++ b/hawkbit-rest/hawkbit-rest-core/src/test/java/org/eclipse/hawkbit/rest/util/JsonBuilder.java @@ -225,14 +225,11 @@ public abstract class JsonBuilder { public static String deploymentActionFeedback(final String id, final String execution, final String finished, final Collection messages) throws JSONException { - return new JSONObject().put("id", id).put("time", "20140511T121314") - .put("status", - new JSONObject().put("execution", execution) - .put("result", - new JSONObject().put("finished", finished).put("progress", - new JSONObject().put("cnt", 2).put("of", 5))) - .put("details", new JSONArray(messages))) - .toString(); + return new JSONObject().put("id", id).put("status", new JSONObject().put("execution", execution) + .put("result", + new JSONObject().put("finished", finished).put("progress", + new JSONObject().put("cnt", 2).put("of", 5))) + .put("details", new JSONArray(messages))).toString(); } /** @@ -566,7 +563,7 @@ public abstract class JsonBuilder { public static String cancelActionFeedback(final String id, final String execution, final String message) throws JSONException { - return new JSONObject().put("id", id).put("time", "20140511T121314") + return new JSONObject().put("id", id) .put("status", new JSONObject().put("execution", execution) .put("result", new JSONObject().put("finished", "success")) From 2191db40d704d23f9d8ec443ccacceba71daccab Mon Sep 17 00:00:00 2001 From: Florian Ruschbaschan <46709526+floruschbaschan@users.noreply.github.com> Date: Mon, 11 Jan 2021 15:50:06 +0100 Subject: [PATCH 04/10] Add Github actions stale and first-interaction workflow (#1054) * Add Github actions workflow Signed-off-by: Florian Ruschbaschan * Change secret name Signed-off-by: Florian Ruschbaschan --- .github/config.yml | 5 ----- .github/no-response.yml | 8 -------- .github/workflows/first-interaction.yml | 14 ++++++++++++++ .github/workflows/stale.yml | 23 +++++++++++++++++++++++ 4 files changed, 37 insertions(+), 13 deletions(-) delete mode 100644 .github/config.yml delete mode 100644 .github/no-response.yml create mode 100644 .github/workflows/first-interaction.yml create mode 100644 .github/workflows/stale.yml diff --git a/.github/config.yml b/.github/config.yml deleted file mode 100644 index 9b2879d16..000000000 --- a/.github/config.yml +++ /dev/null @@ -1,5 +0,0 @@ -newPRWelcomeComment: > - Thanks for taking the time to contribute to hawkBit! We really appreciate this. - Make yourself comfortable while I'm looking for a committer to help you with your contribution. - - Please make sure you read the [contribution guide](https://github.com/eclipse/hawkbit/blob/master/CONTRIBUTING.md) and signed the Eclipse Contributor Agreement (ECA). diff --git a/.github/no-response.yml b/.github/no-response.yml deleted file mode 100644 index 4cc4708c7..000000000 --- a/.github/no-response.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Configuration for probot-no-response - https://github.com/probot/no-response - -daysUntilClose: 15 -responseRequiredLabel: awaiting - -closeComment: > - There has been no response from the original author so I closed this issue. - Please reach out if you have or find the answers we need so that we can investigate further. diff --git a/.github/workflows/first-interaction.yml b/.github/workflows/first-interaction.yml new file mode 100644 index 000000000..8740ee95d --- /dev/null +++ b/.github/workflows/first-interaction.yml @@ -0,0 +1,14 @@ +name: First User Interaction + +on: [pull_request] + +jobs: + greeting: + runs-on: ubuntu-latest + steps: + - uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.PAT_SECRET }} + pr-message: |- + Thanks @${{ github.actor }} for taking the time to contribute to hawkBit! We really appreciate this. Make yourself comfortable while I'm looking for a committer to help you with your contribution. + Please make sure you read the [contribution guide](https://github.com/eclipse/hawkbit/blob/master/CONTRIBUTING.md) and signed the Eclipse Contributor Agreement (ECA). diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..0eb670e43 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,23 @@ +name: Mark & close stale issues + +on: + workflow_dispatch: + schedule: + - cron: "0 0 * * *" + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + repo-token: ${{ secrets.PAT_SECRET }} + days-before-stale: -1 + days-before-close: 15 + stale-issue-label: 'awaiting' + close-issue-message: |- + There has been no response from the original author so I closed this issue. + Please reach out if you have or find the answers we need so that we can investigate further. + only-labels: 'awaiting' + skip-stale-issue-message: 'true' + skip-stale-pr-message: 'true' From 8816396d18880d2020743ce2e83a08446449d0db Mon Sep 17 00:00:00 2001 From: Florian Ruschbaschan <46709526+floruschbaschan@users.noreply.github.com> Date: Tue, 12 Jan 2021 11:29:48 +0100 Subject: [PATCH 05/10] Add existsByInstalledOrAssignedDistributionSet (#1064) Signed-off-by: Florian Ruschbaschan --- .../hawkbit/repository/TargetManagement.java | 14 ++++++++++++++ .../repository/jpa/JpaTargetManagement.java | 7 +++++++ .../hawkbit/repository/jpa/TargetRepository.java | 10 ++++++++++ .../repository/jpa/TargetManagementTest.java | 12 +++++++++++- ...wModulesToDistributionSetAssignmentSupport.java | 4 ++-- 5 files changed, 44 insertions(+), 3 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java index 58be29bca..7194fab9e 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java @@ -125,6 +125,20 @@ public interface TargetManagement { + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) long countByInstalledDistributionSet(long distId); + /** + * Checks if there is already a {@link Target} that has the given distribution set Id assigned or installed. + * + * @param distId + * to search for + * @return true if a {@link Target} exists. + * + * @throws EntityNotFoundException + * if distribution set with given ID does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR + + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + boolean existsByInstalledOrAssignedDistributionSet(long distId); + /** * Count {@link TargetFilterQuery}s for given target filter query. * diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java index 8e1450480..e2549c32f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java @@ -646,6 +646,13 @@ public class JpaTargetManagement implements TargetManagement { return targetRepository.countByInstalledDistributionSetId(distId); } + @Override + public boolean existsByInstalledOrAssignedDistributionSet(final long distId) { + throwEntityNotFoundIfDsDoesNotExist(distId); + + return targetRepository.existsByInstalledOrAssignedDistributionSet(distId); + } + @Override public Page findByTargetFilterQueryAndNonDS(final Pageable pageRequest, final long distributionSetId, final String targetFilterQuery) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java index 59146744e..474920f76 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java @@ -208,6 +208,16 @@ public interface TargetRepository extends BaseEntityRepository, */ Long countByInstalledDistributionSetId(Long distId); + /** + * Checks if there is already a {@link Target} that has the given distribution set Id assigned or installed. + * + * @param distId + * to check + * @return true if a {@link Target} exists. + */ + @Query("SELECT CASE WHEN COUNT(t)>0 THEN 'true' ELSE 'false' END FROM JpaTarget t WHERE t.installedDistributionSet.id=:distId OR t.assignedDistributionSet.id=:distId") + boolean existsByInstalledOrAssignedDistributionSet(@Param("distId") Long distId); + /** * Finds all {@link Target}s in the repository. * diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java index 3bb78a538..992af5e1d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java @@ -39,9 +39,9 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; +import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException; -import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata; @@ -108,6 +108,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { "DistributionSet"); verifyThrownExceptionBy(() -> targetManagement.countByInstalledDistributionSet(NOT_EXIST_IDL), "DistributionSet"); + verifyThrownExceptionBy(() -> targetManagement.existsByInstalledOrAssignedDistributionSet(NOT_EXIST_IDL), + "DistributionSet"); verifyThrownExceptionBy(() -> targetManagement.countByTargetFilterQuery(NOT_EXIST_IDL), "TargetFilterQuery"); verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDS(NOT_EXIST_IDL, "name==*"), @@ -465,10 +467,14 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { .isEqualTo(0); assertThat(targetManagement.countByInstalledDistributionSet(set.getId())).as("Target count is wrong") .isEqualTo(0); + assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set.getId())).as("Target count is wrong") + .isFalse(); assertThat(targetManagement.countByAssignedDistributionSet(set2.getId())).as("Target count is wrong") .isEqualTo(0); assertThat(targetManagement.countByInstalledDistributionSet(set2.getId())).as("Target count is wrong") .isEqualTo(0); + assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())).as("Target count is wrong") + .isFalse(); Target target = createTargetWithAttributes("4711"); @@ -488,10 +494,14 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { .isEqualTo(0); assertThat(targetManagement.countByInstalledDistributionSet(set.getId())).as("Target count is wrong") .isEqualTo(1); + assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set.getId())).as("Target count is wrong") + .isTrue(); assertThat(targetManagement.countByAssignedDistributionSet(set2.getId())).as("Target count is wrong") .isEqualTo(1); assertThat(targetManagement.countByInstalledDistributionSet(set2.getId())).as("Target count is wrong") .isEqualTo(0); + assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())).as("Target count is wrong") + .isTrue(); assertThat(target.getLastTargetQuery()).as("Target query is not work").isGreaterThanOrEqualTo(current); assertThat(deploymentManagement.getAssignedDistributionSet("4711").get()).as("Assigned ds size is wrong") .isEqualTo(set2); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/grid/support/assignment/SwModulesToDistributionSetAssignmentSupport.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/grid/support/assignment/SwModulesToDistributionSetAssignmentSupport.java index b717c01e6..cb3d458ac 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/grid/support/assignment/SwModulesToDistributionSetAssignmentSupport.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/grid/support/assignment/SwModulesToDistributionSetAssignmentSupport.java @@ -114,8 +114,8 @@ public class SwModulesToDistributionSetAssignmentSupport return false; } - if (targetManagement.countByFilters(null, null, null, ds.getId(), Boolean.FALSE, "") > 0) { - /* Distribution is already assigned */ + if (targetManagement.existsByInstalledOrAssignedDistributionSet(ds.getId())) { + /* Distribution is already assigned/installed */ notification.displayValidationError(i18n.getMessage("message.dist.inuse", ds.getNameVersion())); return false; } From 94b7c12cde1b38eda5414bd88d6d068008cfb9f9 Mon Sep 17 00:00:00 2001 From: Dominic Schabel Date: Tue, 12 Jan 2021 11:56:44 +0100 Subject: [PATCH 06/10] Fixes #1067 JSON body response for HTTP 404 error may contain unsafe URL path characters. Thus removing path from the response Signed-off-by: Dominic Schabel --- ...orController.java => ErrorController.java} | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) rename hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/{StreamAwareErrorController.java => ErrorController.java} (64%) diff --git a/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/StreamAwareErrorController.java b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java similarity index 64% rename from hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/StreamAwareErrorController.java rename to hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java index 39793afe7..da4c46be2 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/StreamAwareErrorController.java +++ b/hawkbit-runtime/hawkbit-update-server/src/main/java/org/eclipse/hawkbit/app/ErrorController.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.app; +import java.util.Map; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -23,22 +25,23 @@ import org.springframework.web.bind.annotation.RequestMapping; /** * Error page controller that ensures that ocet stream does not return text in * case of an error. - * */ @Controller // Exception squid:S3752 - errors need handling for all methods @SuppressWarnings("squid:S3752") -public class StreamAwareErrorController extends BasicErrorController { +public class ErrorController extends BasicErrorController { + + private static final String PATH = "path"; /** - * A new {@link StreamAwareErrorController}. + * A new {@link ErrorController}. * * @param errorAttributes * the error attributes * @param serverProperties * configuration properties */ - public StreamAwareErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) { + public ErrorController(final ErrorAttributes errorAttributes, final ServerProperties serverProperties) { super(errorAttributes, serverProperties.getError()); } @@ -48,4 +51,19 @@ public class StreamAwareErrorController extends BasicErrorController { return new ResponseEntity<>(status); } + @Override + @RequestMapping + public ResponseEntity> error(final HttpServletRequest request) { + final HttpStatus status = getStatus(request); + final Map body = getErrorAttributesWithoutPath(request); + return new ResponseEntity<>(body, status); + } + + private Map getErrorAttributesWithoutPath(final HttpServletRequest request) { + final Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); + if (body != null && body.containsKey(PATH)) { + body.remove(PATH); + } + return body; + } } From 4ea5d7655bae0656b6294f761c9d2d0898d54a82 Mon Sep 17 00:00:00 2001 From: Dominic Schabel Date: Wed, 13 Jan 2021 17:46:53 +0100 Subject: [PATCH 07/10] Bosch licence header 2021 added Signed-off-by: Dominic Schabel --- licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt | 6 ++++++ pom.xml | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt diff --git a/licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt b/licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt new file mode 100644 index 000000000..a1239d150 --- /dev/null +++ b/licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt @@ -0,0 +1,6 @@ +Copyright (c) 2021 Bosch.IO GmbH and others. + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html diff --git a/pom.xml b/pom.xml index 2322cc211..0f2edb295 100644 --- a/pom.xml +++ b/pom.xml @@ -329,6 +329,8 @@ licenses/LICENSE_HEADER_TEMPLATE_BOSCH.txt licenses/LICENSE_HEADER_TEMPLATE_BOSCH_18.txt licenses/LICENSE_HEADER_TEMPLATE_BOSCH_19.txt + licenses/LICENSE_HEADER_TEMPLATE_BOSCH_20.txt + licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_18.txt licenses/LICENSE_HEADER_TEMPLATE_DEVOLO_19.txt licenses/LICENSE_HEADER_TEMPLATE_KIWIGRID_19.txt From e9f11d2a2094a145fab53e5fce39de09c5994627 Mon Sep 17 00:00:00 2001 From: Kai Zimmermann Date: Thu, 14 Jan 2021 09:07:03 +0100 Subject: [PATCH 08/10] DB and RabbitMQ integration tests and PostgreSQL testing/bug fixing (#1047) * Initial matrixSigned-off-by: Kai Zimmermann * License header * MySQL DB testSigned-off-by: Kai Zimmermann * Create matrix for DBsSigned-off-by: Kai Zimmermann * RabbitMQ and H2Signed-off-by: Kai Zimmermann * MySQL 8.0Signed-off-by: Kai Zimmermann * Postgresql test supportSigned-off-by: Kai Zimmermann * Postgresql test supportSigned-off-by: Kai Zimmermann * Fix DB issues post and mssql Signed-off-by: Kai Zimmermann * Wait MSSQL Signed-off-by: Kai Zimmermann * Fix postgresql tests. Signed-off-by: Kai Zimmermann * MSSQL startup fix.Signed-off-by: Kai Zimmermann * Fix syntax error Signed-off-by: Kai Zimmermann * Further fix postgres tests.Signed-off-by: Kai Zimmermann * Revert unnecessary changes. Signed-off-by: Kai Zimmermann * Add SonarCloud Signed-off-by: Kai Zimmermann * Simplify devcontainer. Test JDK 15Signed-off-by: Kai Zimmermann --- .azure-pipelines/integration-tests.yml | 119 ++++++++++++++++++ .azure-pipelines/maven-template.yml | 50 ++++++++ .azure-pipelines/rabbitmq-template.yml | 9 ++ .devcontainer/Dockerfile | 9 ++ .devcontainer/devcontainer.json | 35 ++++++ README.md | 6 +- .../jpa/JpaDeploymentManagement.java | 15 +-- .../repository/jpa/JpaSystemManagement.java | 15 ++- .../repository/jpa/rsql/RSQLUtility.java | 36 +++--- .../jpa/AbstractJpaIntegrationTest.java | 9 ++ .../jpa/rsql/RSQLActionFieldsTest.java | 18 ++- .../rsql/RSQLDistributionSetFieldTest.java | 18 ++- .../jpa/rsql/RSQLRolloutGroupFields.java | 17 ++- .../jpa/rsql/RSQLSoftwareModuleFieldTest.java | 20 ++- .../RSQLSoftwareModuleTypeFieldsTest.java | 14 +++ .../rsql/RSQLTargetFilterQueryFieldsTest.java | 15 +++ .../repository/jpa/rsql/RSQLUtilityTest.java | 3 +- .../hawkbit-repository-test/pom.xml | 4 + .../test/util/AbstractIntegrationTest.java | 3 +- .../test/util/AbstractSqlTestDatabase.java | 55 ++++++++ .../test/util/MsSqlTestDatabase.java | 37 ++---- .../test/util/MySqlTestDatabase.java | 37 ++---- .../test/util/PostgreSqlTestDatabase.java | 68 ++++++++++ .../rest/resource/MgmtTargetResourceTest.java | 27 +++- .../org/eclipse/hawkbit/app/CorsTest.java | 21 ++-- .../LICENSE_HEADER_TEMPLATE_MICROSOFT_20.txt | 6 + pom.xml | 5 +- 27 files changed, 557 insertions(+), 114 deletions(-) create mode 100644 .azure-pipelines/integration-tests.yml create mode 100644 .azure-pipelines/maven-template.yml create mode 100644 .azure-pipelines/rabbitmq-template.yml create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractSqlTestDatabase.java create mode 100644 hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/PostgreSqlTestDatabase.java create mode 100644 licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_20.txt diff --git a/.azure-pipelines/integration-tests.yml b/.azure-pipelines/integration-tests.yml new file mode 100644 index 000000000..b9fe187ec --- /dev/null +++ b/.azure-pipelines/integration-tests.yml @@ -0,0 +1,119 @@ +# Build hawkBit and run tests with Apache Maven. +# Runs a matrix of various DB,JDK,RabbitMQ versions that are supported by hawkBit +# +# Requires the SonarCloud plugin and connection setup +# https://kaizimmerm.com/post/azure-pipelines-for-the-oss-developer/#analyze-the-code-with-sonarcloud +# +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/en-us/azure/devops/pipelines/ecosystems/java +# https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema +# https://docs.microsoft.com/en-us/azure/devops/pipelines/release/caching?view=azure-devops#maven + +pool: + vmImage: "ubuntu-18.04" + +# Default values for the hawbit build. Can be overriden in the variable group 'hawkbit' +# see https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups +variables: + # Defines defaults + - name: sonarCloudConnection + value: 'hawkBitSonar' + - name: sonarCloudOrganization + value: 'hawkbit' + - name: sonarProjectKey + value: 'org.eclipse:hawkbit' + # Override defaults + - group: hawkbit + +jobs: + - job: JDK_11 + displayName: Verify with JDK-11 and SonarCloud analysis + steps: + - template: rabbitmq-template.yml + - template: maven-template.yml + parameters: + mavenGoals: "verify -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=$(sonarCloudOrganization) -Dsonar.projectKey=$(sonarProjectKey)" + jdkVersionOption: "1.11" + sonarQubeRunAnalysis: true + sonarCloudConnection: $(sonarCloudConnection) + sonarCloudOrganization: $(sonarCloudOrganization) + - job: JDK_8 + displayName: Build with JDK-8 (hawkBit default) + steps: + - template: maven-template.yml + parameters: + mavenGoals: "install license:check" + - job: + dependsOn: JDK_8 + condition: succeeded() + displayName: RABBIT + strategy: + matrix: + 3.6: + rabbitmqVersion: "3.6" + 3.7: + rabbitmqVersion: "3.7" + 3.8: + rabbitmqVersion: "3.8" + steps: + - template: rabbitmq-template.yml + parameters: + rabbitmqVersion: $(rabbitmqVersion) + - template: maven-template.yml + parameters: + mavenGoals: "verify" + - job: + dependsOn: JDK_8 + condition: succeeded() + displayName: MYSQL + strategy: + matrix: + 5.6: + dbVersion: "5.6" + 5.7: + dbVersion: "5.7" + steps: + - template: rabbitmq-template.yml + - script: "docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=8236472364 -e MYSQL_DATABASE=hawkbit -d mysql:$(dbVersion)" + displayName: "Setup MYSQL Database docker instance" + - template: maven-template.yml + parameters: + mavenGoals: "verify -Dspring.jpa.database=MYSQL -Dspring.datasource.driverClassName=org.mariadb.jdbc.Driver -Dspring.datasource.url=jdbc:mysql://localhost:3306/hawkbit -Dspring.datasource.username=root -Dspring.datasource.password=8236472364" + - job: + dependsOn: JDK_8 + condition: succeeded() + displayName: MSSQL + strategy: + matrix: + 2017: + dbVersion: "2017-latest" + 2019: + dbVersion: "2019-latest" + steps: + - template: rabbitmq-template.yml + - script: | + docker run --name mssql -p 1433:1433 -e ACCEPT_EULA=Y -e SA_PASSWORD=1234567890.Ab -d mcr.microsoft.com/mssql/server:$(dbVersion) + until [ "`/usr/bin/docker inspect -f {{.State.Running}} mssql`" == "true" ]; do sleep 1; done + sleep 120 + until docker exec mssql /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "1234567890.Ab" -Q "CREATE DATABASE hawkbit"; do sleep 1; done + displayName: "Setup MSSQL Database docker instance" + - template: maven-template.yml + parameters: + mavenGoals: "verify -Dspring.jpa.database=SQL_SERVER -Dspring.datasource.url=jdbc:sqlserver://localhost:1433;database=hawkbit -Dspring.datasource.username=SA -Dspring.datasource.password=1234567890.Ab -Dspring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver" + - job: + dependsOn: JDK_8 + condition: succeeded() + displayName: POSTGRESQL + strategy: + matrix: + 12: + dbVersion: "12" + 13: + dbVersion: "13" + steps: + - template: rabbitmq-template.yml + - script: "docker run --name postgres -p 5432:5432 -e POSTGRES_PASSWORD=1234567890 -e POSTGRES_DB=hawkbit -d postgres:$(dbVersion)" + displayName: "Setup POSTGRESQL Database docker instance" + - template: maven-template.yml + parameters: + mavenGoals: "verify -Dspring.jpa.database=POSTGRESQL -Dspring.datasource.url=jdbc:postgresql://localhost:5432/hawkbit?currentSchema=hawkbit -Dspring.datasource.username=postgres -Dspring.datasource.password=1234567890 -Dspring.datasource.driverClassName=org.postgresql.Driver" diff --git a/.azure-pipelines/maven-template.yml b/.azure-pipelines/maven-template.yml new file mode 100644 index 000000000..5f8ba8445 --- /dev/null +++ b/.azure-pipelines/maven-template.yml @@ -0,0 +1,50 @@ +parameters: + - name: mavenGoals + displayName: Maven Goal + type: string + - name: jdkVersionOption + displayName: JDK Version + type: string + default: "1.8" + - name: sonarQubeRunAnalysis + displayName: Enable SonarQube analysis + type: boolean + default: false + - name: sonarCloudConnection + displayName: Optional SonarCloud connection + type: string + default: '' + - name: sonarCloudOrganization + displayName: Optional SonarCloud organization + type: string + default: '' + - name: mavenCacheFolder + displayName: Maven Cache Folder + type: string + default: $(Pipeline.Workspace)/.m2/repository + +steps: +- task: SonarCloudPrepare@1 + condition: eq('${{ parameters.sonarQubeRunAnalysis }}', true) + displayName: 'Prepare SonarCloud analysis configuration' + inputs: + SonarCloud: ${{ parameters.sonarCloudConnection }} + organization: ${{ parameters.sonarCloudOrganization }} + scannerMode: Other +- task: Cache@2 + inputs: + key: 'maven | "$(Agent.OS)" | "${{ parameters.jdkVersionOption }}" | **/pom.xml' + path: ${{ parameters.mavenCacheFolder }} + displayName: Cache Maven local repo +- task: Maven@3 + inputs: + mavenPomFile: "pom.xml" + mavenOptions: "-Xmx3072m" + javaHomeOption: "JDKVersion" + jdkVersionOption: ${{ parameters.jdkVersionOption }} + jdkArchitectureOption: "x64" + publishJUnitResults: true + sonarQubeRunAnalysis: ${{ parameters.sonarQubeRunAnalysis }} + sqMavenPluginVersionChoice: 'latest' + testResultsFiles: "**/surefire-reports/TEST-*.xml" + goals: "${{ parameters.mavenGoals }} -Dmaven.repo.local=${{ parameters.mavenCacheFolder }}" diff --git a/.azure-pipelines/rabbitmq-template.yml b/.azure-pipelines/rabbitmq-template.yml new file mode 100644 index 000000000..032beac37 --- /dev/null +++ b/.azure-pipelines/rabbitmq-template.yml @@ -0,0 +1,9 @@ +parameters: + - name: rabbitmqVersion + displayName: RabbitMQ Version + type: string + default: "3.8" + +steps: + - script: docker run -d --name rabbit -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_VHOST=/ rabbitmq:${{ parameters.rabbitmqVersion }}-management + displayName: "Setup RabbitMQ docker instance" diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..d26b2b9a8 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,9 @@ +# [Choice] Java version: 8. 11, 15 +ARG VARIANT=8 +FROM mcr.microsoft.com/vscode/devcontainers/java:${VARIANT} + +RUN su vscode -c "source /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\"" + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..f6214547e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,35 @@ +{ + "name": "Java", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update the VARIANT arg to pick a Java version: 8, 11, 15 + "VARIANT": "8" + } + }, + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": "/bin/bash", + "java.home": "/docker-java-home", + "java.format.settings.url": "https://raw.githubusercontent.com/eclipse/hawkbit/master/eclipse_codeformatter.xml", + "maven.executable.path": "/usr/local/sdkman/candidates/maven/current/bin/mvn" + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "vscjava.vscode-java-pack", + "DavidAnson.vscode-markdownlint", + "ms-azuretools.vscode-docker", + "ms-azure-devops.azure-pipelines", + "sonarsource.sonarlint-vscode", + "pivotal.vscode-spring-boot", + "vscjava.vscode-spring-boot-dashboard" + ], + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ + 8080 + ], + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "java -version", + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/README.md b/README.md index 69405733b..c4a23dd2c 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ There are clients outside of the Eclipse IoT eco system as well, e.g.: # Runtime dependencies and support -## Java Runtime Environment: 1.8 +## Java Runtime Environment: 1.8,11 ## SQL database @@ -56,12 +56,12 @@ There are clients outside of the Eclipse IoT eco system as well, e.g.: | --------------------------------- | :------------------------------------------------: | :-----------------------------------------------------------------------: | :-------------------------------------------------------: | :----------------------------------------------------------------: | :----------------: | | DDLs maintained by project | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | Test dependencies defined | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: | | -| Versions tested | 1.4 | MySQL 5.6/5.7, AWS Aurora | MS SQL Server 2017 | PostgreSQL 12.1 | DB2 Server v11.1 | +| Versions tested | 1.4 | MySQL 5.6/5.7, AWS Aurora | MS SQL Server 2017/2019 | PostgreSQL 12/13 | DB2 Server v11.1 | | Docker image with driver provided | :white_check_mark: | :white_check_mark: (Tag: "-mysql") | :white_check_mark: | :white_check_mark: | | | JDBC driver | [H2 1.4](https://github.com/h2database/h2database) | [MariaDB Connector/J 2.0](https://github.com/MariaDB/mariadb-connector-j) | [MSSQL-JDBC 6.4](https://github.com/Microsoft/mssql-jdbc) | [PostgreSQL JDBC Driver 42.2.10](https://github.com/pgjdbc/pgjdbc) | | | Status | Test, Dev | Production grade | Production grade | Test, Dev | Test, Dev | -## (Optional) RabbitMQ: 3.6,3.7 +## (Optional) RabbitMQ: 3.6,3.7,3.8 # Getting Started diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index e52308bd1..6fc3b8bef 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -123,6 +123,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED = new EnumMap<>(Database.class); QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.SQL_SERVER, "DELETE TOP (" + ACTION_PAGE_LIMIT + ") FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at "); + QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.POSTGRESQL, + "DELETE FROM sp_action WHERE id IN (SELECT id FROM sp_action WHERE tenant=#tenant AND status IN (%s) AND last_modified_at<#last_modified_at LIMIT " + + ACTION_PAGE_LIMIT + ")"); } private final EntityManager entityManager; @@ -209,8 +212,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl final Map> assignmentsByDsIds = convertRequest(validatedRequests); final List results = assignmentsByDsIds.entrySet().stream() - .map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(), actionMessage, - strategy)) + .map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(), + actionMessage, strategy)) .collect(Collectors.toList()); strategy.sendDeploymentEvents(results); return results; @@ -239,8 +242,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } } - private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy, final Long dsID, - final Collection targetsWithActionType, final String actionMessage, + private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy, + final Long dsID, final Collection targetsWithActionType, final String actionMessage, final AbstractDsAssignmentStrategy assignmentStrategy) { final RetryCallback retryCallback = retryContext -> assignDistributionSetToTargets( initiatedBy, dsID, targetsWithActionType, actionMessage, assignmentStrategy); @@ -427,9 +430,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl return targetsWithActionType.stream() .map(twt -> assignmentStrategy.createTargetAction(initiatedBy, twt, targets, set)) - .filter(Objects::nonNull) - .map(actionRepository::save) - .collect(Collectors.toList()); + .filter(Objects::nonNull).map(actionRepository::save).collect(Collectors.toList()); } private void createActionsStatus(final Collection actions, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java index 20adcd018..baebea565 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java @@ -38,6 +38,7 @@ import org.eclipse.persistence.config.PersistenceUnitProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.dao.ConcurrencyFailureException; @@ -45,6 +46,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.orm.jpa.vendor.Database; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.transaction.PlatformTransactionManager; @@ -123,11 +125,15 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst @Autowired private ArtifactRepository artifactRepository; + @Autowired + private JpaProperties properties; + @Override public SystemUsageReport getSystemUsageStatistics() { final Number count = (Number) entityManager.createNativeQuery( - "select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0") + "select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " + + (isPostgreSql(properties) ? "false" : "0")) .getSingleResult(); long sumOfArtifacts = 0; @@ -141,7 +147,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst .getSingleResult()).longValue(); final long artifacts = ((Number) entityManager.createNativeQuery( - "SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = 0") + "SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " + + (isPostgreSql(properties) ? "false" : "0")) .getSingleResult()).longValue(); final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action") @@ -151,6 +158,10 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst tenantMetaDataRepository.count()); } + private static boolean isPostgreSql(final JpaProperties properties) { + return Database.POSTGRESQL.equals(properties.getDatabase()); + } + @Override public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() { final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtility.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtility.java index b92376a7f..7e0e03dff 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtility.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtility.java @@ -35,6 +35,7 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.criteria.Subquery; +import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.text.StrLookup; import org.eclipse.hawkbit.repository.FieldNameProvider; import org.eclipse.hawkbit.repository.FieldValueConverter; @@ -375,7 +376,8 @@ public final class RSQLUtility { */ private Path getFieldPath(final A enumField, final String finalProperty) { return (Path) getFieldPath(root, getSubAttributesFrom(finalProperty), enumField.isMap(), - this::getJoinFieldPath).orElseThrow(() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null)); + this::getJoinFieldPath).orElseThrow( + () -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null)); } private Path getJoinFieldPath(final Path fieldPath, final String fieldNameSplit) { @@ -396,8 +398,8 @@ public final class RSQLUtility { return fieldPath; } - private static Optional> getFieldPath(final Root root, final String[] split, final boolean isMapKeyField, - final BiFunction, String, Path> joinFieldPathProvider) { + private static Optional> getFieldPath(final Root root, final String[] split, + final boolean isMapKeyField, final BiFunction, String, Path> joinFieldPathProvider) { Path fieldPath = null; for (int i = 0; i < split.length; i++) { if (!(isMapKeyField && i == (split.length - 1))) { @@ -685,7 +687,11 @@ public final class RSQLUtility { } private Predicate getEqualToPredicate(final Object transformedValue, final Path fieldPath) { - if (transformedValue instanceof String) { + if (transformedValue == null) { + return cb.isNull(pathOfString(fieldPath)); + } + + if ((transformedValue instanceof String) && !NumberUtils.isCreatable((String) transformedValue)) { if (StringUtils.isEmpty(transformedValue)) { return cb.or(cb.isNull(pathOfString(fieldPath)), cb.equal(pathOfString(fieldPath), "")); } @@ -694,10 +700,6 @@ public final class RSQLUtility { return cb.like(cb.upper(pathOfString(fieldPath)), sqlValue, ESCAPE_CHAR); } - if (transformedValue == null) { - return cb.isNull(pathOfString(fieldPath)); - } - return cb.equal(fieldPath, transformedValue); } @@ -708,7 +710,7 @@ public final class RSQLUtility { return toNotNullPredicate(fieldPath); } - if (transformedValue instanceof String) { + if ((transformedValue instanceof String) && !NumberUtils.isCreatable((String) transformedValue)) { if (StringUtils.isEmpty(transformedValue)) { return toNotNullAndNotEmptyPredicate(fieldPath); } @@ -725,7 +727,7 @@ public final class RSQLUtility { return toNotEqualWithSubQueryPredicate(enumField, sqlValue, fieldNames); } - return toNotEqualPredicate(fieldPath, transformedValue); + return toNullOrNotEqualPredicate(fieldPath, transformedValue); } private void clearOuterJoinsIfNotNeeded() { @@ -738,15 +740,15 @@ public final class RSQLUtility { return cb.isNotNull(pathOfString(fieldPath)); } - private Predicate toNotEqualPredicate(final Path fieldPath, final Object transformedValue) { - return cb.notEqual(fieldPath, transformedValue); - } - private Predicate toNullOrNotLikePredicate(final Path fieldPath, final String sqlValue) { return cb.or(cb.isNull(pathOfString(fieldPath)), cb.notLike(cb.upper(pathOfString(fieldPath)), sqlValue, ESCAPE_CHAR)); } + private Predicate toNullOrNotEqualPredicate(final Path fieldPath, final Object transformedValue) { + return cb.or(cb.isNull(pathOfString(fieldPath)), cb.notEqual(fieldPath, transformedValue)); + } + private Predicate toNotNullAndNotEmptyPredicate(final Path fieldPath) { return cb.and(cb.isNotNull(pathOfString(fieldPath)), cb.notEqual(pathOfString(fieldPath), "")); } @@ -797,9 +799,11 @@ public final class RSQLUtility { } private static Path getInnerFieldPath(final Root subqueryRoot, final String[] split, - final boolean isMapKeyField) { + final boolean isMapKeyField) { return getFieldPath(subqueryRoot, split, isMapKeyField, - (fieldPath, fieldNameSplit) -> getInnerJoinFieldPath(subqueryRoot, fieldPath, fieldNameSplit)).orElseThrow(() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null)); + (fieldPath, fieldNameSplit) -> getInnerJoinFieldPath(subqueryRoot, fieldPath, fieldNameSplit)) + .orElseThrow(() -> new RSQLParameterUnsupportedFieldException( + "RSQL field path cannot be empty", null)); } private static Path getInnerJoinFieldPath(final Root subqueryRoot, final Path fieldPath, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java index 04a9b4668..4b4cf1c05 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/AbstractJpaIntegrationTest.java @@ -31,7 +31,9 @@ import org.eclipse.hawkbit.repository.test.util.AbstractIntegrationTest; import org.eclipse.hawkbit.repository.test.util.RolloutTestApprovalStrategy; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration; +import org.springframework.orm.jpa.vendor.Database; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.transaction.annotation.Transactional; @@ -101,6 +103,13 @@ public abstract class AbstractJpaIntegrationTest extends AbstractIntegrationTest @Autowired protected RolloutTestApprovalStrategy approvalStrategy; + @Autowired + private JpaProperties jpaProperties; + + protected Database getDatabase() { + return jpaProperties.getDatabase(); + } + @Transactional(readOnly = true) protected List findActionsByRolloutAndStatus(final Rollout rollout, final Action.Status actionStatus) { return Lists.newArrayList(actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), actionStatus)); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java index 91810a190..527798be4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLActionFieldsTest.java @@ -24,6 +24,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; +import org.springframework.orm.jpa.vendor.Database; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -55,7 +56,7 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { final JpaAction newAction = new JpaAction(); newAction.setActionType(ActionType.SOFT); newAction.setDistributionSet(dsA); - newAction.setActive(i % 2 == 0); + newAction.setActive((i % 2) == 0); newAction.setStatus(Status.RUNNING); newAction.setTarget(target); newAction.setWeight(45); @@ -71,9 +72,18 @@ public class RSQLActionFieldsTest extends AbstractJpaIntegrationTest { public void testFilterByParameterId() { assertRSQLQuery(ActionFields.ID.name() + "==" + action.getId(), 1); assertRSQLQuery(ActionFields.ID.name() + "!=" + action.getId(), 10); - assertRSQLQuery(ActionFields.ID.name() + "==noExist*", 0); - assertRSQLQuery(ActionFields.ID.name() + "=in=(" + action.getId() + ",1000000)", 1); - assertRSQLQuery(ActionFields.ID.name() + "=out=(" + action.getId() + ",1000000)", 10); + assertRSQLQuery(ActionFields.ID.name() + "==" + -1, 0); + assertRSQLQuery(ActionFields.ID.name() + "!=" + -1, 11); + + // Not supported for numbers + if (Database.POSTGRESQL.equals(getDatabase())) { + return; + } + + assertRSQLQuery(ActionFields.ID.name() + "==*", 11); + assertRSQLQuery(ActionFields.ID.name() + "==noexist*", 0); + assertRSQLQuery(ActionFields.ID.name() + "=in=(" + action.getId() + ",10000000)", 1); + assertRSQLQuery(ActionFields.ID.name() + "=out=(" + action.getId() + ",10000000)", 10); } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLDistributionSetFieldTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLDistributionSetFieldTest.java index d64f1c079..9001e07cb 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLDistributionSetFieldTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLDistributionSetFieldTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.orm.jpa.vendor.Database; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -32,10 +33,12 @@ import io.qameta.allure.Story; @Story("RSQL filter distribution set") public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest { + private DistributionSet ds; + @Before public void seuptBeforeTest() { - DistributionSet ds = testdataFactory.createDistributionSet("DS"); + ds = testdataFactory.createDistributionSet("DS"); ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS")); createDistributionSetMetadata(ds.getId(), entityFactory.generateDsMetadata("metaKey", "metaValue")); @@ -59,7 +62,20 @@ public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest { @Test @Description("Test filter distribution set by id") public void testFilterByParameterId() { + assertRSQLQuery(DistributionSetFields.ID.name() + "==" + ds.getId(), 1); + assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + ds.getId(), 4); + assertRSQLQuery(DistributionSetFields.ID.name() + "==" + -1, 0); + assertRSQLQuery(DistributionSetFields.ID.name() + "!=" + -1, 5); + + // Not supported for numbers + if (Database.POSTGRESQL.equals(getDatabase())) { + return; + } + assertRSQLQuery(DistributionSetFields.ID.name() + "==*", 5); + assertRSQLQuery(DistributionSetFields.ID.name() + "==noexist*", 0); + assertRSQLQuery(DistributionSetFields.ID.name() + "=in=(" + ds.getId() + ",10000000)", 1); + assertRSQLQuery(DistributionSetFields.ID.name() + "=out=(" + ds.getId() + ",10000000)", 4); } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLRolloutGroupFields.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLRolloutGroupFields.java index 958fc1365..9f5db18ae 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLRolloutGroupFields.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLRolloutGroupFields.java @@ -21,6 +21,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.orm.jpa.vendor.Database; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -47,11 +48,21 @@ public class RSQLRolloutGroupFields extends AbstractJpaIntegrationTest { @Test @Description("Test filter rollout group by id") public void testFilterByParameterId() { + assertRSQLQuery(RolloutGroupFields.ID.name() + "==*", 3); assertRSQLQuery(RolloutGroupFields.ID.name() + "==" + rolloutGroupId, 1); assertRSQLQuery(RolloutGroupFields.ID.name() + "!=" + rolloutGroupId, 3); - assertRSQLQuery(RolloutGroupFields.ID.name() + "==noExist*", 0); - assertRSQLQuery(RolloutGroupFields.ID.name() + "=in=(" + rolloutGroupId + ")", 1); - assertRSQLQuery(RolloutGroupFields.ID.name() + "=out=(" + rolloutGroupId + ")", 3); + assertRSQLQuery(RolloutGroupFields.ID.name() + "==" + -1, 0); + assertRSQLQuery(RolloutGroupFields.ID.name() + "!=" + -1, 4); + + // Not supported for numbers + if (Database.POSTGRESQL.equals(getDatabase())) { + return; + } + + assertRSQLQuery(RolloutGroupFields.ID.name() + "==*", 4); + assertRSQLQuery(RolloutGroupFields.ID.name() + "==noexist*", 0); + assertRSQLQuery(RolloutGroupFields.ID.name() + "=in=(" + rolloutGroupId + ",10000000)", 1); + assertRSQLQuery(RolloutGroupFields.ID.name() + "=out=(" + rolloutGroupId + ",10000000)", 2); } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java index d6b9d8048..87c9c8597 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleFieldTest.java @@ -20,6 +20,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.orm.jpa.vendor.Database; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -29,10 +30,12 @@ import io.qameta.allure.Story; @Story("RSQL filter software module") public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest { + private SoftwareModule ah; + @Before public void setupBeforeTest() { - final SoftwareModule ah = softwareModuleManagement.create(entityFactory.softwareModule().create().type(appType) - .name("agent-hub").version("1.0.1").description("agent-hub")); + ah = softwareModuleManagement.create(entityFactory.softwareModule().create().type(appType).name("agent-hub") + .version("1.0.1").description("agent-hub")); softwareModuleManagement.create(entityFactory.softwareModule().create().type(runtimeType).name("oracle-jre") .version("1.7.2").description("aa")); softwareModuleManagement.create( @@ -55,7 +58,20 @@ public class RSQLSoftwareModuleFieldTest extends AbstractJpaIntegrationTest { @Test @Description("Test filter software module by id") public void testFilterByParameterId() { + assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + ah.getId(), 1); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + ah.getId(), 4); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "==" + -1, 0); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "!=" + -1, 5); + + // Not supported for numbers + if (Database.POSTGRESQL.equals(getDatabase())) { + return; + } + assertRSQLQuery(SoftwareModuleFields.ID.name() + "==*", 5); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "==noexist*", 0); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "=in=(" + ah.getId() + ",1000000)", 1); + assertRSQLQuery(SoftwareModuleFields.ID.name() + "=out=(" + ah.getId() + ",1000000)", 4); } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleTypeFieldsTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleTypeFieldsTest.java index 065600abd..365086d89 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleTypeFieldsTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLSoftwareModuleTypeFieldsTest.java @@ -17,6 +17,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.junit.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.orm.jpa.vendor.Database; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -29,7 +30,20 @@ public class RSQLSoftwareModuleTypeFieldsTest extends AbstractJpaIntegrationTest @Test @Description("Test filter software module test type by id") public void testFilterByParameterId() { + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==" + osType.getId(), 1); + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "!=" + osType.getId(), 2); + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==" + -1, 0); + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "!=" + -1, 3); + + // Not supported for numbers + if (Database.POSTGRESQL.equals(getDatabase())) { + return; + } + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==*", 3); + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "==noexist*", 0); + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "=in=(" + osType.getId() + ",1000000)", 1); + assertRSQLQuery(SoftwareModuleTypeFields.ID.name() + "=out=(" + osType.getId() + ",1000000)", 2); } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java index cb265d7db..d1d69b21b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLTargetFilterQueryFieldsTest.java @@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.test.util.TestdataFactory; import org.junit.Before; import org.junit.Test; import org.springframework.data.domain.Page; +import org.springframework.orm.jpa.vendor.Database; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -54,7 +55,21 @@ public class RSQLTargetFilterQueryFieldsTest extends AbstractJpaIntegrationTest @Test @Description("Test filter target filter query by id") public void testFilterByParameterId() { + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==" + filter1.getId(), 1); + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "!=" + filter1.getId(), 2); + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==" + -1, 0); + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "!=" + -1, 3); + + // Not supported for numbers + if (Database.POSTGRESQL.equals(getDatabase())) { + return; + } + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==*", 3); + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "==noexist*", 0); + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "=in=(" + filter1.getId() + ",10000000)", 1); + assertRSQLQuery(TargetFilterQueryFields.ID.name() + "=out=(" + filter1.getId() + ",10000000)", 2); + } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtilityTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtilityTest.java index f122db806..13adf70a8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtilityTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/rsql/RSQLUtilityTest.java @@ -196,8 +196,7 @@ public class RSQLUtilityTest { when(criteriaBuilderMock.upper(eq(pathOfString(baseSoftwareModuleRootMock)))) .thenReturn(pathOfString(baseSoftwareModuleRootMock)); when(criteriaBuilderMock.like(any(Expression.class), anyString(), eq('\\'))).thenReturn(mock(Predicate.class)); - when(criteriaBuilderMock. greaterThanOrEqualTo(any(Expression.class), any(String.class))) - .thenReturn(mock(Predicate.class)); + when(criteriaBuilderMock.equal(any(Expression.class), any(String.class))).thenReturn(mock(Predicate.class)); // test RSQLUtility.parse(correctRsql, SoftwareModuleFields.class, null, testDb).toPredicate(baseSoftwareModuleRootMock, diff --git a/hawkbit-repository/hawkbit-repository-test/pom.xml b/hawkbit-repository/hawkbit-repository-test/pom.xml index 9ac0cf751..8ee1c6e45 100644 --- a/hawkbit-repository/hawkbit-repository-test/pom.xml +++ b/hawkbit-repository/hawkbit-repository-test/pom.xml @@ -51,6 +51,10 @@ com.microsoft.sqlserver mssql-jdbc + + org.postgresql + postgresql + diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java index e2c74b99d..71e872d79 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java @@ -107,7 +107,8 @@ import com.google.common.io.Files; // test execution. So, the order execution between EventVerifier and Cleanup is // important! @TestExecutionListeners(inheritListeners = true, listeners = { EventVerifier.class, CleanupTestExecutionListener.class, - MySqlTestDatabase.class, MsSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS) + MySqlTestDatabase.class, MsSqlTestDatabase.class, + PostgreSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS) @TestPropertySource(properties = "spring.main.allow-bean-definition-overriding=true") public abstract class AbstractIntegrationTest { private static final Logger LOG = LoggerFactory.getLogger(AbstractIntegrationTest.class); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractSqlTestDatabase.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractSqlTestDatabase.java new file mode 100644 index 000000000..e8bd992a6 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractSqlTestDatabase.java @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2020 Microsoft and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.test.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.support.AbstractTestExecutionListener; + +/** + * A {@link TestExecutionListener} for creating and dropping MySql schemas if + * tests are setup with MySql. + */ +public abstract class AbstractSqlTestDatabase extends AbstractTestExecutionListener { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractSqlTestDatabase.class); + protected String schemaName; + protected String uri; + protected String username; + protected String password; + + @Override + public void beforeTestClass(final TestContext testContext) throws Exception { + if (isRunningWithSql()) { + LOG.info("Setting up database for test class {}", testContext.getTestClass().getName()); + this.username = System.getProperty("spring.datasource.username"); + this.password = System.getProperty("spring.datasource.password"); + this.uri = System.getProperty("spring.datasource.url"); + createSchemaUri(); + createSchema(); + } + } + + @Override + public void afterTestClass(final TestContext testContext) throws Exception { + if (isRunningWithSql()) { + dropSchema(); + } + } + + protected abstract void createSchemaUri(); + + protected abstract boolean isRunningWithSql(); + + protected abstract void createSchema(); + + protected abstract void dropSchema(); +} diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MsSqlTestDatabase.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MsSqlTestDatabase.java index 4b9ca4930..dd4bbdd6e 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MsSqlTestDatabase.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MsSqlTestDatabase.java @@ -16,53 +16,31 @@ import java.sql.SQLException; import org.apache.commons.lang3.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; -import org.springframework.test.context.support.AbstractTestExecutionListener; /** * A {@link TestExecutionListener} for creating and dropping MS SQL Server * schemas if tests are setup with MS SQL Server. */ -public class MsSqlTestDatabase extends AbstractTestExecutionListener { +public class MsSqlTestDatabase extends AbstractSqlTestDatabase { private static final Logger LOG = LoggerFactory.getLogger(MsSqlTestDatabase.class); - private String schemaName; - private String uri; - private String username; - private String password; @Override - public void beforeTestClass(final TestContext testContext) throws Exception { - if (isRunningWithMsSql()) { - LOG.info("Setting up mysql schema for test class {}", testContext.getTestClass().getName()); - this.username = System.getProperty("spring.datasource.username"); - this.password = System.getProperty("spring.datasource.password"); - this.uri = System.getProperty("spring.datasource.url"); - createSchemaUri(); - createSchema(); - } - } - - @Override - public void afterTestClass(final TestContext testContext) throws Exception { - if (isRunningWithMsSql()) { - dropSchema(); - } - } - - private void createSchemaUri() { + protected void createSchemaUri() { schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10); this.uri = this.uri.substring(0, uri.indexOf(';')); System.setProperty("spring.datasource.url", uri + ";database=" + schemaName); } - private static boolean isRunningWithMsSql() { + @Override + protected boolean isRunningWithSql() { return "SQL_SERVER".equals(System.getProperty("spring.jpa.database")); } - private void createSchema() { + @Override + protected void createSchema() { try (Connection connection = DriverManager.getConnection(uri, username, password)) { try (PreparedStatement statement = connection.prepareStatement("CREATE DATABASE " + schemaName + ";")) { LOG.info("Creating schema {} on uri {}", schemaName, uri); @@ -75,7 +53,8 @@ public class MsSqlTestDatabase extends AbstractTestExecutionListener { } - private void dropSchema() { + @Override + protected void dropSchema() { try (Connection connection = DriverManager.getConnection(uri, username, password)) { // Needed to avoid the DROP is rejected with "database still in use" try (PreparedStatement statement = connection diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MySqlTestDatabase.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MySqlTestDatabase.java index db62572d9..6ebdc0337 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MySqlTestDatabase.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/MySqlTestDatabase.java @@ -16,53 +16,31 @@ import java.sql.SQLException; import org.apache.commons.lang3.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; -import org.springframework.test.context.support.AbstractTestExecutionListener; /** * A {@link TestExecutionListener} for creating and dropping MySql schemas if * tests are setup with MySql. */ -public class MySqlTestDatabase extends AbstractTestExecutionListener { +public class MySqlTestDatabase extends AbstractSqlTestDatabase { private static final Logger LOG = LoggerFactory.getLogger(MySqlTestDatabase.class); - private String schemaName; - private String uri; - private String username; - private String password; @Override - public void beforeTestClass(final TestContext testContext) throws Exception { - if (isRunningWithMySql()) { - LOG.info("Setting up mysql schema for test class {}", testContext.getTestClass().getName()); - this.username = System.getProperty("spring.datasource.username"); - this.password = System.getProperty("spring.datasource.password"); - this.uri = System.getProperty("spring.datasource.url"); - createSchemaUri(); - createSchema(); - } - } - - @Override - public void afterTestClass(final TestContext testContext) throws Exception { - if (isRunningWithMySql()) { - dropSchema(); - } - } - - private void createSchemaUri() { + protected void createSchemaUri() { schemaName = "SP" + RandomStringUtils.randomAlphanumeric(10); this.uri = this.uri.substring(0, uri.lastIndexOf('/') + 1); System.setProperty("spring.datasource.url", uri + schemaName); } - private boolean isRunningWithMySql() { + @Override + protected boolean isRunningWithSql() { return "MYSQL".equals(System.getProperty("spring.jpa.database")); } - private void createSchema() { + @Override + protected void createSchema() { try (Connection connection = DriverManager.getConnection(uri, username, password)) { try (PreparedStatement statement = connection.prepareStatement("CREATE SCHEMA " + schemaName + ";")) { LOG.info("Creating schema {} on uri {}", schemaName, uri); @@ -75,7 +53,8 @@ public class MySqlTestDatabase extends AbstractTestExecutionListener { } - private void dropSchema() { + @Override + protected void dropSchema() { try (Connection connection = DriverManager.getConnection(uri, username, password)) { try (PreparedStatement statement = connection.prepareStatement("DROP SCHEMA " + schemaName + ";")) { LOG.info("Dropping schema {} on uri {}", schemaName, uri); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/PostgreSqlTestDatabase.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/PostgreSqlTestDatabase.java new file mode 100644 index 000000000..5789aad3a --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/PostgreSqlTestDatabase.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2020 Microsoft and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.test.util; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.apache.commons.lang3.RandomStringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.test.context.TestExecutionListener; + +/** + * A {@link TestExecutionListener} for creating and dropping MySql schemas if + * tests are setup with MySql. + */ +public class PostgreSqlTestDatabase extends AbstractSqlTestDatabase { + + private static final Logger LOG = LoggerFactory.getLogger(PostgreSqlTestDatabase.class); + + @Override + protected void createSchemaUri() { + schemaName = "sp" + RandomStringUtils.randomAlphanumeric(10).toLowerCase(); + this.uri = this.uri.substring(0, uri.indexOf('?')); + + System.setProperty("spring.datasource.url", uri + "?currentSchema=" + schemaName); + } + + @Override + protected boolean isRunningWithSql() { + return "POSTGRESQL".equals(System.getProperty("spring.jpa.database")); + } + + @Override + protected void createSchema() { + try (Connection connection = DriverManager.getConnection(uri, username, password)) { + try (PreparedStatement statement = connection.prepareStatement("CREATE schema " + schemaName + ";")) { + LOG.info("Creating schema {} on uri {}", schemaName, uri); + statement.execute(); + LOG.info("Created schema {} on uri {}", schemaName, uri); + } + } catch (final SQLException e) { + LOG.error("Schema creation failed!", e); + } + + } + + @Override + protected void dropSchema() { + try (Connection connection = DriverManager.getConnection(uri, username, password)) { + try (PreparedStatement statement = connection.prepareStatement("DROP schema " + schemaName + " CASCADE;")) { + LOG.info("Dropping schema {} on uri {}", schemaName, uri); + statement.execute(); + LOG.info("Dropped schema {} on uri {}", schemaName, uri); + } + } catch (final SQLException e) { + LOG.error("Schema drop failed!", e); + } + } +} diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java index e794f12d5..cd396f04f 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java @@ -64,12 +64,15 @@ import org.eclipse.hawkbit.util.IpUtil; import org.json.JSONArray; import org.json.JSONObject; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort.Direction; import org.springframework.hateoas.MediaTypes; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.orm.jpa.vendor.Database; import org.springframework.test.web.servlet.MvcResult; import com.jayway.jsonpath.JsonPath; @@ -113,6 +116,9 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest private static final String JSON_PATH_DESCRIPTION = JSON_PATH_ROOT + JSON_PATH_FIELD_DESCRIPTION; private static final String JSON_PATH_LAST_REQUEST_AT = JSON_PATH_ROOT + JSON_PATH_FIELD_LAST_REQUEST_AT; + @Autowired + private JpaProperties jpaProperties; + @Test @Description("Ensures that actions list is in exptected order.") public void getActionStatusReturnsCorrectType() throws Exception { @@ -2015,11 +2021,22 @@ public class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest assignDistributionSet(dsId, targetId, customWeightHigh); assignDistributionSet(dsId, targetId, customWeightLow); - mvc.perform(get("/rest/v1/targets/{targetId}/actions", targetId) - .param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "WEIGHT:ASC")).andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()).andExpect(jsonPath("content.[0].weight").doesNotExist()) - .andExpect(jsonPath("content.[1].weight", equalTo(customWeightLow))) - .andExpect(jsonPath("content.[2].weight", equalTo(customWeightHigh))); + // POSTGRESQL sets null values at the end, not the beginning + if (Database.POSTGRESQL.equals(jpaProperties.getDatabase())) { + mvc.perform(get("/rest/v1/targets/{targetId}/actions", targetId) + .param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "WEIGHT:ASC")) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(jsonPath("content.[0].weight", equalTo(customWeightLow))) + .andExpect(jsonPath("content.[1].weight", equalTo(customWeightHigh))) + .andExpect(jsonPath("content.[2].weight").doesNotExist()); + } else { + mvc.perform(get("/rest/v1/targets/{targetId}/actions", targetId) + .param(MgmtRestConstants.REQUEST_PARAMETER_SORTING, "WEIGHT:ASC")) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) + .andExpect(jsonPath("content.[0].weight").doesNotExist()) + .andExpect(jsonPath("content.[1].weight", equalTo(customWeightLow))) + .andExpect(jsonPath("content.[2].weight", equalTo(customWeightHigh))); + } } diff --git a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java index 42fe538d8..d7e7e27a7 100644 --- a/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java +++ b/hawkbit-runtime/hawkbit-update-server/src/test/java/org/eclipse/hawkbit/app/CorsTest.java @@ -15,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.test.util.MsSqlTestDatabase; import org.eclipse.hawkbit.repository.test.util.MySqlTestDatabase; +import org.eclipse.hawkbit.repository.test.util.PostgreSqlTestDatabase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -23,30 +24,31 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.test.context.support.WithUserDetails; import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.TestExecutionListeners.MergeMode; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import org.springframework.test.context.TestExecutionListeners.MergeMode; import io.qameta.allure.Description; import io.qameta.allure.Feature; import io.qameta.allure.Story; @RunWith(SpringRunner.class) -@SpringBootTest(properties = {"hawkbit.dmf.rabbitmq.enabled=false", "hawkbit.server.security.cors.enabled=true", - "hawkbit.server.security.cors.allowedOrigins=" + CorsTest.ALLOWED_ORIGIN_FIRST + "," + CorsTest.ALLOWED_ORIGIN_SECOND}) -@TestExecutionListeners(listeners = { MySqlTestDatabase.class, MsSqlTestDatabase.class }, - mergeMode = MergeMode.MERGE_WITH_DEFAULTS) +@SpringBootTest(properties = { "hawkbit.dmf.rabbitmq.enabled=false", "hawkbit.server.security.cors.enabled=true", + "hawkbit.server.security.cors.allowedOrigins=" + CorsTest.ALLOWED_ORIGIN_FIRST + "," + + CorsTest.ALLOWED_ORIGIN_SECOND }) +@TestExecutionListeners(listeners = { MySqlTestDatabase.class, MsSqlTestDatabase.class, + PostgreSqlTestDatabase.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS) @Feature("Integration Test - Security") @Story("CORS") public class CorsTest { final static String ALLOWED_ORIGIN_FIRST = "http://test.first.origin"; final static String ALLOWED_ORIGIN_SECOND = "http://test.second.origin"; - + private final static String INVALID_ORIGIN = "http://test.invalid.origin"; private final static String INVALID_CORS_REQUEST = "Invalid CORS request"; @@ -73,15 +75,16 @@ public class CorsTest { .andExpect(status().isForbidden()).andReturn().getResponse().getContentAsString(); assertThat(invalidOriginResponseBody).isEqualTo(INVALID_CORS_REQUEST); - final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin(MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST) - .andExpect(status().isForbidden()).andReturn().getResponse().getContentAsString(); + final String invalidCorsUrlResponseBody = performOptionsRequestToUrlWithOrigin( + MgmtRestConstants.BASE_SYSTEM_MAPPING, ALLOWED_ORIGIN_FIRST).andExpect(status().isForbidden()) + .andReturn().getResponse().getContentAsString(); assertThat(invalidCorsUrlResponseBody).isEqualTo(INVALID_CORS_REQUEST); } private ResultActions performOptionsRequestToRestWithOrigin(final String origin) throws Exception { return performOptionsRequestToUrlWithOrigin(MgmtRestConstants.BASE_V1_REQUEST_MAPPING, origin); } - + private ResultActions performOptionsRequestToUrlWithOrigin(final String url, final String origin) throws Exception { return mvc.perform(options(url).header("Access-Control-Request-Method", "GET").header("Origin", origin)); } diff --git a/licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_20.txt b/licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_20.txt new file mode 100644 index 000000000..fc2ac22c3 --- /dev/null +++ b/licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_20.txt @@ -0,0 +1,6 @@ +Copyright (c) 2020 Microsoft and others. + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html diff --git a/pom.xml b/pom.xml index 0f2edb295..1cca10851 100644 --- a/pom.xml +++ b/pom.xml @@ -167,7 +167,7 @@ 9.1.3 1.11.2 2.13.6 - 2.7.7 + 2.7.3 1.1.8 25.0-jre 2.2.4 @@ -332,11 +332,14 @@ licenses/LICENSE_HEADER_TEMPLATE_BOSCH_20.txt licenses/LICENSE_HEADER_TEMPLATE_BOSCH_21.txt licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_18.txt + licenses/LICENSE_HEADER_TEMPLATE_MICROSOFT_20.txt licenses/LICENSE_HEADER_TEMPLATE_DEVOLO_19.txt licenses/LICENSE_HEADER_TEMPLATE_KIWIGRID_19.txt licenses/LICENSE_HEADER_TEMPLATE_ENAPTER.txt + .azure-pipelines/* + .devcontainer/* **/banner.txt **/helm/** **/README From ba81ae6fb4efa472179e03517c98b5f1bd3316b5 Mon Sep 17 00:00:00 2001 From: Dominic Schabel Date: Thu, 14 Jan 2021 13:44:22 +0100 Subject: [PATCH 09/10] Fix introduced issues Signed-off-by: Dominic Schabel --- .../repository/jpa/JpaSystemManagement.java | 33 ++++++++++++------- .../RepositoryApplicationConfiguration.java | 12 +++---- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java index baebea565..59ada5c26 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaSystemManagement.java @@ -61,6 +61,7 @@ import org.springframework.validation.annotation.Validated; @Transactional(readOnly = true) @Validated public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, SystemManagement { + private static final Logger LOGGER = LoggerFactory.getLogger(JpaSystemManagement.class); private static final int MAX_TENANTS_QUERY = 1000; @@ -125,16 +126,28 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst @Autowired private ArtifactRepository artifactRepository; - @Autowired - private JpaProperties properties; + private final String countArtifactQuery; + private final String countSoftwareModulesQuery; + + /** + * Constructor. + * + * @param properties + * properties to get the underlying database + */ + public JpaSystemManagement(final JpaProperties properties) { + + final String isDeleted = isPostgreSql(properties) ? "false" : "0"; + countArtifactQuery = "SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " + + isDeleted; + countSoftwareModulesQuery = "select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " + + isDeleted; + } @Override public SystemUsageReport getSystemUsageStatistics() { - final Number count = (Number) entityManager.createNativeQuery( - "select SUM(file_size) from sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " - + (isPostgreSql(properties) ? "false" : "0")) - .getSingleResult(); + final Number count = (Number) entityManager.createNativeQuery(countSoftwareModulesQuery).getSingleResult(); long sumOfArtifacts = 0; if (count != null) { @@ -146,10 +159,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst final long targets = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target") .getSingleResult()).longValue(); - final long artifacts = ((Number) entityManager.createNativeQuery( - "SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_base_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " - + (isPostgreSql(properties) ? "false" : "0")) - .getSingleResult()).longValue(); + final long artifacts = ((Number) entityManager.createNativeQuery(countArtifactQuery).getSingleResult()) + .longValue(); final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action") .getSingleResult()).longValue(); @@ -159,7 +170,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst } private static boolean isPostgreSql(final JpaProperties properties) { - return Database.POSTGRESQL.equals(properties.getDatabase()); + return Database.POSTGRESQL == properties.getDatabase(); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index 91e36f5c0..34b3f450a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -15,6 +15,8 @@ import java.util.concurrent.ScheduledExecutorService; import javax.persistence.EntityManager; import javax.sql.DataSource; +import com.google.common.collect.Maps; + import org.eclipse.hawkbit.artifact.repository.ArtifactRepository; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; @@ -124,8 +126,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; -import com.google.common.collect.Maps; - /** * General configuration for hawkBit's Repository. * @@ -426,8 +426,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { */ @Bean @ConditionalOnMissingBean - SystemManagement systemManagement() { - return new JpaSystemManagement(); + SystemManagement systemManagement(final JpaProperties properties) { + return new JpaSystemManagement(properties); } /** @@ -760,8 +760,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { @Bean @ConditionalOnMissingBean AutoAssignExecutor autoAssignExecutor(final TargetFilterQueryManagement targetFilterQueryManagement, - final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, - final PlatformTransactionManager transactionManager) { + final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, + final PlatformTransactionManager transactionManager) { return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement, transactionManager); } From 70ee9472bf7b4e06d8407d79f118d903d68d0bc3 Mon Sep 17 00:00:00 2001 From: Stefan Schake Date: Thu, 14 Jan 2021 15:52:39 +0100 Subject: [PATCH 10/10] Fixup force delete action documentation (#1042) This was doing a GET request and didn't even specify the action ID. Signed-off-by: Stefan Schake --- .../src/main/asciidoc/targets-api-guide.adoc | 4 ++-- .../TargetResourceDocumentationTest.java | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/targets-api-guide.adoc b/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/targets-api-guide.adoc index ecfbf4254..81a3b3937 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/targets-api-guide.adoc +++ b/hawkbit-rest/hawkbit-rest-docs/src/main/asciidoc/targets-api-guide.adoc @@ -373,11 +373,11 @@ include::{snippets}/targets/delete-action-from-target/path-parameters.adoc[] ==== Request query parameter -include::{snippets}/targets/delete-actions-from-target-with-parameters/request-parameters.adoc[] +include::{snippets}/targets/delete-action-from-target-with-parameters/request-parameters.adoc[] ==== Request parameter example -include::{snippets}/targets/delete-actions-from-target-with-parameters/http-request.adoc[] +include::{snippets}/targets/delete-action-from-target-with-parameters/http-request.adoc[] === Response (Status 204) diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java index 189b801ad..937e916de 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java @@ -308,13 +308,17 @@ public class TargetResourceDocumentationTest extends AbstractApiRestDocumentatio } @Test - @Description("Handles the GET request of retrieving all targets within SP based by parameter. Required Permission: READ_TARGET.") - public void deleteActionsFromTargetWithParameters() throws Exception { - generateActionForTarget(targetId); - - mockMvc.perform(get(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/" + targetId + "/" - + MgmtRestConstants.TARGET_V1_ACTIONS + "?force=true")).andExpect(status().isOk()) - .andDo(MockMvcResultPrinter.print()).andDo(this.document.document( + @Description("Optionally force quits an active action, only active actions can be deleted. Required Permission: UPDATE_TARGET.") + public void deleteActionFromTargetWithParameters() throws Exception { + final Action action = generateActionForTarget(targetId, false); + deploymentManagement.cancelAction(action.getId()); + + mockMvc.perform(delete(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/{targetId}/" + + MgmtRestConstants.TARGET_V1_ACTIONS + "/{actionId}?force=true", targetId, action.getId())) + .andExpect(status().isNoContent()).andDo(MockMvcResultPrinter.print()) + .andDo(this.document.document( + pathParameters(parameterWithName("targetId").description(ApiModelPropertiesGeneric.ITEM_ID), + parameterWithName("actionId").description(ApiModelPropertiesGeneric.ITEM_ID)), requestParameters(parameterWithName("force").description(MgmtApiModelProperties.FORCE)))); }